import { ActivityIndicator, Pressable, Text, View } from 'react-native'
import { ChevronRight, FileText, Minus, Plus, Trash2 } from 'lucide-react-native'
import { colors } from '../theme/mobile-theme'
import { MOBILE_GIT_STATUS_LABELS } from './mobile-git-status'
import { formatMobileBranchEntryMeta } from './mobile-branch-entry-format'
import {
statusColor,
type MobileBranchEntryView,
type MobileGitStatusEntryView
} from './mobile-source-control-screen-state'
import type { MobileSourceControlState } from './use-mobile-source-control-state'
import { styles } from './mobile-source-control-styles'
type RowState = Pick<
MobileSourceControlState,
| 'busyAction'
| 'openingPath'
| 'openingBranchPath'
| 'openFile'
| 'runGitAction'
| 'setDiscardTarget'
>
export function makeRenderFileRow(state: RowState) {
const { busyAction, openingPath, openingBranchPath, openFile, runGitAction, setDiscardTarget } =
state
return function FileRow({ item }: { item: MobileGitStatusEntryView }) {
const rowBusy =
busyAction === item.stageActionId ||
busyAction === item.unstageActionId ||
busyAction === item.discardActionId ||
openingPath === item.path
const rowDisabled =
!item.canOpen || busyAction !== null || openingPath !== null || openingBranchPath !== null
const ioBusy = busyAction !== null || openingPath !== null || openingBranchPath !== null
return (
[
styles.fileRow,
pressed && item.canOpen && styles.fileRowPressed,
rowDisabled && styles.fileRowDisabled,
!item.canOpen && styles.fileRowUnavailable
]}
onPress={() => void openFile(item)}
disabled={rowDisabled}
accessibilityLabel={`Open changed file ${item.path}`}
>
{MOBILE_GIT_STATUS_LABELS[item.status]}
{item.path}
{item.oldPath ? (
from {item.oldPath}
) : item.conflictStatus === 'unresolved' ? (
Unresolved conflict
) : null}
{rowBusy ? (
) : item.area === 'staged' ? (
[
styles.iconButton,
ioBusy && styles.iconButtonDisabled,
pressed && styles.iconButtonPressed
]}
disabled={ioBusy}
onPress={() =>
void runGitAction(item.unstageActionId, 'git.unstage', { filePath: item.path })
}
hitSlop={8}
accessibilityLabel={`Unstage ${item.path}`}
>
) : item.canStage || item.canDiscard ? (
{item.canStage ? (
[
styles.iconButton,
ioBusy && styles.iconButtonDisabled,
pressed && styles.iconButtonPressed
]}
disabled={ioBusy}
onPress={() =>
void runGitAction(item.stageActionId, 'git.stage', { filePath: item.path })
}
hitSlop={8}
accessibilityLabel={`Stage ${item.path}`}
>
) : null}
{item.canDiscard ? (
[
styles.iconButton,
ioBusy && styles.iconButtonDisabled,
pressed && styles.iconButtonPressed
]}
disabled={ioBusy}
onPress={() => setDiscardTarget(item)}
hitSlop={8}
accessibilityLabel={`Discard ${item.path}`}
>
) : null}
) : null}
{!rowBusy && item.canOpen ? (
) : null}
)
}
}
type BranchState = Pick<
MobileSourceControlState,
| 'shouldShowBranchCompareSection'
| 'branchCompareSummaryText'
| 'branchEntries'
| 'branchCompareState'
| 'branchCompareResult'
| 'busyAction'
| 'openBranchDiff'
| 'openingBranchPath'
| 'openingPath'
>
export function BranchCompareHeader({ state }: { state: BranchState }) {
const {
shouldShowBranchCompareSection,
branchCompareSummaryText,
branchEntries,
branchCompareState,
branchCompareResult
} = state
if (!shouldShowBranchCompareSection) {
return null
}
return (
Committed on Branch
{branchCompareSummaryText ? (
{branchCompareSummaryText}
) : null}
{branchEntries.length}
{branchCompareState.kind === 'loading' ? (
Loading committed changes...
) : branchCompareState.kind === 'error' ? (
{branchCompareState.message}
) : branchCompareResult && branchCompareResult.summary.status !== 'ready' ? (
{branchCompareResult.summary.errorMessage ?? 'Committed changes unavailable.'}
) : null}
)
}
export function BranchFileRow({
entry,
state
}: {
entry: MobileBranchEntryView
state: BranchState
}) {
const { busyAction, openBranchDiff, openingBranchPath, openingPath } = state
const rowBusy = openingBranchPath === entry.path
const rowDisabled =
!entry.canOpen || busyAction !== null || openingPath !== null || openingBranchPath !== null
const meta = formatMobileBranchEntryMeta(entry)
return (
[
styles.fileRow,
pressed && entry.canOpen && styles.fileRowPressed,
rowDisabled && styles.fileRowDisabled,
!entry.canOpen && styles.fileRowUnavailable
]}
onPress={() => void openBranchDiff(entry)}
disabled={rowDisabled}
accessibilityLabel={`Open committed change ${entry.path}`}
>
{MOBILE_GIT_STATUS_LABELS[entry.status]}
{entry.path}
{meta ? (
{meta}
) : null}
{rowBusy ? (
) : entry.canOpen ? (
) : null}
)
}