From 8b72bef22ed6fef08efdb11df2905f1f4907a060 Mon Sep 17 00:00:00 2001 From: Dorian Date: Fri, 13 Mar 2026 00:10:11 +0000 Subject: [PATCH] fix: query all 3 Nostr relays in parallel, pick latest profile (BUG-F8) fetchNostrProfile now uses Promise.allSettled to query all relays concurrently. Aggregates results with latest-created_at-wins strategy instead of stopping at the first relay that responds. Co-Authored-By: Claude Opus 4.6 --- frontend/src/composables/useNostr.ts | 42 +++++++++++++++++----------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/frontend/src/composables/useNostr.ts b/frontend/src/composables/useNostr.ts index a060568..668aae7 100644 --- a/frontend/src/composables/useNostr.ts +++ b/frontend/src/composables/useNostr.ts @@ -581,26 +581,33 @@ async function fetchNostrProfilePic(pk: string): Promise { return profile?.picture || null } -/** Fetch full Nostr profile (kind:0 metadata) with caching */ +/** Fetch full Nostr profile (kind:0 metadata) with caching. + * Queries all relays in parallel, picks result with latest created_at. */ async function fetchNostrProfile(pk: string): Promise { const cached = profileCache.get(pk) if (cached && Date.now() - cached.ts < PROFILE_TTL) return cached.data - for (const relay of RELAYS) { - try { - const profile = await queryRelayProfile(relay, pk) - if (profile) { - profileCache.set(pk, { data: profile, ts: Date.now() }) - return profile - } - } catch { - continue + const results = await Promise.allSettled( + RELAYS.map(relay => queryRelayProfile(relay, pk)) + ) + + // Pick the profile with the latest created_at (newest wins) + let best: { profile: NostrProfile; createdAt: number } | null = null + for (const r of results) { + if (r.status !== 'fulfilled' || !r.value) continue + if (!best || r.value.createdAt > best.createdAt) { + best = r.value } } + + if (best) { + profileCache.set(pk, { data: best.profile, ts: Date.now() }) + return best.profile + } return null } -function queryRelayProfile(url: string, pk: string): Promise { +function queryRelayProfile(url: string, pk: string): Promise<{ profile: NostrProfile; createdAt: number } | null> { return new Promise((resolve) => { let timedOut = false const timeout = setTimeout(() => { @@ -625,11 +632,14 @@ function queryRelayProfile(url: string, pk: string): Promise