From 4aba8e3cca10a66447dc59dd00fd21c1a881c9af Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 12 Jan 2026 14:14:38 +0000 Subject: [PATCH] refactor: consolidate NIP-53 chat and zap into single REQ - Use single filter with kinds [1311, 9735] instead of separate subscriptions with combineLatest - Enables proper pagination for "load older" with single page fetches - Filter invalid zaps inline during event mapping --- src/lib/chat/adapters/nip-53-adapter.ts | 79 +++++++++---------------- 1 file changed, 27 insertions(+), 52 deletions(-) diff --git a/src/lib/chat/adapters/nip-53-adapter.ts b/src/lib/chat/adapters/nip-53-adapter.ts index 15c0b6f..9d05b40 100644 --- a/src/lib/chat/adapters/nip-53-adapter.ts +++ b/src/lib/chat/adapters/nip-53-adapter.ts @@ -1,4 +1,4 @@ -import { Observable, combineLatest } from "rxjs"; +import { Observable } from "rxjs"; import { map, first } from "rxjs/operators"; import type { Filter } from "nostr-tools"; import { nip19 } from "nostr-tools"; @@ -254,80 +254,55 @@ export class Nip53Adapter extends ChatProtocolAdapter { `[NIP-53] Loading messages for ${aTagValue} from ${relays.length} relays`, ); - // Filter for live chat messages (kind 1311) - const chatFilter: Filter = { - kinds: [1311], - "#a": [aTagValue], - limit: options?.limit || 50, - }; - - // Filter for zaps (kind 9735) targeting this activity - const zapFilter: Filter = { - kinds: [9735], + // Single filter for live chat messages (kind 1311) and zaps (kind 9735) + const filter: Filter = { + kinds: [1311, 9735], "#a": [aTagValue], limit: options?.limit || 50, }; if (options?.before) { - chatFilter.until = options.before; - zapFilter.until = options.before; + filter.until = options.before; } if (options?.after) { - chatFilter.since = options.after; - zapFilter.since = options.after; + filter.since = options.after; } - // Start persistent subscriptions to the relays for both chat and zaps + // Start a persistent subscription to the relays pool - .subscription(relays, [chatFilter], { + .subscription(relays, [filter], { eventStore, }) .subscribe({ next: (response) => { if (typeof response === "string") { - console.log("[NIP-53] EOSE received for messages"); + console.log("[NIP-53] EOSE received"); } else { console.log( - `[NIP-53] Received message: ${response.id.slice(0, 8)}...`, + `[NIP-53] Received event k${response.kind}: ${response.id.slice(0, 8)}...`, ); } }, }); - pool - .subscription(relays, [zapFilter], { - eventStore, - }) - .subscribe({ - next: (response) => { - if (typeof response === "string") { - console.log("[NIP-53] EOSE received for zaps"); - } else { - console.log(`[NIP-53] Received zap: ${response.id.slice(0, 8)}...`); - } - }, - }); + // Return observable from EventStore which will update automatically + return eventStore.timeline(filter).pipe( + map((events) => { + const messages = events + .map((event) => { + // Convert zaps (kind 9735) using zapToMessage + if (event.kind === 9735) { + // Only include valid zaps + if (!isValidZap(event)) return null; + return this.zapToMessage(event, conversation.id); + } + // All other events (kind 1311) use eventToMessage + return this.eventToMessage(event, conversation.id); + }) + .filter((msg): msg is Message => msg !== null); - // Combine chat messages and zaps from EventStore - const chatMessages$ = eventStore.timeline(chatFilter); - const zapMessages$ = eventStore.timeline(zapFilter); - - return combineLatest([chatMessages$, zapMessages$]).pipe( - map(([chatEvents, zapEvents]) => { - const chatMsgs = chatEvents.map((event) => - this.eventToMessage(event, conversation.id), - ); - - const zapMsgs = zapEvents - .filter((event) => isValidZap(event)) - .map((event) => this.zapToMessage(event, conversation.id)); - - const allMessages = [...chatMsgs, ...zapMsgs]; - console.log( - `[NIP-53] Timeline has ${chatMsgs.length} messages, ${zapMsgs.length} zaps`, - ); - - return allMessages.sort((a, b) => a.timestamp - b.timestamp); + console.log(`[NIP-53] Timeline has ${messages.length} events`); + return messages.sort((a, b) => a.timestamp - b.timestamp); }), ); }