fix(desktop): restore table tabs on startup
This commit is contained in:
parent
2f1a6875b7
commit
ad33db0886
|
|
@ -847,17 +847,17 @@ function initApp() {
|
|||
}
|
||||
|
||||
async function reconnectRestoredTabs() {
|
||||
if (isDesktop) return;
|
||||
const connectionIds = new Set(queryStore.tabs.map((t) => t.connectionId).filter(Boolean));
|
||||
for (const id of connectionIds) {
|
||||
const activeConnectionId = activeTab.value?.connectionId || connectionStore.activeConnectionId;
|
||||
if (activeConnectionId && connectionStore.getConfig(activeConnectionId)) {
|
||||
connectionStore.activeConnectionId = activeConnectionId;
|
||||
try {
|
||||
await connectionStore.ensureConnected(id);
|
||||
await connectionStore.ensureConnected(activeConnectionId);
|
||||
} catch {}
|
||||
}
|
||||
for (const tab of queryStore.tabs) {
|
||||
if (tab.mode === "data" && tab.tableMeta && tab.sql) {
|
||||
queryStore.executeTabSql(tab.id, tab.sql).catch(() => {});
|
||||
}
|
||||
|
||||
const tab = activeTab.value;
|
||||
if (tab?.mode === "data" && tab.tableMeta && tab.sql) {
|
||||
queryStore.executeTabSql(tab.id, tab.sql).catch(() => {});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,8 +9,18 @@ export interface SavedOpenTab {
|
|||
schema?: string;
|
||||
sql: string;
|
||||
savedSqlId?: string;
|
||||
lastExecutedSql?: string;
|
||||
resultBaseSql?: string;
|
||||
resultSortedSql?: string;
|
||||
resultSortColumn?: string;
|
||||
resultSortColumnIndex?: number;
|
||||
resultSortDirection?: QueryTab["resultSortDirection"];
|
||||
resultPageLimit?: number;
|
||||
resultPageOffset?: number;
|
||||
whereInput?: string;
|
||||
pinned?: boolean;
|
||||
mode?: QueryTab["mode"];
|
||||
structureTableName?: string;
|
||||
objectBrowser?: QueryTab["objectBrowser"];
|
||||
objectSource?: QueryTab["objectSource"];
|
||||
tableMeta?: QueryTab["tableMeta"];
|
||||
|
|
@ -31,8 +41,18 @@ export function serializeOpenTabs(tabs: QueryTab[]): SavedOpenTab[] {
|
|||
schema: tab.schema,
|
||||
sql: tab.sql,
|
||||
savedSqlId: tab.savedSqlId,
|
||||
...(tab.lastExecutedSql !== undefined ? { lastExecutedSql: tab.lastExecutedSql } : {}),
|
||||
...(tab.resultBaseSql !== undefined ? { resultBaseSql: tab.resultBaseSql } : {}),
|
||||
...(tab.resultSortedSql !== undefined ? { resultSortedSql: tab.resultSortedSql } : {}),
|
||||
...(tab.resultSortColumn !== undefined ? { resultSortColumn: tab.resultSortColumn } : {}),
|
||||
...(tab.resultSortColumnIndex !== undefined ? { resultSortColumnIndex: tab.resultSortColumnIndex } : {}),
|
||||
...(tab.resultSortDirection !== undefined ? { resultSortDirection: tab.resultSortDirection } : {}),
|
||||
...(tab.resultPageLimit !== undefined ? { resultPageLimit: tab.resultPageLimit } : {}),
|
||||
...(tab.resultPageOffset !== undefined ? { resultPageOffset: tab.resultPageOffset } : {}),
|
||||
...(tab.whereInput !== undefined ? { whereInput: tab.whereInput } : {}),
|
||||
pinned: tab.pinned,
|
||||
mode: tab.mode,
|
||||
...(tab.structureTableName !== undefined ? { structureTableName: tab.structureTableName } : {}),
|
||||
objectBrowser: tab.objectBrowser,
|
||||
objectSource: tab.objectSource,
|
||||
tableMeta: tab.tableMeta,
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ import { useSavedSqlStore } from "@/stores/savedSqlStore";
|
|||
import { useSettingsStore } from "@/stores/settingsStore";
|
||||
|
||||
const PINNED_TREE_NODES_STORAGE_KEY = "dbx-pinned-tree-nodes";
|
||||
const ACTIVE_CONNECTION_STORAGE_KEY = "dbx-active-connection";
|
||||
type ImportSource = "dbx" | "navicat" | "dbeaver";
|
||||
|
||||
interface LoadTreeOptions {
|
||||
|
|
@ -69,13 +70,12 @@ function redisDbLabel(db: number, loadedKeyCount?: number, totalKeyCount?: numbe
|
|||
export const useConnectionStore = defineStore("connection", () => {
|
||||
const connections = ref<ConnectionConfig[]>([]);
|
||||
const isDesktop = isTauriRuntime();
|
||||
const activeConnectionId = ref<string | null>(!isDesktop ? localStorage.getItem("dbx-active-connection") : null);
|
||||
const activeConnectionId = ref<string | null>(localStorage.getItem(ACTIVE_CONNECTION_STORAGE_KEY));
|
||||
const selectedTreeNodeId = ref<string | null>(null);
|
||||
|
||||
watch(activeConnectionId, (id) => {
|
||||
if (isDesktop) return;
|
||||
if (id) localStorage.setItem("dbx-active-connection", id);
|
||||
else localStorage.removeItem("dbx-active-connection");
|
||||
if (id) localStorage.setItem(ACTIVE_CONNECTION_STORAGE_KEY, id);
|
||||
else localStorage.removeItem(ACTIVE_CONNECTION_STORAGE_KEY);
|
||||
});
|
||||
const treeNodes = ref<TreeNode[]>([]);
|
||||
const pinnedTreeNodeIds = ref<Set<string>>(new Set());
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@ import { TABLE_DATA_EXPORT_PAGE_SIZE } from "@/lib/tableDataExport";
|
|||
import * as api from "@/lib/api";
|
||||
import { useConnectionStore } from "@/stores/connectionStore";
|
||||
import { useSettingsStore } from "@/stores/settingsStore";
|
||||
import { isTauriRuntime } from "@/lib/tauriRuntime";
|
||||
import type { SavedSqlFile } from "@/types/database";
|
||||
|
||||
const STORAGE_KEY = "dbx-open-tabs";
|
||||
|
|
@ -41,9 +40,7 @@ function saveTabs(tabs: QueryTab[], activeTabId: string | null) {
|
|||
|
||||
function loadSavedTabs(): { tabs: QueryTab[]; activeTabId: string | null } {
|
||||
try {
|
||||
return restoreOpenTabsState(localStorage.getItem(STORAGE_KEY), localStorage.getItem(ACTIVE_TAB_KEY), {
|
||||
queryOnly: isTauriRuntime(),
|
||||
});
|
||||
return restoreOpenTabsState(localStorage.getItem(STORAGE_KEY), localStorage.getItem(ACTIVE_TAB_KEY));
|
||||
} catch {
|
||||
return { tabs: [], activeTabId: null };
|
||||
}
|
||||
|
|
@ -117,8 +114,18 @@ export const useQueryStore = defineStore("query", () => {
|
|||
schema: t.schema,
|
||||
sql: t.sql,
|
||||
savedSqlId: t.savedSqlId,
|
||||
lastExecutedSql: t.lastExecutedSql,
|
||||
resultBaseSql: t.resultBaseSql,
|
||||
resultSortedSql: t.resultSortedSql,
|
||||
resultSortColumn: t.resultSortColumn,
|
||||
resultSortColumnIndex: t.resultSortColumnIndex,
|
||||
resultSortDirection: t.resultSortDirection,
|
||||
resultPageLimit: t.resultPageLimit,
|
||||
resultPageOffset: t.resultPageOffset,
|
||||
whereInput: t.whereInput,
|
||||
pinned: t.pinned,
|
||||
mode: t.mode,
|
||||
structureTableName: t.structureTableName,
|
||||
objectBrowser: t.objectBrowser,
|
||||
objectSource: t.objectSource,
|
||||
tableMeta: t.tableMeta,
|
||||
|
|
|
|||
|
|
@ -62,6 +62,38 @@ test("serializes object source query tabs with save context", () => {
|
|||
assert.equal(saved[0]?.customTitle, true);
|
||||
});
|
||||
|
||||
test("serializes table tabs with reload context", () => {
|
||||
const saved = serializeOpenTabs([
|
||||
queryTab({
|
||||
mode: "data",
|
||||
lastExecutedSql: "select * from users limit 100 offset 100",
|
||||
resultPageLimit: 100,
|
||||
resultPageOffset: 100,
|
||||
whereInput: "active = true",
|
||||
tableMeta: { schema: "public", tableName: "users", columns: [], primaryKeys: [] },
|
||||
}),
|
||||
]);
|
||||
|
||||
assert.deepEqual(
|
||||
{
|
||||
mode: saved[0]?.mode,
|
||||
lastExecutedSql: saved[0]?.lastExecutedSql,
|
||||
resultPageLimit: saved[0]?.resultPageLimit,
|
||||
resultPageOffset: saved[0]?.resultPageOffset,
|
||||
whereInput: saved[0]?.whereInput,
|
||||
tableMeta: saved[0]?.tableMeta,
|
||||
},
|
||||
{
|
||||
mode: "data",
|
||||
lastExecutedSql: "select * from users limit 100 offset 100",
|
||||
resultPageLimit: 100,
|
||||
resultPageOffset: 100,
|
||||
whereInput: "active = true",
|
||||
tableMeta: { schema: "public", tableName: "users", columns: [], primaryKeys: [] },
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
test("restores unsaved query tabs and active tab after restart", () => {
|
||||
const raw = JSON.stringify([
|
||||
queryTab({ id: "tab-1", sql: "select 1" }),
|
||||
|
|
@ -102,7 +134,60 @@ test("restores object source save context", () => {
|
|||
assert.equal(restored.tabs[0]?.customTitle, true);
|
||||
});
|
||||
|
||||
test("desktop restore keeps legacy query tabs without a mode", () => {
|
||||
test("restores data and structure tabs with table state", () => {
|
||||
const raw = JSON.stringify([
|
||||
queryTab({
|
||||
id: "data",
|
||||
title: "public.users",
|
||||
mode: "data",
|
||||
sql: 'SELECT * FROM "public"."users" LIMIT 50 OFFSET 50;',
|
||||
lastExecutedSql: 'SELECT * FROM "public"."users" LIMIT 50 OFFSET 50;',
|
||||
resultPageLimit: 50,
|
||||
resultPageOffset: 50,
|
||||
whereInput: "id > 10",
|
||||
tableMeta: {
|
||||
schema: "public",
|
||||
tableName: "users",
|
||||
columns: [
|
||||
{
|
||||
name: "id",
|
||||
data_type: "integer",
|
||||
is_nullable: false,
|
||||
column_default: null,
|
||||
is_primary_key: true,
|
||||
extra: null,
|
||||
},
|
||||
],
|
||||
primaryKeys: ["id"],
|
||||
},
|
||||
}),
|
||||
queryTab({
|
||||
id: "structure",
|
||||
title: "Edit users",
|
||||
mode: "structure",
|
||||
sql: "",
|
||||
structureTableName: "users",
|
||||
}),
|
||||
]);
|
||||
|
||||
const restored = restoreOpenTabsState(raw, "data");
|
||||
|
||||
assert.deepEqual(
|
||||
restored.tabs.map((tab) => ({ id: tab.id, mode: tab.mode })),
|
||||
[
|
||||
{ id: "data", mode: "data" },
|
||||
{ id: "structure", mode: "structure" },
|
||||
],
|
||||
);
|
||||
assert.equal(restored.activeTabId, "data");
|
||||
assert.equal(restored.tabs[0]?.tableMeta?.tableName, "users");
|
||||
assert.equal(restored.tabs[0]?.resultPageLimit, 50);
|
||||
assert.equal(restored.tabs[0]?.resultPageOffset, 50);
|
||||
assert.equal(restored.tabs[0]?.whereInput, "id > 10");
|
||||
assert.equal(restored.tabs[1]?.structureTableName, "users");
|
||||
});
|
||||
|
||||
test("query-only restore keeps legacy query tabs without a mode", () => {
|
||||
const raw = JSON.stringify([
|
||||
{
|
||||
id: "legacy",
|
||||
|
|
|
|||
Loading…
Reference in New Issue