Browse Source

Made ToC auto-expanding

master
silberengel 8 months ago
parent
commit
bd6d96ed62
  1. 7
      src/lib/components/publications/Publication.svelte
  2. 20
      src/lib/components/publications/TableOfContents.svelte
  3. 35
      src/lib/data_structures/publication_tree.ts

7
src/lib/components/publications/Publication.svelte

@ -182,7 +182,7 @@ @@ -182,7 +182,7 @@
},
{ threshold: 0.5 },
);
loadMore(8);
loadMore(12);
return () => {
observer.disconnect();
@ -210,6 +210,11 @@ @@ -210,6 +210,11 @@
depth={2}
onSectionFocused={(address: string) =>
publicationTree.setBookmark(address)}
onLoadMore={() => {
if (!isLoading && !isDone) {
loadMore(4);
}
}}
/>
</Sidebar>
{/if}

20
src/lib/components/publications/TableOfContents.svelte

@ -12,10 +12,11 @@ @@ -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 @@ @@ -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 @@ @@ -145,27 +154,28 @@
<!-- TODO: Figure out how to style indentations. -->
<!-- TODO: Make group title fonts the same as entry title fonts. -->
<SidebarGroup>
{#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}
<SidebarItem
label={entry.title}
href={`#${address}`}
spanClass="px-2 text-ellipsis"
class={isVisible ? "toc-highlight" : ""}
class={`${isVisible ? "toc-highlight" : ""} ${isLastEntry ? "pb-4" : ""}`}
onclick={() => handleSectionClick(address)}
/>
{:else}
{@const childDepth = depth + 1}
<SidebarDropdownWrapper
label={entry.title}
btnClass="flex items-center p-2 w-full font-normal text-gray-900 rounded-lg transition duration-75 group hover:bg-primary-50 dark:text-white dark:hover:bg-primary-800 {isVisible ? 'toc-highlight' : ''}"
btnClass="flex items-center p-2 w-full font-normal text-gray-900 rounded-lg transition duration-75 group hover:bg-primary-50 dark:text-white dark:hover:bg-primary-800 {isVisible ? 'toc-highlight' : ''} {isLastEntry ? 'pb-4' : ''}"
bind:isOpen={() => expanded, (open) => setEntryExpanded(address, open)}
>
<Self rootAddress={address} depth={childDepth} {onSectionFocused} />
<Self rootAddress={address} depth={childDepth} {onSectionFocused} {onLoadMore} />
</SidebarDropdownWrapper>
{/if}
{/each}

35
src/lib/data_structures/publication_tree.ts

@ -46,6 +46,11 @@ export class PublicationTree implements AsyncIterable<NDKEvent | null> { @@ -46,6 +46,11 @@ export class PublicationTree implements AsyncIterable<NDKEvent | null> {
*/
#events: Map<string, NDKEvent>;
/**
* Simple cache for fetched events to avoid re-fetching.
*/
#eventCache: Map<string, NDKEvent> = new Map();
/**
* An ordered list of the addresses of the leaves of the tree.
*/
@ -589,13 +594,11 @@ export class PublicationTree implements AsyncIterable<NDKEvent | null> { @@ -589,13 +594,11 @@ export class PublicationTree implements AsyncIterable<NDKEvent | null> {
}
// Augment the tree with the children of the current event.
for (const childAddress of currentChildAddresses) {
if (this.#nodes.has(childAddress)) {
continue;
}
const childPromises = currentChildAddresses
.filter(childAddress => !this.#nodes.has(childAddress))
.map(childAddress => this.#addNode(childAddress, currentNode!));
await 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,13 +633,24 @@ export class PublicationTree implements AsyncIterable<NDKEvent | null> { @@ -630,13 +633,24 @@ export class PublicationTree implements AsyncIterable<NDKEvent | null> {
address: string,
parentNode: PublicationTreeNode,
): Promise<PublicationTreeNode> {
// Check cache first
let event = this.#eventCache.get(address);
if (!event) {
const [kind, pubkey, dTag] = address.split(":");
const event = await this.#ndk.fetchEvent({
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(
`[PublicationTree] Event with address ${address} not found.`,
@ -665,9 +679,10 @@ export class PublicationTree implements AsyncIterable<NDKEvent | null> { @@ -665,9 +679,10 @@ export class PublicationTree implements AsyncIterable<NDKEvent | null> {
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));

Loading…
Cancel
Save