Files
orca/mobile/src/source-control/mobile-source-control-styles.ts
T
Brennan Benson c6d2180417 fix(mobile): keep source-control layout steady while Create PR eligibility loads (#11467)
* fix(mobile): keep source-control layout steady while Create PR eligibility loads

The Create PR entry unmounted until the first hostedReview.getCreationEligibility
answer arrived, so on a cold open the changed-files list painted first and then
shifted down 54pt (createPrBlock marginTop 12 + createPrButton minHeight 42)
when the button appeared — while the user was already tapping (#8411).

- buildMobileCreatePrAction: cold loading now reserves the row with a disabled
  placeholder instead of unmounting it.
- useMobileHostedReviewEligibility: a fetch-imminent idle frame renders as an
  in-flight load, so the reservation is present on the first painted frame.
- New per-worktree+branch memory of the last resolved eligibility seeds cold
  loads, so branches whose answer is hidden (existing review, unsupported
  provider) do not get a placeholder that collapses on every reopen.

Fixes #8411

* fix(mobile): harden source-control layout reservation

* fix(mobile): keep review status row footprint fixed

* fix(mobile): derive eligibility state from keyed snapshots
2026-07-31 15:35:56 -07:00

248 lines
5.7 KiB
TypeScript

import { StyleSheet } from 'react-native'
import { colors, radii, spacing, typography } from '../theme/mobile-theme'
import { diffStyles } from './mobile-source-control-diff-styles'
import { listStyles } from './mobile-source-control-list-styles'
const baseStyles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: colors.bgBase
},
header: {
backgroundColor: colors.bgPanel,
borderBottomWidth: StyleSheet.hairlineWidth,
borderBottomColor: colors.borderSubtle
},
topBar: {
minHeight: 58,
flexDirection: 'row',
alignItems: 'center',
paddingHorizontal: spacing.sm
},
backButton: {
width: 36,
height: 36,
borderRadius: 18,
alignItems: 'center',
justifyContent: 'center',
marginRight: spacing.xs
},
backButtonPressed: {
backgroundColor: colors.bgRaised
},
titleBlock: {
flex: 1,
minWidth: 0
},
title: {
color: colors.textPrimary,
fontSize: 16,
fontWeight: '700'
},
meta: {
color: colors.textSecondary,
fontSize: typography.metaSize,
marginTop: 2
},
refreshButton: {
width: 36,
height: 36,
borderRadius: radii.button,
alignItems: 'center',
justifyContent: 'center',
marginLeft: spacing.xs
},
refreshButtonPressed: {
backgroundColor: colors.bgRaised
},
refreshButtonDisabled: {
opacity: 0.45
},
summaryCard: {
margin: spacing.lg,
marginBottom: spacing.sm,
padding: spacing.md,
borderRadius: radii.card,
backgroundColor: colors.bgPanel,
borderWidth: StyleSheet.hairlineWidth,
borderColor: colors.borderSubtle
},
summaryHeader: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
gap: spacing.md
},
branchLine: {
flex: 1,
minWidth: 0,
flexDirection: 'row',
alignItems: 'center',
gap: spacing.xs
},
branchText: {
flex: 1,
color: colors.textPrimary,
fontSize: typography.bodySize,
fontWeight: '600'
},
syncText: {
color: colors.textSecondary,
fontSize: typography.metaSize
},
countRow: {
flexDirection: 'row',
flexWrap: 'wrap',
gap: spacing.md,
marginTop: spacing.sm
},
countText: {
color: colors.textSecondary,
fontSize: typography.metaSize
},
// Separate line under counts — keeps Abort inside the card on narrow phones.
conflictRow: {
flexDirection: 'row',
flexWrap: 'wrap',
alignItems: 'center',
gap: spacing.sm,
marginTop: spacing.sm,
alignSelf: 'flex-start',
maxWidth: '100%'
},
conflictText: {
color: colors.statusAmber,
fontSize: typography.metaSize,
textTransform: 'capitalize'
},
// Match bulk-action hit target so Abort reads as a real control, not a chip.
abortButton: {
minHeight: 32,
paddingHorizontal: spacing.md,
paddingVertical: spacing.xs,
borderRadius: radii.button,
borderWidth: 1,
borderColor: colors.statusAmber,
backgroundColor: colors.bgRaised,
alignItems: 'center',
justifyContent: 'center',
flexShrink: 0
},
abortPressed: {
opacity: 0.75
},
abortButtonDisabled: {
opacity: 0.45
},
abortText: {
color: colors.statusAmber,
fontSize: typography.bodySize,
fontWeight: '600',
textTransform: 'capitalize'
},
reconnectBanner: {
flexDirection: 'row',
alignItems: 'center',
gap: spacing.sm,
marginHorizontal: spacing.lg,
marginTop: spacing.lg,
marginBottom: -spacing.sm,
paddingHorizontal: spacing.md,
paddingVertical: spacing.sm,
borderRadius: radii.button,
backgroundColor: colors.bgRaised,
borderWidth: StyleSheet.hairlineWidth,
borderColor: colors.statusAmber
},
reconnectBannerText: {
color: colors.textPrimary,
fontSize: typography.metaSize
},
actionError: {
marginTop: spacing.sm,
paddingHorizontal: spacing.md,
paddingVertical: spacing.sm,
borderRadius: radii.button,
backgroundColor: colors.bgRaised,
borderWidth: StyleSheet.hairlineWidth,
borderColor: colors.statusRed
},
actionErrorText: {
color: colors.textPrimary,
fontSize: typography.metaSize,
lineHeight: 16
},
bulkRow: {
flexDirection: 'row',
gap: spacing.sm,
marginTop: spacing.md
},
bulkButton: {
flex: 1,
minHeight: 36,
borderRadius: radii.button,
backgroundColor: colors.bgRaised,
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'row',
gap: spacing.xs
},
bulkMenuButton: {
width: 42,
minHeight: 36,
borderRadius: radii.button,
backgroundColor: colors.bgRaised,
alignItems: 'center',
justifyContent: 'center'
},
bulkButtonDisabled: {
opacity: 0.45
},
bulkButtonPressed: {
opacity: 0.75
},
bulkButtonText: {
color: colors.textPrimary,
fontSize: typography.bodySize,
fontWeight: '600'
},
createPrBlock: {
marginTop: spacing.md
},
createPrButton: {
height: 42,
borderRadius: radii.button,
backgroundColor: colors.textPrimary,
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'row',
gap: spacing.xs,
paddingHorizontal: spacing.md
},
createPrButtonDisabled: {
backgroundColor: colors.bgRaised,
borderWidth: StyleSheet.hairlineWidth,
borderColor: colors.borderSubtle
},
createPrButtonPressed: {
opacity: 0.78
},
createPrButtonText: {
color: colors.bgBase,
fontSize: typography.bodySize,
fontWeight: '700'
},
createPrButtonTextDisabled: {
color: colors.textSecondary
},
createPrButtonHint: {
fontSize: typography.metaSize,
fontWeight: '600',
lineHeight: 16,
textAlign: 'center',
flexShrink: 1
}
})
export const styles = { ...baseStyles, ...listStyles, ...diffStyles }