diff --git a/src/lib/components/Publication.svelte b/src/lib/components/Publication.svelte index 4ff9c7c..167cf67 100644 --- a/src/lib/components/Publication.svelte +++ b/src/lib/components/Publication.svelte @@ -9,25 +9,64 @@ TextPlaceholder, Tooltip, } from "flowbite-svelte"; - import { onMount } from "svelte"; + import { getContext, onMount } from "svelte"; import { BookOutline } from "flowbite-svelte-icons"; - import Preview from "./Preview.svelte"; - import { pharosInstance } from "$lib/parser"; import { page } from "$app/state"; - import { ndkInstance } from "$lib/ndk"; import type { NDKEvent } from "@nostr-dev-kit/ndk"; import PublicationSection from "./PublicationSection.svelte"; + import type { PublicationTree } from "$lib/data_structures/publication_tree"; - let { rootId, publicationType, indexEvent } = $props<{ - rootId: string, + let { rootAddress, publicationType, indexEvent } = $props<{ + rootAddress: string, publicationType: string, indexEvent: NDKEvent }>(); - if (rootId !== $pharosInstance.getRootIndexId()) { - console.error("Root ID does not match parser root index ID"); + const publicationTree = getContext('publicationTree') as PublicationTree; + + // #region Loading + + // TODO: Test load handling. + + let leaves = $state([]); + let isLoading = $state(false); + let lastElementRef = $state(null); + + let observer: IntersectionObserver; + + async function loadMore(count: number) { + isLoading = true; + + for (let i = 0; i < count; i++) { + const nextItem = await publicationTree.next(); + if (nextItem.done) { + break; + } + leaves.push(nextItem.value); + } + + isLoading = false; + } + + function setLastElementRef(el: HTMLElement, i: number) { + if (i === leaves.length - 1) { + lastElementRef = el; + } } + $effect(() => { + if (!lastElementRef) { + return; + } + + observer.observe(lastElementRef!); + return () => observer.unobserve(lastElementRef!); + }); + + // #endregion + + // #region ToC + const tocBreakpoint = 1140; let activeHash = $state(page.url.hash); @@ -82,21 +121,33 @@ } } + // #endregion + onMount(() => { // Always check whether the TOC sidebar should be visible. setTocVisibilityOnResize(); - window.addEventListener("hashchange", scrollToElementWithOffset); // Also handle the case where the user lands on the page with a hash in the URL scrollToElementWithOffset(); - window.addEventListener("resize", setTocVisibilityOnResize); window.addEventListener("click", hideTocOnClick); + // Set up the intersection observer. + observer = new IntersectionObserver((entries) => { + entries.forEach((entry) => { + if (entry.isIntersecting && !isLoading) { + loadMore(8); + } + }); + }, { threshold: 0.5 }); + loadMore(16); + return () => { window.removeEventListener("hashchange", scrollToElementWithOffset); window.removeEventListener("resize", setTocVisibilityOnResize); window.removeEventListener("click", hideTocOnClick); + + observer.disconnect(); }; }); @@ -115,7 +166,7 @@ Show Table of Contents {/if} - +
- - + {#each leaves as leaf, i} + setLastElementRef(el, i)} + /> + {/each}