Browse Source

Render publication content and headers

Incomplete.  Some headers are still missing, and we don't yet gracefully handle events that can't be found.
master
buttercat1791 11 months ago
parent
commit
03612ad6de
  1. 10
      src/lib/components/Publication.svelte
  2. 84
      src/lib/components/PublicationSection.svelte
  3. 2
      src/routes/publication/+page.svelte

10
src/lib/components/Publication.svelte

@ -39,8 +39,9 @@
for (let i = 0; i < count; i++) { for (let i = 0; i < count; i++) {
const nextItem = await publicationTree.next(); const nextItem = await publicationTree.next();
if (nextItem.done) { if (leaves.includes(nextItem.value) || nextItem.done) {
break; isLoading = false;
return;
} }
leaves.push(nextItem.value); leaves.push(nextItem.value);
} }
@ -136,11 +137,11 @@
observer = new IntersectionObserver((entries) => { observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => { entries.forEach((entry) => {
if (entry.isIntersecting && !isLoading) { if (entry.isIntersecting && !isLoading) {
loadMore(8); loadMore(4);
} }
}); });
}, { threshold: 0.5 }); }, { threshold: 0.5 });
loadMore(16); loadMore(8);
return () => { return () => {
window.removeEventListener("hashchange", scrollToElementWithOffset); window.removeEventListener("hashchange", scrollToElementWithOffset);
@ -186,6 +187,7 @@
{#each leaves as leaf, i} {#each leaves as leaf, i}
<PublicationSection <PublicationSection
rootAddress={rootAddress} rootAddress={rootAddress}
leaves={leaves}
address={leaf.tagAddress()} address={leaf.tagAddress()}
ref={(el) => setLastElementRef(el, i)} ref={(el) => setLastElementRef(el, i)}
/> />

84
src/lib/components/PublicationSection.svelte

@ -1,27 +1,82 @@
<script lang='ts'> <script lang='ts'>
import type { PublicationTree } from "$lib/data_structures/publication_tree"; import type { PublicationTree } from "$lib/data_structures/publication_tree";
import { contentParagraph } from "$lib/snippets/PublicationSnippets.svelte"; import { contentParagraph, sectionHeading } from "$lib/snippets/PublicationSnippets.svelte";
import { NDKEvent } from "@nostr-dev-kit/ndk";
import { TextPlaceholder } from "flowbite-svelte"; import { TextPlaceholder } from "flowbite-svelte";
import { getContext } from "svelte"; import { getContext } from "svelte";
import type { Asciidoctor, Document } from "asciidoctor";
let { let {
address, address,
rootAddress, rootAddress,
leaves,
ref, ref,
}: { }: {
address: string, address: string,
rootAddress: string, rootAddress: string,
leaves: NDKEvent[],
ref: (ref: HTMLElement) => void, ref: (ref: HTMLElement) => void,
} = $props(); } = $props();
const publicationTree = getContext<PublicationTree>('publicationTree'); const publicationTree: PublicationTree = getContext('publicationTree');
const asciidoctor: Asciidoctor = getContext('asciidoctor');
let sectionEvent = $derived.by(async () => await publicationTree.getEvent(address)); let leafEvent: Promise<NDKEvent | null> = $derived.by(async () =>
let rootEvent = $derived.by(async () => await publicationTree.getEvent(rootAddress)); await publicationTree.getEvent(address));
let publicationType = $derived.by(async () => let rootEvent: Promise<NDKEvent | null> = $derived.by(async () =>
await publicationTree.getEvent(rootAddress));
let publicationType: Promise<string | undefined> = $derived.by(async () =>
(await rootEvent)?.getMatchingTags('type')[0]?.[1]); (await rootEvent)?.getMatchingTags('type')[0]?.[1]);
let hierarchy = $derived.by(async () => await publicationTree.getHierarchy(address)); let leafHierarchy: Promise<NDKEvent[]> = $derived.by(async () =>
let depth = $derived.by(async () => (await hierarchy).length); await publicationTree.getHierarchy(address));
let leafContent: Promise<string | Document> = $derived.by(async () =>
asciidoctor.convert((await leafEvent)?.content ?? ''));
let previousLeafEvent: NDKEvent | null = $derived.by(() => {
const index = leaves.findIndex(leaf => leaf.tagAddress() === address);
if (index === 0) {
return null;
}
return leaves[index - 1];
});
let previousLeafHierarchy: Promise<NDKEvent[] | null> = $derived.by(async () => {
const previousLeaf = await previousLeafEvent;
if (!previousLeaf) {
return null;
}
return await publicationTree.getHierarchy(previousLeafEvent?.tagAddress() ?? '')
});
let divergingBranches = $derived.by(async () => {
const currentHierarchy = await leafHierarchy;
const previousHierarchy = await previousLeafHierarchy;
const branches: [NDKEvent, number][] = [];
if (!previousHierarchy) {
for (let i = 0; i < currentHierarchy.length - 1; i++) {
branches.push([currentHierarchy[i], i]);
}
return branches;
}
const minLength = Math.min(currentHierarchy.length, previousHierarchy.length);
// Find the first diverging node.
let divergingIndex = 0;
while (
divergingIndex < minLength &&
currentHierarchy[divergingIndex].tagAddress() === previousHierarchy[divergingIndex].tagAddress()
) {
divergingIndex++;
}
// Add all branches from the first diverging node to the current leaf.
for (let i = divergingIndex; i < currentHierarchy.length - 1; i++) {
branches.push([currentHierarchy[i], i]);
}
return branches;
});
let sectionRef: HTMLElement; let sectionRef: HTMLElement;
@ -31,16 +86,19 @@
} }
ref(sectionRef); 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 bind:this={sectionRef}> <section bind:this={sectionRef}>
{#await Promise.all([sectionEvent, publicationType])} {#await Promise.all([leafContent, publicationType, divergingBranches])}
<TextPlaceholder size='xxl' /> <TextPlaceholder size='xxl' />
{:then [sectionEvent, publicationType]} {:then [leafContent, publicationType, divergingBranches]}
<!-- TODO: Gracefully handle nulls. --> <!-- TODO: Ensure we render all headings, not just the first one. -->
{@render contentParagraph(sectionEvent?.content ?? '', publicationType ?? 'article', false)} {#each divergingBranches as [branch, depth]}
{@render sectionHeading(branch.getMatchingTags('title')[0]?.[1] ?? '', depth)}
{/each}
{@render contentParagraph(leafContent.toString(), publicationType ?? 'article', false)}
{/await} {/await}
</section> </section>

2
src/routes/publication/+page.svelte

@ -4,12 +4,14 @@
import type { PageProps } from "./$types"; import type { PageProps } from "./$types";
import { onDestroy, setContext } from "svelte"; import { onDestroy, setContext } from "svelte";
import { PublicationTree } from "$lib/data_structures/publication_tree"; import { PublicationTree } from "$lib/data_structures/publication_tree";
import Processor from "asciidoctor";
let { data }: PageProps = $props(); let { data }: PageProps = $props();
const publicationTree = new PublicationTree(data.indexEvent, data.ndk); const publicationTree = new PublicationTree(data.indexEvent, data.ndk);
setContext('publicationTree', publicationTree); setContext('publicationTree', publicationTree);
setContext('asciidoctor', Processor());
// Get publication metadata for OpenGraph tags // Get publication metadata for OpenGraph tags
let title = $derived(data.indexEvent?.getMatchingTags('title')[0]?.[1] || data.parser?.getIndexTitle(data.parser?.getRootIndexId()) || 'Alexandria Publication'); let title = $derived(data.indexEvent?.getMatchingTags('title')[0]?.[1] || data.parser?.getIndexTitle(data.parser?.getRootIndexId()) || 'Alexandria Publication');

Loading…
Cancel
Save