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. 6
      src/routes/publication/+page.svelte

82
src/lib/components/Publication.svelte

@ -9,25 +9,64 @@ @@ -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<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;
let activeHash = $state(page.url.hash);
@ -82,21 +121,33 @@ @@ -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();
};
});
</script>
@ -115,7 +166,7 @@ @@ -115,7 +166,7 @@
</Button>
<Tooltip>Show Table of Contents</Tooltip>
{/if}
<!-- TODO: Get TOC from parser. -->
<!-- TODO: Use loader to build ToC. -->
<!-- {#if showToc}
<Sidebar class='sidebar-leather fixed top-20 left-0 px-4 w-60' {activeHash}>
<SidebarWrapper>
@ -132,8 +183,13 @@ @@ -132,8 +183,13 @@
</Sidebar>
{/if} -->
<div class="flex flex-col space-y-4 max-w-2xl">
<!-- TODO: Pass in the correct address for each section. -->
<PublicationSection rootAddress={rootId} address={''} />
{#each leaves as leaf, i}
<PublicationSection
rootAddress={rootAddress}
address={leaf.tagAddress()}
ref={(el) => setLastElementRef(el, i)}
/>
{/each}
</div>
<style>

22
src/lib/components/PublicationSection.svelte

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

6
src/routes/publication/+page.svelte

@ -18,7 +18,7 @@ @@ -18,7 +18,7 @@
// Get image and summary from the event tags if available
// If image unavailable, use the Alexandria default pic.
let image = $derived(data.indexEvent?.getMatchingTags('image')[0]?.[1] || '/screenshots/old_books.jpg');
let summary = $derived(data.indexEvent?.getMatchingTags('summary')[0]?.[1] || 'Alexandria is a digital library, utilizing Nostr events for curated publications and wiki pages.');
let summary = $derived(data.indexEvent?.getMatchingTags('summary')[0]?.[1] || 'Alexandria is a digital library, utilizing Nostr events for curated publications and wiki pages.');
onDestroy(() => data.parser.reset());
</script>
@ -48,9 +48,9 @@ @@ -48,9 +48,9 @@
<TextPlaceholder divClass='skeleton-leather w-full' size="xxl" />
{:then}
<Publication
rootId={data.parser.getRootIndexId()}
rootAddress={data.indexEvent.tagAddress()}
publicationType={data.publicationType}
indexEvent={data.indexEvent}
indexEvent={data.indexEvent}
/>
{/await}
</main>

Loading…
Cancel
Save