Fix generic npub names: query profile aggregators in parallel in useAuthor

Previously useAuthor only hit the global profile-aggregator relays as a
fallback when the main pool returned zero kind 0 events. On the curated skate
relay many followed users have no profile, so names fell back to genUserName.
Now query the main pool and aggregators (purplepag.es, nostr.band, nos.lol,
damus) in parallel and keep the newest kind 0, so real display names resolve.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
This commit is contained in:
shakespeare.diy 2026-07-25 14:01:55 +07:00
parent 90d300d5dd
commit a46be4be68

View file

@ -23,25 +23,38 @@ export function useAuthor(pubkey: string | undefined) {
const querySignal = AbortSignal.any([signal, AbortSignal.timeout(15000)]); const querySignal = AbortSignal.any([signal, AbortSignal.timeout(15000)]);
try { try {
// Try main relays first // Query the main pool AND the global profile aggregators IN PARALLEL,
let [event] = await nostr.query( // then keep the NEWEST kind 0 across all of them. Doing both at once
// (rather than only falling back when the main pool is empty) means a
// followed user's real name resolves even when their profile lives on a
// big indexer but not on the app's curated skate relay — the previous
// "generic npub name" bug.
const results = await Promise.allSettled([
nostr.query(
[{ kinds: [0], authors: [pubkey], limit: 1 }], [{ kinds: [0], authors: [pubkey], limit: 1 }],
{ signal: querySignal }, { signal: querySignal },
); ),
(async () => {
// If not found, try fallback profile aggregator relays
if (!event) {
try {
const fallbackGroup = nostr.group(FALLBACK_RELAYS); const fallbackGroup = nostr.group(FALLBACK_RELAYS);
[event] = await fallbackGroup.query( return fallbackGroup.query(
[{ kinds: [0], authors: [pubkey], limit: 1 }], [{ kinds: [0], authors: [pubkey], limit: 1 }],
{ signal: AbortSignal.timeout(5000) }, { signal: AbortSignal.timeout(8000) },
); );
} catch { })(),
// Fallback failed, continue with no event ]);
// Collect every kind 0 event we found and pick the most recent one.
const candidates: NostrEvent[] = [];
for (const r of results) {
if (r.status === 'fulfilled' && Array.isArray(r.value)) {
candidates.push(...r.value);
} }
} }
const event = candidates
.filter((e) => e.kind === 0 && e.pubkey === pubkey)
.sort((a, b) => b.created_at - a.created_at)[0];
if (!event) { if (!event) {
// No profile found - this is okay, user may not have published one yet // No profile found - this is okay, user may not have published one yet
return {}; return {};