Browse Source

Use more idiomatic Svelte 5/SvelteKit 2 loading patterns

master
buttercat1791 1 year ago
parent
commit
316fcb1b7a
  1. 62
      package-lock.json
  2. 2
      package.json
  3. 6
      src/app.d.ts
  4. 78
      src/lib/components/Article.svelte
  5. 29
      src/lib/components/Preview.svelte
  6. 7
      src/lib/parser.ts
  7. 1
      src/routes/+layout.server.ts
  8. 24
      src/routes/+layout.ts
  9. 50
      src/routes/+page.svelte
  10. 4
      src/routes/new/compose/+page.svelte
  11. 18
      src/routes/new/edit/+page.svelte
  12. 31
      src/routes/publication/+error.svelte
  13. 42
      src/routes/publication/+page.ts
  14. 2
      svelte.config.js

62
package-lock.json generated

@ -21,7 +21,7 @@
"@sveltejs/adapter-auto": "3.x", "@sveltejs/adapter-auto": "3.x",
"@sveltejs/adapter-static": "3.x", "@sveltejs/adapter-static": "3.x",
"@sveltejs/kit": "2.x", "@sveltejs/kit": "2.x",
"@sveltejs/vite-plugin-svelte": "3.x", "@sveltejs/vite-plugin-svelte": "4.x",
"@types/he": "1.2.x", "@types/he": "1.2.x",
"@types/node": "22.x", "@types/node": "22.x",
"autoprefixer": "10.x", "autoprefixer": "10.x",
@ -1318,59 +1318,45 @@
} }
}, },
"node_modules/@sveltejs/vite-plugin-svelte": { "node_modules/@sveltejs/vite-plugin-svelte": {
"version": "3.1.2", "version": "4.0.4",
"resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-3.1.2.tgz", "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-4.0.4.tgz",
"integrity": "sha512-Txsm1tJvtiYeLUVRNqxZGKR/mI+CzuIQuc2gn+YCs9rMTowpNZ2Nqt53JdL8KF9bLhAf2ruR/dr9eZCwdTriRA==", "integrity": "sha512-0ba1RQ/PHen5FGpdSrW7Y3fAMQjrXantECALeOiOdBdzR5+5vPP6HVZRLmZaQL+W8m++o+haIAKq5qT+MiZ7VA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@sveltejs/vite-plugin-svelte-inspector": "^2.1.0", "@sveltejs/vite-plugin-svelte-inspector": "^3.0.0-next.0||^3.0.0",
"debug": "^4.3.4", "debug": "^4.3.7",
"deepmerge": "^4.3.1", "deepmerge": "^4.3.1",
"kleur": "^4.1.5", "kleur": "^4.1.5",
"magic-string": "^0.30.10", "magic-string": "^0.30.12",
"svelte-hmr": "^0.16.0", "vitefu": "^1.0.3"
"vitefu": "^0.2.5"
}, },
"engines": { "engines": {
"node": "^18.0.0 || >=20" "node": "^18.0.0 || ^20.0.0 || >=22"
}, },
"peerDependencies": { "peerDependencies": {
"svelte": "^4.0.0 || ^5.0.0-next.0", "svelte": "^5.0.0-next.96 || ^5.0.0",
"vite": "^5.0.0" "vite": "^5.0.0"
} }
}, },
"node_modules/@sveltejs/vite-plugin-svelte/node_modules/@sveltejs/vite-plugin-svelte-inspector": { "node_modules/@sveltejs/vite-plugin-svelte-inspector": {
"version": "2.1.0", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-2.1.0.tgz", "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-3.0.1.tgz",
"integrity": "sha512-9QX28IymvBlSCqsCll5t0kQVxipsfhFFL+L2t3nTWfXnddYwxBuAEtTtlaVQpRz9c37BhJjltSeY4AJSC03SSg==", "integrity": "sha512-2CKypmj1sM4GE7HjllT7UKmo4Q6L5xFRd7VMGEWhYnZ+wc6AUVU01IBd7yUi6WnFndEwWoMNOd6e8UjoN0nbvQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"debug": "^4.3.4" "debug": "^4.3.7"
}, },
"engines": { "engines": {
"node": "^18.0.0 || >=20" "node": "^18.0.0 || ^20.0.0 || >=22"
}, },
"peerDependencies": { "peerDependencies": {
"@sveltejs/vite-plugin-svelte": "^3.0.0", "@sveltejs/vite-plugin-svelte": "^4.0.0-next.0||^4.0.0",
"svelte": "^4.0.0 || ^5.0.0-next.0", "svelte": "^5.0.0-next.96 || ^5.0.0",
"vite": "^5.0.0" "vite": "^5.0.0"
} }
}, },
"node_modules/@sveltejs/vite-plugin-svelte/node_modules/svelte-hmr": {
"version": "0.16.0",
"resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.16.0.tgz",
"integrity": "sha512-Gyc7cOS3VJzLlfj7wKS0ZnzDVdv3Pn2IuVeJPk9m2skfhcu5bq3wtIZyQGggr7/Iim5rH5cncyQft/kRLupcnA==",
"dev": true,
"license": "ISC",
"engines": {
"node": "^12.20 || ^14.13.1 || >= 16"
},
"peerDependencies": {
"svelte": "^3.19.0 || ^4.0.0"
}
},
"node_modules/@tailwindcss/forms": { "node_modules/@tailwindcss/forms": {
"version": "0.5.9", "version": "0.5.9",
"resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.9.tgz", "resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.9.tgz",
@ -5481,13 +5467,17 @@
} }
}, },
"node_modules/vitefu": { "node_modules/vitefu": {
"version": "0.2.5", "version": "1.0.4",
"resolved": "https://registry.npmjs.org/vitefu/-/vitefu-0.2.5.tgz", "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.0.4.tgz",
"integrity": "sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q==", "integrity": "sha512-y6zEE3PQf6uu/Mt6DTJ9ih+kyJLr4XcSgHR2zUkM8SWDhuixEJxfJ6CZGMHh1Ec3vPLoEA0IHU5oWzVqw8ulow==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"workspaces": [
"tests/deps/*",
"tests/projects/*"
],
"peerDependencies": { "peerDependencies": {
"vite": "^3.0.0 || ^4.0.0 || ^5.0.0" "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0"
}, },
"peerDependenciesMeta": { "peerDependenciesMeta": {
"vite": { "vite": {

2
package.json

@ -26,7 +26,7 @@
"@sveltejs/adapter-auto": "3.x", "@sveltejs/adapter-auto": "3.x",
"@sveltejs/adapter-static": "3.x", "@sveltejs/adapter-static": "3.x",
"@sveltejs/kit": "2.x", "@sveltejs/kit": "2.x",
"@sveltejs/vite-plugin-svelte": "3.x", "@sveltejs/vite-plugin-svelte": "4.x",
"@types/he": "1.2.x", "@types/he": "1.2.x",
"@types/node": "22.x", "@types/node": "22.x",
"autoprefixer": "10.x", "autoprefixer": "10.x",

6
src/app.d.ts vendored

@ -4,7 +4,11 @@ declare global {
namespace App { namespace App {
// interface Error {} // interface Error {}
// interface Locals {} // interface Locals {}
// interface PageData {} interface PageData {
ndk?: NDK;
parser?: Pharos;
waitable?: Promise<any>;
}
// interface Platform {} // interface Platform {}
} }
} }

78
src/lib/components/Article.svelte

@ -1,28 +1,18 @@
<script lang="ts"> <script lang='ts'>
import { ndk } from '$lib/ndk'; import { Button, Sidebar, SidebarGroup, SidebarItem, SidebarWrapper, Skeleton, TextPlaceholder, Tooltip } from 'flowbite-svelte';
import type { NDKEvent } from '@nostr-dev-kit/ndk';
import { page } from '$app/stores';
import { Alert, Button, Heading, P, Sidebar, SidebarGroup, SidebarItem, SidebarWrapper, Skeleton, TextPlaceholder, Tooltip, xs } from 'flowbite-svelte';
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import { BookOutline, ExclamationCircleOutline } from 'flowbite-svelte-icons'; import { BookOutline } from 'flowbite-svelte-icons';
import Pharos, { parser } from '$lib/parser';
import Preview from './Preview.svelte'; import Preview from './Preview.svelte';
import { goto, invalidateAll } from '$app/navigation'; import { pharosInstance } from '$lib/parser';
import { page } from '$app/state';
export let index: NDKEvent | null | undefined; let { rootId }: { rootId: string } = $props();
$parser ??= new Pharos($ndk); if (rootId !== $pharosInstance.getRootIndexId()) {
console.error('Root ID does not match parser root index ID');
$: activeHash = $page.url.hash;
const getContentRoot = async (index?: NDKEvent | null | undefined): Promise<string | null> => {
if (!index) {
return null;
} }
await $parser.fetch(index); let activeHash = $state(page.url.hash);
return $parser.getRootIndexId();
};
function normalizeHashPath(str: string): string { function normalizeHashPath(str: string): string {
return str return str
@ -95,16 +85,7 @@
}); });
</script> </script>
{#await getContentRoot(index)} {#if showTocButton && !showToc}
<Sidebar class='sidebar-leather fixed top-20 left-0 px-4 w-60'>
<SidebarWrapper>
<Skeleton/>
</SidebarWrapper>
</Sidebar>
<TextPlaceholder divClass='max-w-2xl'/>
{:then rootId}
{#if rootId}
{#if showTocButton && !showToc}
<Button <Button
class='btn-leather fixed top-20 left-4 h-6 w-6' class='btn-leather fixed top-20 left-4 h-6 w-6'
outline={true} outline={true}
@ -118,9 +99,9 @@
<Tooltip> <Tooltip>
Show Table of Contents Show Table of Contents
</Tooltip> </Tooltip>
{/if} {/if}
<!-- TODO: Get TOC from parser. --> <!-- TODO: Get TOC from parser. -->
<!-- {#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>
<SidebarGroup class='sidebar-group-leather overflow-y-scroll'> <SidebarGroup class='sidebar-group-leather overflow-y-scroll'>
@ -134,35 +115,10 @@
</SidebarGroup> </SidebarGroup>
</SidebarWrapper> </SidebarWrapper>
</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'>
<Preview rootId={rootId} /> <Preview {rootId} />
</div> </div>
{/if}
{:catch err}
<Alert>
<div class='flex items-center space-x-2'>
<ExclamationCircleOutline class='w-6 h-6' />
<span class='text-lg font-medium'>
Failed to load publication.
</span>
</div>
<P size='sm'>
Alexandria failed to find one or more of the events comprising this publication.
</P>
<P size='xs'>
{err.message}
</P>
<div class='flex space-x-2'>
<Button class='btn-leather' size='sm' on:click={() => invalidateAll()}>
Try Again
</Button>
<Button class='btn-leather' size='sm' outline on:click={() => goto('/')}>
Return to Home
</Button>
</div>
</Alert>
{/await}
<style> <style>
:global(.sidebar-group-leather) { :global(.sidebar-group-leather) {

29
src/lib/components/Preview.svelte

@ -1,5 +1,6 @@
<script lang="ts"> <script lang="ts">
import { parser, SiblingSearchDirection } from "$lib/parser"; import { page } from "$app/state";
import { pharosInstance, SiblingSearchDirection } from "$lib/parser";
import { Button, ButtonGroup, CloseButton, Heading, Input, P, Textarea, Tooltip } from "flowbite-svelte"; import { Button, ButtonGroup, CloseButton, Heading, Input, P, Textarea, Tooltip } from "flowbite-svelte";
import { CaretDownSolid, CaretUpSolid, EditOutline } from "flowbite-svelte-icons"; import { CaretDownSolid, CaretUpSolid, EditOutline } from "flowbite-svelte-icons";
import { createEventDispatcher } from "svelte"; import { createEventDispatcher } from "svelte";
@ -16,9 +17,9 @@
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
let currentContent: string = $parser.getContent(rootId); let currentContent: string = $pharosInstance.getContent(rootId);
let title: string | undefined = $parser.getIndexTitle(rootId); let title: string | undefined = $pharosInstance.getIndexTitle(rootId);
let orderedChildren: string[] = $parser.getOrderedChildIds(rootId); let orderedChildren: string[] = $pharosInstance.getOrderedChildIds(rootId);
let isEditing: boolean = false; let isEditing: boolean = false;
let hasCursor: boolean = false; let hasCursor: boolean = false;
@ -37,8 +38,8 @@
if (needsUpdate) { if (needsUpdate) {
updateCount++; updateCount++;
needsUpdate = false; needsUpdate = false;
title = $parser.getIndexTitle(rootId); title = $pharosInstance.getIndexTitle(rootId);
currentContent = $parser.getContent(rootId); currentContent = $pharosInstance.getContent(rootId);
} }
if (subtreeNeedsUpdate) { if (subtreeNeedsUpdate) {
@ -46,7 +47,7 @@
subtreeNeedsUpdate = false; subtreeNeedsUpdate = false;
const prevChildCount = orderedChildren.length; const prevChildCount = orderedChildren.length;
orderedChildren = $parser.getOrderedChildIds(rootId); orderedChildren = $pharosInstance.getOrderedChildIds(rootId);
const newChildCount = orderedChildren.length; const newChildCount = orderedChildren.length;
// If the number of children has changed, a child has been added or removed, and a child may // If the number of children has changed, a child has been added or removed, and a child may
@ -62,8 +63,8 @@
$: { $: {
if (parentId && allowEditing) { if (parentId && allowEditing) {
// Check for previous/next siblings on load // Check for previous/next siblings on load
const previousSibling = $parser.getNearestSibling(rootId, depth - 1, SiblingSearchDirection.Previous); const previousSibling = $pharosInstance.getNearestSibling(rootId, depth - 1, SiblingSearchDirection.Previous);
const nextSibling = $parser.getNearestSibling(rootId, depth - 1, SiblingSearchDirection.Next); const nextSibling = $pharosInstance.getNearestSibling(rootId, depth - 1, SiblingSearchDirection.Next);
// Hide arrows if no siblings exist // Hide arrows if no siblings exist
hasPreviousSibling = !!previousSibling[0]; hasPreviousSibling = !!previousSibling[0];
@ -113,7 +114,7 @@
} }
$parser.updateEventContent(id, currentContent); $pharosInstance.updateEventContent(id, currentContent);
} }
isEditing = !editing; isEditing = !editing;
@ -121,25 +122,25 @@
const moveUp = (rootId: string, parentId: string) => { const moveUp = (rootId: string, parentId: string) => {
// Get the previous sibling and its index // Get the previous sibling and its index
const [prevSiblingId, prevIndex] = $parser.getNearestSibling(rootId, depth - 1, SiblingSearchDirection.Previous); const [prevSiblingId, prevIndex] = $pharosInstance.getNearestSibling(rootId, depth - 1, SiblingSearchDirection.Previous);
if (!prevSiblingId || prevIndex == null) { if (!prevSiblingId || prevIndex == null) {
return; return;
} }
// Move the current event before the previous sibling. // Move the current event before the previous sibling.
$parser.moveEvent(rootId, prevSiblingId, false); $pharosInstance.moveEvent(rootId, prevSiblingId, false);
needsUpdate = true; needsUpdate = true;
}; };
const moveDown = (rootId: string, parentId: string) => { const moveDown = (rootId: string, parentId: string) => {
// Get the next sibling and its index // Get the next sibling and its index
const [nextSiblingId, nextIndex] = $parser.getNearestSibling(rootId, depth - 1, SiblingSearchDirection.Next); const [nextSiblingId, nextIndex] = $pharosInstance.getNearestSibling(rootId, depth - 1, SiblingSearchDirection.Next);
if (!nextSiblingId || nextIndex == null) { if (!nextSiblingId || nextIndex == null) {
return; return;
} }
// Move the current event after the next sibling // Move the current event after the next sibling
$parser.moveEvent(rootId, nextSiblingId, true); $pharosInstance.moveEvent(rootId, nextSiblingId, true);
needsUpdate = true; needsUpdate = true;
}; };
</script> </script>

7
src/lib/parser.ts

@ -1,6 +1,7 @@
import NDK, { NDKEvent } from '@nostr-dev-kit/ndk'; import NDK, { NDKEvent } from '@nostr-dev-kit/ndk';
import { getNdkInstance } from './ndk'; import { getNdkInstance } from './ndk';
import asciidoctor, { import asciidoctor from 'asciidoctor';
import type {
AbstractBlock, AbstractBlock,
AbstractNode, AbstractNode,
Asciidoctor, Asciidoctor,
@ -8,7 +9,7 @@ import asciidoctor, {
Document, Document,
Extensions, Extensions,
Section, Section,
type ProcessorOptions ProcessorOptions,
} from 'asciidoctor'; } from 'asciidoctor';
import he from 'he'; import he from 'he';
import { writable, type Writable } from 'svelte/store'; import { writable, type Writable } from 'svelte/store';
@ -1063,4 +1064,4 @@ export default class Pharos {
// #endregion // #endregion
} }
export const parser: Writable<Pharos> = writable(new Pharos(getNdkInstance())); export const pharosInstance: Writable<Pharos> = writable();

1
src/routes/+layout.server.ts

@ -1 +0,0 @@
export const ssr = false;

24
src/routes/+layout.ts

@ -1 +1,23 @@
export const prerender = true; import NDK from '@nostr-dev-kit/ndk';
import type { LayoutLoad } from './$types';
import { standardRelays } from '$lib/consts';
import Pharos, { pharosInstance } from '$lib/parser';
export const ssr = false;
export const load: LayoutLoad = () => {
const ndk = new NDK({
autoConnectUserRelays: true,
enableOutboxModel: true,
explicitRelayUrls: standardRelays,
});
ndk.connect().then(() => console.debug('ndk connected'));
const parser = new Pharos(ndk);
pharosInstance.set(parser);
return {
ndk,
parser,
};
};

50
src/routes/+page.svelte

@ -1,26 +1,41 @@
<script lang="ts"> <script lang='ts'>
import ArticleHeader from "$lib/components/ArticleHeader.svelte"; import ArticleHeader from '$lib/components/ArticleHeader.svelte';
import { FeedType, indexKind, standardRelays } from "$lib/consts"; import { FeedType, indexKind, standardRelays } from '$lib/consts';
import { ndk } from "$lib/ndk"; import { filterValidIndexEvents } from '$lib/utils';
import { filterValidIndexEvents } from "$lib/utils"; import NDK, { NDKEvent, NDKRelaySet, type NDKUser } from '@nostr-dev-kit/ndk';
import { NDKEvent, NDKRelaySet, type NDKUser } from "@nostr-dev-kit/ndk"; import { Button, Dropdown, Radio, Skeleton } from 'flowbite-svelte';
import { Button, Dropdown, Radio, Skeleton } from "flowbite-svelte"; import { ChevronDownOutline } from 'flowbite-svelte-icons';
import { ChevronDownOutline } from "flowbite-svelte-icons"; import type { PageData } from './$types';
import { setContext } from 'svelte';
let { data }: { data: PageData } = $props();
let ndk: NDK = data.ndk;
let user: NDKUser | null | undefined = $state(ndk.activeUser);
let readRelays: string[] | null | undefined = $state(user?.relayUrls);
let userFollows: Set<NDKUser> | null | undefined = $state(null);
let feedType: FeedType = $state(FeedType.Relays);
$effect(() => {
if (user) {
user.follows().then(follows => userFollows = follows);
}
});
const getEvents = (): Promise<Set<NDKEvent>> => const getEvents = (): Promise<Set<NDKEvent>> =>
// @ts-ignore // @ts-ignore
$ndk.fetchEvents( ndk.fetchEvents(
{ kinds: [indexKind] }, { kinds: [indexKind] },
{ {
groupable: true, groupable: true,
skipVerification: false, skipVerification: false,
skipValidation: false skipValidation: false
}, },
NDKRelaySet.fromRelayUrls(standardRelays, $ndk) NDKRelaySet.fromRelayUrls(standardRelays, ndk)
).then(filterValidIndexEvents); ).then(filterValidIndexEvents);
const getEventsFromUserRelays = (userRelays: string[]): Promise<Set<NDKEvent>> => { const getEventsFromUserRelays = (userRelays: string[]): Promise<Set<NDKEvent>> => {
return $ndk return ndk
.fetchEvents( .fetchEvents(
// @ts-ignore // @ts-ignore
{ kinds: [indexKind] }, { kinds: [indexKind] },
@ -35,7 +50,7 @@
} }
const getEventsFromUserFollows = (follows: Set<NDKUser>, userRelays?: string[]): Promise<Set<NDKEvent>> => { const getEventsFromUserFollows = (follows: Set<NDKUser>, userRelays?: string[]): Promise<Set<NDKEvent>> => {
return $ndk return ndk
.fetchEvents( .fetchEvents(
{ {
authors: Array.from(follows ?? []).map(user => user.pubkey), authors: Array.from(follows ?? []).map(user => user.pubkey),
@ -74,17 +89,6 @@
} }
return skeletonIds; return skeletonIds;
} }
let user: NDKUser | null | undefined;
let readRelays: string[] | null | undefined;
let userFollows: Set<NDKUser> | null | undefined;
let feedType: FeedType = FeedType.Relays;
$: {
user = $ndk.activeUser;
readRelays = user?.relayUrls;
user?.follows().then(follows => userFollows = follows);
}
</script> </script>
<div class='leather flex flex-col flex-grow-0 space-y-4 overflow-y-auto w-max p-2'> <div class='leather flex flex-col flex-grow-0 space-y-4 overflow-y-auto w-max p-2'>

4
src/routes/new/compose/+page.svelte

@ -1,6 +1,6 @@
<script lang='ts'> <script lang='ts'>
import Preview from "$lib/components/Preview.svelte"; import Preview from "$lib/components/Preview.svelte";
import { parser } from "$lib/parser"; import { pharosInstance } from "$lib/parser";
import { Heading } from "flowbite-svelte"; import { Heading } from "flowbite-svelte";
let treeNeedsUpdate: boolean = false; let treeNeedsUpdate: boolean = false;
@ -17,7 +17,7 @@
<main class='main-leather flex flex-col space-y-4 max-w-2xl w-full mt-4 mb-4'> <main class='main-leather flex flex-col space-y-4 max-w-2xl w-full mt-4 mb-4'>
<Heading tag='h1' class='h-leather mb-2'>Compose</Heading> <Heading tag='h1' class='h-leather mb-2'>Compose</Heading>
{#key treeUpdateCount} {#key treeUpdateCount}
<Preview rootId={$parser.getRootIndexId()} allowEditing={true} bind:needsUpdate={treeNeedsUpdate} /> <Preview rootId={$pharosInstance.getRootIndexId()} allowEditing={true} bind:needsUpdate={treeNeedsUpdate} />
{/key} {/key}
</main> </main>
</div> </div>

18
src/routes/new/edit/+page.svelte

@ -2,7 +2,7 @@
import { Heading, Textarea, Toolbar, ToolbarButton, Tooltip } from "flowbite-svelte"; import { Heading, Textarea, Toolbar, ToolbarButton, Tooltip } from "flowbite-svelte";
import { CodeOutline, EyeSolid, PaperPlaneOutline } from "flowbite-svelte-icons"; import { CodeOutline, EyeSolid, PaperPlaneOutline } from "flowbite-svelte-icons";
import Preview from "$lib/components/Preview.svelte"; import Preview from "$lib/components/Preview.svelte";
import Pharos, { parser } from "$lib/parser"; import Pharos, { pharosInstance } from "$lib/parser";
import { ndk } from "$lib/ndk"; import { ndk } from "$lib/ndk";
import { goto } from "$app/navigation"; import { goto } from "$app/navigation";
@ -15,16 +15,16 @@
const showPreview = () => { const showPreview = () => {
try { try {
$parser ??= new Pharos($ndk); $pharosInstance ??= new Pharos($ndk);
$parser.reset(); $pharosInstance.reset();
$parser.parse(editorText); $pharosInstance.parse(editorText);
} catch (e) { } catch (e) {
console.error(e); console.error(e);
// TODO: Indicate error in UI. // TODO: Indicate error in UI.
return; return;
} }
rootIndexId = $parser.getRootIndexId(); rootIndexId = $pharosInstance.getRootIndexId();
isEditing = false; isEditing = false;
}; };
@ -34,15 +34,15 @@
const prepareReview = () => { const prepareReview = () => {
try { try {
$parser.reset(); $pharosInstance.reset();
$parser.parse(editorText); $pharosInstance.parse(editorText);
} catch (e) { } catch (e) {
console.error(e); console.error(e);
// TODO: Indicate error in UI. // TODO: Indicate error in UI.
return; return;
} }
$parser.generate($ndk.activeUser?.pubkey!); $pharosInstance.generate($ndk.activeUser?.pubkey!);
goto('/new/compose'); goto('/new/compose');
} }
</script> </script>
@ -55,7 +55,7 @@
<Textarea <Textarea
id='article-content' id='article-content'
class='textarea-leather' class='textarea-leather'
rows=8 rows={8}
placeholder='Write AsciiDoc content' placeholder='Write AsciiDoc content'
bind:value={editorText} bind:value={editorText}
> >

31
src/routes/publication/+error.svelte

@ -0,0 +1,31 @@
<script lang='ts'>
import { invalidateAll, goto } from '$app/navigation';
import { Alert, P, Button } from 'flowbite-svelte';
import { ExclamationCircleOutline } from 'flowbite-svelte-icons';
import { page } from '$app/state';
</script>
<main>
<Alert>
<div class='flex items-center space-x-2'>
<ExclamationCircleOutline class='w-6 h-6' />
<span class='text-lg font-medium'>
Failed to load publication.
</span>
</div>
<P size='sm'>
Alexandria failed to find one or more of the events comprising this publication.
</P>
<P size='xs'>
{page.error?.message}
</P>
<div class='flex space-x-2'>
<Button class='btn-leather w-fit' size='sm' onclick={() => invalidateAll()}>
Try Again
</Button>
<Button class='btn-leather w-fit' size='sm' outline onclick={() => goto('/')}>
Return to Home
</Button>
</div>
</Alert>
</main>

42
src/routes/publication/+page.ts

@ -0,0 +1,42 @@
import { error } from '@sveltejs/kit';
import type { NDKEvent } from '@nostr-dev-kit/ndk';
import type { PageLoad } from './$types';
import { pharosInstance } from '$lib/parser';
export const load: PageLoad = async ({ url, parent }) => {
const id = url.searchParams.get('id');
const dTag = url.searchParams.get('d');
const { ndk, parser } = await parent();
let eventPromise: Promise<NDKEvent | null>;
let indexEvent: NDKEvent | null;
if (id) {
eventPromise = ndk.fetchEvent(id)
.then((ev: NDKEvent | null) => {
return ev;
})
.catch((err: any) => {
error(404, `Failed to fetch publication root event for ID: ${id}\n${err}`);
});
} else if (dTag) {
eventPromise = ndk.fetchEvent({ '#d': [dTag] })
.then((ev: NDKEvent | null) => {
return ev;
})
.catch((err: any) => {
error(404, `Failed to fetch publication root event for d tag: ${dTag}\n${err}`);
});
} else {
error(400, 'No publication root event ID or d tag provided.');
}
indexEvent = await eventPromise as NDKEvent;
const fetchPromise = parser.fetch(indexEvent);
return {
waitable: fetchPromise,
};
};

2
svelte.config.js

@ -12,7 +12,7 @@ const config = {
adapter: adapter({ adapter: adapter({
pages: 'build', pages: 'build',
assets: 'build', assets: 'build',
fallback: null, // TODO: Create a 404.html page. fallback: 'index.html',
precompress: false, precompress: false,
strict: true, strict: true,
}), }),

Loading…
Cancel
Save