Render the forum shell, resources and contacts on the server
This commit is contained in:
parent
a7b5d8f223
commit
40e0be35d9
16 changed files with 259 additions and 30 deletions
|
|
@ -1,3 +1,8 @@
|
|||
import { SSR_ENABLED } from "$lib/config";
|
||||
import { snapshot, type ForumShell } from "$lib/forum/snapshot";
|
||||
|
||||
export const ssr = SSR_ENABLED;
|
||||
|
||||
export async function load({ fetch }) {
|
||||
return { shell: await snapshot<ForumShell>(fetch, "/api/shell") };
|
||||
}
|
||||
|
|
|
|||
9
src/routes/api/contacts/+server.ts
Normal file
9
src/routes/api/contacts/+server.ts
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
import { json } from "@sveltejs/kit";
|
||||
import { fetchProfiles } from "$lib/forum/profiles";
|
||||
import { loadShell } from "$lib/ssr/shell";
|
||||
import { profileQuery } from "$lib/ssr/relay";
|
||||
|
||||
export async function GET() {
|
||||
const shell = await loadShell();
|
||||
return json({ profiles: await fetchProfiles(profileQuery, shell.admins) });
|
||||
}
|
||||
6
src/routes/api/shell/+server.ts
Normal file
6
src/routes/api/shell/+server.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
import { json } from "@sveltejs/kit";
|
||||
import { loadShell } from "$lib/ssr/shell";
|
||||
|
||||
export async function GET() {
|
||||
return json(await loadShell());
|
||||
}
|
||||
|
|
@ -15,6 +15,7 @@
|
|||
} from "$lib/profiles.svelte";
|
||||
import { partialsStore } from "$lib/partials.svelte";
|
||||
import PostContent from "$lib/components/PostContent.svelte";
|
||||
import { page } from "$app/state";
|
||||
|
||||
const partial = $derived(partialsStore.get("contacts"));
|
||||
|
||||
|
|
@ -48,11 +49,30 @@
|
|||
|
||||
const contacts = $derived<Contact[]>(
|
||||
adminPubkeys.list.map((pk) => {
|
||||
const entry = profileStore.profiles.get(pk);
|
||||
const entry = profileStore.profiles.get(pk) ?? snapshotEntry(pk);
|
||||
return { pubkey: pk, npub: entry?.npub ?? nip19.npubEncode(pk), entry };
|
||||
}),
|
||||
);
|
||||
|
||||
// Server-rendered profile, used until the live one is fetched.
|
||||
function snapshotEntry(pk: string): ProfileEntry | undefined {
|
||||
const u = page.data.profiles?.[pk];
|
||||
if (!u) return undefined;
|
||||
const md = u.metadata ?? {};
|
||||
return {
|
||||
pubkey: pk,
|
||||
npub: u.npub,
|
||||
name: md.name,
|
||||
displayName: md.display_name,
|
||||
nip05: md.nip05,
|
||||
picture: md.picture ?? u.image,
|
||||
about: md.about,
|
||||
website: md.website,
|
||||
lud16: md.lud16,
|
||||
fetchedAt: u.lastUpdated || 0,
|
||||
};
|
||||
}
|
||||
|
||||
function displayName(c: Contact): string {
|
||||
return c.entry?.displayName || c.entry?.name || `${c.npub.slice(0, 12)}…`;
|
||||
}
|
||||
|
|
@ -81,14 +101,18 @@
|
|||
{/if}
|
||||
|
||||
{#if loading}
|
||||
<p class="py-6 text-center text-neutral-400 dark:text-neutral-500">Loading…</p>
|
||||
<p class="py-6 text-center text-neutral-400 dark:text-neutral-500">
|
||||
Loading…
|
||||
</p>
|
||||
{:else if contacts.length === 0}
|
||||
<p class="py-6 text-center text-neutral-400 dark:text-neutral-500">No admins listed.</p>
|
||||
<p class="py-6 text-center text-neutral-400 dark:text-neutral-500">
|
||||
No admins listed.
|
||||
</p>
|
||||
{:else}
|
||||
<ul class="mt-2 space-y-3">
|
||||
{#each contacts as c (c.pubkey)}
|
||||
<li
|
||||
class="rounded-lg border border-neutral-100 dark:border-neutral-800 p-4 shadow-sm"
|
||||
class="rounded-lg border border-neutral-100 p-4 shadow-sm dark:border-neutral-800"
|
||||
>
|
||||
<div class="flex gap-4">
|
||||
{#if c.entry?.picture}
|
||||
|
|
@ -99,7 +123,7 @@
|
|||
/>
|
||||
{:else}
|
||||
<span
|
||||
class="flex h-14 w-14 shrink-0 items-center justify-center rounded-full bg-neutral-200 dark:bg-neutral-700 text-lg font-semibold text-neutral-500 dark:text-neutral-400"
|
||||
class="flex h-14 w-14 shrink-0 items-center justify-center rounded-full bg-neutral-200 text-lg font-semibold text-neutral-500 dark:bg-neutral-700 dark:text-neutral-400"
|
||||
>
|
||||
{displayName(c)[0].toUpperCase()}
|
||||
</span>
|
||||
|
|
@ -107,7 +131,9 @@
|
|||
|
||||
<div class="flex min-w-0 flex-1 items-start justify-between gap-3">
|
||||
<div class="min-w-0">
|
||||
<p class="truncate text-2xl font-medium text-neutral-900 dark:text-neutral-100">
|
||||
<p
|
||||
class="truncate text-2xl font-medium text-neutral-900 dark:text-neutral-100"
|
||||
>
|
||||
{displayName(c)}
|
||||
</p>
|
||||
{#if c.entry?.nip05}
|
||||
|
|
@ -129,7 +155,9 @@
|
|||
|
||||
<div class="mt-3 md:pl-18">
|
||||
{#if c.entry?.about}
|
||||
<p class="whitespace-pre-line text-neutral-600 dark:text-neutral-400">
|
||||
<p
|
||||
class="whitespace-pre-line text-neutral-600 dark:text-neutral-400"
|
||||
>
|
||||
{c.entry.about}
|
||||
</p>
|
||||
{/if}
|
||||
|
|
@ -147,7 +175,10 @@
|
|||
</a>
|
||||
{/if}
|
||||
{#if c.entry?.lud16}
|
||||
<span class="text-neutral-500 dark:text-neutral-400" title="Lightning address">
|
||||
<span
|
||||
class="text-neutral-500 dark:text-neutral-400"
|
||||
title="Lightning address"
|
||||
>
|
||||
⚡ {c.entry.lud16}
|
||||
</span>
|
||||
{/if}
|
||||
|
|
@ -158,7 +189,9 @@
|
|||
{@const rooms = roomsOf(c.pubkey)}
|
||||
{#if rooms.length > 0}
|
||||
<p class="mt-2 text-neutral-500 dark:text-neutral-400">
|
||||
<span class="text-neutral-400 dark:text-neutral-500">Manages:</span>
|
||||
<span class="text-neutral-400 dark:text-neutral-500"
|
||||
>Manages:</span
|
||||
>
|
||||
{#each rooms as r, i}<a
|
||||
href="/room/{r.id}"
|
||||
class="text-accent hover:underline">{r.name}</a
|
||||
|
|
|
|||
6
src/routes/contacts/+page.ts
Normal file
6
src/routes/contacts/+page.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
import { snapshot, type Profiles } from "$lib/forum/snapshot";
|
||||
|
||||
export async function load({ fetch }) {
|
||||
const data = await snapshot<{ profiles: Profiles }>(fetch, "/api/contacts");
|
||||
return { profiles: data?.profiles ?? null };
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue