import { sanitizeFeatureBranchName } from "@t3tools/shared/git"; import { useNavigation, type StaticScreenProps } from "@react-navigation/native"; import { useState } from "react"; import { Platform, Pressable, ScrollView, View } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { AndroidSheetHeader } from "../../../components/AndroidScreenHeader"; import { AppText as Text, AppTextInput as TextInput } from "../../../components/AppText"; import { cn } from "../../../lib/cn"; import { useEnvironmentQuery } from "../../../state/query"; 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 { vcsEnvironment } from "../../../state/vcs"; import { SheetActionButton } from "./gitSheetComponents"; type GitBranchesSheetProps = StaticScreenProps<{ readonly environmentId: string; readonly threadId: string; }>; export function GitBranchesSheet(_props: GitBranchesSheetProps) { const navigation = useNavigation(); const insets = useSafeAreaInsets(); const { selectedThread } = useThreadSelection(); const { selectedThreadCwd, selectedThreadWorktreePath } = useSelectedThreadWorktree(); const gitState = useSelectedThreadGitState(); const gitActions = useSelectedThreadGitActions(); const gitStatus = useEnvironmentQuery( selectedThread !== null && selectedThreadCwd !== null ? vcsEnvironment.status({ environmentId: selectedThread.environmentId, input: { cwd: selectedThreadCwd }, }) : null, ); const currentBranchLabel = gitStatus.data?.refName ?? selectedThread?.branch ?? "Detached HEAD"; const currentWorktreePath = selectedThreadWorktreePath; const availableBranches = gitState.selectedThreadBranches; const branchesLoading = gitState.selectedThreadBranchesLoading; const busy = gitState.gitOperationLabel !== null; const [newBranchName, setNewBranchName] = useState(""); const [worktreeBaseBranch, setWorktreeBaseBranch] = useState( currentBranchLabel === "Detached HEAD" ? "main" : currentBranchLabel, ); const [worktreeBranchName, setWorktreeBranchName] = useState(""); const disabledExistingBranchNames: Array = []; for (const branch of availableBranches) { if (branch.worktreePath !== null && branch.worktreePath !== currentWorktreePath) { disabledExistingBranchNames.push(branch.name); } } const disabledExistingBranches = new Set(disabledExistingBranchNames); return ( {Platform.OS === "android" ? ( navigation.goBack()} /> ) : null} New branch { const branch = sanitizeFeatureBranchName(newBranchName.trim()); if (branch.length === 0) return; void gitActions.onCreateSelectedThreadBranch(branch).then(() => { setNewBranchName(""); navigation.goBack(); }); }} /> New worktree { const baseBranch = worktreeBaseBranch.trim(); const newBranch = worktreeBranchName.trim(); if (baseBranch.length === 0 || newBranch.length === 0) return; void gitActions.onCreateSelectedThreadWorktree({ baseBranch, newBranch }).then(() => { setWorktreeBranchName(""); navigation.goBack(); }); }} /> Existing branches {branchesLoading ? ( Loading branches... ) : null} {!branchesLoading && availableBranches.length === 0 ? ( No local branches found. ) : null} {availableBranches.map((branch) => { const disabled = disabledExistingBranches.has(branch.name); const subtitle = branch.worktreePath ? branch.worktreePath === currentWorktreePath ? "Checked out in this thread" : "Checked out in another worktree" : branch.isDefault ? "Default branch" : "Local branch"; return ( { void gitActions.onCheckoutSelectedThreadBranch(branch.name).then(() => { navigation.goBack(); }); }} > {branch.name} {subtitle} ); })} ); }