Fetch and show real threads
This commit is contained in:
parent
9df8fce89f
commit
75d1d16cf1
4 changed files with 220 additions and 42 deletions
105
src/lib/threads.svelte.ts
Normal file
105
src/lib/threads.svelte.ts
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
import { SimplePool } from "@nostr/tools";
|
||||
import { loadNostrUser, type NostrUser } from "@nostr/gadgets/metadata";
|
||||
import { RELAY_URL, GROUP_ID } from "$lib/config";
|
||||
|
||||
export type ThreadData = {
|
||||
id: string;
|
||||
title: string;
|
||||
authorPubkey: string;
|
||||
createdAt: number;
|
||||
replyCount: number;
|
||||
latestAt: number;
|
||||
latestPubkey: string;
|
||||
replierPubkeys: string[]; // unique reply authors, excl. OP, max 4
|
||||
};
|
||||
|
||||
let threads = $state<ThreadData[]>([]);
|
||||
let profiles = $state<Record<string, NostrUser>>({});
|
||||
|
||||
export const threadStore = {
|
||||
get threads() { return threads; },
|
||||
get profiles() { return profiles; },
|
||||
};
|
||||
|
||||
async function loadProfile(pubkey: string) {
|
||||
if (profiles[pubkey]) return;
|
||||
const user = await loadNostrUser(pubkey);
|
||||
profiles[pubkey] = user;
|
||||
}
|
||||
|
||||
export async function loadThreads() {
|
||||
const pool = new SimplePool();
|
||||
try {
|
||||
// Step 1: fetch threads
|
||||
const events = await pool.querySync([RELAY_URL], {
|
||||
kinds: [11],
|
||||
"#h": [GROUP_ID],
|
||||
limit: 50,
|
||||
});
|
||||
|
||||
events.sort((a, b) => b.created_at - a.created_at);
|
||||
|
||||
threads = events.map((e) => ({
|
||||
id: e.id,
|
||||
title: e.tags.find((t) => t[0] === "title")?.[1] ?? "(untitled)",
|
||||
authorPubkey: e.pubkey,
|
||||
createdAt: e.created_at,
|
||||
replyCount: 0,
|
||||
latestAt: e.created_at,
|
||||
latestPubkey: e.pubkey,
|
||||
replierPubkeys: [],
|
||||
}));
|
||||
|
||||
events.forEach((e) => loadProfile(e.pubkey));
|
||||
|
||||
if (events.length === 0) return;
|
||||
|
||||
// Step 2: fetch replies for all threads in one request
|
||||
const replies = await pool.querySync([RELAY_URL], {
|
||||
kinds: [1111],
|
||||
"#E": events.map((e) => e.id),
|
||||
});
|
||||
|
||||
// Aggregate per-thread reply data
|
||||
const replyMap = new Map<
|
||||
string,
|
||||
{ count: number; latestAt: number; latestPubkey: string; pubkeys: Set<string> }
|
||||
>();
|
||||
|
||||
for (const reply of replies) {
|
||||
const rootId = reply.tags.find((t) => t[0] === "E")?.[1];
|
||||
if (!rootId) continue;
|
||||
if (!replyMap.has(rootId)) {
|
||||
replyMap.set(rootId, {
|
||||
count: 0,
|
||||
latestAt: 0,
|
||||
latestPubkey: reply.pubkey,
|
||||
pubkeys: new Set(),
|
||||
});
|
||||
}
|
||||
const rd = replyMap.get(rootId)!;
|
||||
rd.count++;
|
||||
rd.pubkeys.add(reply.pubkey);
|
||||
if (reply.created_at > rd.latestAt) {
|
||||
rd.latestAt = reply.created_at;
|
||||
rd.latestPubkey = reply.pubkey;
|
||||
}
|
||||
}
|
||||
|
||||
threads = threads.map((t) => {
|
||||
const rd = replyMap.get(t.id);
|
||||
if (!rd) return t;
|
||||
return {
|
||||
...t,
|
||||
replyCount: rd.count,
|
||||
latestAt: rd.latestAt,
|
||||
latestPubkey: rd.latestPubkey,
|
||||
replierPubkeys: [...rd.pubkeys].slice(0, 4),
|
||||
};
|
||||
});
|
||||
|
||||
replies.forEach((r) => loadProfile(r.pubkey));
|
||||
} finally {
|
||||
pool.close([RELAY_URL]);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue