mirror of
https://github.com/stablyai/orca.git
synced 2026-09-29 08:03:20 +00:00
refactor(mobile-web): put the transport connection state on the wire
The shell collapsed six transport states into four, and every page route expanded them back. Both mappings are gone; the page reads the state the transport reported. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
This commit is contained in:
@@ -22,7 +22,6 @@ import { MobileWebHybridShellPresentation } from '../src/mobile-web/MobileWebHyb
|
||||
import { mobileWebShellLoadFailureWarning } from '../src/mobile-web/mobile-web-shell-load-failure-warning'
|
||||
import { useMobileWebNavigationIntentHandoff } from '../src/mobile-web/use-mobile-web-navigation-intent-handoff'
|
||||
import { useMobileWebColdResumeRoute } from '../src/mobile-web/use-mobile-web-cold-resume-route'
|
||||
import { mobileWebBridgeConnectionState } from '../src/mobile-web/mobile-web-bridge-connection-state'
|
||||
import { MobileWebOneShotResponseDrop } from '../src/mobile-web/mobile-web-one-shot-response-drop'
|
||||
import { useMobileWebE2eHostSelection } from '../src/mobile-web/mobile-web-e2e-host-selection'
|
||||
import { useMobileWebAppForegroundAuthority } from '../src/mobile-web/use-mobile-web-app-foreground-authority'
|
||||
@@ -266,7 +265,7 @@ export default function HybridScreen() {
|
||||
}, [postInit])
|
||||
|
||||
useEffect(() => {
|
||||
brokerRef.current?.updateConnectionState(mobileWebBridgeConnectionState(state))
|
||||
brokerRef.current?.updateConnectionState(state)
|
||||
const current = session
|
||||
if (!current || pageDocument.initializedSessionRef.current !== current.sessionId) {
|
||||
return
|
||||
@@ -276,7 +275,7 @@ export default function HybridScreen() {
|
||||
type: 'connection',
|
||||
shellSessionId: current.sessionId,
|
||||
buildId: current.buildId,
|
||||
state: mobileWebBridgeConnectionState(state),
|
||||
state,
|
||||
reconnectAttempts: reconnects,
|
||||
lastConnectedAt: lastConnected
|
||||
})
|
||||
|
||||
@@ -14,18 +14,12 @@ export default function HostMobileWebAccountsRoute() {
|
||||
: undefined,
|
||||
[shell.client, shell.hostDisplayName]
|
||||
)
|
||||
const connectionState =
|
||||
shell.connection === 'offline'
|
||||
? 'disconnected'
|
||||
: shell.connection === 'recovering'
|
||||
? 'reconnecting'
|
||||
: shell.connection
|
||||
|
||||
return (
|
||||
<AccountsScreen
|
||||
hostId={HOSTED_PAGE_HOST_ID}
|
||||
operations={operations}
|
||||
connectionState={connectionState}
|
||||
connectionState={shell.connection}
|
||||
nativeHostBinding={false}
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -9,17 +9,11 @@ export default function HostMobileWebFileExplorerRoute() {
|
||||
() => (shell.client ? webHostFileExplorerOperations(shell.client) : undefined),
|
||||
[shell.client]
|
||||
)
|
||||
const connectionState =
|
||||
shell.connection === 'offline'
|
||||
? 'disconnected'
|
||||
: shell.connection === 'recovering'
|
||||
? 'reconnecting'
|
||||
: shell.connection
|
||||
|
||||
return (
|
||||
<MobileFileExplorerScreen
|
||||
operations={operations}
|
||||
connectionState={connectionState}
|
||||
connectionState={shell.connection}
|
||||
nativeHostBinding={false}
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -9,17 +9,11 @@ export default function HostMobileWebFilePreviewRoute() {
|
||||
() => (shell.client ? webHostFilePreviewOperations(shell.client) : undefined),
|
||||
[shell.client]
|
||||
)
|
||||
const connectionState =
|
||||
shell.connection === 'offline'
|
||||
? 'disconnected'
|
||||
: shell.connection === 'recovering'
|
||||
? 'reconnecting'
|
||||
: shell.connection
|
||||
|
||||
return (
|
||||
<MobileFilePreviewRoute
|
||||
operations={operations}
|
||||
connectionState={connectionState}
|
||||
connectionState={shell.connection}
|
||||
nativeHostBinding={false}
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -16,19 +16,13 @@ export default function HostMobileWebDiffReviewRoute() {
|
||||
() => (shell.client && workspaceId ? webHostDiffReviewClient(shell.client, workspaceId) : null),
|
||||
[shell.client, workspaceId]
|
||||
)
|
||||
const connectionState =
|
||||
shell.connection === 'offline'
|
||||
? 'disconnected'
|
||||
: shell.connection === 'recovering'
|
||||
? 'reconnecting'
|
||||
: shell.connection
|
||||
|
||||
return (
|
||||
<MobileDiffReviewRoute
|
||||
routeName={workspaceName}
|
||||
binding={{
|
||||
client,
|
||||
connectionState,
|
||||
connectionState: shell.connection,
|
||||
reconnect: async () => {
|
||||
await shell.client?.navigationReconnect()
|
||||
},
|
||||
|
||||
@@ -83,12 +83,6 @@ export default function HostMobileWebSessionRoute() {
|
||||
() => (shell.client ? webHostSessionChatPendingDeliveryOperations(shell.client) : undefined),
|
||||
[shell.client]
|
||||
)
|
||||
const connectionState =
|
||||
shell.connection === 'offline'
|
||||
? 'disconnected'
|
||||
: shell.connection === 'recovering'
|
||||
? 'reconnecting'
|
||||
: shell.connection
|
||||
|
||||
return (
|
||||
<SessionScreen
|
||||
@@ -104,7 +98,7 @@ export default function HostMobileWebSessionRoute() {
|
||||
sessionNativeChatOperations={sessionNativeChatOperations}
|
||||
sessionChatDraftOperations={sessionChatDraftOperations}
|
||||
sessionChatPendingDeliveryOperations={sessionChatPendingDeliveryOperations}
|
||||
connectionState={connectionState}
|
||||
connectionState={shell.connection}
|
||||
nativeHostBinding={false}
|
||||
reconnect={() =>
|
||||
shell.client
|
||||
|
||||
@@ -17,19 +17,13 @@ export default function HostMobileWebSourceControlRoute() {
|
||||
shell.client && workspaceId ? webHostSourceControlClient(shell.client, workspaceId) : null,
|
||||
[shell.client, workspaceId]
|
||||
)
|
||||
const connectionState =
|
||||
shell.connection === 'offline'
|
||||
? 'disconnected'
|
||||
: shell.connection === 'recovering'
|
||||
? 'reconnecting'
|
||||
: shell.connection
|
||||
|
||||
return (
|
||||
<MobileSourceControlRoute
|
||||
routeName={workspaceName}
|
||||
binding={{
|
||||
client,
|
||||
connectionState,
|
||||
connectionState: shell.connection,
|
||||
reconnect: async () => {
|
||||
await shell.client?.navigationReconnect()
|
||||
},
|
||||
|
||||
@@ -41,12 +41,6 @@ export default function HostMobileWebTasksRoute() {
|
||||
workspaceCreation: webHostWorkspaceCreationOperations(shell.client)
|
||||
}
|
||||
}, [shell.client])
|
||||
const connectionState =
|
||||
shell.connection === 'offline'
|
||||
? 'disconnected'
|
||||
: shell.connection === 'recovering'
|
||||
? 'reconnecting'
|
||||
: shell.connection
|
||||
|
||||
return (
|
||||
<MobileTasksScreen
|
||||
@@ -65,7 +59,7 @@ export default function HostMobileWebTasksRoute() {
|
||||
providerWriteOperations={operations?.providerWrite}
|
||||
readOperations={operations?.read}
|
||||
workspaceCreationOperations={operations?.workspaceCreation}
|
||||
connectionState={connectionState}
|
||||
connectionState={shell.connection}
|
||||
connectionMetrics={{
|
||||
reconnectAttempts: shell.reconnectAttempts,
|
||||
lastConnectedAt: shell.lastConnectedAt
|
||||
|
||||
@@ -45,12 +45,6 @@ export default function HostMobileWebRoute() {
|
||||
),
|
||||
[pathname, router, shell.client]
|
||||
)
|
||||
const connectionState =
|
||||
shell.connection === 'offline'
|
||||
? 'disconnected'
|
||||
: shell.connection === 'recovering'
|
||||
? 'reconnecting'
|
||||
: shell.connection
|
||||
const hostStatusGates = useWebHostStatusGates({
|
||||
client: shell.client,
|
||||
connection: shell.connection
|
||||
@@ -60,7 +54,7 @@ export default function HostMobileWebRoute() {
|
||||
<HostScreen
|
||||
hostId={HOSTED_PAGE_HOST_ID}
|
||||
nativeHostBinding={false}
|
||||
connectionState={connectionState}
|
||||
connectionState={shell.connection}
|
||||
connectionMetrics={{
|
||||
reconnectAttempts: shell.reconnectAttempts,
|
||||
lastConnectedAt: shell.lastConnectedAt
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
import type { ConnectionState } from '../transport/types'
|
||||
|
||||
export function mobileWebBridgeConnectionState(
|
||||
state: ConnectionState
|
||||
): 'connecting' | 'connected' | 'offline' | 'recovering' {
|
||||
if (state === 'connected') {
|
||||
return 'connected'
|
||||
}
|
||||
if (state === 'reconnecting') {
|
||||
return 'recovering'
|
||||
}
|
||||
if (state === 'connecting' || state === 'handshaking') {
|
||||
return 'connecting'
|
||||
}
|
||||
return 'offline'
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import { requireMobileWebConnectedClient } from './mobile-web-connected-client'
|
||||
import type {
|
||||
MobileWebBridgePageMessage,
|
||||
MobileWebConnectionState,
|
||||
MobileWebResumeRoute
|
||||
} from '../../../src/shared/mobile-web/bridge-contract'
|
||||
import type { RpcClient } from '../transport/rpc-client'
|
||||
@@ -115,7 +116,7 @@ export class MobileWebCapabilityBroker {
|
||||
void this.messages.error(requestId, 'cancelled', false)
|
||||
}
|
||||
}
|
||||
updateConnectionState(state: 'connecting' | 'connected' | 'offline' | 'recovering'): void {
|
||||
updateConnectionState(state: MobileWebConnectionState): void {
|
||||
if (state !== 'connected') {
|
||||
this.authorities.terminalArtifact.clear()
|
||||
void this.speechAuthority.cancel('disconnected')
|
||||
|
||||
@@ -4,7 +4,6 @@ import {
|
||||
type MobileWebResumeRoute
|
||||
} from '../../../src/shared/mobile-web/bridge-contract'
|
||||
import type { ConnectionState } from '../transport/types'
|
||||
import { mobileWebBridgeConnectionState } from './mobile-web-bridge-connection-state'
|
||||
import { MOBILE_WEB_PRODUCTION_GRANTS } from './mobile-web-production-grants'
|
||||
|
||||
type MobileWebShellInitArgs = {
|
||||
@@ -28,7 +27,7 @@ export function mobileWebShellInitMessage(
|
||||
type: 'init',
|
||||
shellSessionId: args.shellSessionId,
|
||||
buildId: args.buildId,
|
||||
connection: mobileWebBridgeConnectionState(args.state),
|
||||
connection: args.state,
|
||||
hostDisplayName: args.hostDisplayName,
|
||||
reconnectAttempts: args.reconnectAttempts,
|
||||
lastConnectedAt: args.lastConnectedAt,
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
parseMobileWebBridgeInitialMessage,
|
||||
parseMobileWebBridgeShellMessage,
|
||||
type MobileWebBridgeMessageContext,
|
||||
type MobileWebConnectionState,
|
||||
type MobileWebNavigationRoute,
|
||||
type MobileWebResumeRoute
|
||||
} from '../../shared/mobile-web/bridge-contract'
|
||||
@@ -29,7 +30,7 @@ import { subscribeToMobileWebShellMessages } from './native-shell-message-inbox'
|
||||
export type MobileWebNativeShellState = {
|
||||
client: MobileWebBridgeClient | null
|
||||
context: MobileWebBridgeMessageContext | null
|
||||
connection: 'connecting' | 'connected' | 'offline' | 'recovering'
|
||||
connection: MobileWebConnectionState
|
||||
hostDisplayName: string | null
|
||||
reconnectAttempts: number
|
||||
lastConnectedAt: number | null
|
||||
|
||||
@@ -516,7 +516,7 @@ describe('mobile web bridge shell contract', () => {
|
||||
type: 'connection',
|
||||
shellSessionId: SHELL_SESSION_ID,
|
||||
buildId: BUILD_ID,
|
||||
state: 'recovering'
|
||||
state: 'reconnecting'
|
||||
}
|
||||
|
||||
expect(
|
||||
|
||||
@@ -165,7 +165,16 @@ const ShellEnvelopeSchema = z.object({
|
||||
buildId: BuildIdSchema
|
||||
})
|
||||
|
||||
const ConnectionStateSchema = z.enum(['connecting', 'connected', 'offline', 'recovering'])
|
||||
// The shell publishes the transport's own state; the page decides what to show for each.
|
||||
const ConnectionStateSchema = z.enum([
|
||||
'connecting',
|
||||
'handshaking',
|
||||
'connected',
|
||||
'disconnected',
|
||||
'reconnecting',
|
||||
'auth-failed'
|
||||
])
|
||||
export type MobileWebConnectionState = z.infer<typeof ConnectionStateSchema>
|
||||
const ConnectionMetricsShape = {
|
||||
reconnectAttempts: z.number().int().nonnegative().max(1_000_000).optional(),
|
||||
lastConnectedAt: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER).nullable().optional()
|
||||
|
||||
Reference in New Issue
Block a user