import { ActivityIndicator, Pressable, ScrollView, SectionList, Text, TextInput, View } from 'react-native' import { Minus, MoreHorizontal, Plus, Sparkles } from 'lucide-react-native' import { colors, spacing } from '../theme/mobile-theme' import { MobileSourceControlCreatePrEntry } from './MobileSourceControlCreatePrEntry' import { MobileCommitFailurePanel } from './MobileCommitFailurePanel' import { KEYBOARD_COMMIT_BAR_CLEARANCE } from './mobile-source-control-screen-state' import { makeRenderFileRow, BranchCompareFooter } from './MobileSourceControlFileRows' import type { MobileSourceControlState } from './use-mobile-source-control-state' import { styles } from './mobile-source-control-styles' import { hubStyles } from './mobile-source-control-hub-styles' import { t } from '@/i18n/mobile-i18n' type Props = { state: MobileSourceControlState } // Changes tab: local file changes only — uncommitted (staged/unstaged) plus // committed-on-branch vs base. PR conflicts and push status live elsewhere. export function MobileSourceControlContent({ state }: Props) { const { insets, connState, busyAction, commitMessage, setCommitMessage, generatingMessage, setShowActionSheet, setDiscardTarget, actionError, commitFailureRecovery, commitFailureRecoveryAction, keyboardLift, openingPath, openingBranchPath, sections, hasVisibleChanges, stageablePaths, unstageablePaths, stagedCount, primaryAction, createPrAction, stageAll, unstageAll, generateCommitMessage, cancelGenerateCommitMessage, openFile, openBranchDiff, runGitAction } = state const ioBusy = busyAction !== null || openingPath !== null || openingBranchPath !== null const shouldShowGenerateButton = stagedCount > 0 || generatingMessage const createPrHeroActive = createPrAction.visible && !createPrAction.disabled && !createPrAction.pushFirst const branchCompareFooter = ( ) return ( <> {connState !== 'connected' ? ( // Why: once data has loaded the screen looks alive even when the // desktop link is down, so taps appear to do nothing (STA-1511). // Surface the reconnect state where the user is looking. {t('mobileSourceControlContent.reconnecting')} ) : null} {commitFailureRecovery ? ( ) : actionError ? ( {actionError} ) : null} [ styles.bulkButton, (stageablePaths.length === 0 || ioBusy) && styles.bulkButtonDisabled, pressed && styles.bulkButtonPressed ]} onPress={() => void stageAll()} disabled={ioBusy || stageablePaths.length === 0} > {busyAction === 'stage-all' ? ( ) : ( )} {t('mobileSourceControlContent.stage')} [ styles.bulkButton, (unstageablePaths.length === 0 || ioBusy) && styles.bulkButtonDisabled, pressed && styles.bulkButtonPressed ]} onPress={() => void unstageAll()} disabled={ioBusy || unstageablePaths.length === 0} > {busyAction === 'unstage-all' ? ( ) : ( )} {t('mobileSourceControlContent.unstage')} [ styles.bulkMenuButton, pressed && styles.bulkButtonPressed, ioBusy && styles.bulkButtonDisabled ]} onPress={() => setShowActionSheet(true)} disabled={ioBusy} hitSlop={8} accessibilityLabel={t('mobileSourceControlContent.open')} > {!hasVisibleChanges ? ( {t('mobileSourceControlContent.noLocal')} {t('mobileSourceControlContent.working')} ) : sections.length === 0 ? ( // Why: RN SectionList with empty `sections` often skips ListFooterComponent, // which hid "Committed on Branch" when only branch files remain. {branchCompareFooter} ) : ( `${item.area}:${item.path}:${item.oldPath ?? ''}`} renderSectionHeader={({ section }) => ( {section.title} {section.data.length} )} ListFooterComponent={branchCompareFooter} stickySectionHeadersEnabled={false} contentContainerStyle={styles.listContent} /> )} 0 ? keyboardLift + KEYBOARD_COMMIT_BAR_CLEARANCE : keyboardLift, paddingBottom: keyboardLift > 0 ? spacing.md : spacing.md + insets.bottom } ]} > {stagedCount === 0 ? ( {t('mobileSourceControlContent.noStaged')} ) : ( )} {shouldShowGenerateButton ? ( [ styles.generateButton, busyAction !== null && styles.commitButtonDisabled, pressed && styles.commitButtonPressed ]} // Why: commit-message AI belongs to the commit path; hiding it // during Stage All keeps the quick action visually unambiguous. disabled={busyAction !== null} onPress={() => generatingMessage ? cancelGenerateCommitMessage() : void generateCommitMessage() } accessibilityLabel={ generatingMessage ? t('mobileSourceControlContent.cancel') : t('mobileSourceControlContent.generate') } > {generatingMessage ? ( ) : ( )} ) : null} [ styles.commitButton, createPrHeroActive && styles.commitButtonSecondary, primaryAction.disabled && styles.commitButtonDisabled, pressed && styles.commitButtonPressed ]} onPress={primaryAction.onPress} disabled={primaryAction.disabled} accessibilityLabel={primaryAction.accessibilityLabel} accessibilityHint={primaryAction.accessibilityHint} > {primaryAction.loading ? ( ) : ( {primaryAction.label} )} ) }