Paginate notifications

This commit is contained in:
dtonon 2026-09-23 10:42:26 +02:00
parent 8695d3e40e
commit 982c20d1fa
3 changed files with 104 additions and 18 deletions

View file

@ -6,14 +6,18 @@ import {
fetchThreadTitles,
parseNotification,
type Notification,
type NotificationPage,
} from "$lib/forum/notifications";
export type { Notification };
const SEEN_KEY = "notifications_seen";
const scope = () => (MODE === "simple" ? GROUP_ID : undefined);
let pubkey = $state<string | null>(null);
let items = $state<Notification[] | null>(null);
let done = $state(true);
let loadingMore = $state(false);
let seenAt = $state(0);
let liveSub: { close(): void } | null = null;
let req = 0; // supersedes an in-flight load on login/logout
@ -33,6 +37,12 @@ export const notificationsStore = {
get items() {
return items;
},
get done() {
return done;
},
get loadingMore() {
return loadingMore;
},
get seenAt() {
return seenAt;
},
@ -55,23 +65,24 @@ export async function startNotifications(pk: string) {
pubkey = pk;
seenAt = readSeen(pk);
const current = ++req;
const scope = MODE === "simple" ? GROUP_ID : undefined;
const groupId = scope();
let result: Notification[] = [];
let result: NotificationPage = { items: [], done: true };
try {
result = await fetchNotifications(queryForum, pk, scope);
result = await fetchNotifications(queryForum, pk, { groupId });
} catch (e) {
console.error("[notifications] load failed", e);
}
if (current !== req) return;
items = result;
for (const n of result) ensureProfile(n.pubkey);
items = result.items;
done = result.done;
for (const n of result.items) ensureProfile(n.pubkey);
liveSub = subscribeForum(
{
kinds: [1111, 9],
"#p": [pk],
...(scope ? { "#h": [scope] } : {}),
...(groupId ? { "#h": [groupId] } : {}),
since: Math.floor(Date.now() / 1000),
},
{
@ -97,9 +108,35 @@ export function stopNotifications() {
liveSub = null;
pubkey = null;
items = null;
done = true;
loadingMore = false;
seenAt = 0;
}
// Append the next page of older notifications.
export async function loadMoreNotifications() {
const pk = pubkey;
const current = req;
if (!pk || !items || done || loadingMore) return;
loadingMore = true;
const oldest = items[items.length - 1];
try {
const next = await fetchNotifications(queryForum, pk, {
groupId: scope(),
until: oldest?.createdAt,
exclude: new Set(items.map((n) => n.id)),
});
if (current !== req || !items) return;
items = [...items, ...next.items];
done = next.done;
for (const n of next.items) ensureProfile(n.pubkey);
} catch (e) {
console.error("[notifications] failed to load more", e);
} finally {
if (current === req) loadingMore = false;
}
}
// Everything loaded so far counts as read.
export function markNotificationsSeen() {
if (!pubkey) return;