From f1846e06cb96aefb048638fc9859f822603ad665 Mon Sep 17 00:00:00 2001 From: npub1223z34hd7vtwc6qj4s7flsxkj644nlre2nthu7lrrmkumhu3xddsrx9r6w <52a228d6edf316ec6812ac3c9fc0d696ab59fc7954d77e7be31eedcddf91335b@sprout-oss.stage.blox.sqprod.co> Date: Tue, 16 Jun 2026 00:19:54 -0700 Subject: [PATCH] fix(timeline): keep prior batch profiles during users-batch key churn As messages stream in, new author pubkeys join the users-batch query's pubkey set and the queryKey ("users-batch", ...pubkeys) changes. React Query treats the new key as a brand-new query and drops `data` to undefined for a frame before the new batch resolves. That single undefined frame is the root of both regressions tho saw on first-load: (a) Content-bust: the consumer collapses the profile lookup to {} when data is undefined, so already-resolved rows revert to truncated pubkeys and drop their avatars mid-stream. (b) Gaping empty space: the near-empty rebuild hands MessageTimeline a new messages reference, churning the virtualizer's measurement cache and scrollMargin so older rows pop in one at a time above a hole. Adding placeholderData: keepPreviousData (v5 function form) retains the previously resolved batch through the key transition, so settled rows stay settled while the new batch fetches in the background. Verified empirically against the installed query-core v5.100.14: the immediate frame after a new author arrives keeps the prior profiles instead of going undefined. Co-authored-by: Taylor Ho Signed-off-by: Taylor Ho --- desktop/src/features/profile/hooks.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/desktop/src/features/profile/hooks.ts b/desktop/src/features/profile/hooks.ts index 9f1eca8bf..7e08ba1d3 100644 --- a/desktop/src/features/profile/hooks.ts +++ b/desktop/src/features/profile/hooks.ts @@ -1,5 +1,10 @@ import * as React from "react"; -import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; +import { + keepPreviousData, + useMutation, + useQuery, + useQueryClient, +} from "@tanstack/react-query"; import { getProfile, @@ -283,6 +288,15 @@ export function useUsersBatchQuery( queryFn: () => getUsersBatch(normalizedPubkeys), staleTime: 60_000, gcTime: 5 * 60 * 1_000, + // As messages stream in, new author pubkeys join the set and the queryKey + // changes — React Query treats that as a brand-new query and would drop + // `data` to undefined for a frame. That collapses the consumer's profile + // lookup to {}, reverting already-resolved rows to truncated pubkeys with + // no avatar (content-bust) AND handing the timeline a near-empty messages + // array that churns the virtualizer's measurement cache (gaping space). + // keepPreviousData retains the prior resolved batch through the key + // transition so settled rows stay settled while the new batch fetches. + placeholderData: keepPreviousData, }); // Seed individual "user-profile" cache entries so avatar clicks are instant