import { RelayPool } from 'applesauce-relay' import { lastValueFrom, take } from 'rxjs' import { nip19 } from 'nostr-tools' import { AddressPointer } from 'nostr-tools/nip19' import { Helpers } from 'applesauce-core' import { RELAYS } from '../config/relays' import { prioritizeLocalRelays, partitionRelays, createParallelReqStreams } from '../utils/helpers' import { merge, toArray as rxToArray } from 'rxjs' const { getArticleTitle } = Helpers /** * Fetch article title for a single naddr * Returns the title or null if not found/error */ export async function fetchArticleTitle( relayPool: RelayPool, naddr: string ): Promise { try { const decoded = nip19.decode(naddr) if (decoded.type !== 'naddr') { return null } const pointer = decoded.data as AddressPointer // Define relays to query const baseRelays = pointer.relays && pointer.relays.length > 0 ? pointer.relays : RELAYS const orderedRelays = prioritizeLocalRelays(baseRelays) const { local: localRelays, remote: remoteRelays } = partitionRelays(orderedRelays) // Fetch the article event const filter = { kinds: [pointer.kind], authors: [pointer.pubkey], '#d': [pointer.identifier] } // Parallel local+remote: collect up to one event from each const { local$, remote$ } = createParallelReqStreams(relayPool, localRelays, remoteRelays, filter, 1200, 5000) const events = await lastValueFrom( merge(local$.pipe(take(1)), remote$.pipe(take(1))).pipe(rxToArray()) ) as unknown as { created_at: number }[] if (events.length === 0) { return null } // Sort by created_at and take the most recent events.sort((a, b) => b.created_at - a.created_at) const article = events[0] as unknown as Parameters[0] return getArticleTitle(article) || null } catch (err) { console.warn('Failed to fetch article title for', naddr, err) return null } } /** * Fetch titles for multiple naddrs in parallel * Returns a map of naddr -> title */ export async function fetchArticleTitles( relayPool: RelayPool, naddrs: string[] ): Promise> { const titleMap = new Map() // Fetch all titles in parallel const results = await Promise.allSettled( naddrs.map(async (naddr) => { const title = await fetchArticleTitle(relayPool, naddr) return { naddr, title } }) ) // Process results results.forEach((result) => { if (result.status === 'fulfilled' && result.value.title) { titleMap.set(result.value.naddr, result.value.title) } }) return titleMap }