import { create } from "zustand"; import { useSessionStore } from "@/stores/session-store"; import { queryClient } from "@/query/query-client"; const SUCCESS_DISPLAY_MS = 1000; export type CheckoutGitActionStatus = "idle" | "pending" | "success"; export type CheckoutGitAsyncActionId = | "commit" | "push" | "create-pr" | "merge-branch" | "merge-from-base" | "archive-worktree"; type CheckoutKey = string; type StatusMap = Partial>; function checkoutKey(serverId: string, cwd: string): CheckoutKey { return `${serverId}::${cwd}`; } function resolveClient(serverId: string) { const session = useSessionStore.getState().sessions[serverId]; const client = session?.client ?? null; if (!client) { throw new Error("Daemon client unavailable"); } return client; } function setStatus( key: CheckoutKey, actionId: CheckoutGitAsyncActionId, status: CheckoutGitActionStatus, ) { useCheckoutGitActionsStore.setState((state) => { const current = state.statusByCheckout[key]?.[actionId] ?? "idle"; if (current === status) { return state; } return { ...state, statusByCheckout: { ...state.statusByCheckout, [key]: { ...(state.statusByCheckout[key] ?? {}), [actionId]: status, }, }, }; }); } function invalidateCheckoutGitQueries(serverId: string, cwd: string) { void queryClient.invalidateQueries({ queryKey: ["checkoutStatus", serverId, cwd], }); void queryClient.invalidateQueries({ predicate: (query) => { const key = query.queryKey; return ( Array.isArray(key) && key[0] === "checkoutDiff" && key[1] === serverId && key[2] === cwd ); }, }); void queryClient.invalidateQueries({ predicate: (query) => { const key = query.queryKey; return ( Array.isArray(key) && key[0] === "checkoutPrStatus" && key[1] === serverId && key[2] === cwd ); }, }); } function invalidateWorktreeList() { void queryClient.invalidateQueries({ predicate: (query) => Array.isArray(query.queryKey) && query.queryKey[0] === "paseoWorktreeList", }); void queryClient.invalidateQueries({ predicate: (query) => Array.isArray(query.queryKey) && query.queryKey[0] === "sidebarPaseoWorktreeList", }); } function removeWorktreeFromCachedLists(input: { serverId: string; worktreePath: string }): void { const serverId = input.serverId.trim(); const worktreePath = input.worktreePath.trim(); if (!serverId || !worktreePath) { return; } const removeFromList = (current: unknown) => { if (!Array.isArray(current)) { return current; } const filtered = current.filter((entry) => entry?.worktreePath !== worktreePath); return filtered.length === current.length ? current : filtered; }; queryClient.setQueriesData( { predicate: (query) => Array.isArray(query.queryKey) && query.queryKey[0] === "paseoWorktreeList" && query.queryKey[1] === serverId, }, removeFromList, ); queryClient.setQueriesData( { predicate: (query) => Array.isArray(query.queryKey) && query.queryKey[0] === "sidebarPaseoWorktreeList" && query.queryKey[1] === serverId, }, removeFromList, ); } const successTimers = new Map>(); const inFlight = new Map>(); function inFlightKey(key: CheckoutKey, actionId: CheckoutGitAsyncActionId): string { return `${key}::${actionId}`; } interface CheckoutGitActionsStoreState { statusByCheckout: Record; getStatus: (params: { serverId: string; cwd: string; actionId: CheckoutGitAsyncActionId; }) => CheckoutGitActionStatus; commit: (params: { serverId: string; cwd: string }) => Promise; push: (params: { serverId: string; cwd: string }) => Promise; createPr: (params: { serverId: string; cwd: string }) => Promise; mergeBranch: (params: { serverId: string; cwd: string; baseRef: string }) => Promise; mergeFromBase: (params: { serverId: string; cwd: string; baseRef: string }) => Promise; archiveWorktree: (params: { serverId: string; cwd: string; worktreePath: string; }) => Promise; } async function runCheckoutAction({ serverId, cwd, actionId, run, }: { serverId: string; cwd: string; actionId: CheckoutGitAsyncActionId; run: () => Promise; }): Promise { const key = checkoutKey(serverId, cwd); const inflightId = inFlightKey(key, actionId); const existing = inFlight.get(inflightId); if (existing) { await existing; return; } const prevTimer = successTimers.get(inflightId); if (prevTimer) { clearTimeout(prevTimer); successTimers.delete(inflightId); } setStatus(key, actionId, "pending"); const promise = (async () => { try { await run(); invalidateCheckoutGitQueries(serverId, cwd); setStatus(key, actionId, "success"); const timer = setTimeout(() => { setStatus(key, actionId, "idle"); successTimers.delete(inflightId); }, SUCCESS_DISPLAY_MS); successTimers.set(inflightId, timer); } catch (error) { setStatus(key, actionId, "idle"); throw error; } finally { inFlight.delete(inflightId); } })(); inFlight.set(inflightId, promise); await promise; } export const useCheckoutGitActionsStore = create()((set, get) => ({ statusByCheckout: {}, getStatus: ({ serverId, cwd, actionId }) => { const key = checkoutKey(serverId, cwd); return get().statusByCheckout[key]?.[actionId] ?? "idle"; }, commit: async ({ serverId, cwd }) => { await runCheckoutAction({ serverId, cwd, actionId: "commit", run: async () => { const client = resolveClient(serverId); const payload = await client.checkoutCommit(cwd, { addAll: true }); if (payload.error) { throw new Error(payload.error.message); } }, }); }, push: async ({ serverId, cwd }) => { await runCheckoutAction({ serverId, cwd, actionId: "push", run: async () => { const client = resolveClient(serverId); const payload = await client.checkoutPush(cwd); if (payload.error) { throw new Error(payload.error.message); } }, }); }, createPr: async ({ serverId, cwd }) => { await runCheckoutAction({ serverId, cwd, actionId: "create-pr", run: async () => { const client = resolveClient(serverId); const payload = await client.checkoutPrCreate(cwd, {}); if (payload.error) { throw new Error(payload.error.message); } }, }); }, mergeBranch: async ({ serverId, cwd, baseRef }) => { await runCheckoutAction({ serverId, cwd, actionId: "merge-branch", run: async () => { const client = resolveClient(serverId); const payload = await client.checkoutMerge(cwd, { baseRef, strategy: "merge", requireCleanTarget: true, }); if (payload.error) { throw new Error(payload.error.message); } }, }); }, mergeFromBase: async ({ serverId, cwd, baseRef }) => { await runCheckoutAction({ serverId, cwd, actionId: "merge-from-base", run: async () => { const client = resolveClient(serverId); const payload = await client.checkoutMergeFromBase(cwd, { baseRef, requireCleanTarget: true, }); if (payload.error) { throw new Error(payload.error.message); } }, }); }, archiveWorktree: async ({ serverId, cwd, worktreePath }) => { await runCheckoutAction({ serverId, cwd, actionId: "archive-worktree", run: async () => { const client = resolveClient(serverId); const payload = await client.archivePaseoWorktree({ worktreePath }); if (payload.error) { throw new Error(payload.error.message); } removeWorktreeFromCachedLists({ serverId, worktreePath }); invalidateWorktreeList(); }, }); }, })); export function __resetCheckoutGitActionsStoreForTests() { for (const timer of successTimers.values()) { clearTimeout(timer); } successTimers.clear(); inFlight.clear(); useCheckoutGitActionsStore.setState({ statusByCheckout: {} }); }