Browse Source

Begin to implement import on visibility for publication content

master
buttercat1791 11 months ago
parent
commit
46f61da01d
  1. 82
      src/lib/components/Publication.svelte
  2. 22
      src/lib/components/PublicationSection.svelte
  3. 2
      src/routes/publication/+page.svelte

82
src/lib/components/Publication.svelte

@ -9,25 +9,64 @@
TextPlaceholder, TextPlaceholder,
Tooltip, Tooltip,
} from "flowbite-svelte"; } from "flowbite-svelte";
import { onMount } from "svelte"; import { getContext, onMount } from "svelte";
import { BookOutline } from "flowbite-svelte-icons"; import { BookOutline } from "flowbite-svelte-icons";
import Preview from "./Preview.svelte";
import { pharosInstance } from "$lib/parser";
import { page } from "$app/state"; import { page } from "$app/state";
import { ndkInstance } from "$lib/ndk";
import type { NDKEvent } from "@nostr-dev-kit/ndk"; import type { NDKEvent } from "@nostr-dev-kit/ndk";
import PublicationSection from "./PublicationSection.svelte"; import PublicationSection from "./PublicationSection.svelte";
import type { PublicationTree } from "$lib/data_structures/publication_tree";
let { rootId, publicationType, indexEvent } = $props<{ let { rootAddress, publicationType, indexEvent } = $props<{
rootId: string, rootAddress: string,
publicationType: string, publicationType: string,
indexEvent: NDKEvent indexEvent: NDKEvent
}>(); }>();
if (rootId !== $pharosInstance.getRootIndexId()) { const publicationTree = getContext('publicationTree') as PublicationTree;
console.error("Root ID does not match parser root index ID");
// #region Loading
// TODO: Test load handling.
let leaves = $state<NDKEvent[]>([]);
let isLoading = $state<boolean>(false);
let lastElementRef = $state<HTMLElement | null>(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; const tocBreakpoint = 1140;
let activeHash = $state(page.url.hash); let activeHash = $state(page.url.hash);
@ -82,21 +121,33 @@
} }
} }
// #endregion
onMount(() => { onMount(() => {
// Always check whether the TOC sidebar should be visible. // Always check whether the TOC sidebar should be visible.
setTocVisibilityOnResize(); setTocVisibilityOnResize();
window.addEventListener("hashchange", scrollToElementWithOffset); window.addEventListener("hashchange", scrollToElementWithOffset);
// Also handle the case where the user lands on the page with a hash in the URL // Also handle the case where the user lands on the page with a hash in the URL
scrollToElementWithOffset(); scrollToElementWithOffset();
window.addEventListener("resize", setTocVisibilityOnResize); window.addEventListener("resize", setTocVisibilityOnResize);
window.addEventListener("click", hideTocOnClick); 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 () => { return () => {
window.removeEventListener("hashchange", scrollToElementWithOffset); window.removeEventListener("hashchange", scrollToElementWithOffset);
window.removeEventListener("resize", setTocVisibilityOnResize); window.removeEventListener("resize", setTocVisibilityOnResize);
window.removeEventListener("click", hideTocOnClick); window.removeEventListener("click", hideTocOnClick);
observer.disconnect();
}; };
}); });
</script> </script>
@ -115,7 +166,7 @@
</Button> </Button>
<Tooltip>Show Table of Contents</Tooltip> <Tooltip>Show Table of Contents</Tooltip>
{/if} {/if}
<!-- TODO: Get TOC from parser. --> <!-- TODO: Use loader to build ToC. -->
<!-- {#if showToc} <!-- {#if showToc}
<Sidebar class='sidebar-leather fixed top-20 left-0 px-4 w-60' {activeHash}> <Sidebar class='sidebar-leather fixed top-20 left-0 px-4 w-60' {activeHash}>
<SidebarWrapper> <SidebarWrapper>
@ -132,8 +183,13 @@
</Sidebar> </Sidebar>
{/if} --> {/if} -->
<div class="flex flex-col space-y-4 max-w-2xl"> <div class="flex flex-col space-y-4 max-w-2xl">
<!-- TODO: Pass in the correct address for each section. --> {#each leaves as leaf, i}
<PublicationSection rootAddress={rootId} address={''} /> <PublicationSection
rootAddress={rootAddress}
address={leaf.tagAddress()}
ref={(el) => setLastElementRef(el, i)}
/>
{/each}
</div> </div>
<style> <style>

22
src/lib/components/PublicationSection.svelte

@ -4,7 +4,15 @@
import { TextPlaceholder } from "flowbite-svelte"; import { TextPlaceholder } from "flowbite-svelte";
import { getContext } from "svelte"; import { getContext } from "svelte";
let { address, rootAddress }: { address: string, rootAddress: string } = $props(); let {
address,
rootAddress,
ref,
}: {
address: string,
rootAddress: string,
ref: (ref: HTMLElement) => void,
} = $props();
const publicationTree = getContext<PublicationTree>('publicationTree'); const publicationTree = getContext<PublicationTree>('publicationTree');
@ -14,10 +22,20 @@
(await rootEvent)?.getMatchingTags('type')[0]?.[1]); (await rootEvent)?.getMatchingTags('type')[0]?.[1]);
let hierarchy = $derived.by(async () => await publicationTree.getHierarchy(address)); let hierarchy = $derived.by(async () => await publicationTree.getHierarchy(address));
let depth = $derived.by(async () => (await hierarchy).length); let depth = $derived.by(async () => (await hierarchy).length);
let sectionRef: HTMLElement;
$effect(() => {
if (!sectionRef) {
return;
}
ref(sectionRef);
})
</script> </script>
<!-- TODO: Correctly handle events that are the start of a content section. --> <!-- TODO: Correctly handle events that are the start of a content section. -->
<section> <section bind:this={sectionRef}>
{#await Promise.all([sectionEvent, publicationType])} {#await Promise.all([sectionEvent, publicationType])}
<TextPlaceholder size='xxl' /> <TextPlaceholder size='xxl' />
{:then [sectionEvent, publicationType]} {:then [sectionEvent, publicationType]}

2
src/routes/publication/+page.svelte

@ -48,7 +48,7 @@
<TextPlaceholder divClass='skeleton-leather w-full' size="xxl" /> <TextPlaceholder divClass='skeleton-leather w-full' size="xxl" />
{:then} {:then}
<Publication <Publication
rootId={data.parser.getRootIndexId()} rootAddress={data.indexEvent.tagAddress()}
publicationType={data.publicationType} publicationType={data.publicationType}
indexEvent={data.indexEvent} indexEvent={data.indexEvent}
/> />

Loading…
Cancel
Save