From c6d238348c766dfb1a65b9dd630fb955b2018eed Mon Sep 17 00:00:00 2001
From: t8y2 <1156263951@qq.com>
Date: Sun, 19 Jul 2026 11:48:25 +0800
Subject: [PATCH] fix(schema): avoid redundant filter refreshes
---
.../sidebar/VisibleSchemasDialog.vue | 103 +++++++++---------
.../visibleSchemasDialogState.spec.ts | 49 +++++++++
.../sidebar/visibleSchemasDialogState.ts | 44 ++++++++
3 files changed, 147 insertions(+), 49 deletions(-)
create mode 100644 apps/desktop/src/components/sidebar/__tests__/visibleSchemasDialogState.spec.ts
create mode 100644 apps/desktop/src/components/sidebar/visibleSchemasDialogState.ts
diff --git a/apps/desktop/src/components/sidebar/VisibleSchemasDialog.vue b/apps/desktop/src/components/sidebar/VisibleSchemasDialog.vue
index b6efe71b56..840ebe4bb0 100644
--- a/apps/desktop/src/components/sidebar/VisibleSchemasDialog.vue
+++ b/apps/desktop/src/components/sidebar/VisibleSchemasDialog.vue
@@ -1,13 +1,15 @@
@@ -182,16 +193,16 @@ async function saveSelection() {
{{ t("visibleSchemas.selectedCount", { selected: selectedCount, total: totalCount }) }}
-
@@ -200,7 +211,7 @@ async function saveSelection() {
{{ t("visibleSchemas.emptySelection") }}
-
+
{{ t("common.loading") }}
@@ -211,24 +222,18 @@ async function saveSelection() {
{{ t("grid.noSearchResults") }}
-
-
-
-
- {{ schema }}
-
-
+
+
+
+
+ {{ item.name }}
+
+
- {{ t("dangerDialog.cancel") }}
-
+ {{ t("dangerDialog.cancel") }}
+
{{ t("visibleSchemas.save") }}
diff --git a/apps/desktop/src/components/sidebar/__tests__/visibleSchemasDialogState.spec.ts b/apps/desktop/src/components/sidebar/__tests__/visibleSchemasDialogState.spec.ts
new file mode 100644
index 0000000000..7f4fd9e62c
--- /dev/null
+++ b/apps/desktop/src/components/sidebar/__tests__/visibleSchemasDialogState.spec.ts
@@ -0,0 +1,49 @@
+import { describe, expect, it } from "vitest";
+import { addVisibleSchemas, initialVisibleSchemaSelection, resolveVisibleSchemaSaveAction, selectVisibleSchemas } from "../visibleSchemasDialogState";
+
+describe("visibleSchemasDialogState", () => {
+ const schemaNames = ["public", "sales", "audit"];
+
+ it("supports select all, clear, and filtered selection without mutating the previous set", () => {
+ const selected = selectVisibleSchemas(schemaNames);
+ const cleared = new Set
();
+ const filtered = addVisibleSchemas(cleared, ["sales", "audit"]);
+
+ expect([...selected]).toEqual(schemaNames);
+ expect([...cleared]).toEqual([]);
+ expect([...filtered]).toEqual(["sales", "audit"]);
+ expect([...addVisibleSchemas(filtered, ["public", "sales"])]).toEqual(["sales", "audit", "public"]);
+ expect([...filtered]).toEqual(["sales", "audit"]);
+ });
+
+ it("keeps an explicitly empty normal-mode filter empty", () => {
+ expect([...initialVisibleSchemaSelection(schemaNames, undefined)]).toEqual(schemaNames);
+ expect([...initialVisibleSchemaSelection(schemaNames, [])]).toEqual([]);
+ });
+
+ it("keeps draft-mode empty or absent selections as all selected", () => {
+ expect([...initialVisibleSchemaSelection(schemaNames, undefined, true)]).toEqual(schemaNames);
+ expect([...initialVisibleSchemaSelection(schemaNames, [], true)]).toEqual(schemaNames);
+ });
+
+ it("normalizes configured schemas against the available list", () => {
+ expect([...initialVisibleSchemaSelection(schemaNames, ["missing", "audit", "audit"])]).toEqual(["audit"]);
+ });
+
+ it("skips persistence when an unchanged filter is saved", () => {
+ expect(resolveVisibleSchemaSaveAction(new Set(["audit", "public"]), schemaNames, ["public", "audit"])).toEqual({ type: "none" });
+ });
+
+ it("stores changed partial selections in stable schema order", () => {
+ expect(resolveVisibleSchemaSaveAction(new Set(["audit", "public"]), schemaNames, ["sales"])).toEqual({
+ type: "set",
+ schemaNames: ["public", "audit"],
+ });
+ });
+
+ it("represents a full selection as show-all and avoids redundant tree refreshes", () => {
+ const selected = new Set(schemaNames);
+ expect(resolveVisibleSchemaSaveAction(selected, schemaNames, undefined)).toEqual({ type: "none" });
+ expect(resolveVisibleSchemaSaveAction(selected, schemaNames, ["public"])).toEqual({ type: "clear" });
+ });
+});
diff --git a/apps/desktop/src/components/sidebar/visibleSchemasDialogState.ts b/apps/desktop/src/components/sidebar/visibleSchemasDialogState.ts
new file mode 100644
index 0000000000..9d52a540cc
--- /dev/null
+++ b/apps/desktop/src/components/sidebar/visibleSchemasDialogState.ts
@@ -0,0 +1,44 @@
+import { normalizeVisibleSchemaSelection } from "@/lib/database/visibleDatabases";
+
+export type VisibleSchemaSaveAction = { type: "none" } | { type: "clear" } | { type: "set"; schemaNames: string[] };
+
+export function initialVisibleSchemaSelection(schemaNames: string[], configured: string[] | undefined, emptySelectionMeansAll = false): Set {
+ if (configured === undefined || (emptySelectionMeansAll && configured.length === 0)) return new Set(schemaNames);
+ return new Set(normalizeVisibleSchemaSelection(configured, schemaNames));
+}
+
+export function selectVisibleSchemas(schemaNames: string[]): Set {
+ return new Set(schemaNames);
+}
+
+export function addVisibleSchemas(selection: Set, schemaNames: string[]): Set {
+ const next = new Set(selection);
+ for (const name of schemaNames) next.add(name);
+ return next;
+}
+
+export function resolveVisibleSchemaSaveAction(selection: Set, schemaNames: string[], configured: string[] | undefined): VisibleSchemaSaveAction {
+ const normalized = normalizeSelectionInSchemaOrder(selection, schemaNames);
+ const availableCount = new Set(schemaNames).size;
+
+ if (normalized.length === availableCount) {
+ return configured ? { type: "clear" } : { type: "none" };
+ }
+ if (configured && sameSelection(normalized, normalizeVisibleSchemaSelection(configured, schemaNames))) {
+ return { type: "none" };
+ }
+ return { type: "set", schemaNames: normalized };
+}
+
+function normalizeSelectionInSchemaOrder(selection: Set, schemaNames: string[]): string[] {
+ return normalizeVisibleSchemaSelection(
+ schemaNames.filter((name) => selection.has(name)),
+ schemaNames,
+ );
+}
+
+function sameSelection(left: string[], right: string[]): boolean {
+ if (left.length !== right.length) return false;
+ const rightSet = new Set(right);
+ return left.every((name) => rightSet.has(name));
+}