From 13fc22fae4f954fa3ec126a0856bb87336159ca8 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sat, 21 Feb 2026 14:20:53 -0600 Subject: [PATCH] Seed event cache from feed items so useEvent resolves instantly --- src/hooks/useEvent.ts | 9 ++++----- src/hooks/useFeed.ts | 19 +++++++++++++++---- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/hooks/useEvent.ts b/src/hooks/useEvent.ts index 7d695faf..d61c0863 100644 --- a/src/hooks/useEvent.ts +++ b/src/hooks/useEvent.ts @@ -52,19 +52,18 @@ export function useEvent(eventId: string | undefined, relays?: string[], authorH return useQuery({ queryKey: ['event', eventId ?? ''], - queryFn: async ({ signal }) => { + queryFn: async () => { if (!eventId) return null; - const querySignal = AbortSignal.any([signal, AbortSignal.timeout(5000)]); const filter: NostrFilter[] = [{ ids: [eventId], limit: 1 }]; // 1. Query the user's configured relays first - const events = await nostr.query(filter, { signal: querySignal }); + const events = await nostr.query(filter, { signal: AbortSignal.timeout(1000) }); if (events.length > 0) return events[0]; // 2. If not found and we have relay hints, try those relays directly if (relays && relays.length > 0) { try { - const hintSignal = AbortSignal.any([signal, AbortSignal.timeout(5000)]); + const hintSignal = AbortSignal.timeout(1000); const hintEvents = await nostr.group(relays).query(filter, { signal: hintSignal }); if (hintEvents.length > 0) return hintEvents[0]; } catch { @@ -75,7 +74,7 @@ export function useEvent(eventId: string | undefined, relays?: string[], authorH // 3. Last resort: if we have the author's pubkey, fetch their NIP-65 relay // list and try their write relays (where they publish content) if (authorHint) { - const found = await queryAuthorRelays(nostr, authorHint, filter, signal); + const found = await queryAuthorRelays(nostr, authorHint, filter, AbortSignal.timeout(1000)); if (found) return found; } diff --git a/src/hooks/useFeed.ts b/src/hooks/useFeed.ts index a8251409..06593e79 100644 --- a/src/hooks/useFeed.ts +++ b/src/hooks/useFeed.ts @@ -133,6 +133,15 @@ export function useFeed(tab: 'follows' | 'global' | 'communities') { return [...pubkeys]; } + /** Seed the `['event', id]` query cache with events we already have in hand. */ + function cacheEvents(items: FeedItem[]): void { + for (const { event } of items) { + if (!queryClient.getQueryData(['event', event.id])) { + queryClient.setQueryData(['event', event.id], event); + } + } + } + /** * Fetch kind 0 metadata for the given pubkeys and seed each into the * individual `['author', pubkey]` query cache so that subsequent @@ -286,8 +295,8 @@ export function useFeed(tab: 'follows' | 'global' | 'communities') { const dedupedItems = Array.from(seen.values()).sort((a, b) => b.sortTimestamp - a.sortTimestamp); - // Cache kind 0 for any remaining authors and mentioned pubkeys - // not already covered by the NIP-05 metadata fetch above. + // Seed event and author caches so downstream hooks resolve instantly. + cacheEvents(dedupedItems); await fetchAndCacheAuthors(collectPubkeys(dedupedItems)); return { items: dedupedItems, oldestQueryTimestamp }; @@ -368,7 +377,8 @@ export function useFeed(tab: 'follows' | 'global' | 'communities') { const dedupedItems = Array.from(seen.values()).sort((a, b) => b.sortTimestamp - a.sortTimestamp); - // Fetch and cache kind 0 profiles for all authors and mentioned pubkeys + // Seed event and author caches so downstream hooks resolve instantly. + cacheEvents(dedupedItems); await fetchAndCacheAuthors(collectPubkeys(dedupedItems)); return { items: dedupedItems, oldestQueryTimestamp }; @@ -397,7 +407,8 @@ export function useFeed(tab: 'follows' | 'global' | 'communities') { .sort((a, b) => b.created_at - a.created_at) .map((ev) => ({ event: ev, sortTimestamp: ev.created_at })); - // Fetch and cache kind 0 profiles for all authors and mentioned pubkeys + // Seed event and author caches so downstream hooks resolve instantly. + cacheEvents(items); await fetchAndCacheAuthors(collectPubkeys(items)); return { items, oldestQueryTimestamp };