84 lines
3.2 KiB
TypeScript
84 lines
3.2 KiB
TypeScript
import { Pressable, Text, View } from 'react-native'
|
|
import { GitBranch } from 'lucide-react-native'
|
|
import { colors } from '../theme/mobile-theme'
|
|
import { styles } from './mobile-source-control-styles'
|
|
import { MobileSourceControlPrChip } from './MobileSourceControlPrChip'
|
|
import type { MobilePrChipSummary } from './mobile-pr-chip-summary'
|
|
import { mobileConflictAbortLabel } from './mobile-source-control-conflict-abort'
|
|
|
|
type Props = {
|
|
branchLabel: string
|
|
syncLabel: string | null
|
|
unstagedCount: number
|
|
stagedCount: number
|
|
branchCount: number
|
|
conflictOperation: string | null
|
|
// True while any serial git IO is in flight — disables Abort so ops don't race.
|
|
conflictBusy: boolean
|
|
// True only while abort-merge / abort-rebase itself is running (label accuracy).
|
|
conflictAborting: boolean
|
|
onAbortConflict: (operation: string) => void
|
|
// The PR chip is shown only on repos with a hosted-review remote; null hides it.
|
|
prChip: MobilePrChipSummary | null
|
|
onOpenPr: () => void
|
|
}
|
|
|
|
// Persistent card at the top of every hub segment: branch identity, sync/counts,
|
|
// conflict state, and the PR chip. Shared so PR/History see the same status the
|
|
// Changes lens does without re-deriving it.
|
|
export function MobileSourceControlBranchCard({
|
|
branchLabel,
|
|
syncLabel,
|
|
unstagedCount,
|
|
stagedCount,
|
|
branchCount,
|
|
conflictOperation,
|
|
conflictBusy,
|
|
conflictAborting,
|
|
onAbortConflict,
|
|
prChip,
|
|
onOpenPr
|
|
}: Props) {
|
|
const showConflict = conflictOperation !== null && conflictOperation !== 'unknown'
|
|
return (
|
|
<View style={styles.summaryCard}>
|
|
<View style={styles.summaryHeader}>
|
|
<View style={styles.branchLine}>
|
|
<GitBranch size={15} color={colors.textSecondary} strokeWidth={2.1} />
|
|
<Text style={styles.branchText} numberOfLines={1}>
|
|
{branchLabel}
|
|
</Text>
|
|
</View>
|
|
{syncLabel ? <Text style={styles.syncText}>{syncLabel}</Text> : null}
|
|
</View>
|
|
<View style={styles.countRow}>
|
|
<Text style={styles.countText}>{unstagedCount} changed</Text>
|
|
<Text style={styles.countText}>{stagedCount} staged</Text>
|
|
{branchCount > 0 ? <Text style={styles.countText}>{branchCount} on branch</Text> : null}
|
|
</View>
|
|
{/* Own row so Abort never overflows past the card when counts are long. */}
|
|
{showConflict ? (
|
|
<View style={styles.conflictRow}>
|
|
<Text style={styles.conflictText}>{conflictOperation}</Text>
|
|
{conflictOperation === 'merge' || conflictOperation === 'rebase' ? (
|
|
<Pressable
|
|
style={({ pressed }) => [
|
|
styles.abortButton,
|
|
conflictBusy && styles.abortButtonDisabled,
|
|
pressed && !conflictBusy && styles.abortPressed
|
|
]}
|
|
disabled={conflictBusy}
|
|
onPress={() => onAbortConflict(conflictOperation)}
|
|
>
|
|
<Text style={styles.abortText}>
|
|
{mobileConflictAbortLabel(conflictOperation, conflictAborting)}
|
|
</Text>
|
|
</Pressable>
|
|
) : null}
|
|
</View>
|
|
) : null}
|
|
{prChip ? <MobileSourceControlPrChip summary={prChip} onPress={onOpenPr} /> : null}
|
|
</View>
|
|
)
|
|
}
|