mirror of
https://github.com/stablyai/orca.git
synced 2026-10-20 16:03:20 +00:00
fix(mobile): clarify bounded host connection state
This commit is contained in:
+14
-3
@@ -32,7 +32,10 @@ import type { RpcClient } from '../src/transport/rpc-client'
|
||||
import { sendSingleFlightRequest } from '../src/transport/request-single-flight'
|
||||
import { useCloseHost, useForceReconnect, usePrimeHosts } from '../src/transport/client-context'
|
||||
import { useAllHostClients } from '../src/transport/all-host-client-connections'
|
||||
import { selectHomeAutoConnectHostIds } from '../src/transport/home-host-auto-connect'
|
||||
import {
|
||||
resolveHomeHostConnectionState,
|
||||
selectHomeAutoConnectHostIds
|
||||
} from '../src/transport/home-host-auto-connect'
|
||||
import { classifyConnection } from '../src/transport/connection-health'
|
||||
import { subscribeToDesktopNotifications } from '../src/notifications/mobile-notifications'
|
||||
import {
|
||||
@@ -777,7 +780,11 @@ export default function HomeScreen() {
|
||||
}
|
||||
ItemSeparatorComponent={CardGap}
|
||||
renderItem={({ item }) => {
|
||||
const state = hostStates[item.id] ?? 'connecting'
|
||||
const state = resolveHomeHostConnectionState(
|
||||
item.id,
|
||||
hostStates[item.id],
|
||||
autoConnectHostIds
|
||||
)
|
||||
const attempts = hostAttempts[item.id] ?? 0
|
||||
const lastConnectedAt = hostLastConnected[item.id] ?? null
|
||||
const info = worktreeInfo[item.id]
|
||||
@@ -975,7 +982,11 @@ export default function HomeScreen() {
|
||||
if (!host) {
|
||||
return []
|
||||
}
|
||||
const state = hostStates[host.id] ?? 'connecting'
|
||||
const state = resolveHomeHostConnectionState(
|
||||
host.id,
|
||||
hostStates[host.id],
|
||||
autoConnectHostIds
|
||||
)
|
||||
const isLive =
|
||||
state === 'connected' ||
|
||||
state === 'connecting' ||
|
||||
|
||||
@@ -38,7 +38,6 @@ export function useAllHostClients(hostIds: string[], options?: UseAllHostClients
|
||||
ctx.release(id)
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [key])
|
||||
|
||||
return useMemo(() => {
|
||||
@@ -56,6 +55,5 @@ export function useAllHostClients(hostIds: string[], options?: UseAllHostClients
|
||||
? [{ hostId, client, state: ctx.getState(hostId), path: ctx.getActivePath(hostId) }]
|
||||
: []
|
||||
})
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [key, tick])
|
||||
}
|
||||
|
||||
@@ -20,8 +20,9 @@ vi.mock('./connection-revival-triggers', () => ({
|
||||
subscribeConnectionRevivalTriggers: () => () => {}
|
||||
}))
|
||||
|
||||
import { RpcClientProvider, useCloseHost, useHostClient } from './client-context'
|
||||
import { RpcClientProvider, useCloseHost, useForceReconnect, useHostClient } from './client-context'
|
||||
import { useAllHostClients } from './all-host-client-connections'
|
||||
import { selectHomeAutoConnectHostIds } from './home-host-auto-connect'
|
||||
|
||||
type FakeClient = RpcClient & {
|
||||
emitState: (state: ConnectionState) => void
|
||||
@@ -344,4 +345,95 @@ describe('useAllHostClients', () => {
|
||||
act(() => renderer?.unmount())
|
||||
}
|
||||
})
|
||||
|
||||
it('keeps startup connection fanout constant for a large saved-host list', async () => {
|
||||
const hosts = Array.from({ length: 1_000 }, (_, index) => ({
|
||||
...HOST,
|
||||
id: `host-${index}`,
|
||||
name: `Host ${index}`,
|
||||
lastConnected: index
|
||||
}))
|
||||
const hostIds = hosts.map((host) => host.id)
|
||||
const autoConnectHostIds = selectHomeAutoConnectHostIds(hosts)
|
||||
connectMock.mockReturnValue(makeFakeClient('connected'))
|
||||
loadHostsMock.mockResolvedValue(hosts)
|
||||
|
||||
let renderer: ReactTestRenderer | null = null
|
||||
function Probe(): null {
|
||||
useAllHostClients(hostIds, { autoConnectHostIds })
|
||||
return null
|
||||
}
|
||||
|
||||
const restore = suppressReactTestRendererDeprecationWarning()
|
||||
try {
|
||||
await act(async () => {
|
||||
renderer = create(createElement(RpcClientProvider, null, createElement(Probe)))
|
||||
await Promise.resolve()
|
||||
})
|
||||
expect(connectMock).toHaveBeenCalledTimes(3)
|
||||
expect(connectMock.mock.calls.map(([host]) => host.id)).toEqual([
|
||||
'host-999',
|
||||
'host-998',
|
||||
'host-997'
|
||||
])
|
||||
} finally {
|
||||
restore()
|
||||
act(() => renderer?.unmount())
|
||||
}
|
||||
})
|
||||
|
||||
it('retains connect-all behavior when no startup subset is provided', async () => {
|
||||
const host2 = { ...HOST, id: 'host-2', name: 'Host 2' }
|
||||
connectMock.mockReturnValue(makeFakeClient('connected'))
|
||||
loadHostsMock.mockResolvedValue([HOST, host2])
|
||||
|
||||
let renderer: ReactTestRenderer | null = null
|
||||
function Probe(): null {
|
||||
useAllHostClients([HOST.id, host2.id])
|
||||
return null
|
||||
}
|
||||
|
||||
const restore = suppressReactTestRendererDeprecationWarning()
|
||||
try {
|
||||
await act(async () => {
|
||||
renderer = create(createElement(RpcClientProvider, null, createElement(Probe)))
|
||||
await Promise.resolve()
|
||||
})
|
||||
expect(connectMock).toHaveBeenCalledTimes(2)
|
||||
} finally {
|
||||
restore()
|
||||
act(() => renderer?.unmount())
|
||||
}
|
||||
})
|
||||
|
||||
it('allows an excluded host to connect manually', async () => {
|
||||
connectMock.mockReturnValue(makeFakeClient('connected'))
|
||||
loadHostsMock.mockResolvedValue([HOST])
|
||||
|
||||
let reconnect: ((hostId: string) => Promise<void>) | null = null
|
||||
let renderer: ReactTestRenderer | null = null
|
||||
function Probe(): null {
|
||||
useAllHostClients([HOST.id], { autoConnectHostIds: [] })
|
||||
reconnect = useForceReconnect()
|
||||
return null
|
||||
}
|
||||
|
||||
const restore = suppressReactTestRendererDeprecationWarning()
|
||||
try {
|
||||
await act(async () => {
|
||||
renderer = create(createElement(RpcClientProvider, null, createElement(Probe)))
|
||||
})
|
||||
expect(connectMock).not.toHaveBeenCalled()
|
||||
if (!reconnect) {
|
||||
throw new Error('reconnect harness did not initialize')
|
||||
}
|
||||
await act(async () => {
|
||||
await reconnect?.(HOST.id)
|
||||
})
|
||||
expect(connectMock).toHaveBeenCalledOnce()
|
||||
} finally {
|
||||
restore()
|
||||
act(() => renderer?.unmount())
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import type { HostProfile } from './types'
|
||||
import { HOME_AUTO_CONNECT_LIMIT, selectHomeAutoConnectHostIds } from './home-host-auto-connect'
|
||||
import {
|
||||
HOME_AUTO_CONNECT_LIMIT,
|
||||
resolveHomeHostConnectionState,
|
||||
selectHomeAutoConnectHostIds
|
||||
} from './home-host-auto-connect'
|
||||
|
||||
function host(id: string, lastConnected: number, credentials = true): HostProfile {
|
||||
return {
|
||||
@@ -13,7 +17,7 @@ function host(id: string, lastConnected: number, credentials = true): HostProfil
|
||||
}
|
||||
}
|
||||
|
||||
describe('selectHomeAutoConnectHostIds', () => {
|
||||
describe('home host auto-connect', () => {
|
||||
it('limits startup connections to the most recently used credentialed hosts', () => {
|
||||
const hosts = [
|
||||
host('old', 1),
|
||||
@@ -35,4 +39,18 @@ describe('selectHomeAutoConnectHostIds', () => {
|
||||
|
||||
expect(hosts.map((item) => item.id)).toEqual(['old', 'new'])
|
||||
})
|
||||
|
||||
it('only presents hosts in the startup subset as connecting before clients open', () => {
|
||||
const autoConnectHostIds = ['recent']
|
||||
|
||||
expect(resolveHomeHostConnectionState('recent', undefined, autoConnectHostIds)).toBe(
|
||||
'connecting'
|
||||
)
|
||||
expect(resolveHomeHostConnectionState('stale', undefined, autoConnectHostIds)).toBe(
|
||||
'disconnected'
|
||||
)
|
||||
expect(resolveHomeHostConnectionState('stale', 'connected', autoConnectHostIds)).toBe(
|
||||
'connected'
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { HostProfile } from './types'
|
||||
import type { ConnectionState, HostProfile } from './types'
|
||||
|
||||
export const HOME_AUTO_CONNECT_LIMIT = 3
|
||||
|
||||
@@ -14,3 +14,11 @@ export function selectHomeAutoConnectHostIds(
|
||||
.slice(0, Math.max(0, limit))
|
||||
.map((host) => host.id)
|
||||
}
|
||||
|
||||
export function resolveHomeHostConnectionState(
|
||||
hostId: string,
|
||||
state: ConnectionState | undefined,
|
||||
autoConnectHostIds: readonly string[]
|
||||
): ConnectionState {
|
||||
return state ?? (autoConnectHostIds.includes(hostId) ? 'connecting' : 'disconnected')
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user