diff --git a/apps/desktop/src/components/sidebar/TreeItem.vue b/apps/desktop/src/components/sidebar/TreeItem.vue index 7da49baa85..014ab40a34 100644 --- a/apps/desktop/src/components/sidebar/TreeItem.vue +++ b/apps/desktop/src/components/sidebar/TreeItem.vue @@ -2989,6 +2989,7 @@ async function createDuckDbAttachedDatabaseFile() { attached_databases: [...(config.attached_databases ?? []), { name, path }], }); } + await connectionStore.ensureVisibleDatabase(node.connectionId, name); await connectionStore.loadDatabases(node.connectionId, { force: true }); connectionStore.selectedTreeNodeId = `${node.connectionId}:${name}`; toast(t("contextMenu.createDuckDbFileSuccess", { name }), 3000); @@ -3008,6 +3009,7 @@ async function confirmCreateDatabase() { if (config?.db_type === "mongodb") { await api.mongoCreateDatabase(node.connectionId, name); toast(t("contextMenu.createDatabaseSuccess", { name }), 3000); + await connectionStore.ensureVisibleDatabase(node.connectionId, name); await connectionStore.loadMongoDatabases(node.connectionId); return; } @@ -3021,6 +3023,7 @@ async function confirmCreateDatabase() { }); await api.executeQuery(node.connectionId, "", sql); toast(t("contextMenu.createDatabaseSuccess", { name }), 3000); + await connectionStore.ensureVisibleDatabase(node.connectionId, name); await connectionStore.loadDatabases(node.connectionId, { force: true }); } catch (e: any) { toast(t("contextMenu.tableOperationFailed", { message: e?.message || String(e) }), 5000); diff --git a/apps/desktop/src/lib/connection/connectionVisibleDatabases.ts b/apps/desktop/src/lib/connection/connectionVisibleDatabases.ts index bf93f1d876..2d242ef16e 100644 --- a/apps/desktop/src/lib/connection/connectionVisibleDatabases.ts +++ b/apps/desktop/src/lib/connection/connectionVisibleDatabases.ts @@ -25,6 +25,13 @@ export function initialVisibleDatabaseSelection(databaseNames: string[], visible return filterDatabaseNamesForVisiblePicker(databaseNames, connection); } +export function appendVisibleDatabaseSelection(visibleDatabases: string[] | undefined, databaseName: string): string[] | undefined { + if (!Array.isArray(visibleDatabases)) return undefined; + const name = databaseName.trim(); + if (!name || visibleDatabases.includes(name)) return visibleDatabases; + return [...visibleDatabases, name]; +} + export function visibleDatabaseSelectionIsStale(previous: VisibleDatabaseConnectionFields, current: VisibleDatabaseConnectionFields): boolean { return visibleDatabaseFingerprint(previous) !== visibleDatabaseFingerprint(current); } diff --git a/apps/desktop/src/stores/connectionStore.ts b/apps/desktop/src/stores/connectionStore.ts index d08d28d6fc..3e12aae58f 100644 --- a/apps/desktop/src/stores/connectionStore.ts +++ b/apps/desktop/src/stores/connectionStore.ts @@ -61,6 +61,7 @@ import { kvRootNodeLabel } from "@/lib/kv/kvRootPresentation"; import { REDIS_SCAN_PAGE_SIZE_DEFAULT } from "@/lib/redis/redisKeyPattern"; import { appendAgentDriverUpdateHint, hasAgentDriverUpdate, type AgentDriverInstallState } from "@/lib/connection/agentDriverInstallHint"; import { appendConnectionErrorHints } from "@/lib/connection/connectionErrorHints"; +import { appendVisibleDatabaseSelection } from "@/lib/connection/connectionVisibleDatabases"; import { createMetadataLoadTrace, logMetadataLoadTrace, MetadataLoadCoordinator, type MetadataLoadTraceLogger } from "@/lib/metadata/metadataLoadCoordinator"; import type { MetadataScopeInput } from "@/lib/metadata/metadataLoadScope"; import { MetadataResultCache, type MetadataCacheInvalidation } from "@/lib/metadata/metadataResultCache"; @@ -1637,6 +1638,14 @@ export const useConnectionStore = defineStore("connection", () => { await reloadConnectionDatabaseChildren(connectionId); } + async function ensureVisibleDatabase(connectionId: string, databaseName: string) { + const config = getConfig(connectionId); + if (!config) return; + const visibleDatabases = appendVisibleDatabaseSelection(config.visible_databases, databaseName); + if (visibleDatabases === config.visible_databases) return; + await updateVisibleDatabasesConfig(connectionId, visibleDatabases); + } + async function updateVisibleDatabasesConfig(connectionId: string, visibleDatabases: string[] | undefined) { const idx = connections.value.findIndex((connection) => connection.id === connectionId); if (idx < 0) return; @@ -4776,6 +4785,7 @@ export const useConnectionStore = defineStore("connection", () => { isDefaultDatabase, setVisibleDatabases, clearVisibleDatabases, + ensureVisibleDatabase, setVisibleSchemas, clearVisibleSchemas, removeConnection, diff --git a/packages/app-tests/connectionVisibleDatabases.test.ts b/packages/app-tests/connectionVisibleDatabases.test.ts index 537ed6b3b0..274b02fe20 100644 --- a/packages/app-tests/connectionVisibleDatabases.test.ts +++ b/packages/app-tests/connectionVisibleDatabases.test.ts @@ -1,6 +1,6 @@ import assert from "node:assert/strict"; import { test } from "vitest"; -import { buildDraftVisibleDatabasesConnectionId, connectionCanChooseVisibleDatabases, visibleDatabaseSelectionIsStale, initialVisibleDatabaseSelection } from "../../apps/desktop/src/lib/connection/connectionVisibleDatabases.ts"; +import { appendVisibleDatabaseSelection, buildDraftVisibleDatabasesConnectionId, connectionCanChooseVisibleDatabases, visibleDatabaseSelectionIsStale, initialVisibleDatabaseSelection } from "../../apps/desktop/src/lib/connection/connectionVisibleDatabases.ts"; import { connectionUsesVisibleSchemaFilter, filterDatabaseNamesForConnection, filterDatabaseNamesForVisiblePicker } from "../../apps/desktop/src/lib/database/visibleDatabases.ts"; import type { ConnectionConfig } from "../../apps/desktop/src/types/database.ts"; @@ -67,6 +67,17 @@ test("connection database filtering still applies saved visible database filters assert.deepEqual(filterDatabaseNamesForConnection(["app", "analytics", "mysql", "sys"], config({ visible_databases: ["app"] })), ["app"]); }); +test("append visible database selection only when filter is enabled", () => { + assert.deepEqual(appendVisibleDatabaseSelection(["app"], "analytics"), ["app", "analytics"]); + assert.deepEqual(appendVisibleDatabaseSelection(["app", "analytics"], "analytics"), ["app", "analytics"]); + assert.equal(appendVisibleDatabaseSelection(undefined, "analytics"), undefined); +}); + +test("append visible database selection trims new database names and ignores empty names", () => { + assert.deepEqual(appendVisibleDatabaseSelection(["app"], " analytics "), ["app", "analytics"]); + assert.deepEqual(appendVisibleDatabaseSelection(["app"], " "), ["app"]); +}); + test("ZooKeeper connections do not offer visible database selection", () => { assert.equal(connectionCanChooseVisibleDatabases(config({ db_type: "zookeeper" })), false); });