From d5ef8982955ce9fa25888e8df2a885d3a208bf55 Mon Sep 17 00:00:00 2001 From: npub1t2tgm7d8f995uqvmnm8h88sg3wnpp9a5xysjf6dg3tjmgt3ltulqdp8ehr <5a968df9a7494b4e019b9ecf739e088ba61097b4312124e9a88ae5b42e3f5f3e@sprout-oss.stage.blox.sqprod.co> Date: Fri, 19 Jun 2026 13:46:43 -0400 Subject: [PATCH] fix(desktop): remount timeline scroll node per channel Co-authored-by: npub1t2tgm7d8f995uqvmnm8h88sg3wnpp9a5xysjf6dg3tjmgt3ltulqdp8ehr <5a968df9a7494b4e019b9ecf739e088ba61097b4312124e9a88ae5b42e3f5f3e@sprout-oss.stage.blox.sqprod.co> Signed-off-by: npub1t2tgm7d8f995uqvmnm8h88sg3wnpp9a5xysjf6dg3tjmgt3ltulqdp8ehr <5a968df9a7494b4e019b9ecf739e088ba61097b4312124e9a88ae5b42e3f5f3e@sprout-oss.stage.blox.sqprod.co> --- desktop/src/features/channels/ui/ChannelPane.tsx | 2 +- desktop/src/features/messages/ui/MessageTimeline.tsx | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/desktop/src/features/channels/ui/ChannelPane.tsx b/desktop/src/features/channels/ui/ChannelPane.tsx index 965f1e3b3ad..c41ada17d18 100644 --- a/desktop/src/features/channels/ui/ChannelPane.tsx +++ b/desktop/src/features/channels/ui/ChannelPane.tsx @@ -262,7 +262,7 @@ export const ChannelPane = React.memo(function ChannelPane({ useComposerHeightPadding( timelineScrollRef, composerWrapperRef, - `${isSinglePanelView}:${hasMainComposerOverlay}`, + `${activeChannelId}:${isSinglePanelView}:${hasMainComposerOverlay}`, ); const clearWelcomeComposerDismissTimer = React.useCallback(() => { diff --git a/desktop/src/features/messages/ui/MessageTimeline.tsx b/desktop/src/features/messages/ui/MessageTimeline.tsx index e53fbdcd50c..19a37ddf800 100644 --- a/desktop/src/features/messages/ui/MessageTimeline.tsx +++ b/desktop/src/features/messages/ui/MessageTimeline.tsx @@ -205,6 +205,12 @@ const MessageTimelineBase = React.forwardRef< const scrollRestorationId = targetMessageId ? `message-timeline:${channelId ?? "none"}:target:${targetMessageId}` : `message-timeline:${channelId ?? "none"}`; + // Keep the scroll node's DOM lifetime scoped to a channel. TanStack Router's + // scroll-restoration listener runs outside React and may write a saved + // scrollTop into the current scroll element during navigation; reusing the + // same node across channel routes can leave the newly-loaded message list + // painted at a stale offset until the user's next scroll event forces layout. + const scrollContainerDomKey = channelId ?? "none"; const timelineBodySurface = selectTimelineBodySurface({ deferredCount: deferredMessages.length, @@ -339,6 +345,7 @@ const MessageTimelineBase = React.forwardRef< )} data-scroll-restoration-id={scrollRestorationId} data-testid="message-timeline" + key={scrollContainerDomKey} onScroll={onScroll} ref={scrollContainerRef} >