mirror of
https://github.com/stablyai/orca.git
synced 2026-10-07 08:02:21 +00:00
fix(sidebar): preserve host filters across stale UI updates (#25737)
This commit is contained in:
@@ -156,6 +156,8 @@ export function usePersistedUIWriter(): void {
|
||||
sortBy: s.sortBy,
|
||||
projectOrderBy: s.projectOrderBy,
|
||||
showSleepingWorkspaces: s.showSleepingWorkspaces,
|
||||
workspaceHostScope: s.workspaceHostScope,
|
||||
visibleWorkspaceHostIds: s.visibleWorkspaceHostIds,
|
||||
hideDefaultBranchWorkspace: s.hideDefaultBranchWorkspace,
|
||||
hideAutomationGeneratedWorkspaces: s.hideAutomationGeneratedWorkspaces,
|
||||
hideCliCreatedWorkspaces: s.hideCliCreatedWorkspaces,
|
||||
|
||||
@@ -270,6 +270,40 @@ describe('workspace view preferences: cross-client persistence (STA-5781)', () =
|
||||
expect(after.hideCliCreatedWorkspaces).toBe(before.hideCliCreatedWorkspaces)
|
||||
})
|
||||
|
||||
it('saves a revealed remote host after an unrelated older broadcast', async () => {
|
||||
act(() => {
|
||||
authority.set({ workspaceHostScope: 'local', visibleWorkspaceHostIds: ['local'] })
|
||||
})
|
||||
deliverBroadcasts()
|
||||
act(() => {
|
||||
authority.set({ sidebarWidth: 320 })
|
||||
store.getState().setVisibleWorkspaceHostIds(['local', 'runtime:m4air'])
|
||||
})
|
||||
pendingBroadcasts.reverse()
|
||||
deliverBroadcasts()
|
||||
expect(store.getState().visibleWorkspaceHostIds).toEqual(['local', 'runtime:m4air'])
|
||||
await flushDesktopDebounce()
|
||||
deliverBroadcasts()
|
||||
expect(authority.get().visibleWorkspaceHostIds).toEqual(['local', 'runtime:m4air'])
|
||||
expect(authority.get().sidebarWidth).toBe(320)
|
||||
})
|
||||
|
||||
it('saves All hosts after flipping back during a host-selection write', async () => {
|
||||
holdAcks = true
|
||||
act(() => store.getState().setWorkspaceHostScope('local'))
|
||||
await flushDesktopDebounce()
|
||||
act(() => store.getState().setWorkspaceHostScope('all'))
|
||||
deliverBroadcasts()
|
||||
expect(store.getState().visibleWorkspaceHostIds).toBeNull()
|
||||
expect(store.getState().workspaceHostScope).toBe('all')
|
||||
await resolveAcks()
|
||||
holdAcks = false
|
||||
await flushDesktopDebounce()
|
||||
deliverBroadcasts()
|
||||
expect(authority.get().visibleWorkspaceHostIds).toBeNull()
|
||||
expect(authority.get().workspaceHostScope).toBe('all')
|
||||
})
|
||||
|
||||
it('persists a left sidebar close across an unrelated sync and restores it on startup', async () => {
|
||||
act(() => {
|
||||
store.getState().toggleSidebar()
|
||||
|
||||
@@ -21,6 +21,8 @@ function makeBaseline(overrides: Partial<PersistedUIWriteBaseline> = {}): Persis
|
||||
sortBy: 'recent',
|
||||
projectOrderBy: 'manual',
|
||||
showSleepingWorkspaces: true,
|
||||
workspaceHostScope: 'all',
|
||||
visibleWorkspaceHostIds: null,
|
||||
hideDefaultBranchWorkspace: false,
|
||||
hideAutomationGeneratedWorkspaces: false,
|
||||
hideCliCreatedWorkspaces: false,
|
||||
@@ -58,11 +60,13 @@ describe('PERSISTED_UI_WRITE_BASELINE_FIELDS', () => {
|
||||
describe('diffPersistedUIWriteFields', () => {
|
||||
it('is empty when values are equal even across fresh array/record identities', () => {
|
||||
const a = makeBaseline({
|
||||
visibleWorkspaceHostIds: ['local', 'runtime:m4air'],
|
||||
filterRepoIds: ['r1', 'r2'],
|
||||
showDotfilesByWorktree: { w1: true },
|
||||
acknowledgedAgentsByPaneKey: { p1: 5 }
|
||||
})
|
||||
const b = makeBaseline({
|
||||
visibleWorkspaceHostIds: ['local', 'runtime:m4air'],
|
||||
filterRepoIds: ['r1', 'r2'],
|
||||
showDotfilesByWorktree: { w1: true },
|
||||
acknowledgedAgentsByPaneKey: { p1: 5 }
|
||||
@@ -70,6 +74,15 @@ describe('diffPersistedUIWriteFields', () => {
|
||||
expect(diffPersistedUIWriteFields(a, b)).toEqual({})
|
||||
})
|
||||
|
||||
it('distinguishes All hosts from a selected host list', () => {
|
||||
expect(
|
||||
diffPersistedUIWriteFields(
|
||||
makeBaseline({ visibleWorkspaceHostIds: null }),
|
||||
makeBaseline({ visibleWorkspaceHostIds: ['local'] })
|
||||
)
|
||||
).toEqual({ visibleWorkspaceHostIds: null })
|
||||
})
|
||||
|
||||
it('reports only the diverged fields, valued from the current mirror', () => {
|
||||
const baseline = makeBaseline()
|
||||
const current = makeBaseline({ showSleepingWorkspaces: false, filterRepoIds: ['r1'] })
|
||||
|
||||
@@ -21,6 +21,8 @@ export type PersistedUIWriteBaseline = {
|
||||
sortBy: PersistedUIState['sortBy']
|
||||
projectOrderBy: PersistedUIState['projectOrderBy']
|
||||
showSleepingWorkspaces: boolean
|
||||
workspaceHostScope: PersistedUIState['workspaceHostScope']
|
||||
visibleWorkspaceHostIds: PersistedUIState['visibleWorkspaceHostIds']
|
||||
hideDefaultBranchWorkspace: boolean
|
||||
hideAutomationGeneratedWorkspaces: boolean
|
||||
hideCliCreatedWorkspaces: boolean
|
||||
@@ -52,6 +54,8 @@ const PERSISTED_UI_WRITE_BASELINE_FIELD_SET = {
|
||||
sortBy: true,
|
||||
projectOrderBy: true,
|
||||
showSleepingWorkspaces: true,
|
||||
workspaceHostScope: true,
|
||||
visibleWorkspaceHostIds: true,
|
||||
hideDefaultBranchWorkspace: true,
|
||||
hideAutomationGeneratedWorkspaces: true,
|
||||
hideCliCreatedWorkspaces: true,
|
||||
@@ -104,6 +108,9 @@ function writeFieldEqual(field: keyof PersistedUIWriteBaseline, a: unknown, b: u
|
||||
if (field === 'filterRepoIds') {
|
||||
return stringArrayEqual(a as readonly string[], b as readonly string[])
|
||||
}
|
||||
if (field === 'visibleWorkspaceHostIds') {
|
||||
return a === b || (Array.isArray(a) && Array.isArray(b) && stringArrayEqual(a, b))
|
||||
}
|
||||
if (
|
||||
field === 'explorerDisplayRootByWorktree' ||
|
||||
field === 'showDotfilesByWorktree' ||
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { createUIStore, makePersistedUI } from './ui-slice-test-harness'
|
||||
|
||||
beforeEach(() => {
|
||||
vi.stubGlobal('window', { api: { ui: { set: vi.fn().mockResolvedValue(undefined) } } })
|
||||
})
|
||||
afterEach(() => vi.unstubAllGlobals())
|
||||
|
||||
describe('host visibility across saved UI broadcasts', () => {
|
||||
it('keeps a newly revealed remote host when an older UI snapshot arrives', () => {
|
||||
const store = createUIStore()
|
||||
const previous = makePersistedUI({
|
||||
workspaceHostScope: 'local',
|
||||
visibleWorkspaceHostIds: ['local']
|
||||
})
|
||||
store.getState().hydratePersistedUI(previous)
|
||||
|
||||
store.getState().setVisibleWorkspaceHostIds(['local', 'runtime:m4air'])
|
||||
store.getState().hydratePersistedUI(previous, 'sync')
|
||||
|
||||
expect(store.getState().visibleWorkspaceHostIds).toEqual(['local', 'runtime:m4air'])
|
||||
})
|
||||
|
||||
it('keeps All hosts while the prior selection is being saved', () => {
|
||||
const store = createUIStore()
|
||||
const previous = makePersistedUI({ workspaceHostScope: 'all', visibleWorkspaceHostIds: null })
|
||||
store.getState().hydratePersistedUI(previous)
|
||||
const fields = ['workspaceHostScope', 'visibleWorkspaceHostIds'] as const
|
||||
store.getState().setWorkspaceHostScope('local')
|
||||
store.getState().notePersistedUIWriteStarted(fields)
|
||||
store.getState().setWorkspaceHostScope('all')
|
||||
|
||||
store
|
||||
.getState()
|
||||
.hydratePersistedUI(
|
||||
makePersistedUI({ workspaceHostScope: 'local', visibleWorkspaceHostIds: ['local'] }),
|
||||
'sync'
|
||||
)
|
||||
|
||||
expect(store.getState().workspaceHostScope).toBe('all')
|
||||
expect(store.getState().visibleWorkspaceHostIds).toBeNull()
|
||||
})
|
||||
|
||||
it('accepts another client’s host selection when there is no local edit', () => {
|
||||
const store = createUIStore()
|
||||
store.getState().hydratePersistedUI(makePersistedUI())
|
||||
store.getState().hydratePersistedUI(
|
||||
makePersistedUI({
|
||||
workspaceHostScope: 'runtime:m4air',
|
||||
visibleWorkspaceHostIds: ['runtime:m4air']
|
||||
}),
|
||||
'sync'
|
||||
)
|
||||
|
||||
expect(store.getState().workspaceHostScope).toBe('runtime:m4air')
|
||||
expect(store.getState().visibleWorkspaceHostIds).toEqual(['runtime:m4air'])
|
||||
})
|
||||
})
|
||||
@@ -480,7 +480,7 @@ describe('createUISlice hydratePersistedUI', () => {
|
||||
expect(setUI).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('persists workspace host scope changes', () => {
|
||||
it('updates workspace host scope for the guarded UI writer', () => {
|
||||
const setUI = vi.fn(() => Promise.resolve())
|
||||
vi.stubGlobal('window', { api: { ui: { set: setUI } } })
|
||||
const store = createUIStore()
|
||||
@@ -489,13 +489,10 @@ describe('createUISlice hydratePersistedUI', () => {
|
||||
|
||||
expect(store.getState().workspaceHostScope).toBe('runtime:env-1')
|
||||
expect(store.getState().visibleWorkspaceHostIds).toEqual(['runtime:env-1'])
|
||||
expect(setUI).toHaveBeenCalledWith({
|
||||
workspaceHostScope: 'runtime:env-1',
|
||||
visibleWorkspaceHostIds: ['runtime:env-1']
|
||||
})
|
||||
expect(setUI).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('persists visible workspace host changes independently of focused host', () => {
|
||||
it('updates host visibility independently of focused host for the guarded UI writer', () => {
|
||||
const setUI = vi.fn(() => Promise.resolve())
|
||||
vi.stubGlobal('window', { api: { ui: { set: setUI } } })
|
||||
const store = createUIStore()
|
||||
@@ -505,10 +502,7 @@ describe('createUISlice hydratePersistedUI', () => {
|
||||
|
||||
expect(store.getState().workspaceHostScope).toBe('runtime:env-1')
|
||||
expect(store.getState().visibleWorkspaceHostIds).toEqual(['local', 'runtime:env-1'])
|
||||
expect(setUI).toHaveBeenLastCalledWith({
|
||||
workspaceHostScope: 'runtime:env-1',
|
||||
visibleWorkspaceHostIds: ['local', 'runtime:env-1']
|
||||
})
|
||||
expect(setUI).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('persists workspace host order changes', () => {
|
||||
|
||||
@@ -67,9 +67,6 @@ export function createUiPreferenceActions(set: UISliceSet, get: UISliceGet): Par
|
||||
const normalized = normalizeExecutionHostScope(scope)
|
||||
const visibleWorkspaceHostIds = normalized === 'all' ? null : [normalized]
|
||||
set({ workspaceHostScope: normalized, visibleWorkspaceHostIds })
|
||||
window.api.ui
|
||||
.set({ workspaceHostScope: normalized, visibleWorkspaceHostIds })
|
||||
.catch(console.error)
|
||||
},
|
||||
visibleWorkspaceHostIds: null,
|
||||
setVisibleWorkspaceHostIds: (ids) => {
|
||||
@@ -82,9 +79,6 @@ export function createUiPreferenceActions(set: UISliceSet, get: UISliceGet): Par
|
||||
workspaceHostScope = normalized[0]
|
||||
}
|
||||
set({ visibleWorkspaceHostIds: normalized, workspaceHostScope })
|
||||
window.api.ui
|
||||
.set({ visibleWorkspaceHostIds: normalized, workspaceHostScope })
|
||||
.catch(console.error)
|
||||
},
|
||||
workspaceHostOrder: [],
|
||||
setWorkspaceHostOrder: (ids) => {
|
||||
|
||||
Reference in New Issue
Block a user