import { useCallback, useEffect, useMemo } from "react"; import { buildWorkspaceAttachmentScopeKey, useWorkspaceAttachmentsStore, } from "@/attachments/workspace-attachments-store"; import { buildReviewDraftKey, buildReviewDraftScopeKey, useInlineReviewController, useResolvedDiffMode, useReviewAttachmentSnapshot, useSetDiffModeOverride, } from "@/review"; import { useCheckoutDiffQuery } from "@/git/use-diff-query"; import { useCheckoutStatusQuery } from "@/git/use-status-query"; interface UseWorkingDiffOptions { serverId: string; workspaceId?: string; cwd: string; ignoreWhitespace: boolean; enabled: boolean; queryScope?: string; } export function useWorkingDiff({ serverId, workspaceId, cwd, ignoreWhitespace, enabled, queryScope, }: UseWorkingDiffOptions) { const { status, isLoading: isStatusLoading, isError: isStatusError, error: statusError, } = useCheckoutStatusQuery({ serverId, cwd }); const gitStatus = status && status.isGit ? status : null; const isGit = Boolean(gitStatus); const notGit = status !== null && !status.isGit && !status.error; const statusErrorMessage = status?.error?.message ?? (isStatusError && statusError instanceof Error ? statusError.message : null); const baseRef = gitStatus?.baseRef ?? undefined; const hasUncommittedChanges = Boolean(gitStatus?.isDirty); const currentBranchName = gitStatus?.currentBranch && gitStatus.currentBranch !== "HEAD" ? gitStatus.currentBranch : null; const reviewDraftScopeKey = useMemo( () => buildReviewDraftScopeKey({ serverId, workspaceId, cwd, baseRef, ignoreWhitespace, }), [baseRef, cwd, ignoreWhitespace, serverId, workspaceId], ); const diffMode = useResolvedDiffMode({ scopeKey: reviewDraftScopeKey, hasUncommittedChanges, }); const setDiffModeOverride = useSetDiffModeOverride(); const selectDiffMode = useCallback( (mode: "uncommitted" | "base") => { setDiffModeOverride({ scopeKey: reviewDraftScopeKey, override: { serverId, cwd, mode, isDirtyAtSelection: hasUncommittedChanges }, }); }, [cwd, hasUncommittedChanges, reviewDraftScopeKey, serverId, setDiffModeOverride], ); const selectUncommitted = useCallback(() => selectDiffMode("uncommitted"), [selectDiffMode]); const selectBase = useCallback(() => selectDiffMode("base"), [selectDiffMode]); const { files, payloadError: diffPayloadError, diffTooLarge, isLoading: isDiffLoading, } = useCheckoutDiffQuery({ serverId, cwd, mode: diffMode, baseRef, ignoreWhitespace, enabled: enabled && isGit, queryScope, }); const reviewDraftKey = useMemo( () => buildReviewDraftKey({ serverId, workspaceId, cwd, mode: diffMode, baseRef, ignoreWhitespace, }), [baseRef, cwd, diffMode, ignoreWhitespace, serverId, workspaceId], ); const reviewActions = useInlineReviewController({ reviewDraftKey }); const reviewAttachment = useReviewAttachmentSnapshot({ key: reviewDraftKey, diffFiles: files, cwd, mode: diffMode, baseRef, }); return { status, isStatusLoading, isGit, notGit, statusErrorMessage, baseRef, currentBranchName, diffMode, selectUncommitted, selectBase, files, diffPayloadError, diffTooLarge, isDiffLoading, reviewActions, reviewAttachment, }; } export function usePublishWorkingDiffAttachment({ serverId, workspaceId, cwd, attachment, enabled, }: { serverId: string; workspaceId?: string; cwd: string; attachment: ReturnType["reviewAttachment"]; enabled: boolean; }) { const scopeKey = useMemo( () => buildWorkspaceAttachmentScopeKey({ serverId, workspaceId, cwd }), [cwd, serverId, workspaceId], ); const setWorkspaceAttachments = useWorkspaceAttachmentsStore( (state) => state.setWorkspaceAttachments, ); const clearWorkspaceAttachments = useWorkspaceAttachmentsStore( (state) => state.clearWorkspaceAttachments, ); useEffect(() => { if (!enabled) { return; } const attachments = attachment ? [attachment] : []; setWorkspaceAttachments({ scopeKey, attachments }); return () => { const current = useWorkspaceAttachmentsStore.getState().attachmentsByScope[scopeKey]; if (current === attachments) { clearWorkspaceAttachments({ scopeKey }); } }; }, [attachment, clearWorkspaceAttachments, enabled, scopeKey, setWorkspaceAttachments]); }