Add NIP-50 thread search helper
This commit is contained in:
parent
5887a5705e
commit
043be1d3fd
1 changed files with 71 additions and 0 deletions
71
src/lib/search.ts
Normal file
71
src/lib/search.ts
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
import type { Event } from "@nostr/tools/core";
|
||||
import type { Filter } from "@nostr/tools/filter";
|
||||
import { queryForum } from "$lib/relay";
|
||||
import { GROUP_ID, MODE } from "$lib/config";
|
||||
import { groupsStore } from "$lib/groups.svelte";
|
||||
|
||||
export type SearchResult = {
|
||||
threadId: string;
|
||||
title: string;
|
||||
snippet: string;
|
||||
matchKind: "thread" | "reply";
|
||||
createdAt: number;
|
||||
};
|
||||
|
||||
function snippetOf(content: string): string {
|
||||
const flat = content.replace(/\s+/g, " ").trim();
|
||||
return flat.length > 140 ? flat.slice(0, 140) + "…" : flat;
|
||||
}
|
||||
|
||||
// NIP-50 search over the forum relay: thread OPs (kind 11) and replies
|
||||
// (kind 1111), deduped by thread. Simple mode scopes to the single group;
|
||||
// full mode spans every visible room.
|
||||
export async function searchThreads(query: string): Promise<SearchResult[]> {
|
||||
const filter: Filter = { kinds: [11, 1111], search: query, limit: 30 };
|
||||
if (MODE === "simple") filter["#h"] = [GROUP_ID];
|
||||
else if (groupsStore.list.length > 0)
|
||||
filter["#h"] = groupsStore.list.map((g) => g.id);
|
||||
|
||||
const events = await queryForum(filter, { label: "search" });
|
||||
|
||||
const byThread = new Map<string, SearchResult>();
|
||||
const opless: SearchResult[] = [];
|
||||
for (const e of events) {
|
||||
if (e.kind === 11) {
|
||||
const existing = byThread.get(e.id);
|
||||
// An OP match wins over a reply match on the same thread
|
||||
if (existing && existing.matchKind === "thread") continue;
|
||||
byThread.set(e.id, {
|
||||
threadId: e.id,
|
||||
title: e.tags.find((t) => t[0] === "title")?.[1] ?? "(untitled)",
|
||||
snippet: snippetOf(e.content),
|
||||
matchKind: "thread",
|
||||
createdAt: e.created_at,
|
||||
});
|
||||
} else {
|
||||
const root = e.tags.find((t) => t[0] === "E")?.[1];
|
||||
if (!root || byThread.has(root)) continue;
|
||||
const r: SearchResult = {
|
||||
threadId: root,
|
||||
title: "",
|
||||
snippet: snippetOf(e.content),
|
||||
matchKind: "reply",
|
||||
createdAt: e.created_at,
|
||||
};
|
||||
byThread.set(root, r);
|
||||
opless.push(r);
|
||||
}
|
||||
}
|
||||
|
||||
// Backfill titles for reply-only matches; the group relay truncates
|
||||
// multi-id queries, so fetch each OP on its own
|
||||
await Promise.all(
|
||||
opless.map(async (r) => {
|
||||
const ops = await queryForum({ kinds: [11], ids: [r.threadId] });
|
||||
const op: Event | undefined = ops[0];
|
||||
r.title = op?.tags.find((t) => t[0] === "title")?.[1] ?? "(untitled)";
|
||||
}),
|
||||
);
|
||||
|
||||
return [...byThread.values()].sort((a, b) => b.createdAt - a.createdAt);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue