From 19f668cf7c1d57991765ff5ddcdea70b3ffe757e Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Sat, 1 Aug 2026 13:10:10 -0700 Subject: [PATCH] fix(mobile): preserve edited reconnect profiles --- mobile/app/h/[hostId]/edit.tsx | 12 +++++++++--- mobile/src/host-edit-save-flow.test.ts | 15 +++++++++------ .../host-reconnect-profile-cache.test.ts | 15 +++++++++++++++ src/main/runtime/runtime-rpc.ts | 1 + 4 files changed, 34 insertions(+), 9 deletions(-) diff --git a/mobile/app/h/[hostId]/edit.tsx b/mobile/app/h/[hostId]/edit.tsx index 445400834a9..b6aef56672e 100644 --- a/mobile/app/h/[hostId]/edit.tsx +++ b/mobile/app/h/[hostId]/edit.tsx @@ -17,7 +17,10 @@ import { colors, radii, spacing, typography } from '../../../src/theme/mobile-th import { loadHosts, updateHostNameAndEndpoint } from '../../../src/transport/host-store' import { getHostListLoadRevision } from '../../../src/transport/host-list-load-sharing' import { displayHostEndpoint } from '../../../src/transport/host-endpoint' -import { resolveHostEndpointEdit } from '../../../src/transport/host-endpoint-edit' +import { + hostProfileAfterEdit, + resolveHostEndpointEdit +} from '../../../src/transport/host-endpoint-edit' import { useForceReconnect, usePrimeHosts } from '../../../src/transport/client-context' import { showForceReconnectError } from '../../../src/transport/force-reconnect-feedback' import type { HostProfile } from '../../../src/transport/types' @@ -106,6 +109,7 @@ export default function EditHostScreen() { ...(willRename ? { name: nextName } : {}), ...(nextEndpoint !== undefined ? { endpoint: nextEndpoint } : {}) } + let reconnectProfile = hostProfileAfterEdit(host, updates) savingRef.current = true setSaving(true) setSaveError(null) @@ -131,11 +135,13 @@ export default function EditHostScreen() { if (hostLoadRevision !== getHostListLoadRevision()) { continue } - if (!hosts.some(({ id }) => id === host.id)) { + const loadedHost = hosts.find(({ id }) => id === host.id) + if (!loadedHost) { break } primeHosts(hosts, hostLoadRevision) if (hostLoadRevision === getHostListLoadRevision()) { + reconnectProfile = loadedHost break } } @@ -151,7 +157,7 @@ export default function EditHostScreen() { // Why: reconnect is a follow-on side effect of a save that already // committed — its failure or a hang must not be reported as a save // failure or block navigating back. - void forceReconnectHost(host.id).catch(showForceReconnectError) + void forceReconnectHost(host.id, reconnectProfile).catch(showForceReconnectError) } } diff --git a/mobile/src/host-edit-save-flow.test.ts b/mobile/src/host-edit-save-flow.test.ts index 2a709c7b8c7..b895b7b6f89 100644 --- a/mobile/src/host-edit-save-flow.test.ts +++ b/mobile/src/host-edit-save-flow.test.ts @@ -187,7 +187,7 @@ describe('edit host handleSave', () => { endpoint: 'ws://192.168.1.20:6768' }) expect(dependencies.primeHosts).toHaveBeenCalledWith([savedHost], 0) - expect(dependencies.forceReconnectHost).toHaveBeenCalledWith('host-1') + expect(dependencies.forceReconnectHost).toHaveBeenCalledWith('host-1', savedHost) expect(dependencies.back).toHaveBeenCalledTimes(1) act(() => renderer.unmount()) @@ -211,7 +211,7 @@ describe('edit host handleSave', () => { endpoint: 'ws://192.168.1.20:6768' }) expect(dependencies.primeHosts).toHaveBeenCalledWith([savedHost], 0) - expect(dependencies.forceReconnectHost).toHaveBeenCalledWith('host-1') + expect(dependencies.forceReconnectHost).toHaveBeenCalledWith('host-1', savedHost) expect(dependencies.back).toHaveBeenCalledTimes(1) act(() => renderer.unmount()) @@ -264,7 +264,10 @@ describe('edit host handleSave', () => { await pressSave(renderer) expect(dependencies.primeHosts).not.toHaveBeenCalled() - expect(dependencies.forceReconnectHost).toHaveBeenCalledWith('host-1') + expect(dependencies.forceReconnectHost).toHaveBeenCalledWith('host-1', { + ...HOST_FIXTURE, + endpoint: 'ws://192.168.1.20:6768' + }) expect(dependencies.back).toHaveBeenCalledTimes(1) act(() => renderer.unmount()) @@ -282,7 +285,7 @@ describe('edit host handleSave', () => { await Promise.resolve() }) - expect(dependencies.forceReconnectHost).toHaveBeenCalledWith('host-1') + expect(dependencies.forceReconnectHost).toHaveBeenCalledWith('host-1', savedHost) expect(dependencies.back).toHaveBeenCalledTimes(1) expect(dependencies.alert).toHaveBeenCalledWith('Unable to reconnect', 'connect failed') @@ -304,7 +307,7 @@ describe('edit host handleSave', () => { await pressSave(renderer) expect(dependencies.primeHosts).toHaveBeenCalledWith([relayHost], 0) - expect(dependencies.forceReconnectHost).toHaveBeenCalledWith('host-1') + expect(dependencies.forceReconnectHost).toHaveBeenCalledWith('host-1', relayHost) act(() => renderer.unmount()) }) @@ -325,7 +328,7 @@ describe('edit host handleSave', () => { await pressSave(renderer) expect(dependencies.primeHosts).toHaveBeenCalledWith([editedHost], 2) - expect(dependencies.forceReconnectHost).toHaveBeenCalledWith('host-1') + expect(dependencies.forceReconnectHost).toHaveBeenCalledWith('host-1', editedHost) act(() => renderer.unmount()) }) diff --git a/mobile/src/transport/host-reconnect-profile-cache.test.ts b/mobile/src/transport/host-reconnect-profile-cache.test.ts index 8d818808c00..39906a159f3 100644 --- a/mobile/src/transport/host-reconnect-profile-cache.test.ts +++ b/mobile/src/transport/host-reconnect-profile-cache.test.ts @@ -80,4 +80,19 @@ describe('HostReconnectProfileCache', () => { expect(cache.get(HOST.id, 5)).toEqual(relayHost) }) + + it('keeps an explicit endpoint edit ahead of its retired lifecycle publisher', () => { + const cache = new HostReconnectProfileCache() + const initialVersion = cache.prime(HOST, 4) + const publish = cache.publisher(HOST.id, initialVersion, () => 4) + const editedHost = { ...HOST, endpoint: 'ws://127.0.0.1:2' } + + cache.reconnectProfile(HOST.id, 4, editedHost) + publish({ + ...HOST, + endpoints: [{ id: 'direct-primary', kind: 'lan', url: HOST.endpoint }] + }) + + expect(cache.get(HOST.id, 4)).toEqual(editedHost) + }) }) diff --git a/src/main/runtime/runtime-rpc.ts b/src/main/runtime/runtime-rpc.ts index 48903b5a1fe..3a13a32a25b 100644 --- a/src/main/runtime/runtime-rpc.ts +++ b/src/main/runtime/runtime-rpc.ts @@ -416,6 +416,7 @@ const MOBILE_RPC_METHOD_ALLOWLIST = new Set([ 'worktree.activate', 'worktree.create', 'worktree.forceDeleteBranch', + 'worktree.list', 'worktree.prefetchCreateBase', 'worktree.ps', 'worktree.show',