fix(desktop): keep a window-state IPC that lands before the first connection publishes
`onWindowStateChanged` merged the fullscreen / traffic-light payload into `$connection`, but only when a connection was already published; while `$connection` was null (mid-boot, or between a dropped primary and its fallback resolving) the payload was dropped. Main snapshots the chrome state into each descriptor at mint time, so a toggle that fires after the mint but before the renderer publishes it is newer than the snapshot and never reached the titlebar cluster until the next toggle — the third root-cause atom of #108641. Buffer the last payload while nothing is published and fold it into the next non-null publish, then clear it: later descriptors carry their own fresh snapshot.
This commit is contained in:
@@ -2248,3 +2248,40 @@ describe('useGatewayBoot remote reconnect loop (real hook, fake socket)', () =>
|
||||
expect($gatewayState.get()).toBe('open')
|
||||
})
|
||||
})
|
||||
|
||||
describe('window-state IPC before the first connection publishes (#108641)', () => {
|
||||
it('a fullscreen toggle that lands while getConnection is still pending reaches the published connection', async () => {
|
||||
// Main snapshots chrome state into the descriptor at mint time; a toggle
|
||||
// that fires after the mint but before the renderer publishes it is newer
|
||||
// than the snapshot and used to be dropped because $connection was null.
|
||||
let windowState: ((payload: Record<string, unknown>) => void) | null = null
|
||||
const pending = deferred<Record<string, unknown>>()
|
||||
|
||||
const desktop = {
|
||||
...fakeDesktop(),
|
||||
getConnection: vi.fn(() => pending.promise),
|
||||
onWindowStateChanged: vi.fn((callback: (payload: Record<string, unknown>) => void) => {
|
||||
windowState = callback
|
||||
|
||||
return () => undefined
|
||||
})
|
||||
}
|
||||
|
||||
;(window as { hermesDesktop?: unknown }).hermesDesktop = desktop
|
||||
|
||||
render(<Harness />)
|
||||
await flushAsync()
|
||||
expect($connection.get()).toBeNull()
|
||||
|
||||
act(() => windowState?.({ isFullscreen: true, nativeOverlayWidth: 0, windowButtonPosition: null }))
|
||||
expect($connection.get()).toBeNull()
|
||||
|
||||
await act(async () => {
|
||||
pending.resolve({ ...primaryConn, isFullscreen: false, windowButtonPosition: { x: 12, y: 16 } })
|
||||
await vi.advanceTimersByTimeAsync(0)
|
||||
})
|
||||
|
||||
expect($connection.get()?.isFullscreen).toBe(true)
|
||||
expect($connection.get()?.windowButtonPosition).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
import { useEffect, useRef } from 'react'
|
||||
|
||||
import { shouldApplyPostBootProgressError } from '@/components/boot-failure-reauth'
|
||||
import type { DesktopBootProgress, HermesConnection } from '@/global'
|
||||
import type { DesktopBootProgress, HermesConnection, HermesWindowState } from '@/global'
|
||||
import { HermesGateway } from '@/hermes'
|
||||
import { translateNow } from '@/i18n'
|
||||
import { desktopDefaultCwd } from '@/lib/desktop-fs'
|
||||
@@ -199,7 +199,20 @@ export function useGatewayBoot({
|
||||
let cancelled = false
|
||||
const desktop = window.hermesDesktop
|
||||
|
||||
// Window-state IPC (fullscreen / traffic-light position) that lands while
|
||||
// no connection is published — mid-boot, or between a dropped primary and
|
||||
// its fallback resolving — has nowhere to merge into. Main snapshots the
|
||||
// chrome state into each descriptor at mint time, so a toggle that happens
|
||||
// AFTER the mint but BEFORE the renderer publishes it is newer than the
|
||||
// snapshot and would otherwise be lost until the next toggle (#108641).
|
||||
let pendingWindowState: HermesWindowState | null = null
|
||||
|
||||
const publish = (next: HermesConnection | null) => {
|
||||
if (next && pendingWindowState) {
|
||||
next = { ...next, ...pendingWindowState }
|
||||
pendingWindowState = null
|
||||
}
|
||||
|
||||
callbacksRef.current.onConnectionReady(next)
|
||||
setConnection(next)
|
||||
desktop?.setActiveConnectionRoute?.(
|
||||
@@ -1108,6 +1121,8 @@ export function useGatewayBoot({
|
||||
|
||||
if (current) {
|
||||
publish({ ...current, ...payload })
|
||||
} else {
|
||||
pendingWindowState = payload
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user