fix(ui): reset session view on stop and exit
This commit is contained in:
@@ -1213,6 +1213,38 @@ test("closing and reopening Model activity preserves the complete activity log",
|
|||||||
expect(useSessionStore.getState().activityLog).toEqual(beforeClose);
|
expect(useSessionStore.getState().activityLog).toEqual(beforeClose);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test.each(["stop", "session_exit"])("%s restores the empty landing with model activity closed", async (exit) => {
|
||||||
|
const close = vi.fn(() => new HttpResponse(null, { status: 204 }));
|
||||||
|
server.use(
|
||||||
|
http.post("/api/sessions/:id/resume", () => resumeResult("s1")),
|
||||||
|
http.post("/api/sessions/:id/close", close),
|
||||||
|
);
|
||||||
|
wrap(adminUser);
|
||||||
|
await userEvent.click(await screen.findByText("Attiva uno"));
|
||||||
|
await userEvent.click(await screen.findByRole("button", { name: "Resume" }));
|
||||||
|
await userEvent.click(screen.getByRole("button", { name: "Show model activity" }));
|
||||||
|
act(() => useSessionStore.getState().setLastUserEntry({ kind: "input", text: "Previous question" }));
|
||||||
|
expect(screen.getByLabelText("Model activity timeline")).toHaveTextContent("Previous question");
|
||||||
|
const source = FakeEventSource.instances.at(-1)!;
|
||||||
|
|
||||||
|
if (exit === "stop") {
|
||||||
|
await userEvent.click(screen.getByRole("button", { name: /stop and save session/i }));
|
||||||
|
await userEvent.click(screen.getAllByRole("button", { name: "Stop & save" })[0]);
|
||||||
|
} else {
|
||||||
|
act(() => source.emit({ type: "system_event", event: "session_exit" }));
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(await screen.findByText(/type your question/i)).toBeInTheDocument();
|
||||||
|
expect(screen.queryByRole("heading", { name: "Model activity" })).not.toBeInTheDocument();
|
||||||
|
expect(screen.getByTestId("app-shell")).toHaveAttribute("data-activity-layout", "closed");
|
||||||
|
expect(screen.queryByRole("complementary", { name: "Session summary" })).not.toBeInTheDocument();
|
||||||
|
expect(screen.getByRole("complementary", { name: "Session navigation" })).toBeVisible();
|
||||||
|
expect(screen.getByRole("textbox", { name: /new question/i })).toHaveValue("");
|
||||||
|
expect(useSessionStore.getState().activityLog).toEqual([]);
|
||||||
|
expect(source.closed).toBe(true);
|
||||||
|
expect(close).toHaveBeenCalledTimes(exit === "stop" ? 1 : 0);
|
||||||
|
});
|
||||||
|
|
||||||
test("a new question after finalization restores the landing and administration navigation", async () => {
|
test("a new question after finalization restores the landing and administration navigation", async () => {
|
||||||
useSessionStore.getState().resetSession();
|
useSessionStore.getState().resetSession();
|
||||||
let finalized = false;
|
let finalized = false;
|
||||||
|
|||||||
@@ -694,6 +694,20 @@ export function AppShell({ canLogout }: AppShellProps) {
|
|||||||
authGeneration,
|
authGeneration,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
function resetSessionView() {
|
||||||
|
invalidateResumeIntent();
|
||||||
|
newSessionOperationRef.current = null;
|
||||||
|
resetSession();
|
||||||
|
selectActiveSession(null);
|
||||||
|
// Panel visibility belongs to the shell, outside the session store.
|
||||||
|
// Clear it too so leaving a session restores the initial landing layout.
|
||||||
|
setPanelSession(null);
|
||||||
|
setShowActivity(false);
|
||||||
|
setCreatingSession(false);
|
||||||
|
setAwaitingQuestion(false);
|
||||||
|
setStopConfirm(false);
|
||||||
|
}
|
||||||
|
|
||||||
// A backend "session_exit" system event (e.g. the replay server emitting it
|
// A backend "session_exit" system event (e.g. the replay server emitting it
|
||||||
// when the reviewer picks "Esci") asks us to leave the live session view and
|
// when the reviewer picks "Esci") asks us to leave the live session view and
|
||||||
// return to the landing state. We deliberately do NOT also POST /close here —
|
// return to the landing state. We deliberately do NOT also POST /close here —
|
||||||
@@ -705,10 +719,7 @@ export function AppShell({ canLogout }: AppShellProps) {
|
|||||||
if (ev === "session_exit") {
|
if (ev === "session_exit") {
|
||||||
// Never let a streamed event terminate the managed Pi child. Only the
|
// Never let a streamed event terminate the managed Pi child. Only the
|
||||||
// explicit “Stop & save” action is allowed to call /close.
|
// explicit “Stop & save” action is allowed to call /close.
|
||||||
invalidateResumeIntent();
|
resetSessionView();
|
||||||
resetSession();
|
|
||||||
selectActiveSession(null);
|
|
||||||
setAwaitingQuestion(false);
|
|
||||||
}
|
}
|
||||||
// The final workflow turn ends with the session already finalized on disk:
|
// The final workflow turn ends with the session already finalized on disk:
|
||||||
// refetch now instead of waiting for the 10s poll, so the completed state
|
// refetch now instead of waiting for the 10s poll, so the completed state
|
||||||
@@ -725,15 +736,8 @@ export function AppShell({ canLogout }: AppShellProps) {
|
|||||||
}
|
}
|
||||||
if (!canLeaveDatabaseManagement()) return;
|
if (!canLeaveDatabaseManagement()) return;
|
||||||
navigate({ surface: "core" }, true);
|
navigate({ surface: "core" }, true);
|
||||||
invalidateResumeIntent();
|
resetSessionView();
|
||||||
newSessionOperationRef.current = null;
|
|
||||||
resetSession();
|
|
||||||
// Restore the landing layout, including the right-hand administration rail.
|
|
||||||
setPanelSession(null);
|
|
||||||
setShowActivity(false);
|
|
||||||
setAwaitingQuestion(true);
|
setAwaitingQuestion(true);
|
||||||
setCreatingSession(false);
|
|
||||||
selectActiveSession(null);
|
|
||||||
// Best effort only: session creation keeps the authoritative readiness gate.
|
// Best effort only: session creation keeps the authoritative readiness gate.
|
||||||
// Composer focus is deliberately independent of this network request.
|
// Composer focus is deliberately independent of this network request.
|
||||||
void prewarmRuntime().catch(() => undefined);
|
void prewarmRuntime().catch(() => undefined);
|
||||||
@@ -781,9 +785,7 @@ export function AppShell({ canLogout }: AppShellProps) {
|
|||||||
} finally {
|
} finally {
|
||||||
if (!isAuthOperationCurrent(guard, { sessionId: id, disposalEpoch: operationEpochRef.current })
|
if (!isAuthOperationCurrent(guard, { sessionId: id, disposalEpoch: operationEpochRef.current })
|
||||||
|| activeSessionIdRef.current !== id) return;
|
|| activeSessionIdRef.current !== id) return;
|
||||||
resetSession();
|
resetSessionView();
|
||||||
selectActiveSession(null);
|
|
||||||
setAwaitingQuestion(false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user