From 65a92afce86595e7abd9607c2f7ab10edaf91a01 Mon Sep 17 00:00:00 2001 From: Guoyu Su Date: Thu, 9 Jul 2026 11:47:13 +0800 Subject: [PATCH] fix(desktop): preserve sidebar groups on reload --- apps/desktop/src/stores/connectionStore.ts | 3 +- .../connectionStoreGroupedConnect.test.ts | 58 +++++++++++++++++++ 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/apps/desktop/src/stores/connectionStore.ts b/apps/desktop/src/stores/connectionStore.ts index 323ae4cf05..268599065b 100644 --- a/apps/desktop/src/stores/connectionStore.ts +++ b/apps/desktop/src/stores/connectionStore.ts @@ -4926,9 +4926,10 @@ export const useConnectionStore = defineStore("connection", () => { const saved = await api.loadConnections(); connections.value = saved.map(normalizeConnection); const savedLayout = await api.loadSidebarLayout(); + const currentLayout = sidebarLayout.value.groups.length || sidebarLayout.value.order.length ? sidebarLayout.value : null; sidebarLayout.value = reconcileLayout( connections.value.map((c) => c.id), - savedLayout, + savedLayout ?? currentLayout, ); rebuildTreeNodes(); })().finally(() => { diff --git a/packages/app-tests/connectionStoreGroupedConnect.test.ts b/packages/app-tests/connectionStoreGroupedConnect.test.ts index da02b3700a..c1cc76b7d9 100644 --- a/packages/app-tests/connectionStoreGroupedConnect.test.ts +++ b/packages/app-tests/connectionStoreGroupedConnect.test.ts @@ -153,6 +153,64 @@ test("duplicating a grouped connection keeps the copy in the same group", async } }); +test("reloading connections preserves the current grouped layout when the saved layout is temporarily unavailable", async () => { + const originalFetch = globalThis.fetch; + const storage = installMemoryStorage(); + const savedConnections: ConnectionConfig[] = [ + conn("pg", "pg"), + conn("pg2", "pg2"), + conn("pg3", "pg3"), + conn("pg4", "pg4"), + ]; + let savedLayout: SidebarLayout | null = { + groups: [ + { id: "group-a", name: "dir[a]", collapsed: false }, + { id: "group-b", name: "dir[b]", collapsed: false }, + ], + order: [ + { type: "group", id: "group-a", connectionIds: ["pg", "pg2"] }, + { type: "group", id: "group-b", connectionIds: ["pg3", "pg4"] }, + ], + }; + + globalThis.fetch = (async (input, init) => { + const url = String(input); + if (url === "/api/connection/list") { + return new Response(JSON.stringify(savedConnections), { status: 200, headers: { "Content-Type": "application/json" } }); + } + if (url === "/api/layout/sidebar") { + if (init?.method === "POST") { + savedLayout = JSON.parse(String(init.body ?? "null")); + return new Response("null", { status: 200, headers: { "Content-Type": "application/json" } }); + } + return new Response(JSON.stringify(savedLayout), { status: 200, headers: { "Content-Type": "application/json" } }); + } + return new Response("null", { status: 200, headers: { "Content-Type": "application/json" } }); + }) as typeof fetch; + + try { + setActivePinia(createPinia()); + const store = useConnectionStore(); + await store.initFromDisk(); + + assert.deepEqual( + store.treeNodes.map((node) => node.label), + ["dir[a]", "dir[b]"], + ); + + savedLayout = null; + await store.initFromDisk(); + + assert.deepEqual( + store.treeNodes.map((node) => node.label), + ["dir[a]", "dir[b]"], + ); + } finally { + globalThis.fetch = originalFetch; + storage.restore(); + } +}); + test("importing grouped dbx connections remaps exported layout to new connection ids", async () => { const originalFetch = globalThis.fetch; const storage = installMemoryStorage();