fix(chat): complete initial placement immediately after history refresh settles (#3918)
This commit is contained in:
@@ -1543,6 +1543,21 @@ type InitialScrollPositionEffectParams = Omit<InitialScrollPositionParams, "plac
|
||||
activationPendingRef: React.RefObject<boolean>;
|
||||
};
|
||||
|
||||
function handleEmptyTranscriptPlacement(params: {
|
||||
hasEmptyTranscript: boolean;
|
||||
envSwitchPlacementToken: number | null;
|
||||
historyRefreshPending: boolean;
|
||||
didInitialScroll: React.RefObject<boolean>;
|
||||
activationPendingRef: React.RefObject<boolean>;
|
||||
}): boolean {
|
||||
if (!params.hasEmptyTranscript) return false;
|
||||
if (params.envSwitchPlacementToken !== null && !params.historyRefreshPending) {
|
||||
markInitialScrollConsumed(params.didInitialScroll, params.activationPendingRef);
|
||||
completeEnvSwitchPlacement(params.envSwitchPlacementToken);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function runInitialScrollPositionEffect(
|
||||
params: InitialScrollPositionEffectParams,
|
||||
): void | (() => void) {
|
||||
@@ -1565,13 +1580,13 @@ function runInitialScrollPositionEffect(
|
||||
activationPendingRef,
|
||||
} = params;
|
||||
if (!isVisible || (envSwitchPlacementToken !== null && isRestoringLayout)) return;
|
||||
const hasEmptyTranscript = itemCount === 0;
|
||||
if (envSwitchPlacementToken !== null && hasEmptyTranscript) {
|
||||
if (historyRefreshPending) return;
|
||||
markInitialScrollConsumed(didInitialScroll, activationPendingRef);
|
||||
completeEnvSwitchPlacement(envSwitchPlacementToken);
|
||||
return;
|
||||
}
|
||||
const hasEmptyTranscript = handleEmptyTranscriptPlacement({
|
||||
hasEmptyTranscript: itemCount === 0,
|
||||
envSwitchPlacementToken,
|
||||
historyRefreshPending,
|
||||
didInitialScroll,
|
||||
activationPendingRef,
|
||||
});
|
||||
if (didInitialScroll.current || hasEmptyTranscript) return;
|
||||
const element = scrollRef.current;
|
||||
if (!element) return;
|
||||
@@ -1605,7 +1620,11 @@ function runInitialScrollPositionEffect(
|
||||
if (phase === "provisional") provisionalTokenRef.current = envSwitchPlacementToken;
|
||||
};
|
||||
|
||||
if (activationPendingRef.current) return scheduleAfterPanelRestore(applyInitialScroll);
|
||||
const isEnvSwitchSettling =
|
||||
envSwitchPlacementToken !== null && provisionalTokenRef.current === envSwitchPlacementToken;
|
||||
if (activationPendingRef.current && !isEnvSwitchSettling) {
|
||||
return scheduleAfterPanelRestore(applyInitialScroll);
|
||||
}
|
||||
applyInitialScroll();
|
||||
}
|
||||
|
||||
|
||||
@@ -86,6 +86,7 @@ const HARNESS_RENDER_ERROR = "harness did not render";
|
||||
const NATIVE_SCROLL_MANAGEMENT_TEST_ID = "native-scroll-management-container";
|
||||
const AUTO_SCROLL_CONTAINER_TEST_ID = "auto-scroll-container";
|
||||
const CACHED_MESSAGE_ID = "cached-message";
|
||||
const SETTLED_MESSAGE_ID = "settled-message";
|
||||
const TEST_MESSAGES = [{} as Message];
|
||||
/** Always returns false: the harness never locks programmatic scrolling. */
|
||||
const NEVER_LOCKED = () => false;
|
||||
@@ -412,18 +413,18 @@ describe("useNativeScrollManagement transcript pagination", () => {
|
||||
metrics.scrollHeight = 1400;
|
||||
rerender(
|
||||
<NativeScrollManagementHarness
|
||||
items={[transcriptMessage("settled-message")]}
|
||||
items={[transcriptMessage(SETTLED_MESSAGE_ID)]}
|
||||
metrics={metrics}
|
||||
sessionId="session-b"
|
||||
enabled
|
||||
/>,
|
||||
);
|
||||
act(() => {
|
||||
for (let frame = frames.shift(); frame; frame = frames.shift()) frame(0);
|
||||
});
|
||||
|
||||
expect(scrollContainer.scrollTop).toBe(1400);
|
||||
expect(mockDockviewState.pendingChatInitialPlacement).toBeNull();
|
||||
act(() => {
|
||||
for (let frame = frames.shift(); frame; frame = frames.shift()) frame(0);
|
||||
});
|
||||
} finally {
|
||||
vi.unstubAllGlobals();
|
||||
}
|
||||
@@ -476,17 +477,17 @@ describe("useNativeScrollManagement transcript pagination", () => {
|
||||
metrics.scrollTop = 915;
|
||||
rerender(
|
||||
<NativeScrollManagementHarness
|
||||
items={[transcriptMessage("settled-message")]}
|
||||
items={[transcriptMessage(SETTLED_MESSAGE_ID)]}
|
||||
metrics={metrics}
|
||||
sessionId="session-b"
|
||||
/>,
|
||||
);
|
||||
act(() => {
|
||||
for (let frame = frames.shift(); frame; frame = frames.shift()) frame(0);
|
||||
});
|
||||
|
||||
expect(scrollContainer.scrollTop).toBe(320);
|
||||
expect(scrollContainer.scrollTop).not.toBe(810);
|
||||
act(() => {
|
||||
for (let frame = frames.shift(); frame; frame = frames.shift()) frame(0);
|
||||
});
|
||||
} finally {
|
||||
vi.unstubAllGlobals();
|
||||
}
|
||||
@@ -556,19 +557,19 @@ describe("useNativeScrollManagement transcript pagination", () => {
|
||||
metrics.scrollTop = 480;
|
||||
rerender(
|
||||
<NativeScrollManagementHarness
|
||||
items={[transcriptMessage("settled-message")]}
|
||||
items={[transcriptMessage(SETTLED_MESSAGE_ID)]}
|
||||
metrics={metrics}
|
||||
sessionId="session-b"
|
||||
enabled
|
||||
hasUnreadDivider
|
||||
/>,
|
||||
);
|
||||
act(() => {
|
||||
for (let frame = frames.shift(); frame; frame = frames.shift()) frame(0);
|
||||
});
|
||||
|
||||
expect(metrics.scrollTop).toBe(480);
|
||||
expect(mockDockviewState.pendingChatInitialPlacement).toBeNull();
|
||||
act(() => {
|
||||
for (let frame = frames.shift(); frame; frame = frames.shift()) frame(0);
|
||||
});
|
||||
} finally {
|
||||
vi.unstubAllGlobals();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user