Fix Jump to Latest sticking visible while scrolled to the live bottom.
This commit is contained in:
@@ -785,9 +785,16 @@ export function RoomTimeline({ room, eventId, roomInputRef, editor }: RoomTimeli
|
||||
atLiveEndRef.current = liveTimelineLinked && rangeAtEnd;
|
||||
|
||||
// Historical / non-live windows are never "at bottom" of the room.
|
||||
// When we return to the live end, re-check scroll — IntersectionObserver may
|
||||
// not re-fire if the anchor stayed intersecting across the transition.
|
||||
useEffect(() => {
|
||||
if (!liveTimelineLinked || !rangeAtEnd) {
|
||||
setAtBottom(false);
|
||||
return;
|
||||
}
|
||||
const scrollEl = scrollRef.current;
|
||||
if (scrollEl && isScrolledToBottom(scrollEl, 100)) {
|
||||
setAtBottom(true);
|
||||
}
|
||||
}, [liveTimelineLinked, rangeAtEnd]);
|
||||
|
||||
@@ -1034,11 +1041,16 @@ export function RoomTimeline({ room, eventId, roomInputRef, editor }: RoomTimeli
|
||||
if (!targetEntry) return;
|
||||
|
||||
// Leave the live bottom immediately so Jump to Latest stays available.
|
||||
if (!targetEntry.isIntersecting || !atLiveEndRef.current) {
|
||||
// Only intersection clears atBottom here; !atLiveEnd is handled by the
|
||||
// liveTimelineLinked/rangeAtEnd effect so we don't stick false when the
|
||||
// live end reconnects without a new intersection event.
|
||||
if (!targetEntry.isIntersecting) {
|
||||
setAtBottom(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!atLiveEndRef.current) return;
|
||||
|
||||
setAtBottom(true);
|
||||
setLatestUnreadMessage(null);
|
||||
if (document.hasFocus()) {
|
||||
@@ -1145,7 +1157,16 @@ export function RoomTimeline({ room, eventId, roomInputRef, editor }: RoomTimeli
|
||||
const onScroll = () => {
|
||||
if (restoringScrollRef.current) return;
|
||||
window.cancelAnimationFrame(raf);
|
||||
raf = window.requestAnimationFrame(() => persistTimelineScroll(roomId));
|
||||
raf = window.requestAnimationFrame(() => {
|
||||
persistTimelineScroll(roomId);
|
||||
// Keep Jump to Latest in sync with real scroll position when on the live end.
|
||||
// IntersectionObserver alone can miss transitions and leave atBottom stuck.
|
||||
if (atLiveEndRef.current) {
|
||||
const at = isScrolledToBottom(scrollEl, 100);
|
||||
setAtBottom(at);
|
||||
if (at) setLatestUnreadMessage(null);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
scrollEl.addEventListener('scroll', onScroll, { passive: true });
|
||||
@@ -1250,6 +1271,9 @@ export function RoomTimeline({ room, eventId, roomInputRef, editor }: RoomTimeli
|
||||
scrollToBottomRef.current.smooth,
|
||||
scrollToBottomRef.current.force
|
||||
);
|
||||
if (atLiveEndRef.current) {
|
||||
setAtBottom(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [scrollToBottomCount]);
|
||||
@@ -2588,7 +2612,12 @@ export function RoomTimeline({ room, eventId, roomInputRef, editor }: RoomTimeli
|
||||
</MessageBase>
|
||||
</>
|
||||
))}
|
||||
<span ref={atBottomAnchorRef} />
|
||||
{/* 1×1 so IntersectionObserver reliably detects the live bottom */}
|
||||
<span
|
||||
ref={atBottomAnchorRef}
|
||||
aria-hidden
|
||||
style={{ display: 'block', width: 1, height: 1 }}
|
||||
/>
|
||||
</Box>
|
||||
</Scroll>
|
||||
{(showUnreadTop || returnToEventId) && (
|
||||
|
||||
Reference in New Issue
Block a user