diff --git a/src/lib/components/publications/Publication.svelte b/src/lib/components/publications/Publication.svelte index c751f5d..52489e5 100644 --- a/src/lib/components/publications/Publication.svelte +++ b/src/lib/components/publications/Publication.svelte @@ -182,7 +182,7 @@ }, { threshold: 0.5 }, ); - loadMore(8); + loadMore(12); return () => { observer.disconnect(); @@ -210,6 +210,11 @@ depth={2} onSectionFocused={(address: string) => publicationTree.setBookmark(address)} + onLoadMore={() => { + if (!isLoading && !isDone) { + loadMore(4); + } + }} /> {/if} diff --git a/src/lib/components/publications/TableOfContents.svelte b/src/lib/components/publications/TableOfContents.svelte index 0a16108..a2fc748 100644 --- a/src/lib/components/publications/TableOfContents.svelte +++ b/src/lib/components/publications/TableOfContents.svelte @@ -12,10 +12,11 @@ import Self from "./TableOfContents.svelte"; import { onMount, onDestroy } from "svelte"; - let { depth, onSectionFocused } = $props<{ + let { depth, onSectionFocused, onLoadMore } = $props<{ rootAddress: string; depth: number; onSectionFocused?: (address: string) => void; + onLoadMore?: () => void; }>(); let toc = getContext("toc") as TableOfContents; @@ -58,6 +59,14 @@ } onSectionFocused?.(address); + + // Check if this is the last entry and trigger loading more events + const currentEntries = entries; + const lastEntry = currentEntries[currentEntries.length - 1]; + if (lastEntry && lastEntry.address === address) { + console.debug('[TableOfContents] Last entry clicked, triggering load more'); + onLoadMore?.(); + } } // Check if an entry is currently visible @@ -145,27 +154,28 @@ - {#each entries as entry} + {#each entries as entry, index} {@const address = entry.address} {@const expanded = toc.expandedMap.get(address) ?? false} {@const isLeaf = toc.leaves.has(address)} {@const isVisible = isEntryVisible(address)} + {@const isLastEntry = index === entries.length - 1} {#if isLeaf} handleSectionClick(address)} /> {:else} {@const childDepth = depth + 1} expanded, (open) => setEntryExpanded(address, open)} > - + {/if} {/each} diff --git a/src/lib/data_structures/publication_tree.ts b/src/lib/data_structures/publication_tree.ts index f87b1c3..8b3a8f3 100644 --- a/src/lib/data_structures/publication_tree.ts +++ b/src/lib/data_structures/publication_tree.ts @@ -45,6 +45,11 @@ export class PublicationTree implements AsyncIterable { * A map of addresses in the tree to their corresponding events. */ #events: Map; + + /** + * Simple cache for fetched events to avoid re-fetching. + */ + #eventCache: Map = new Map(); /** * An ordered list of the addresses of the leaves of the tree. @@ -589,13 +594,11 @@ export class PublicationTree implements AsyncIterable { } // Augment the tree with the children of the current event. - for (const childAddress of currentChildAddresses) { - if (this.#nodes.has(childAddress)) { - continue; - } - - await this.#addNode(childAddress, currentNode!); - } + const childPromises = currentChildAddresses + .filter(childAddress => !this.#nodes.has(childAddress)) + .map(childAddress => this.#addNode(childAddress, currentNode!)); + + await Promise.all(childPromises); // Push the popped address's children onto the stack for the next iteration. while (currentChildAddresses.length > 0) { @@ -630,12 +633,23 @@ export class PublicationTree implements AsyncIterable { address: string, parentNode: PublicationTreeNode, ): Promise { - const [kind, pubkey, dTag] = address.split(":"); - const event = await this.#ndk.fetchEvent({ - kinds: [parseInt(kind)], - authors: [pubkey], - "#d": [dTag], - }); + // Check cache first + let event = this.#eventCache.get(address); + + if (!event) { + const [kind, pubkey, dTag] = address.split(":"); + const fetchedEvent = await this.#ndk.fetchEvent({ + kinds: [parseInt(kind)], + authors: [pubkey], + "#d": [dTag], + }); + + // Cache the event if found + if (fetchedEvent) { + this.#eventCache.set(address, fetchedEvent); + event = fetchedEvent; + } + } if (!event) { console.debug( @@ -665,9 +679,10 @@ export class PublicationTree implements AsyncIterable { children: [], }; - for (const address of childAddresses) { - this.addEventByAddress(address, event); - } + const childPromises = childAddresses.map(address => + this.addEventByAddress(address, event) + ); + await Promise.all(childPromises); this.#nodeResolvedObservers.forEach((observer) => observer(address));