diff --git a/src/lib/components/Notifications.svelte b/src/lib/components/Notifications.svelte
index 6b84efc..490021e 100644
--- a/src/lib/components/Notifications.svelte
+++ b/src/lib/components/Notifications.svelte
@@ -7,12 +7,27 @@
import {
notificationsStore,
markNotificationsSeen,
+ loadMoreNotifications,
type Notification,
} from "$lib/notifications.svelte";
import { summarize } from "$lib/seo";
import * as nip19 from "@nostr/tools/nip19";
+ const PAGE = 5;
+
const items = $derived(notificationsStore.items);
+ // Visible slice; "Show more" reveals another page from what is loaded and
+ // fetches older ones once the loaded list runs out.
+ let shown = $state(PAGE);
+ const visible = $derived(items?.slice(0, shown) ?? null);
+ const hasMore = $derived(
+ !!items && (items.length > shown || !notificationsStore.done),
+ );
+
+ async function showMore() {
+ if (items && items.length <= shown) await loadMoreNotifications();
+ shown += PAGE;
+ }
// Unread threshold frozen when the list first shows, so new items keep their
// mark while on screen even though they are counted as seen right away.
@@ -20,7 +35,13 @@
$effect(() => {
if (!items) return;
- if (openedAt === null) openedAt = untrack(() => notificationsStore.seenAt);
+ if (openedAt === null) {
+ const seenAt = untrack(() => notificationsStore.seenAt);
+ openedAt = seenAt;
+ // Every new item is visible at once; the page size is only a floor
+ const unread = items.filter((n) => n.createdAt > seenAt).length;
+ shown = Math.max(PAGE, unread);
+ }
markNotificationsSeen();
});
@@ -86,7 +107,7 @@
{:else}
- {#each items as n (n.id)}
+ {#each visible ?? [] as n (n.id)}
{@const unread = n.createdAt > (openedAt ?? 0)}
{@const name = authorName(n.pubkey)}
{@const picture = authorPicture(n.pubkey)}
@@ -163,5 +184,18 @@
{/each}