Resolve host new-worktree route action before commit (#3242)
This commit is contained in:
parent
67f74a29a8
commit
c082f21d2e
|
|
@ -62,6 +62,11 @@ import {
|
|||
loadPreferences,
|
||||
savePreferences
|
||||
} from '../../../src/storage/preferences'
|
||||
import {
|
||||
createInitialHostRouteActionState,
|
||||
resolveHostRouteActionState,
|
||||
setHostRouteNewWorktreeVisible
|
||||
} from '../../../src/host-route-action-state'
|
||||
|
||||
// Why: locally-typed subset of the desktop's RuntimeStatus we read from
|
||||
// `status.get`. Only the version fields matter to mobile today; everything
|
||||
|
|
@ -340,7 +345,9 @@ export default function HostScreen() {
|
|||
const [actionTarget, setActionTarget] = useState<Worktree | null>(null)
|
||||
const [confirmDelete, setConfirmDelete] = useState<Worktree | null>(null)
|
||||
const [confirmRemoveHost, setConfirmRemoveHost] = useState(false)
|
||||
const [showNewWorktree, setShowNewWorktree] = useState(false)
|
||||
const [routeActionState, setRouteActionState] = useState(() =>
|
||||
createInitialHostRouteActionState(action)
|
||||
)
|
||||
const [sleptIds, setSleptIds] = useState<Set<string>>(new Set())
|
||||
|
||||
// Persisted pin state
|
||||
|
|
@ -348,9 +355,16 @@ export default function HostScreen() {
|
|||
const [_prefsLoaded, setPrefsLoaded] = useState(false)
|
||||
const [collapsedGroups, setCollapsedGroups] = useState<Set<string>>(new Set())
|
||||
|
||||
useEffect(() => {
|
||||
if (action === 'newWorktree') setShowNewWorktree(true)
|
||||
}, [action])
|
||||
const resolvedRouteActionState = resolveHostRouteActionState(routeActionState, action)
|
||||
// Why: `action=newWorktree` is a route-derived open edge. Resolve it before
|
||||
// commit, but don't reopen after the user closes while the same URL remains.
|
||||
if (resolvedRouteActionState !== routeActionState) {
|
||||
setRouteActionState(resolvedRouteActionState)
|
||||
}
|
||||
const showNewWorktree = resolvedRouteActionState.showNewWorktree
|
||||
const setShowNewWorktreeVisible = useCallback((visible: boolean) => {
|
||||
setRouteActionState((current) => setHostRouteNewWorktreeVisible(current, visible))
|
||||
}, [])
|
||||
|
||||
// Load persisted pins and preferences
|
||||
useEffect(() => {
|
||||
|
|
@ -871,7 +885,7 @@ export default function HostScreen() {
|
|||
|
||||
<Pressable
|
||||
style={styles.newButton}
|
||||
onPress={() => setShowNewWorktree(true)}
|
||||
onPress={() => setShowNewWorktreeVisible(true)}
|
||||
disabled={connState !== 'connected'}
|
||||
>
|
||||
<Plus
|
||||
|
|
@ -1246,7 +1260,7 @@ export default function HostScreen() {
|
|||
const params = new URLSearchParams({ name: worktreeName, created: '1' })
|
||||
router.push(`/h/${hostId}/session/${encodeURIComponent(worktreeId)}?${params.toString()}`)
|
||||
}}
|
||||
onClose={() => setShowNewWorktree(false)}
|
||||
onClose={() => setShowNewWorktreeVisible(false)}
|
||||
/>
|
||||
</SafeAreaView>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,50 @@
|
|||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import {
|
||||
createInitialHostRouteActionState,
|
||||
resolveHostRouteActionState,
|
||||
setHostRouteNewWorktreeVisible
|
||||
} from './host-route-action-state'
|
||||
|
||||
describe('host route action state', () => {
|
||||
it('opens new worktree modal on an initial newWorktree action', () => {
|
||||
expect(createInitialHostRouteActionState('newWorktree')).toEqual({
|
||||
routeAction: 'newWorktree',
|
||||
showNewWorktree: true
|
||||
})
|
||||
})
|
||||
|
||||
it('keeps initial non-action routes closed', () => {
|
||||
expect(createInitialHostRouteActionState(undefined)).toEqual({
|
||||
routeAction: undefined,
|
||||
showNewWorktree: false
|
||||
})
|
||||
})
|
||||
|
||||
it('opens once when route action changes to newWorktree', () => {
|
||||
expect(
|
||||
resolveHostRouteActionState({ routeAction: undefined, showNewWorktree: false }, 'newWorktree')
|
||||
).toEqual({
|
||||
routeAction: 'newWorktree',
|
||||
showNewWorktree: true
|
||||
})
|
||||
})
|
||||
|
||||
it('does not reopen after user closes while route action is unchanged', () => {
|
||||
const closed = setHostRouteNewWorktreeVisible(
|
||||
{ routeAction: 'newWorktree', showNewWorktree: true },
|
||||
false
|
||||
)
|
||||
|
||||
expect(resolveHostRouteActionState(closed, 'newWorktree')).toBe(closed)
|
||||
})
|
||||
|
||||
it('preserves an already-open modal when route action changes away', () => {
|
||||
expect(
|
||||
resolveHostRouteActionState({ routeAction: 'newWorktree', showNewWorktree: true }, undefined)
|
||||
).toEqual({
|
||||
routeAction: undefined,
|
||||
showNewWorktree: true
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
export type HostRouteActionState = {
|
||||
routeAction: string | undefined
|
||||
showNewWorktree: boolean
|
||||
}
|
||||
|
||||
export function createInitialHostRouteActionState(
|
||||
routeAction: string | undefined
|
||||
): HostRouteActionState {
|
||||
return {
|
||||
routeAction,
|
||||
showNewWorktree: routeAction === 'newWorktree'
|
||||
}
|
||||
}
|
||||
|
||||
export function resolveHostRouteActionState(
|
||||
current: HostRouteActionState,
|
||||
routeAction: string | undefined
|
||||
): HostRouteActionState {
|
||||
if (current.routeAction === routeAction) {
|
||||
return current
|
||||
}
|
||||
return {
|
||||
routeAction,
|
||||
showNewWorktree: current.showNewWorktree || routeAction === 'newWorktree'
|
||||
}
|
||||
}
|
||||
|
||||
export function setHostRouteNewWorktreeVisible(
|
||||
current: HostRouteActionState,
|
||||
showNewWorktree: boolean
|
||||
): HostRouteActionState {
|
||||
if (current.showNewWorktree === showNewWorktree) {
|
||||
return current
|
||||
}
|
||||
return { ...current, showNewWorktree }
|
||||
}
|
||||
Loading…
Reference in New Issue