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 1 year 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