Files
orca/mobile/src/source-control/MobileSourceControlHeader.tsx
T
Jinjing 43f639ddda Consolidate mobile source control into a single tabbed hub (#7923)
* Consolidate mobile source control into a single tabbed hub

Unify the changes list, pull request details, and commit history into
a single multi-segment panel. This improves navigation and state sharing
across different lenses of a worktree's source control.

- Add a segmented control to switch between Changes, PR, and History
- Introduce a persistent branch status card with an integrated PR chip
- Redirect standalone PR and history routes to the new unified hub
- Extract reusable UI and logic for the history list and PR summary

* Keep mobile source control tabs mounted to preserve view state

* Keep PR and History segments mounted (using display: 'none' when hidden) to preserve fetch, scroll, and expand states during tab switches.
* Decouple the History list from blocking on Git status loading.
* Support deep linking directly into the history tab of the main panel instead of using a standalone route.
* Enable retrying failed loads by reviving the transport loop if parked.
* Fix PR chip accessibility label and comment check.

* Optimize and integrate mobile PR view within source control hub

- Lazy-load heavy PR comments and descriptions (Phase 2) only when the
  PR tab is active, using fast metadata (Phase 1) for the branch chip.
- Unmount the PR body when inactive to avoid unnecessary comment tree
  re-renders and preserve WebView resources during commit text editing.
- Implement soft-refresh on HEAD advancement to keep the ready UI
  visible while re-fetching checks post-commit.
- Display the "Aborting..." label only when a merge or rebase abort
  is actively in flight.
- Memoize the git history list and skip branch identity RPCs when
  gating the dock icon.

* Improve mobile git views and concurrent rendering safety

- Pass the `origin` parameter through history and PR redirect routes.
- Move source control panel ref updates to `useEffect` to prevent side
  effects during concurrent renders.
- Resolve commit file changes to empty if disconnected to avoid a stuck
  loading spinner.
- Standardize PR sidebar header button styling and accessibility labels.

* Resolve PR repo probe without active branch to avoid forever spinner

Previously, checking if a repository is a GitHub remote required an
active branch. In a detached HEAD or mid-rebase state (where the branch
is null), the probe never resolved, leaving the PR panel on a forever
spinner.

Decouple the repository probe from the branch presence so the panel
can correctly display the "Current branch unavailable" state. Also,
hide the PR status chip when no branch is active to avoid a spinner
on the chip.
2026-07-09 19:23:36 -07:00

80 lines
2.4 KiB
TypeScript

import { Pressable, Text, View } from 'react-native'
import { ChevronLeft, ExternalLink, RefreshCw, X } from 'lucide-react-native'
import { colors } from '../theme/mobile-theme'
import { styles } from './mobile-source-control-styles'
type Props = {
embedded: boolean
worktreeLabel: string
ioBusy: boolean
onBack: () => void
onRefresh: () => void
// When set (PR segment ready with a host URL), show open-on-web flush-right of
// the title so the control stays visible while the PR body scrolls.
onOpenPrWeb?: () => void
prNumber?: number | null
}
export function MobileSourceControlHeader({
embedded,
worktreeLabel,
ioBusy,
onBack,
onRefresh,
onOpenPrWeb,
prNumber = null
}: Props) {
return (
<View style={styles.topBar}>
<Pressable
style={({ pressed }) => [styles.backButton, pressed && styles.backButtonPressed]}
onPress={onBack}
hitSlop={8}
accessibilityLabel={embedded ? 'Close source control' : 'Back to session'}
>
{embedded ? (
<X size={22} color={colors.textSecondary} strokeWidth={2.2} />
) : (
<ChevronLeft size={22} color={colors.textSecondary} strokeWidth={2.2} />
)}
</Pressable>
<View style={styles.titleBlock}>
<Text style={styles.title} numberOfLines={1}>
Source Control
</Text>
<Text style={styles.meta} numberOfLines={1}>
{worktreeLabel}
</Text>
</View>
{onOpenPrWeb ? (
<Pressable
style={({ pressed }) => [styles.refreshButton, pressed && styles.refreshButtonPressed]}
onPress={onOpenPrWeb}
hitSlop={8}
accessibilityRole="link"
accessibilityLabel={
prNumber != null
? `Open pull request #${prNumber} on the web`
: 'Open pull request on the web'
}
>
<ExternalLink size={18} color={colors.textSecondary} strokeWidth={2.1} />
</Pressable>
) : null}
<Pressable
style={({ pressed }) => [
styles.refreshButton,
ioBusy && styles.refreshButtonDisabled,
pressed && styles.refreshButtonPressed
]}
onPress={onRefresh}
disabled={ioBusy}
hitSlop={8}
accessibilityLabel="Refresh source control"
>
<RefreshCw size={18} color={colors.textSecondary} strokeWidth={2.1} />
</Pressable>
</View>
)
}