diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/DashboardSidebarWorkspaceItem.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/DashboardSidebarWorkspaceItem.tsx index 66da282a0bf..5c94bd975ea 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/DashboardSidebarWorkspaceItem.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/DashboardSidebarWorkspaceItem.tsx @@ -35,7 +35,7 @@ export function DashboardSidebarWorkspaceItem({ hostIsOnline, name, branch, - isSynced, + pendingTransaction, pullRequest, } = workspace; const isMainWorkspace = workspace.type === "main"; @@ -76,7 +76,7 @@ export function DashboardSidebarWorkspaceItem({ const handleAfterBranchRename = (newBranchName: string) => { v2WorkspaceActions.updateWorkspace(id, { branch: newBranchName }); }; - const isPending = !isSynced; + const isPending = pendingTransaction?.type === "insert"; // Keep the delete dialog outside the hidden wrapper below — the destroy // flow reopens it into an error pane on conflict/teardown-failed. const isDeleting = useDeletingWorkspaces().isDeleting(id); @@ -136,7 +136,7 @@ export function DashboardSidebarWorkspaceItem({ isActive={isActive} workspaceStatus={workspaceStatus} onClick={handleClick} - isSynced={isSynced} + isCreatePending={isPending} pullRequestState={pullRequest?.state ?? null} aria-label={isPending ? `Creating workspace: ${name}` : undefined} /> diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarCollapsedWorkspaceButton/DashboardSidebarCollapsedWorkspaceButton.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarCollapsedWorkspaceButton/DashboardSidebarCollapsedWorkspaceButton.tsx index c0a77fa7084..ed72072df15 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarCollapsedWorkspaceButton/DashboardSidebarCollapsedWorkspaceButton.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarCollapsedWorkspaceButton/DashboardSidebarCollapsedWorkspaceButton.tsx @@ -15,7 +15,7 @@ interface DashboardSidebarCollapsedWorkspaceButtonProps hostIsOnline: boolean | null; isActive: boolean; workspaceStatus?: ActivePaneStatus | null; - isSynced: boolean; + isCreatePending: boolean; pullRequestState?: DashboardSidebarWorkspacePullRequest["state"] | null; } @@ -30,7 +30,7 @@ export const DashboardSidebarCollapsedWorkspaceButton = forwardRef< hostIsOnline, isActive, workspaceStatus = null, - isSynced, + isCreatePending, pullRequestState = null, className, ...props @@ -56,7 +56,7 @@ export const DashboardSidebarCollapsedWorkspaceButton = forwardRef< isActive={isActive} variant="collapsed" workspaceStatus={workspaceStatus} - isSynced={isSynced} + isCreatePending={isCreatePending} pullRequestState={pullRequestState} /> diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarExpandedWorkspaceRow/DashboardSidebarExpandedWorkspaceRow.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarExpandedWorkspaceRow/DashboardSidebarExpandedWorkspaceRow.tsx index 00547e49f2b..6d38dc6eb9b 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarExpandedWorkspaceRow/DashboardSidebarExpandedWorkspaceRow.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarExpandedWorkspaceRow/DashboardSidebarExpandedWorkspaceRow.tsx @@ -81,9 +81,9 @@ export const DashboardSidebarExpandedWorkspaceRow = forwardRef< name, branch, pullRequest, - isSynced, + pendingTransaction, } = workspace; - const isPending = !isSynced; + const isPending = pendingTransaction?.type === "insert"; const showsStandaloneActiveStripe = accentColor == null; const localRef = useRef(null); const openUrl = electronTrpc.external.openUrl.useMutation(); @@ -170,7 +170,7 @@ export const DashboardSidebarExpandedWorkspaceRow = forwardRef< isActive={isActive} variant="expanded" workspaceStatus={workspaceStatus} - isSynced={isSynced} + isCreatePending={isPending} pullRequestState={pullRequest.state} /> @@ -183,7 +183,7 @@ export const DashboardSidebarExpandedWorkspaceRow = forwardRef< isActive={isActive} variant="expanded" workspaceStatus={workspaceStatus} - isSynced={isSynced} + isCreatePending={isPending} pullRequestState={null} /> @@ -265,7 +265,7 @@ export const DashboardSidebarExpandedWorkspaceRow = forwardRef< /> ) )} - {isSynced && ( + {!isPending && (
{shortcutLabel && ( diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceIcon/DashboardSidebarWorkspaceIcon.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceIcon/DashboardSidebarWorkspaceIcon.tsx index 22b734ae3c7..178bdf2e036 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceIcon/DashboardSidebarWorkspaceIcon.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceIcon/DashboardSidebarWorkspaceIcon.tsx @@ -24,7 +24,7 @@ interface DashboardSidebarWorkspaceIconProps { isActive: boolean; variant: "collapsed" | "expanded"; workspaceStatus?: ActivePaneStatus | null; - isSynced: boolean; + isCreatePending: boolean; pullRequestState?: DashboardSidebarWorkspacePullRequest["state"] | null; } @@ -54,7 +54,7 @@ export function DashboardSidebarWorkspaceIcon({ isActive, variant, workspaceStatus = null, - isSynced, + isCreatePending, pullRequestState = null, }: DashboardSidebarWorkspaceIconProps) { const overlayPosition = OVERLAY_POSITION[variant]; @@ -102,7 +102,7 @@ export function DashboardSidebarWorkspaceIcon({ return ( <> - {!isSynced || workspaceStatus === "working" ? ( + {isCreatePending || workspaceStatus === "working" ? ( ) : ( renderPrimaryIcon() diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/hooks/useDashboardSidebarData/useDashboardSidebarData.ts b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/hooks/useDashboardSidebarData/useDashboardSidebarData.ts index 4e9798fb00f..ab22e66a404 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/hooks/useDashboardSidebarData/useDashboardSidebarData.ts +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/hooks/useDashboardSidebarData/useDashboardSidebarData.ts @@ -1,13 +1,14 @@ import { eq } from "@tanstack/db"; import { useLiveQuery } from "@tanstack/react-db"; import { useQueries, useQueryClient } from "@tanstack/react-query"; -import { useCallback, useMemo, useRef } from "react"; +import { useCallback, useEffect, useMemo, useRef } from "react"; import { useRelayUrl } from "renderer/hooks/useRelayUrl"; import { getHostServiceClientByUrl } from "renderer/lib/host-service-client"; import { useDashboardSidebarState } from "renderer/routes/_authenticated/hooks/useDashboardSidebarState"; import { useCollections } from "renderer/routes/_authenticated/providers/CollectionsProvider"; import { getVisibleSidebarWorkspaces } from "renderer/routes/_authenticated/providers/CollectionsProvider/dashboardSidebarLocal"; import { useLocalHostService } from "renderer/routes/_authenticated/providers/LocalHostServiceProvider"; +import { useWorkspaceTransactionsStore } from "renderer/stores/workspace-creates"; import type { DashboardSidebarProject, DashboardSidebarProjectChild, @@ -128,6 +129,12 @@ export function useDashboardSidebarData() { const relayUrl = useRelayUrl(); const { toggleProjectCollapsed } = useDashboardSidebarState(); const queryClient = useQueryClient(); + const workspaceTransactionsById = useWorkspaceTransactionsStore( + (state) => state.byWorkspaceId, + ); + const clearWorkspaceTransaction = useWorkspaceTransactionsStore( + (state) => state.clear, + ); const { data: hosts = [] } = useLiveQuery( (q) => @@ -234,8 +241,9 @@ export function useDashboardSidebarData() { rawSidebarWorkspaces.map((workspace) => ({ ...workspace, hostIsOnline: hostsByMachineId.get(workspace.hostId)?.isOnline ?? false, + pendingTransaction: workspaceTransactionsById[workspace.id] ?? null, })), - [hostsByMachineId, rawSidebarWorkspaces], + [hostsByMachineId, rawSidebarWorkspaces, workspaceTransactionsById], ); const sidebarWorkspaces = useMemo( @@ -274,10 +282,28 @@ export function useDashboardSidebarData() { rawLocalMainWorkspaces.map((workspace) => ({ ...workspace, hostIsOnline: hostsByMachineId.get(workspace.hostId)?.isOnline ?? false, + pendingTransaction: workspaceTransactionsById[workspace.id] ?? null, })), - [hostsByMachineId, rawLocalMainWorkspaces], + [hostsByMachineId, rawLocalMainWorkspaces, workspaceTransactionsById], ); + useEffect(() => { + for (const workspace of [ + ...rawSidebarWorkspaces, + ...rawLocalMainWorkspaces, + ]) { + const transaction = workspaceTransactionsById[workspace.id]; + if (workspace.isSynced && transaction?.type === "insert") { + clearWorkspaceTransaction(workspace.id); + } + } + }, [ + clearWorkspaceTransaction, + rawLocalMainWorkspaces, + rawSidebarWorkspaces, + workspaceTransactionsById, + ]); + const visibleSidebarWorkspaces = useMemo(() => { const sidebarProjectIds = new Set( sidebarProjects.map((project) => project.id), @@ -435,6 +461,7 @@ export function useDashboardSidebarData() { updatedAt: workspace.updatedAt, taskId: workspace.taskId, isSynced: workspace.isSynced, + pendingTransaction: workspace.pendingTransaction, }; if (workspace.sectionId) { diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/hooks/useDashboardSidebarShortcuts/useDashboardSidebarShortcuts.ts b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/hooks/useDashboardSidebarShortcuts/useDashboardSidebarShortcuts.ts index 6e32551f2ac..9aefb29e240 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/hooks/useDashboardSidebarShortcuts/useDashboardSidebarShortcuts.ts +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/hooks/useDashboardSidebarShortcuts/useDashboardSidebarShortcuts.ts @@ -59,7 +59,11 @@ export function useDashboardSidebarShortcuts( () => groups .flatMap((project) => getProjectChildrenWorkspaces(project.children)) - .filter((workspace) => workspace.isSynced && !isDeleting(workspace.id)), + .filter( + (workspace) => + workspace.pendingTransaction?.type !== "insert" && + !isDeleting(workspace.id), + ), [groups, isDeleting], ); const workspaceShortcutLabels = diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/types.ts b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/types.ts index d09d3720862..194ade66306 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/types.ts +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/types.ts @@ -1,3 +1,5 @@ +import type { WorkspaceTransactionSnapshot } from "renderer/stores/workspace-creates"; + export type DashboardSidebarWorkspaceHostType = | "local-device" | "remote-device" @@ -42,6 +44,7 @@ export interface DashboardSidebarWorkspace { updatedAt: Date; taskId: string | null; isSynced: boolean; + pendingTransaction: WorkspaceTransactionSnapshot | null; } export interface DashboardSidebarSection { diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/layout.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/layout.tsx index bcbea18309a..b86ddc55411 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/layout.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/layout.tsx @@ -4,6 +4,7 @@ import { createFileRoute, Outlet, useMatchRoute } from "@tanstack/react-router"; import { useEffect, useRef } from "react"; import { useDashboardSidebarState } from "renderer/routes/_authenticated/hooks/useDashboardSidebarState"; import { useCollections } from "renderer/routes/_authenticated/providers/CollectionsProvider"; +import { useWorkspaceTransactionsStore } from "renderer/stores/workspace-creates"; import { WorkspaceCreateErrorState } from "./components/WorkspaceCreateErrorState"; import { WorkspaceCreatingState } from "./components/WorkspaceCreatingState"; import { WorkspaceHostIncompatibleState } from "./components/WorkspaceHostIncompatibleState"; @@ -26,6 +27,13 @@ function V2WorkspaceLayout() { workspaceMatch !== false ? workspaceMatch.workspaceId : null; const collections = useCollections(); const { ensureWorkspaceInSidebar } = useDashboardSidebarState(); + const pendingTransaction = useWorkspaceTransactionsStore((state) => + workspaceId ? (state.byWorkspaceId[workspaceId] ?? null) : null, + ); + const clearWorkspaceTransaction = useWorkspaceTransactionsStore( + (state) => state.clear, + ); + const isCreatePending = pendingTransaction?.type === "insert"; const { data: workspaces, isReady } = useLiveQuery( (q) => @@ -43,21 +51,29 @@ function V2WorkspaceLayout() { ); const workspace = workspaces?.[0] ?? null; const failedEntry = failedEntries?.[0] ?? null; - const isSynced = workspace?.$synced === true; + const canUseWorkspace = + workspace !== null && + (workspace.$synced === true || pendingTransaction?.type === "update"); + + useEffect(() => { + if (workspace?.$synced === true && pendingTransaction?.type === "insert") { + clearWorkspaceTransaction(workspace.id); + } + }, [clearWorkspaceTransaction, pendingTransaction, workspace]); const lastEnsuredWorkspaceIdRef = useRef(null); useEffect(() => { if ( !workspace || - !isSynced || + !canUseWorkspace || lastEnsuredWorkspaceIdRef.current === workspace.id ) return; lastEnsuredWorkspaceIdRef.current = workspace.id; ensureWorkspaceInSidebar(workspace.id, workspace.projectId); - }, [ensureWorkspaceInSidebar, workspace, isSynced]); + }, [ensureWorkspaceInSidebar, workspace, canUseWorkspace]); - const hostStatus = useRemoteHostStatus(isSynced ? workspace : null); + const hostStatus = useRemoteHostStatus(canUseWorkspace ? workspace : null); if (!workspaceId || !isReady || !workspaces) { return
; @@ -70,7 +86,7 @@ function V2WorkspaceLayout() { return ; } - if (!isSynced) { + if (isCreatePending) { return ( ; + } + if (hostStatus.status === "incompatible") { return ( ; isPersisted: { promise: Promise; }; @@ -73,6 +82,9 @@ function useOptimisticMutationRunner() { export function useOptimisticCollectionActions() { const collections = useCollections(); const runMutation = useOptimisticMutationRunner(); + const trackWorkspaceTransaction = useWorkspaceTransactionsStore( + (state) => state.track, + ); return useMemo(() => { const runTaskMutation = ( @@ -178,29 +190,43 @@ export function useOptimisticCollectionActions() { ), }, v2Workspaces: { - updateWorkspace: (workspaceId: string, patch: V2WorkspacePatch) => - runWorkspaceMutation("Failed to update workspace", () => - collections.v2Workspaces.update(workspaceId, (draft) => { - if (patch.name !== undefined) { - draft.name = patch.name; - } - if (patch.branch !== undefined) { - draft.branch = patch.branch; - } - if (patch.hostId !== undefined) { - draft.hostId = patch.hostId; - } - if (patch.taskId !== undefined) { - draft.taskId = patch.taskId; - } - }), - ), - renameWorkspace: (workspaceId: string, name: string) => - runWorkspaceMutation("Failed to rename workspace", () => - collections.v2Workspaces.update(workspaceId, (draft) => { - draft.name = name; - }), - ), + updateWorkspace: (workspaceId: string, patch: V2WorkspacePatch) => { + const transaction = runWorkspaceMutation( + "Failed to update workspace", + () => + collections.v2Workspaces.update(workspaceId, (draft) => { + if (patch.name !== undefined) { + draft.name = patch.name; + } + if (patch.branch !== undefined) { + draft.branch = patch.branch; + } + if (patch.hostId !== undefined) { + draft.hostId = patch.hostId; + } + if (patch.taskId !== undefined) { + draft.taskId = patch.taskId; + } + }), + ); + if (transaction) { + trackWorkspaceTransaction(workspaceId, transaction); + } + return transaction; + }, + renameWorkspace: (workspaceId: string, name: string) => { + const transaction = runWorkspaceMutation( + "Failed to rename workspace", + () => + collections.v2Workspaces.update(workspaceId, (draft) => { + draft.name = name; + }), + ); + if (transaction) { + trackWorkspaceTransaction(workspaceId, transaction); + } + return transaction; + }, }, chatSessions: { deleteSession: (sessionId: string) => { @@ -249,5 +275,5 @@ export function useOptimisticCollectionActions() { ), }, }; - }, [collections, runMutation]); + }, [collections, runMutation, trackWorkspaceTransaction]); } diff --git a/apps/desktop/src/renderer/stores/workspace-creates/index.ts b/apps/desktop/src/renderer/stores/workspace-creates/index.ts index 2622fd34a90..fc4a1ca8696 100644 --- a/apps/desktop/src/renderer/stores/workspace-creates/index.ts +++ b/apps/desktop/src/renderer/stores/workspace-creates/index.ts @@ -6,3 +6,10 @@ export { useWorkspaceCreates, type WorkspacesCreateInput, } from "./useWorkspaceCreates"; +export { + type TrackableWorkspaceTransactionState, + useWorkspaceTransactionsStore, + type WorkspaceTransactionSnapshot, + type WorkspaceTransactionState, + type WorkspaceTransactionType, +} from "./workspaceTransactions"; diff --git a/apps/desktop/src/renderer/stores/workspace-creates/useWorkspaceCreates.ts b/apps/desktop/src/renderer/stores/workspace-creates/useWorkspaceCreates.ts index c14ea755496..5cc96e6e678 100644 --- a/apps/desktop/src/renderer/stores/workspace-creates/useWorkspaceCreates.ts +++ b/apps/desktop/src/renderer/stores/workspace-creates/useWorkspaceCreates.ts @@ -8,6 +8,7 @@ import { useCollections } from "renderer/routes/_authenticated/providers/Collect import type { WorkspaceCreateMutationMetadata } from "renderer/routes/_authenticated/providers/CollectionsProvider/collections"; import type { WorkspacesCreateInput } from "renderer/routes/_authenticated/providers/CollectionsProvider/dashboardSidebarLocal"; import { useLocalHostService } from "renderer/routes/_authenticated/providers/LocalHostServiceProvider"; +import { useWorkspaceTransactionsStore } from "./workspaceTransactions"; import { writeWorkspacePaneLayout } from "./writeWorkspacePaneLayout"; export type { WorkspacesCreateInput }; @@ -38,6 +39,9 @@ export function useWorkspaceCreates(): UseWorkspaceCreatesApi { const userId = session?.user?.id ?? null; const collections = useCollections(); const relayUrl = useRelayUrl(); + const trackWorkspaceTransaction = useWorkspaceTransactionsStore( + (state) => state.track, + ); const submit = useCallback( (args: SubmitArgs): SubmitHandle => { @@ -115,6 +119,7 @@ export function useWorkspaceCreates(): UseWorkspaceCreatesApi { const transaction = collections.v2Workspaces.insert(optimisticRow, { metadata, }); + trackWorkspaceTransaction(workspaceId, transaction); writeWorkspacePaneLayout( collections, { id: workspaceId, projectId: args.snapshot.projectId }, @@ -157,6 +162,7 @@ export function useWorkspaceCreates(): UseWorkspaceCreatesApi { collections, relayUrl, hostService, + trackWorkspaceTransaction, ], ); diff --git a/apps/desktop/src/renderer/stores/workspace-creates/workspaceTransactions.ts b/apps/desktop/src/renderer/stores/workspace-creates/workspaceTransactions.ts new file mode 100644 index 00000000000..52b165b7905 --- /dev/null +++ b/apps/desktop/src/renderer/stores/workspace-creates/workspaceTransactions.ts @@ -0,0 +1,86 @@ +import { create } from "zustand"; + +export type WorkspaceTransactionType = "insert" | "update" | "delete"; +export type WorkspaceTransactionState = "pending" | "persisting"; +export type TrackableWorkspaceTransactionState = + | WorkspaceTransactionState + | "completed" + | "failed"; + +export interface WorkspaceTransactionSnapshot { + id: string; + workspaceId: string; + type: WorkspaceTransactionType; + state: WorkspaceTransactionState; + createdAt: Date; + updatedAt: Date; +} + +interface TrackableWorkspaceTransaction { + id: string; + state: TrackableWorkspaceTransactionState; + createdAt: Date; + mutations: Array<{ type: WorkspaceTransactionType }>; + isPersisted: { + promise: Promise; + }; +} + +interface WorkspaceTransactionsState { + byWorkspaceId: Record; + track: ( + workspaceId: string, + transaction: TrackableWorkspaceTransaction, + ) => void; + clear: (workspaceId: string) => void; +} + +export const useWorkspaceTransactionsStore = create( + (set, get) => ({ + byWorkspaceId: {}, + track: (workspaceId, transaction) => { + const mutation = transaction.mutations[0]; + if (!mutation) return; + if (transaction.state === "completed" || transaction.state === "failed") { + return; + } + + const writeSnapshot = (state: WorkspaceTransactionState) => { + set((current) => ({ + byWorkspaceId: { + ...current.byWorkspaceId, + [workspaceId]: { + id: transaction.id, + workspaceId, + type: mutation.type, + state, + createdAt: transaction.createdAt, + updatedAt: new Date(), + }, + }, + })); + }; + + writeSnapshot(transaction.state); + + void transaction.isPersisted.promise.then( + () => { + if (get().byWorkspaceId[workspaceId]?.id === transaction.id) { + get().clear(workspaceId); + } + }, + () => { + if (get().byWorkspaceId[workspaceId]?.id === transaction.id) { + get().clear(workspaceId); + } + }, + ); + }, + clear: (workspaceId) => + set((state) => { + if (!state.byWorkspaceId[workspaceId]) return state; + const { [workspaceId]: _removed, ...rest } = state.byWorkspaceId; + return { byWorkspaceId: rest }; + }), + }), +);