diff --git a/mobile/app/h/[hostId]/session/[worktreeId].tsx b/mobile/app/h/[hostId]/session/[worktreeId].tsx index 9ea0cb0249f..91a21f396d1 100644 --- a/mobile/app/h/[hostId]/session/[worktreeId].tsx +++ b/mobile/app/h/[hostId]/session/[worktreeId].tsx @@ -121,6 +121,11 @@ import { type MobileNewTabAgentOption, type MobileNewTabAgentSettings } from '../../../../src/session/mobile-new-tab-agent-options' +import { + createMobileSessionCreateWarningState, + dismissMobileSessionCreateWarningState, + reconcileMobileSessionCreateWarningState +} from '../../../../src/session/mobile-session-create-warning-state' import { colors, spacing, radii, typography } from '../../../../src/theme/mobile-theme' import type { DiffComment } from '../../../../../src/shared/types' @@ -981,7 +986,9 @@ export default function SessionScreen() { const [creatingBrowser, setCreatingBrowser] = useState(false) const [creatingMarkdown, setCreatingMarkdown] = useState(false) const [createError, setCreateError] = useState('') - const [createWarning, setCreateWarning] = useState(initialCreateWarning) + const [createWarningState, setCreateWarningState] = useState(() => + createMobileSessionCreateWarningState(initialCreateWarning) + ) const [showCreateTabDrawer, setShowCreateTabDrawer] = useState(false) const [createTabAgentLoadState, setCreateTabAgentLoadState] = useState('idle') @@ -1089,10 +1096,16 @@ export default function SessionScreen() { activeSessionTab?.type !== 'browser' const liveInputEnabled = activeHandle ? liveInputTerminalHandles.has(activeHandle) : false const [browserScreencastSupported, setBrowserScreencastSupported] = useState(null) - - useEffect(() => { - setCreateWarning(initialCreateWarning) - }, [initialCreateWarning]) + const reconciledCreateWarningState = reconcileMobileSessionCreateWarningState( + createWarningState, + initialCreateWarning + ) + // Why: Expo can reuse this screen for a new route. Reconcile before paint + // so a dismissed old creation warning never flashes for the next session. + if (reconciledCreateWarningState !== createWarningState) { + setCreateWarningState(reconciledCreateWarningState) + } + const createWarning = reconciledCreateWarningState.visible const clearToastHideTimer = useCallback(() => { if (!toastHideTimerRef.current) return @@ -3782,7 +3795,7 @@ export default function SessionScreen() { {createWarning} setCreateWarning('')} + onPress={() => setCreateWarningState(dismissMobileSessionCreateWarningState)} accessibilityLabel="Dismiss workspace creation warning" hitSlop={8} > diff --git a/mobile/src/session/mobile-session-create-warning-state.test.ts b/mobile/src/session/mobile-session-create-warning-state.test.ts new file mode 100644 index 00000000000..8cc9c2766a2 --- /dev/null +++ b/mobile/src/session/mobile-session-create-warning-state.test.ts @@ -0,0 +1,29 @@ +import { describe, expect, it } from 'vitest' +import { + createMobileSessionCreateWarningState, + dismissMobileSessionCreateWarningState, + reconcileMobileSessionCreateWarningState +} from './mobile-session-create-warning-state' + +describe('mobile session create warning state', () => { + it('preserves a dismissed warning while the route warning is unchanged', () => { + const dismissed = dismissMobileSessionCreateWarningState( + createMobileSessionCreateWarningState('Created, but setup failed') + ) + + expect(reconcileMobileSessionCreateWarningState(dismissed, 'Created, but setup failed')).toBe( + dismissed + ) + }) + + it('shows a new warning when the route warning changes', () => { + const dismissed = dismissMobileSessionCreateWarningState( + createMobileSessionCreateWarningState('Old warning') + ) + + expect(reconcileMobileSessionCreateWarningState(dismissed, 'New warning')).toEqual({ + source: 'New warning', + visible: 'New warning' + }) + }) +}) diff --git a/mobile/src/session/mobile-session-create-warning-state.ts b/mobile/src/session/mobile-session-create-warning-state.ts new file mode 100644 index 00000000000..be827af6bfb --- /dev/null +++ b/mobile/src/session/mobile-session-create-warning-state.ts @@ -0,0 +1,29 @@ +export type MobileSessionCreateWarningState = { + source: string + visible: string +} + +export function createMobileSessionCreateWarningState( + source: string +): MobileSessionCreateWarningState { + return { source, visible: source } +} + +export function reconcileMobileSessionCreateWarningState( + current: MobileSessionCreateWarningState, + source: string +): MobileSessionCreateWarningState { + if (current.source === source) { + return current + } + return createMobileSessionCreateWarningState(source) +} + +export function dismissMobileSessionCreateWarningState( + current: MobileSessionCreateWarningState +): MobileSessionCreateWarningState { + if (!current.visible) { + return current + } + return { ...current, visible: '' } +}