mirror of
https://github.com/block/buzz.git
synced 2026-11-02 07:22:55 +00:00
fix(desktop): preserve buffered channel unread
Co-authored-by: Morgan Martin <morgan@block.xyz> Signed-off-by: Morgan Martin <morgan@block.xyz>
This commit is contained in:
@@ -23,6 +23,7 @@ import { TimelineSkeleton, useTimelineSkeletonRows } from "./TimelineSkeleton";
|
||||
import { TimelineMessageList } from "./TimelineMessageList";
|
||||
import type { TimelineVirtualizerApi } from "./TimelineMessageList";
|
||||
import { useAnchoredScroll } from "./useAnchoredScroll";
|
||||
import { resolveTimelineReadPresence } from "./anchoredScrollPolicy";
|
||||
import { useLoadOlderOnScroll } from "./useLoadOlderOnScroll";
|
||||
import { useBufferedTimelineMessages } from "./useBufferedTimelineMessages";
|
||||
import {
|
||||
@@ -372,8 +373,17 @@ const MessageTimelineBase = React.forwardRef<
|
||||
});
|
||||
|
||||
React.useEffect(() => {
|
||||
onAtBottomChange?.(isAtBottom);
|
||||
}, [isAtBottom, onAtBottomChange]);
|
||||
// Read presence follows the semantic tail, not the physical virtualizer
|
||||
// floor. While live arrivals are buffered, freezing the rendered tail can
|
||||
// make the shortened DOM report physically at-bottom even though the
|
||||
// reader is still scrolled away from the new messages.
|
||||
onAtBottomChange?.(
|
||||
resolveTimelineReadPresence({
|
||||
isPhysicallyAtBottom: isAtBottom,
|
||||
isSemanticallyAtBottom,
|
||||
}),
|
||||
);
|
||||
}, [isAtBottom, isSemanticallyAtBottom, onAtBottomChange]);
|
||||
const hasConfirmedVirtualizerBottomRef = React.useRef(false);
|
||||
const bottomConfirmationChannelRef = React.useRef(channelId);
|
||||
if (bottomConfirmationChannelRef.current !== channelId) {
|
||||
|
||||
@@ -42,6 +42,18 @@ export function settleProgrammaticBottomPin(
|
||||
);
|
||||
}
|
||||
|
||||
export function resolveTimelineReadPresence({
|
||||
isPhysicallyAtBottom: _isPhysicallyAtBottom,
|
||||
isSemanticallyAtBottom,
|
||||
}: {
|
||||
isPhysicallyAtBottom: boolean;
|
||||
isSemanticallyAtBottom: boolean;
|
||||
}): boolean {
|
||||
// A buffered timeline can shorten the rendered model enough that the
|
||||
// virtualizer reports its physical floor while newer messages remain hidden.
|
||||
return isSemanticallyAtBottom;
|
||||
}
|
||||
|
||||
export function shouldSettleForSplitPanel({
|
||||
isAtBottom,
|
||||
splitPanelOpen,
|
||||
|
||||
@@ -3,6 +3,7 @@ import test from "node:test";
|
||||
|
||||
import {
|
||||
getPinnedCenterDrift,
|
||||
resolveTimelineReadPresence,
|
||||
settleProgrammaticBottomPin,
|
||||
shouldIgnorePinnedCenterScroll,
|
||||
shouldSettleForSplitPanel,
|
||||
@@ -23,6 +24,23 @@ function fakeContainer({ clientHeight, scrollHeight, scrollTop }) {
|
||||
};
|
||||
}
|
||||
|
||||
test("buffered arrivals do not become read when the shortened timeline reports its physical floor", () => {
|
||||
assert.equal(
|
||||
resolveTimelineReadPresence({
|
||||
isPhysicallyAtBottom: true,
|
||||
isSemanticallyAtBottom: false,
|
||||
}),
|
||||
false,
|
||||
);
|
||||
assert.equal(
|
||||
resolveTimelineReadPresence({
|
||||
isPhysicallyAtBottom: true,
|
||||
isSemanticallyAtBottom: true,
|
||||
}),
|
||||
true,
|
||||
);
|
||||
});
|
||||
|
||||
test("split panel settles only an already-bottomed timeline", () => {
|
||||
assert.equal(
|
||||
shouldSettleForSplitPanel({ isAtBottom: true, splitPanelOpen: true }),
|
||||
|
||||
Reference in New Issue
Block a user