diff --git a/src/app.d.ts b/src/app.d.ts index db98703..53556b4 100644 --- a/src/app.d.ts +++ b/src/app.d.ts @@ -1,6 +1,7 @@ // See https://svelte.dev/docs/kit/types#app.d.ts // for information about these interfaces import type { ForumShell, Profiles } from "$lib/forum/snapshot"; +import type { ThreadDetail } from "$lib/forum/thread"; declare global { namespace App { @@ -10,6 +11,7 @@ declare global { interface PageData { shell?: ForumShell | null; profiles?: Profiles | null; + thread?: ThreadDetail | null; } // interface PageState {} // interface Platform {} diff --git a/src/lib/thread.svelte.ts b/src/lib/thread.svelte.ts index 66623a7..829ab9b 100644 --- a/src/lib/thread.svelte.ts +++ b/src/lib/thread.svelte.ts @@ -1,4 +1,5 @@ import { loadNostrUser, type NostrUser } from "$lib/gadgets"; +import { page } from "$app/state"; import { RELAY_URL, GROUP_ID } from "$lib/config"; import { queryForum, publishForum } from "$lib/relay"; import { threads as mockThreads } from "$lib/mock"; @@ -26,15 +27,18 @@ let profiles = $state>({}); // in a private group the current (non-member) user can't read. let status = $state<"loading" | "ready" | "notfound">("loading"); +// Until the live fetch lands, the server snapshot (if any) stands in. Its +// profiles stay as a base layer: the live ones arrive one by one. export const threadDetailStore = { get detail() { - return detail; + return detail ?? page.data.thread ?? null; }, get profiles() { - return profiles; + const base = page.data.profiles; + return base ? { ...base, ...profiles } : profiles; }, get status() { - return status; + return status === "loading" && page.data.thread ? "ready" : status; }, }; diff --git a/src/routes/api/thread/[id]/+server.ts b/src/routes/api/thread/[id]/+server.ts new file mode 100644 index 0000000..2cb4e71 --- /dev/null +++ b/src/routes/api/thread/[id]/+server.ts @@ -0,0 +1,13 @@ +import { error, json } from "@sveltejs/kit"; +import { GROUP_ID } from "$lib/config"; +import { fetchThread, threadAuthors } from "$lib/forum/thread"; +import { fetchProfiles } from "$lib/forum/profiles"; +import { forumQuery, profileQuery } from "$lib/ssr/relay"; + +export async function GET({ params }) { + if (!/^[0-9a-f]{64}$/.test(params.id)) error(404, "Not found"); + const thread = await fetchThread(forumQuery, params.id, GROUP_ID); + if (!thread) error(404, "Not found"); + const profiles = await fetchProfiles(profileQuery, threadAuthors(thread)); + return json({ thread, profiles }); +} diff --git a/src/routes/thread/[id]/+page.ts b/src/routes/thread/[id]/+page.ts new file mode 100644 index 0000000..946559a --- /dev/null +++ b/src/routes/thread/[id]/+page.ts @@ -0,0 +1,10 @@ +import { snapshot, type Profiles } from "$lib/forum/snapshot"; +import type { ThreadDetail } from "$lib/forum/thread"; + +export async function load({ fetch, params }) { + const data = await snapshot<{ thread: ThreadDetail; profiles: Profiles }>( + fetch, + `/api/thread/${params.id}`, + ); + return { thread: data?.thread ?? null, profiles: data?.profiles ?? null }; +}