refactor(app): extract workspace archive transaction (#848)
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
import {
|
||||
clearWorkspaceArchivePending,
|
||||
markWorkspaceArchivePending,
|
||||
} from "@/contexts/session-workspace-upserts";
|
||||
import { useSessionStore, type WorkspaceDescriptor } from "@/stores/session-store";
|
||||
import { resolveWorkspaceMapKeyByIdentity } from "@/utils/workspace-execution";
|
||||
|
||||
export interface WorkspaceArchiveTarget {
|
||||
serverId: string;
|
||||
workspaceId: string;
|
||||
workspaceDirectory?: string | null;
|
||||
}
|
||||
|
||||
interface WorkspaceArchiveClient {
|
||||
archiveWorkspace: (workspaceId: string) => Promise<{ error: string | null }>;
|
||||
}
|
||||
|
||||
interface OptimisticWorkspaceArchiveSnapshot {
|
||||
workspace: WorkspaceDescriptor | null;
|
||||
}
|
||||
|
||||
export interface WorkspaceArchiveFailure {
|
||||
workspaceId: string;
|
||||
error: unknown;
|
||||
}
|
||||
|
||||
function isWorkspaceArchiveFailure(error: unknown): error is WorkspaceArchiveFailure {
|
||||
return (
|
||||
typeof error === "object" &&
|
||||
error !== null &&
|
||||
"workspaceId" in error &&
|
||||
typeof error.workspaceId === "string" &&
|
||||
"error" in error
|
||||
);
|
||||
}
|
||||
|
||||
function hideWorkspaceOptimistically(
|
||||
workspace: WorkspaceArchiveTarget,
|
||||
): OptimisticWorkspaceArchiveSnapshot {
|
||||
const workspaces = useSessionStore.getState().sessions[workspace.serverId]?.workspaces;
|
||||
const workspaceKey = resolveWorkspaceMapKeyByIdentity({
|
||||
workspaces,
|
||||
workspaceId: workspace.workspaceId,
|
||||
});
|
||||
const snapshot = workspaceKey ? (workspaces?.get(workspaceKey) ?? null) : null;
|
||||
markWorkspaceArchivePending({
|
||||
serverId: workspace.serverId,
|
||||
workspaceId: workspace.workspaceId,
|
||||
workspaceDirectory: workspace.workspaceDirectory,
|
||||
});
|
||||
useSessionStore.getState().removeWorkspace(workspace.serverId, workspace.workspaceId);
|
||||
return { workspace: snapshot };
|
||||
}
|
||||
|
||||
function restoreOptimisticallyHiddenWorkspace(input: {
|
||||
serverId: string;
|
||||
workspaceId: string;
|
||||
snapshot: OptimisticWorkspaceArchiveSnapshot;
|
||||
}): void {
|
||||
clearWorkspaceArchivePending({
|
||||
serverId: input.serverId,
|
||||
workspaceId: input.workspaceId,
|
||||
});
|
||||
if (input.snapshot.workspace) {
|
||||
useSessionStore.getState().mergeWorkspaces(input.serverId, [input.snapshot.workspace]);
|
||||
}
|
||||
}
|
||||
|
||||
async function archiveWorkspaceOrThrow(input: {
|
||||
client: WorkspaceArchiveClient;
|
||||
workspaceId: string;
|
||||
}): Promise<void> {
|
||||
const payload = await input.client.archiveWorkspace(input.workspaceId);
|
||||
if (payload.error) {
|
||||
throw new Error(payload.error);
|
||||
}
|
||||
}
|
||||
|
||||
export async function archiveWorkspaceOptimistically(input: {
|
||||
client: WorkspaceArchiveClient;
|
||||
workspace: WorkspaceArchiveTarget;
|
||||
afterHide?: () => void;
|
||||
}): Promise<void> {
|
||||
const snapshot = hideWorkspaceOptimistically(input.workspace);
|
||||
input.afterHide?.();
|
||||
|
||||
try {
|
||||
await archiveWorkspaceOrThrow({
|
||||
client: input.client,
|
||||
workspaceId: input.workspace.workspaceId,
|
||||
});
|
||||
} catch (error) {
|
||||
restoreOptimisticallyHiddenWorkspace({
|
||||
serverId: input.workspace.serverId,
|
||||
workspaceId: input.workspace.workspaceId,
|
||||
snapshot,
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export async function archiveWorkspacesOptimistically(input: {
|
||||
client: WorkspaceArchiveClient;
|
||||
workspaces: WorkspaceArchiveTarget[];
|
||||
}): Promise<WorkspaceArchiveFailure[]> {
|
||||
const results = await Promise.allSettled(
|
||||
input.workspaces.map(async (workspace) => {
|
||||
try {
|
||||
await archiveWorkspaceOptimistically({
|
||||
client: input.client,
|
||||
workspace,
|
||||
});
|
||||
} catch (error) {
|
||||
throw { workspaceId: workspace.workspaceId, error } satisfies WorkspaceArchiveFailure;
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
return results.flatMap((result) =>
|
||||
result.status === "rejected" && isWorkspaceArchiveFailure(result.reason) ? [result.reason] : [],
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user