Render discussion listings and the room overview on the server
This commit is contained in:
parent
c23428f904
commit
2041f6b899
10 changed files with 175 additions and 11 deletions
|
|
@ -3,6 +3,8 @@ import type { NostrUser } from "$lib/gadgets";
|
|||
import type { GroupMetadata, GroupSummary } from "./groups";
|
||||
import type { Resource } from "./resources";
|
||||
import type { Partial } from "./partials";
|
||||
import type { SortMode, ThreadPage } from "./threads";
|
||||
import type { Overview } from "./overview";
|
||||
|
||||
// What the server rendered a page with. Stores treat these as the baseline
|
||||
// (see the `page.data` fallbacks) until their own live fetch lands.
|
||||
|
|
@ -17,6 +19,18 @@ export type ForumShell = {
|
|||
|
||||
export type Profiles = Record<string, NostrUser>;
|
||||
|
||||
export type ThreadsSnapshot = ThreadPage & {
|
||||
groupId: string;
|
||||
sort: SortMode;
|
||||
snapshotAt: number;
|
||||
profiles: Profiles;
|
||||
};
|
||||
|
||||
export type OverviewSnapshot = Overview & {
|
||||
roomIds: string[];
|
||||
profiles: Profiles;
|
||||
};
|
||||
|
||||
type Fetch = typeof fetch;
|
||||
|
||||
// Read a snapshot endpoint. On the server the response is inlined into the
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { page } from "$app/state";
|
||||
import { loadNostrUser, type NostrUser } from "$lib/gadgets";
|
||||
import { ensureForumRelay } from "$lib/relay";
|
||||
import { ingestNostrUser } from "$lib/profiles.svelte";
|
||||
|
|
@ -17,19 +18,26 @@ let recent = $state<RecentThread[]>([]);
|
|||
let profiles = $state<Record<string, NostrUser>>({});
|
||||
let loading = $state(false);
|
||||
let loadedKey = ""; // room-id set last loaded for, to avoid redundant refetches
|
||||
let settled = $state(false); // first live load finished
|
||||
|
||||
// Until the live fetch lands, the server snapshot (if any) stands in.
|
||||
function fallback() {
|
||||
return settled ? null : (page.data.overview ?? null);
|
||||
}
|
||||
|
||||
export const overviewStore = {
|
||||
get activity() {
|
||||
return activity;
|
||||
return fallback()?.activity ?? activity;
|
||||
},
|
||||
get admins() {
|
||||
return admins;
|
||||
return fallback()?.admins ?? admins;
|
||||
},
|
||||
get recent() {
|
||||
return recent;
|
||||
return fallback()?.recent ?? recent;
|
||||
},
|
||||
get profiles() {
|
||||
return profiles;
|
||||
const base = page.data.overview?.profiles;
|
||||
return base ? { ...base, ...profiles } : profiles;
|
||||
},
|
||||
get loading() {
|
||||
return loading;
|
||||
|
|
@ -58,6 +66,7 @@ export async function loadOverview(roomIds: string[]) {
|
|||
activity = o.activity;
|
||||
admins = o.admins;
|
||||
recent = o.recent;
|
||||
settled = true;
|
||||
overviewPubkeys(o).forEach(loadProfile);
|
||||
} finally {
|
||||
loading = false;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { page } from "$app/state";
|
||||
import { loadNostrUser, type NostrUser } from "$lib/gadgets";
|
||||
import { ensureForumRelay } from "$lib/relay";
|
||||
import { ingestNostrUser } from "$lib/profiles.svelte";
|
||||
|
|
@ -21,15 +22,34 @@ let cursor: number | null = null; // sort-key value of the last loaded thread
|
|||
let snapshotAt = 0; // upper time bound, frozen at initial load
|
||||
let reqId = 0; // supersedes in-flight loads when the sort/group changes
|
||||
|
||||
// Which listing the client asked for, and whether its first page is in.
|
||||
let requested = $state<{ groupId: string; sort: SortMode } | null>(null);
|
||||
let settled = $state(false);
|
||||
|
||||
// The server snapshot stands in until the live listing lands, but only for
|
||||
// the listing that was requested: a different room or sort shows "Loading…".
|
||||
function fallback() {
|
||||
const s = page.data.threads;
|
||||
if (!s) return null;
|
||||
if (
|
||||
requested &&
|
||||
(requested.groupId !== s.groupId || requested.sort !== s.sort)
|
||||
)
|
||||
return null;
|
||||
return s;
|
||||
}
|
||||
|
||||
export const threadStore = {
|
||||
get threads() {
|
||||
return threads;
|
||||
if (settled || threads.length > 0) return threads;
|
||||
return fallback()?.threads ?? threads;
|
||||
},
|
||||
get profiles() {
|
||||
return profiles;
|
||||
const base = fallback()?.profiles;
|
||||
return base ? { ...base, ...profiles } : profiles;
|
||||
},
|
||||
get exhausted() {
|
||||
return exhausted;
|
||||
return settled ? exhausted : (fallback()?.done ?? exhausted);
|
||||
},
|
||||
get loading() {
|
||||
return loading;
|
||||
|
|
@ -63,6 +83,7 @@ async function runLoad(append: boolean, groupId: string) {
|
|||
threads = append ? [...threads, ...page.threads] : page.threads;
|
||||
cursor = page.nextCursor ?? cursor;
|
||||
exhausted = page.done || page.threads.length === 0;
|
||||
settled = true;
|
||||
for (const t of page.threads) {
|
||||
loadProfile(t.authorPubkey);
|
||||
loadProfile(t.latestPubkey);
|
||||
|
|
@ -78,6 +99,8 @@ async function runLoad(append: boolean, groupId: string) {
|
|||
|
||||
export async function loadThreads(groupId: string, sort: SortMode = "active") {
|
||||
sortMode = sort;
|
||||
requested = { groupId, sort };
|
||||
settled = false;
|
||||
threads = [];
|
||||
cursor = null;
|
||||
exhausted = false;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue