import { useRouter } from "expo-router"; import { useCallback, useState } from "react"; import { Pressable, ScrollView, View, useColorScheme } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { useThemeColor } from "../../../lib/useThemeColor"; import { AppText as Text, AppTextInput as TextInput } from "../../../components/AppText"; import { useVcsStatus } from "../../../state/use-vcs-status"; import { useThreadSelection } from "../../../state/use-thread-selection"; import { useSelectedThreadGitActions } from "../../../state/use-selected-thread-git-actions"; import { useSelectedThreadGitState } from "../../../state/use-selected-thread-git-state"; import { useSelectedThreadWorktree } from "../../../state/use-selected-thread-worktree"; import { SheetActionButton } from "./gitSheetComponents"; export function GitCommitSheet() { const router = useRouter(); const insets = useSafeAreaInsets(); const isDarkMode = useColorScheme() === "dark"; const { selectedThread } = useThreadSelection(); const { selectedThreadCwd } = useSelectedThreadWorktree(); const gitState = useSelectedThreadGitState(); const gitActions = useSelectedThreadGitActions(); const borderColor = useThemeColor("--color-border"); const borderSubtleColor = useThemeColor("--color-border-subtle"); const inputBorderColor = useThemeColor("--color-input-border"); const inputBg = useThemeColor("--color-input"); const foregroundColor = useThemeColor("--color-foreground"); const gitStatus = useVcsStatus({ environmentId: selectedThread?.environmentId ?? null, cwd: selectedThreadCwd, }); const busy = gitState.gitOperationLabel !== null; const isDefaultRef = gitStatus.data?.isDefaultRef ?? false; const allFiles = gitStatus.data?.workingTree?.files ?? []; const [dialogCommitMessage, setDialogCommitMessage] = useState(""); const [excludedFiles, setExcludedFiles] = useState>(new Set()); const [isEditingFiles, setIsEditingFiles] = useState(false); const selectedFiles = allFiles.filter((file) => !excludedFiles.has(file.path)); const allSelected = excludedFiles.size === 0; const noneSelected = selectedFiles.length === 0; const selectedInsertions = selectedFiles.reduce((sum, file) => sum + file.insertions, 0); const selectedDeletions = selectedFiles.reduce((sum, file) => sum + file.deletions, 0); const selectedFilePreview = selectedFiles.slice(0, 3); const runCommitAction = useCallback( async (featureBranch: boolean) => { const commitMessage = dialogCommitMessage.trim(); router.dismiss(); await gitActions.onRunSelectedThreadGitAction({ action: "commit", featureBranch, ...(commitMessage ? { commitMessage } : {}), ...(!allSelected ? { filePaths: selectedFiles.map((file) => file.path) } : {}), }); }, [allSelected, dialogCommitMessage, gitActions, router, selectedFiles], ); return ( Branch {gitStatus.data?.refName ?? "(detached HEAD)"} {isDefaultRef ? ( Warning: this is the default branch. ) : null} Files {selectedFiles.length} selected ยท +{selectedInsertions} / -{selectedDeletions} {!allSelected && isEditingFiles ? ( setExcludedFiles(new Set())} > Reset ) : null} setIsEditingFiles((current) => !current)} > {isEditingFiles ? "Done" : "Edit"} {allFiles.length === 0 ? ( No changed files are available to commit. ) : !isEditingFiles ? ( {selectedFilePreview.map((file) => ( {file.path} +{file.insertions} -{file.deletions} ))} {selectedFiles.length > selectedFilePreview.length ? ( +{selectedFiles.length - selectedFilePreview.length} more files ) : null} ) : ( {allFiles.map((file) => { const included = !excludedFiles.has(file.path); return ( { setExcludedFiles((current) => { const next = new Set(current); if (next.has(file.path)) { next.delete(file.path); } else { next.add(file.path); } return next; }); }} > {file.path} {!included ? ( Excluded from this commit ) : null} +{file.insertions} -{file.deletions} ); })} )} Commit message void runCommitAction(true)} /> void runCommitAction(false)} /> ); }