返回 DeepSeek-Reasonix
useWorktreeMergeCommands.ts
根目录 / desktop / frontend / src / app-runtime / useWorktreeMergeCommands.ts
1 import { useState } from "react";
2 import { useCommittedCommand } from "../lib/useCommittedCommand";
3 import { runWorktreeMergeLifecycle } from "../lib/worktreeMergeLifecycle";
4 import type { WorktreeMergeResult } from "../lib/types";
5 import type { Translator } from "../lib/i18n";
6
7 /**
8 * Owns the worktree merge coordination: the worktreeMergeTabId overlay state
9 * with its open/close commands and the merged-receipt handler that runs the
10 * navigation-intent-gated close/finalize lifecycle. Callers only wire the
11 * returned state and commands into the topicbar and overlay regions.
12 */
13 export function useWorktreeMergeCommands(input: {
14 noteNavigationIntent: () => number;
15 registeredNavigationIntent: (seq: number) => Promise<string | null>;
16 isNavigationIntentCurrent: (seq: number) => boolean;
17 ensureBlankSurface: (scope: string, workspace: string, seq: number) => Promise<any>;
18 seedSource: (tab: any) => void;
19 listTabs: () => Promise<any[]>;
20 closeWorktree: (request: any) => Promise<any>;
21 finalize: (request: any) => Promise<any>;
22 showToast: (message: string, level: "error", options?: { durationMs?: number }) => void;
23 t: Translator;
24 showCleanup: (cleanup: any, t: Translator) => void;
25 }) {
26 const [worktreeMergeTabId, setWorktreeMergeTabId] = useState<string | null>(null);
27
28 const openWorktreeMerge = useCommittedCommand((tabId: string) => setWorktreeMergeTabId(tabId));
29 const closeWorktreeMerge = useCommittedCommand(() => setWorktreeMergeTabId(null));
30
31 const handleWorktreeMerged = useCommittedCommand(async (result: WorktreeMergeResult) => {
32 const tabToClose = worktreeMergeTabId;
33 if (!tabToClose || !result.sourceRoot || !result.worktreeRoot || !result.targetBranch || !result.mergedCommit || !result.worktreeBranch || !result.worktreeHead) {
34 throw new Error(result.error || input.t("worktree.mergeReceiptInvalid"));
35 }
36 const seq = input.noteNavigationIntent();
37 try {
38 const token = await input.registeredNavigationIntent(seq);
39 if (!token || !input.isNavigationIntentCurrent(seq)) {
40 input.showToast(input.t("worktree.navigationChangedPreserved"), "error", { durationMs: 9000 });
41 return;
42 }
43 const lifecycle = await runWorktreeMergeLifecycle(result, tabToClose, token, {
44 ensureSource: (root) => input.ensureBlankSurface("project", root, seq),
45 isNavigationCurrent: () => input.isNavigationIntentCurrent(seq),
46 seedSource: input.seedSource,
47 listTabs: input.listTabs,
48 closeWorktree: input.closeWorktree,
49 finalize: input.finalize,
50 onNavigationPreserved: () => input.showToast(input.t("worktree.navigationChangedPreserved"), "error", { durationMs: 9000 }),
51 onCloseBlocked: () => input.showToast(input.t("worktree.cleanupViewBlocked"), "error", { durationMs: 8000 }),
52 });
53 if (lifecycle.phase === "finalized") input.showCleanup(lifecycle.cleanup, input.t);
54 } catch (error) {
55 input.showToast(`${input.t("worktree.mergeDoneCleanupFailed")} ${error instanceof Error ? error.message : String(error)}`, "error", { durationMs: 9000 });
56 }
57 });
58 return { worktreeMergeTabId, openWorktreeMerge, closeWorktreeMerge, handleWorktreeMerged };
59 }
60
60 lines TYPESCRIPT