fix(ui): keep the archive Undo working after leaving the Sessions page (#123749)

The archive toast outlives the Sessions page (toast host is app-shell
level), but its Undo action was gated on the page's request scope, which
dies the moment the operator navigates away — clicking Undo within the 6s
window then silently did nothing. Run the un-archive through the shared
sessions mutations store directly (it already fails closed on connection
replacement and publishes errors), mirroring the always-mounted sidebar
undo sibling.
This commit is contained in:
Peter Steinberger
2026-08-14 11:09:02 -07:00
committed by GitHub
parent b9cf0bb96f
commit 3292ff146c
2 changed files with 44 additions and 11 deletions
@@ -131,6 +131,40 @@ describe("sessions page lifecycle", () => {
);
});
it("keeps the archive Undo working after navigating off the Sessions page", async () => {
const key = "agent:main:navigated";
const patch = vi.fn(async () => ({
ok: true as const,
path: "",
key,
entry: { sessionId: key },
}));
const sessions = createSessions({ patch });
const mutableGateway = createGateway({} as GatewayBrowserClient);
mutableGateway.emit({ sessionKey: key });
const page = await createPage(createContext(mutableGateway.gateway, sessions));
const toast = document.createElement("openclaw-toast-host");
document.body.append(toast);
await toast.updateComplete;
await page.archiveSessionWithUndo({
key,
sessionId: "session-nav",
pinned: false,
} as GatewaySessionRow);
await toast.updateComplete;
// The toast host outlives the page; navigation unmounts the page element.
page.remove();
toast.querySelector<HTMLButtonElement>(".app-toast__action")?.click();
await vi.waitFor(() => expect(patch).toHaveBeenCalledTimes(2));
expect(patch).toHaveBeenNthCalledWith(
2,
key,
{ archived: false },
{ agentId: undefined, expectedSessionId: "session-nav" },
);
});
it("submits one trimmed bounded transcript search and adopts its status", async () => {
const response = deferred<SessionsSearchResult>();
const request = vi.fn(() => response.promise);
+10 -11
View File
@@ -1193,21 +1193,20 @@ class SessionsPage extends OpenClawLightDomElement {
if (result !== "completed" || !this.isRequestScopeCurrent(scope)) {
return;
}
// Undo is captured before showing the toast: the toast host outlives this
// page, so the action must run against the shared mutations store (which
// fails closed on connection replacement) rather than page scope — a
// page-scope check would silently no-op after navigating away.
const agentId = this.sessionAgentId(row.key, scope.context);
showToast({
message: t("sessionsView.sessionArchived"),
actionLabel: t("common.undo"),
onAction: () => {
void (async () => {
if (!this.isRequestScopeCurrent(scope)) {
return;
}
await this.patchSession(
row.key,
{ archived: false, ...(row.pinned === true ? { pinned: true } : {}) },
scope,
row.sessionId,
);
})();
void scope.sessions.patch(
row.key,
{ archived: false, ...(row.pinned === true ? { pinned: true } : {}) },
{ agentId, expectedSessionId: row.sessionId },
);
},
});
}