fix: early sentinel trigger + short author prefetch timeout
- Feed sentinel now fires 400px before the bottom (rootMargin: '400px') so the next page fetch starts while the user is still reading, hiding the latency entirely in most cases - Author prefetch timeout cut from 5000ms to 1500ms — relay responds well within that window for cached/known profiles; cards whose authors miss the deadline fall back to individual useAuthor queries rather than blocking the whole page render - Stats prefetch reverted to fire-and-forget — not worth blocking on, useEventStats per-card handles misses with its own skeleton state Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
This commit is contained in:
@@ -42,7 +42,7 @@ export function Feed() {
|
||||
await queryClient.invalidateQueries({ queryKey: ['feed', activeTab] });
|
||||
}, [queryClient, activeTab]);
|
||||
|
||||
const { ref, inView } = useInView();
|
||||
const { ref, inView } = useInView({ rootMargin: '400px' });
|
||||
|
||||
useEffect(() => {
|
||||
if (inView && hasNextPage && !isFetching) {
|
||||
|
||||
+29
-27
@@ -71,34 +71,36 @@ async function prefetchPageData(
|
||||
.map((item) => item.event.id)
|
||||
.filter((id) => queryClient.getQueryData(['event-stats', id]) === undefined);
|
||||
|
||||
await Promise.all([
|
||||
pubkeysToFetch.length > 0
|
||||
? nostr.query(
|
||||
[{ kinds: [0], authors: pubkeysToFetch }],
|
||||
{ signal: AbortSignal.timeout(3000) },
|
||||
).then((profileEvents) => {
|
||||
for (const ev of profileEvents) {
|
||||
let metadata: NostrMetadata | undefined;
|
||||
try { metadata = n.json().pipe(n.metadata()).parse(ev.content); } catch { /* skip */ }
|
||||
seedAuthorCache(queryClient, ev.pubkey, { event: ev, metadata });
|
||||
}
|
||||
}).catch(() => {})
|
||||
: Promise.resolve(),
|
||||
// Await author prefetch with a short deadline — whatever profiles arrive
|
||||
// in time get cached; cards whose authors miss the window fall back to
|
||||
// individual useAuthor queries (acceptable for a minority of cards).
|
||||
if (pubkeysToFetch.length > 0) {
|
||||
await nostr.query(
|
||||
[{ kinds: [0], authors: pubkeysToFetch }],
|
||||
{ signal: AbortSignal.timeout(1500) },
|
||||
).then((profileEvents) => {
|
||||
for (const ev of profileEvents) {
|
||||
let metadata: NostrMetadata | undefined;
|
||||
try { metadata = n.json().pipe(n.metadata()).parse(ev.content); } catch { /* skip */ }
|
||||
seedAuthorCache(queryClient, ev.pubkey, { event: ev, metadata });
|
||||
}
|
||||
}).catch(() => {});
|
||||
}
|
||||
|
||||
eventIdsToFetch.length > 0
|
||||
? nostr.query(
|
||||
[
|
||||
{ kinds: [1, 6, 7, 9735], '#e': eventIdsToFetch, limit: eventIdsToFetch.length * 10 },
|
||||
{ kinds: [1], '#q': eventIdsToFetch, limit: eventIdsToFetch.length * 3 },
|
||||
],
|
||||
{ signal: AbortSignal.timeout(6000) },
|
||||
).then((statEvents) => {
|
||||
for (const id of eventIdsToFetch) {
|
||||
queryClient.setQueryData(['event-stats', id], computePageStats(id, statEvents));
|
||||
}
|
||||
}).catch(() => {})
|
||||
: Promise.resolve(),
|
||||
]);
|
||||
// Stats are fire-and-forget — nice to have pre-cached but not worth blocking on.
|
||||
if (eventIdsToFetch.length > 0) {
|
||||
nostr.query(
|
||||
[
|
||||
{ kinds: [1, 6, 7, 9735], '#e': eventIdsToFetch, limit: eventIdsToFetch.length * 10 },
|
||||
{ kinds: [1], '#q': eventIdsToFetch, limit: eventIdsToFetch.length * 3 },
|
||||
],
|
||||
{ signal: AbortSignal.timeout(6000) },
|
||||
).then((statEvents) => {
|
||||
for (const id of eventIdsToFetch) {
|
||||
queryClient.setQueryData(['event-stats', id], computePageStats(id, statEvents));
|
||||
}
|
||||
}).catch(() => {});
|
||||
}
|
||||
}
|
||||
|
||||
/** Hook to fetch the global or followed feed with infinite scroll pagination. */
|
||||
|
||||
Reference in New Issue
Block a user