|
|
|
@ -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> |
|
|
|
|