mirror of
https://github.com/getpaseo/paseo.git
synced 2026-11-06 08:24:46 +00:00
fix(app): keep a workspace's layout when another client restores it during the undo window
The undo window purged tabs and layout whenever it closed after a finished archive. If another client restored the workspace before this client's toast closed, the restored workspace lost its saved arrangement. The archive now commits only if the workspace is still gone when the window closes.
This commit is contained in:
@@ -164,6 +164,24 @@ describe("archiveWorkspaceWithUndo", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps the layout when another client restores the workspace before the toast closes", async () => {
|
||||
const host = createClient();
|
||||
const toast = createToastHost();
|
||||
const committed: string[] = [];
|
||||
|
||||
const archive = startArchive({ host, toast, workspaceId: "a", committed });
|
||||
host.finishArchive("a");
|
||||
await archive;
|
||||
clearWorkspaceArchivePending({ serverId: SERVER_ID, workspaceId: "a" });
|
||||
getHostRuntimeStore().acceptWorkspaceSnapshots(SERVER_ID, [workspace("a")]);
|
||||
toast.timeOut();
|
||||
|
||||
expect({ committed, row: storedWorkspace("a") }).toEqual({
|
||||
committed: [],
|
||||
row: workspace("a"),
|
||||
});
|
||||
});
|
||||
|
||||
it("commits when the archive finishes after its toast already timed out", async () => {
|
||||
const host = createClient();
|
||||
const toast = createToastHost();
|
||||
|
||||
@@ -3,6 +3,7 @@ import { i18n } from "@/i18n/i18next";
|
||||
import type { WorkspaceDescriptor } from "@/stores/session-store";
|
||||
import {
|
||||
beginOptimisticWorkspaceArchive,
|
||||
isWorkspaceListed,
|
||||
undoWorkspaceArchiveOptimistically,
|
||||
type WorkspaceArchiveTarget,
|
||||
} from "@/workspace/workspace-archive";
|
||||
@@ -50,7 +51,7 @@ class UndoWindow {
|
||||
/** The host finished the archive. It commits now if undo is no longer offered. */
|
||||
archiveFinished(): void {
|
||||
this.archived = true;
|
||||
if (this.phase === "closed") this.input.onArchiveCommitted();
|
||||
if (this.phase === "closed") this.commit();
|
||||
}
|
||||
|
||||
/** The archive failed on the host, so there is nothing left to undo. */
|
||||
@@ -63,9 +64,15 @@ class UndoWindow {
|
||||
if (this.phase !== "open") return;
|
||||
this.phase = "closed";
|
||||
this.release();
|
||||
if (this.archived) this.input.onArchiveCommitted();
|
||||
if (this.archived) this.commit();
|
||||
};
|
||||
|
||||
/** Commits unless the workspace is back by now, for example restored from another client. */
|
||||
private commit(): void {
|
||||
if (isWorkspaceListed(this.input.workspace)) return;
|
||||
this.input.onArchiveCommitted();
|
||||
}
|
||||
|
||||
readonly undo = (): boolean => {
|
||||
if (this.phase !== "open") return false;
|
||||
this.phase = "undone";
|
||||
@@ -87,7 +94,8 @@ class UndoWindow {
|
||||
* Archives a workspace and offers to undo it until the toast goes away.
|
||||
*
|
||||
* `onArchiveCommitted` runs once the archive both finished on the host and can no longer be
|
||||
* undone, so local state the undo would want back (tabs, layout) survives the window.
|
||||
* undone, and only if the workspace is still gone then. Local state the undo would want back
|
||||
* (tabs, layout) survives the window, and survives a restore from another client.
|
||||
* Rejects when the archive fails and was not undone; the row is back by then.
|
||||
*/
|
||||
export async function archiveWorkspaceWithUndo(input: {
|
||||
|
||||
@@ -55,15 +55,24 @@ function isWorkspaceArchiveFailure(error: unknown): error is WorkspaceArchiveFai
|
||||
);
|
||||
}
|
||||
|
||||
function hideWorkspaceOptimistically(
|
||||
workspace: WorkspaceArchiveTarget,
|
||||
): OptimisticWorkspaceArchiveSnapshot {
|
||||
function findListedWorkspace(workspace: WorkspaceArchiveTarget): WorkspaceDescriptor | null {
|
||||
const workspaces = useSessionStore.getState().sessions[workspace.serverId]?.workspaces;
|
||||
const workspaceKey = resolveWorkspaceMapKeyByIdentity({
|
||||
workspaces,
|
||||
workspaceId: workspace.workspaceId,
|
||||
});
|
||||
const snapshot = workspaceKey ? (workspaces?.get(workspaceKey) ?? null) : null;
|
||||
return workspaceKey ? (workspaces?.get(workspaceKey) ?? null) : null;
|
||||
}
|
||||
|
||||
/** True when the workspace is in this client's list, for example after another client restored it. */
|
||||
export function isWorkspaceListed(workspace: WorkspaceArchiveTarget): boolean {
|
||||
return findListedWorkspace(workspace) !== null;
|
||||
}
|
||||
|
||||
function hideWorkspaceOptimistically(
|
||||
workspace: WorkspaceArchiveTarget,
|
||||
): OptimisticWorkspaceArchiveSnapshot {
|
||||
const snapshot = findListedWorkspace(workspace);
|
||||
const intent = markWorkspaceArchivePending({
|
||||
serverId: workspace.serverId,
|
||||
workspaceId: workspace.workspaceId,
|
||||
|
||||
Reference in New Issue
Block a user