From 037f7a07d30ca25a68764e074ec2f448049ca8f3 Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Fri, 31 Jul 2026 00:29:01 -0700 Subject: [PATCH] fix(mobile): open host editor reliably (#11635) --- mobile/app/index.tsx | 3 +- .../transport/host-edit-navigation.test.ts | 29 +++++++++++++++++++ mobile/src/transport/host-edit-navigation.ts | 19 ++++++++++++ 3 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 mobile/src/transport/host-edit-navigation.test.ts create mode 100644 mobile/src/transport/host-edit-navigation.ts diff --git a/mobile/app/index.tsx b/mobile/app/index.tsx index f486fe99c78..4000906c933 100644 --- a/mobile/app/index.tsx +++ b/mobile/app/index.tsx @@ -26,6 +26,7 @@ import { } from '../src/components/AccountUsage' import AsyncStorage from '@react-native-async-storage/async-storage' import { loadHosts } from '../src/transport/host-store' +import { navigateToMobileHostEdit } from '../src/transport/host-edit-navigation' import { removeHostAndCloseClient } from '../src/transport/host-removal-lifecycle' import { pickResumeWorktree } from '../src/worktree/resume-worktree' import type { RpcClient } from '../src/transport/rpc-client' @@ -1010,7 +1011,7 @@ export default function HomeScreen() { closeBeforePress: true, onPress: () => { setActionTarget(null) - router.push(`/h/${host.id}/edit`) + navigateToMobileHostEdit(router, host.id) } }) items.push({ diff --git a/mobile/src/transport/host-edit-navigation.test.ts b/mobile/src/transport/host-edit-navigation.test.ts new file mode 100644 index 00000000000..66ca63c5efa --- /dev/null +++ b/mobile/src/transport/host-edit-navigation.test.ts @@ -0,0 +1,29 @@ +import { describe, expect, it, vi } from 'vitest' +import { mobileHostEditRoute, navigateToMobileHostEdit } from './host-edit-navigation' + +describe('mobileHostEditRoute', () => { + it('keeps the dynamic host segment explicit for a cold host navigator', () => { + expect(mobileHostEditRoute('host-1')).toEqual({ + pathname: '/h/[hostId]/edit', + params: { hostId: 'host-1' } + }) + }) + + it('mounts a cold host navigator before replacing its index with edit', () => { + let nextFrame: FrameRequestCallback | null = null + const push = vi.fn() + const replace = vi.fn() + vi.stubGlobal('requestAnimationFrame', (callback: FrameRequestCallback) => { + nextFrame = callback + return 1 + }) + + navigateToMobileHostEdit({ push, replace }, 'host-1') + expect(push).toHaveBeenCalledWith('/h/host-1') + expect(replace).not.toHaveBeenCalled() + + nextFrame?.(0) + expect(replace).toHaveBeenCalledWith(mobileHostEditRoute('host-1')) + vi.unstubAllGlobals() + }) +}) diff --git a/mobile/src/transport/host-edit-navigation.ts b/mobile/src/transport/host-edit-navigation.ts new file mode 100644 index 00000000000..19dbf703dec --- /dev/null +++ b/mobile/src/transport/host-edit-navigation.ts @@ -0,0 +1,19 @@ +type HostEditRouter = { + push: (href: `/h/${string}`) => void + replace: (href: ReturnType) => void +} + +export function mobileHostEditRoute(hostId: string) { + return { + pathname: '/h/[hostId]/edit' as const, + params: { hostId } + } +} + +export function navigateToMobileHostEdit(router: HostEditRouter, hostId: string): void { + // Why: a cold nested host navigator resolves a deep push to its index route. + router.push(`/h/${hostId}`) + requestAnimationFrame(() => { + router.replace(mobileHostEditRoute(hostId)) + }) +}