diff --git a/apps/desktop/src/components/layout/ContentArea.vue b/apps/desktop/src/components/layout/ContentArea.vue index 4bc9b3727..93e0ce351 100644 --- a/apps/desktop/src/components/layout/ContentArea.vue +++ b/apps/desktop/src/components/layout/ContentArea.vue @@ -2,6 +2,7 @@ import { computed, ref, defineAsyncComponent, watch, nextTick, onMounted, onUnmounted } from "vue"; import { safeLocalStorageGet, safeLocalStorageSet } from "@/lib/backend/safeStorage"; import { appendDebugLog, isDebugLoggingEnabled } from "@/lib/backend/debugLog"; +import { canReloadUnavailableDataTab } from "@/lib/table/tableDataRefresh"; import type { CSSProperties } from "vue"; import { useI18n } from "vue-i18n"; import { Check, Columns3, EyeOff, Loader2, Search, GitBranch, BarChart3, TableProperties, ChevronDown, ChevronUp, Inbox, RefreshCcw, Timer, Wrench, Toolbox, ListChecks, Database, Download, Upload, X, Pin, Rows3, SquareDashed, Minus, Plus } from "@lucide/vue"; @@ -743,6 +744,11 @@ function stopQueryResultAutoRefresh() { function refreshData(): boolean { if (props.activeTab.mode === "etcd") return etcdKeyBrowserRef.value?.refresh?.() ?? false; if (props.activeTab.mode === "zookeeper") return zookeeperKeyBrowserRef.value?.refresh?.() ?? false; + // Restored data tabs intentionally omit row data, so refresh must work before DataGrid mounts. + if (canReloadUnavailableDataTab(props.activeTab)) { + emit("reload"); + return true; + } if (!dataGridRef.value) return false; void dataGridRef.value.onToolbarRefresh(); return true; @@ -799,10 +805,7 @@ function handleModRTarget(target: Element): boolean { if (target.closest("[data-query-editor-root]")) return queryEditorRef.value?.openReplace() ?? false; if (target.closest("[data-cell-detail-editor-root]")) return dataGridRef.value?.openCellDetailSearch() ?? false; if (target.closest("[data-grid-root]")) return refreshData(); - if (props.activeTab.mode === "data" && !props.activeTab.result && !props.activeTab.isExecuting) { - emit("reload"); - return true; - } + if (canReloadUnavailableDataTab(props.activeTab)) return refreshData(); return false; } diff --git a/apps/desktop/src/lib/table/__tests__/tableDataRefresh.spec.ts b/apps/desktop/src/lib/table/__tests__/tableDataRefresh.spec.ts new file mode 100644 index 000000000..76c3c9681 --- /dev/null +++ b/apps/desktop/src/lib/table/__tests__/tableDataRefresh.spec.ts @@ -0,0 +1,23 @@ +import { describe, expect, it } from "vitest"; +import { canReloadUnavailableDataTab } from "@/lib/table/tableDataRefresh"; + +describe("canReloadUnavailableDataTab", () => { + it("allows restored data tabs to reload before the grid mounts", () => { + expect(canReloadUnavailableDataTab({ mode: "data", result: undefined, isExecuting: false })).toBe(true); + }); + + it("does not start duplicate or unrelated reloads", () => { + expect(canReloadUnavailableDataTab({ mode: "data", result: undefined, isExecuting: true })).toBe(false); + expect(canReloadUnavailableDataTab({ mode: "query", result: undefined, isExecuting: false })).toBe(false); + }); + + it("keeps populated data tabs on the DataGrid refresh path", () => { + expect( + canReloadUnavailableDataTab({ + mode: "data", + isExecuting: false, + result: { columns: ["id"], rows: [[1]], row_count: 1, execution_time_ms: 1 }, + }), + ).toBe(false); + }); +}); diff --git a/apps/desktop/src/lib/table/tableDataRefresh.ts b/apps/desktop/src/lib/table/tableDataRefresh.ts new file mode 100644 index 000000000..67ee7d773 --- /dev/null +++ b/apps/desktop/src/lib/table/tableDataRefresh.ts @@ -0,0 +1,7 @@ +import type { QueryTab } from "@/types/database"; + +type RefreshableDataTab = Pick; + +export function canReloadUnavailableDataTab(tab: RefreshableDataTab): boolean { + return tab.mode === "data" && !tab.result && !tab.isExecuting; +}