From 3eefe87380b2b27b9aa07b5dd732158667bca974 Mon Sep 17 00:00:00 2001 From: t8y2 <1156263951@qq.com> Date: Sun, 7 Jun 2026 01:07:31 +0800 Subject: [PATCH] feat(settings): allow disabling update reminders --- apps/desktop/src/App.vue | 51 +++++++++++++++---- .../components/config/DriverStoreDialog.vue | 35 ++++++++++--- .../connection/ConnectionDialog.vue | 3 ++ .../editor/EditorSettingsDialog.vue | 15 ++++++ apps/desktop/src/i18n/locales/en.ts | 3 ++ apps/desktop/src/i18n/locales/es.ts | 3 ++ apps/desktop/src/i18n/locales/it.ts | 3 ++ apps/desktop/src/i18n/locales/pt-BR.ts | 3 ++ apps/desktop/src/i18n/locales/zh-CN.ts | 3 ++ apps/desktop/src/i18n/locales/zh-TW.ts | 3 ++ apps/desktop/src/stores/settingsStore.ts | 6 +++ packages/app-tests/settingsStore.test.ts | 6 +++ 12 files changed, 116 insertions(+), 18 deletions(-) diff --git a/apps/desktop/src/App.vue b/apps/desktop/src/App.vue index 4a9b89130..c77a2b64d 100644 --- a/apps/desktop/src/App.vue +++ b/apps/desktop/src/App.vue @@ -140,13 +140,18 @@ const activeConnection = computed(() => { }); function updateAgentDriverUpdateCount(count: number) { + if (!settingsStore.editorSettings.updateNotificationsEnabled) { + agentDriverUpdateCount.value = 0; + return; + } agentDriverUpdateCount.value = count; } async function refreshAgentDriverUpdateCount() { - if (!isDesktop) return; + if (!isDesktop || !settingsStore.editorSettings.updateNotificationsEnabled) return; try { const drivers = await invoke("list_installed_agents"); + if (!settingsStore.editorSettings.updateNotificationsEnabled) return; updateAgentDriverUpdateCount(countAvailableAgentDriverUpdates(drivers)); } catch { // Driver update availability is only a badge hint; keep the existing count if the registry cannot be reached. @@ -222,6 +227,11 @@ useVisibilityChange(); const appVersion = ref(""); const isClassicLayout = computed(() => settingsStore.editorSettings.appLayout === "classic"); +const updateNotificationsEnabled = computed(() => settingsStore.editorSettings.updateNotificationsEnabled); +const toolbarAgentDriverUpdateCount = computed(() => + updateNotificationsEnabled.value ? agentDriverUpdateCount.value : 0, +); +const toolbarHasUpdateAvailable = computed(() => updateNotificationsEnabled.value && hasUpdateAvailable.value); const hasSqlFileConnections = computed(() => connectionStore.connections.some((c) => supportsSqlFileExecution(c.db_type)), ); @@ -878,6 +888,27 @@ function openDriverStoreFromEvent() { showDriverStore.value = true; } +function runUpdateNotificationChecks() { + if (!updateNotificationsEnabled.value) return; + checkUpdates({ silent: true }); + void refreshAgentDriverUpdateCount(); +} + +watch(updateNotificationsEnabled, (enabled) => { + if (!enabled) { + agentDriverUpdateCount.value = 0; + if (updateCheckTimer) { + clearInterval(updateCheckTimer); + updateCheckTimer = undefined; + } + return; + } + runUpdateNotificationChecks(); + if (!updateCheckTimer) { + updateCheckTimer = setInterval(runUpdateNotificationChecks, UPDATE_CHECK_INTERVAL_MS); + } +}); + onMounted(async () => { console.log("[STARTUP] onMounted begin"); const mountStart = performance.now(); @@ -915,14 +946,11 @@ onMounted(async () => { } initApp(); setupFileDrop().catch(() => {}); - void refreshAgentDriverUpdateCount(); setTimeout(() => { - checkUpdates({ silent: true }); - void refreshAgentDriverUpdateCount(); - updateCheckTimer = setInterval(() => { - checkUpdates({ silent: true }); - void refreshAgentDriverUpdateCount(); - }, UPDATE_CHECK_INTERVAL_MS); + runUpdateNotificationChecks(); + if (updateNotificationsEnabled.value && !updateCheckTimer) { + updateCheckTimer = setInterval(runUpdateNotificationChecks, UPDATE_CHECK_INTERVAL_MS); + } }, 10_000); api .getAppVersion() @@ -966,8 +994,8 @@ onUnmounted(() => { :show-history="showHistory" :show-driver-store="showDriverStore" :checking-updates="checkingUpdates" - :has-update-available="hasUpdateAvailable" - :agent-driver-update-count="agentDriverUpdateCount" + :has-update-available="toolbarHasUpdateAvailable" + :agent-driver-update-count="toolbarAgentDriverUpdateCount" :has-connections="connectionStore.connections.length > 0" :has-sql-file-connections="hasSqlFileConnections" @new-connection="showConnectionDialog = true" @@ -1029,13 +1057,14 @@ onUnmounted(() => {
diff --git a/apps/desktop/src/components/config/DriverStoreDialog.vue b/apps/desktop/src/components/config/DriverStoreDialog.vue index 9b218df3a..32f353aa9 100644 --- a/apps/desktop/src/components/config/DriverStoreDialog.vue +++ b/apps/desktop/src/components/config/DriverStoreDialog.vue @@ -56,6 +56,15 @@ const { t } = useI18n(); const { toast } = useToast(); const isWeb = !isTauriRuntime(); +const props = withDefaults( + defineProps<{ + updateNotificationsEnabled?: boolean; + }>(), + { + updateNotificationsEnabled: true, + }, +); + const emit = defineEmits<{ "update-count-change": [count: number]; }>(); @@ -117,7 +126,9 @@ const progressText = computed(() => { const progressNumber = computed(() => driverInstallProgressPercent(progress.value)); -const updatableCount = computed(() => drivers.value.filter((d) => d.update_available).length); +const updatableCount = computed(() => + props.updateNotificationsEnabled ? drivers.value.filter((d) => d.update_available).length : 0, +); const usageSummary = computed(() => { const usage = driverStoreUsage.value; if (!usage) return []; @@ -142,10 +153,18 @@ function updateAgentDrivers(nextDrivers: AgentDriverInfo[]) { emitDriverUpdateCount(); } -const agentTabUpdateCount = computed(() => drivers.value.filter((d) => d.update_available).length); -const jdbcTabUpdateCount = computed(() => (jdbcPluginStatus.value?.update_available ? 1 : 0)); +const agentTabUpdateCount = computed(() => + props.updateNotificationsEnabled ? drivers.value.filter((d) => d.update_available).length : 0, +); +const jdbcTabUpdateCount = computed(() => + props.updateNotificationsEnabled && jdbcPluginStatus.value?.update_available ? 1 : 0, +); function emitDriverUpdateCount() { + if (!props.updateNotificationsEnabled) { + emit("update-count-change", 0); + return; + } emit("update-count-change", countAvailableDriverUpdates(drivers.value, jdbcPluginStatus.value)); } @@ -722,9 +741,11 @@ onMounted(async () => { void loadJavaRuntimeConfig(); void loadDriverStoreUsage(); - api.listInstalledAgents().then((result) => { - updateAgentDrivers(result); - }); + if (props.updateNotificationsEnabled) { + api.listInstalledAgents().then((result) => { + updateAgentDrivers(result); + }); + } unlisten = await api.listenAgentInstallProgress((payload) => { if (payload.step === "done" || payload.step === "all-done") { @@ -739,7 +760,7 @@ onMounted(async () => { } }); void loadJdbcDrivers(); - void loadJdbcPluginStatus(); + if (props.updateNotificationsEnabled) void loadJdbcPluginStatus(); }); onUnmounted(() => { diff --git a/apps/desktop/src/components/connection/ConnectionDialog.vue b/apps/desktop/src/components/connection/ConnectionDialog.vue index d794c33c1..e429737d5 100644 --- a/apps/desktop/src/components/connection/ConnectionDialog.vue +++ b/apps/desktop/src/components/connection/ConnectionDialog.vue @@ -18,6 +18,7 @@ import type { TransportLayerConfig, } from "@/types/database"; import { useConnectionStore } from "@/stores/connectionStore"; +import { useSettingsStore } from "@/stores/settingsStore"; import { useToast } from "@/composables/useToast"; import DatabaseIcon from "@/components/icons/DatabaseIcon.vue"; import * as api from "@/lib/api"; @@ -77,6 +78,7 @@ type ConnectionForm = Omit; const { t } = useI18n(); const { toast } = useToast(); +const settingsStore = useSettingsStore(); const open = defineModel("open", { default: false }); const isDesktop = isTauriRuntime(); @@ -1688,6 +1690,7 @@ async function loadJdbcDrivers() { async function loadAgentDrivers() { try { agentDrivers.value = await api.listInstalledAgentsLocal(); + if (!settingsStore.editorSettings.updateNotificationsEnabled) return; api .listInstalledAgents() .then((drivers) => { diff --git a/apps/desktop/src/components/editor/EditorSettingsDialog.vue b/apps/desktop/src/components/editor/EditorSettingsDialog.vue index e727dc9c0..5d98645e7 100644 --- a/apps/desktop/src/components/editor/EditorSettingsDialog.vue +++ b/apps/desktop/src/components/editor/EditorSettingsDialog.vue @@ -127,6 +127,7 @@ const editDisconnectTabHandlingMode = ref( settingsStore.editorSettings.disconnectTabHandlingMode, ); const editReuseDataTab = ref(settingsStore.editorSettings.reuseDataTab); +const editUpdateNotificationsEnabled = ref(settingsStore.editorSettings.updateNotificationsEnabled); const editSidebarHiddenTablePrefixes = ref(settingsStore.editorSettings.sidebarHiddenTablePrefixes.join("\n")); const editSidebarHideTableComments = ref(settingsStore.editorSettings.sidebarHideTableComments); const editSidebarAllowHorizontalScroll = ref(settingsStore.editorSettings.sidebarAllowHorizontalScroll); @@ -279,6 +280,7 @@ watch( editAutoSelectActiveSidebarNode.value = settingsStore.editorSettings.autoSelectActiveSidebarNode; editDisconnectTabHandlingMode.value = settingsStore.editorSettings.disconnectTabHandlingMode; editReuseDataTab.value = settingsStore.editorSettings.reuseDataTab; + editUpdateNotificationsEnabled.value = settingsStore.editorSettings.updateNotificationsEnabled; editSidebarHiddenTablePrefixes.value = settingsStore.editorSettings.sidebarHiddenTablePrefixes.join("\n"); editSidebarHideTableComments.value = settingsStore.editorSettings.sidebarHideTableComments; editSidebarAllowHorizontalScroll.value = settingsStore.editorSettings.sidebarAllowHorizontalScroll; @@ -325,6 +327,7 @@ function hasChanges(): boolean { editAutoSelectActiveSidebarNode.value !== settingsStore.editorSettings.autoSelectActiveSidebarNode || editDisconnectTabHandlingMode.value !== settingsStore.editorSettings.disconnectTabHandlingMode || editReuseDataTab.value !== settingsStore.editorSettings.reuseDataTab || + editUpdateNotificationsEnabled.value !== settingsStore.editorSettings.updateNotificationsEnabled || editSidebarHideTableComments.value !== settingsStore.editorSettings.sidebarHideTableComments || editSidebarAllowHorizontalScroll.value !== settingsStore.editorSettings.sidebarAllowHorizontalScroll || editExportBatchSize.value !== settingsStore.editorSettings.exportBatchSize || @@ -358,6 +361,7 @@ async function persistSettings() { autoSelectActiveSidebarNode: editAutoSelectActiveSidebarNode.value, disconnectTabHandlingMode: editDisconnectTabHandlingMode.value, reuseDataTab: editReuseDataTab.value, + updateNotificationsEnabled: editUpdateNotificationsEnabled.value, sidebarHideTableComments: editSidebarHideTableComments.value, sidebarAllowHorizontalScroll: editSidebarAllowHorizontalScroll.value, sidebarHiddenTablePrefixes: normalizeSidebarHiddenTablePrefixes(editSidebarHiddenTablePrefixes.value), @@ -404,6 +408,7 @@ function resetDefaults() { editAutoSelectActiveSidebarNode.value = DEFAULT_EDITOR_SETTINGS.autoSelectActiveSidebarNode; editDisconnectTabHandlingMode.value = DEFAULT_EDITOR_SETTINGS.disconnectTabHandlingMode; editReuseDataTab.value = DEFAULT_EDITOR_SETTINGS.reuseDataTab; + editUpdateNotificationsEnabled.value = DEFAULT_EDITOR_SETTINGS.updateNotificationsEnabled; editSidebarHideTableComments.value = DEFAULT_EDITOR_SETTINGS.sidebarHideTableComments; editSidebarAllowHorizontalScroll.value = DEFAULT_EDITOR_SETTINGS.sidebarAllowHorizontalScroll; editSidebarHiddenTablePrefixes.value = DEFAULT_EDITOR_SETTINGS.sidebarHiddenTablePrefixes.join("\n"); @@ -1491,6 +1496,16 @@ watch(
+
+
+ +

+ {{ t("settings.updateNotificationsEnabledDescription") }} +

+
+ +
+
diff --git a/apps/desktop/src/i18n/locales/en.ts b/apps/desktop/src/i18n/locales/en.ts index 6c24206c9..3c835c996 100644 --- a/apps/desktop/src/i18n/locales/en.ts +++ b/apps/desktop/src/i18n/locales/en.ts @@ -1629,6 +1629,9 @@ export default { showTrayIcon: "Show tray/menu bar icon", showTrayIconDescription: "When disabled, no icon is shown, but closing the window still hides DBX in the background as before.", + updateNotificationsEnabled: "Enable update reminders", + updateNotificationsEnabledDescription: + "When disabled, DBX will not automatically check app or driver updates or show update badges. Manual checks are still available.", dataGridDisplay: "Data grid display", showColumnCommentsInHeader: "Show column comments under names", showColumnCommentsInHeaderDescription: "Display table column comments directly below grid column names.", diff --git a/apps/desktop/src/i18n/locales/es.ts b/apps/desktop/src/i18n/locales/es.ts index e57b4bbe2..3a731347d 100644 --- a/apps/desktop/src/i18n/locales/es.ts +++ b/apps/desktop/src/i18n/locales/es.ts @@ -1518,6 +1518,9 @@ export default { showTrayIcon: "Mostrar icono en bandeja/barra de menú", showTrayIconDescription: "Si está desactivado, no se muestra el icono, pero cerrar la ventana sigue ocultando DBX en segundo plano como antes.", + updateNotificationsEnabled: "Activar recordatorios de actualización", + updateNotificationsEnabledDescription: + "Al desactivarlo, DBX no comprobará automáticamente actualizaciones de la app ni de los controladores, ni mostrará indicadores. Las comprobaciones manuales siguen disponibles.", dataGridDisplay: "Visualización de la tabla", showColumnCommentsInHeader: "Mostrar comentarios bajo los nombres", showColumnCommentsInHeaderDescription: diff --git a/apps/desktop/src/i18n/locales/it.ts b/apps/desktop/src/i18n/locales/it.ts index d02faca00..914f28991 100644 --- a/apps/desktop/src/i18n/locales/it.ts +++ b/apps/desktop/src/i18n/locales/it.ts @@ -1660,6 +1660,9 @@ export default { showTrayIcon: "Mostra icona nella barra delle applicazioni/menu", showTrayIconDescription: "Se disattivato, non viene mostrata alcuna icona, ma la chiusura della finestra nasconderà comunque DBX in background.", + updateNotificationsEnabled: "Abilita promemoria aggiornamenti", + updateNotificationsEnabledDescription: + "Se disattivato, DBX non controllerà automaticamente gli aggiornamenti dell'app o dei driver e non mostrerà badge. I controlli manuali restano disponibili.", dataGridDisplay: "Visualizzazione griglia dati", showColumnCommentsInHeader: "Mostra i commenti delle colonne sotto i nomi", showColumnCommentsInHeaderDescription: diff --git a/apps/desktop/src/i18n/locales/pt-BR.ts b/apps/desktop/src/i18n/locales/pt-BR.ts index fffd3a805..6bbdf0e39 100644 --- a/apps/desktop/src/i18n/locales/pt-BR.ts +++ b/apps/desktop/src/i18n/locales/pt-BR.ts @@ -1651,6 +1651,9 @@ export default { showTrayIcon: "Mostrar ícone na bandeja/barra de menus", showTrayIconDescription: "Quando desativado, nenhum ícone é exibido, mas fechar a janela ainda oculta o DBX em segundo plano como antes.", + updateNotificationsEnabled: "Ativar lembretes de atualização", + updateNotificationsEnabledDescription: + "Quando desativado, o DBX não verificará automaticamente atualizações do app ou dos drivers nem mostrará indicadores. Verificações manuais continuam disponíveis.", dataGridDisplay: "Exibição da grade de dados", showColumnCommentsInHeader: "Mostrar comentários de coluna sob os nomes", showColumnCommentsInHeaderDescription: diff --git a/apps/desktop/src/i18n/locales/zh-CN.ts b/apps/desktop/src/i18n/locales/zh-CN.ts index 89578f1e5..6b2b4fa78 100644 --- a/apps/desktop/src/i18n/locales/zh-CN.ts +++ b/apps/desktop/src/i18n/locales/zh-CN.ts @@ -1599,6 +1599,9 @@ export default { iconThemeBlackDescription: "窗口、托盘和 Logo 都使用黑色 DBX 标识。", showTrayIcon: "显示系统托盘/菜单栏图标", showTrayIconDescription: "关闭后不显示图标,但关闭窗口仍会像之前一样隐藏到后台。", + updateNotificationsEnabled: "启用更新提醒", + updateNotificationsEnabledDescription: + "关闭后,DBX 不会自动检查应用和驱动更新,也不会显示更新红点;仍可手动检查更新。", dataGridDisplay: "数据表格显示", showColumnCommentsInHeader: "在字段名下方显示注释", showColumnCommentsInHeaderDescription: "把表字段注释直接显示在结果表头字段名下方。", diff --git a/apps/desktop/src/i18n/locales/zh-TW.ts b/apps/desktop/src/i18n/locales/zh-TW.ts index d00d082fb..ffa391e4d 100644 --- a/apps/desktop/src/i18n/locales/zh-TW.ts +++ b/apps/desktop/src/i18n/locales/zh-TW.ts @@ -1575,6 +1575,9 @@ export default { iconThemeBlackDescription: "視窗、系統匣和 Logo 都使用黑色 DBX 標誌。", showTrayIcon: "顯示系統匣/選單欄圖示", showTrayIconDescription: "關閉後不顯示圖示,但關閉視窗仍會像之前一樣隱藏到後台。", + updateNotificationsEnabled: "啟用更新提醒", + updateNotificationsEnabledDescription: + "關閉後,DBX 不會自動檢查應用程式和驅動程式更新,也不會顯示更新紅點;仍可手動檢查更新。", dataGridDisplay: "資料表格顯示", showColumnCommentsInHeader: "在欄位名稱下方顯示註解", showColumnCommentsInHeaderDescription: "直接在資料表格欄位名稱下方顯示資料表欄位註解。", diff --git a/apps/desktop/src/stores/settingsStore.ts b/apps/desktop/src/stores/settingsStore.ts index 341744cc4..85e42ac2e 100644 --- a/apps/desktop/src/stores/settingsStore.ts +++ b/apps/desktop/src/stores/settingsStore.ts @@ -258,6 +258,7 @@ export interface EditorSettings { autoSelectActiveSidebarNode: boolean; disconnectTabHandlingMode: DisconnectTabHandlingMode; reuseDataTab: boolean; + updateNotificationsEnabled: boolean; sidebarHiddenTablePrefixes: string[]; sidebarHideTableComments: boolean; sidebarAllowHorizontalScroll: boolean; @@ -322,6 +323,7 @@ export const DEFAULT_EDITOR_SETTINGS: EditorSettings = { autoSelectActiveSidebarNode: false, disconnectTabHandlingMode: "close-tabs", reuseDataTab: false, + updateNotificationsEnabled: true, sidebarHiddenTablePrefixes: [], sidebarHideTableComments: false, sidebarAllowHorizontalScroll: false, @@ -495,6 +497,8 @@ export function normalizeEditorSettings(settings: Partial, exist (settings as Partial & { closeQueryTabsOnDisconnect?: boolean }).closeQueryTabsOnDisconnect, ), reuseDataTab: settings.reuseDataTab ?? DEFAULT_EDITOR_SETTINGS.reuseDataTab, + updateNotificationsEnabled: + settings.updateNotificationsEnabled ?? DEFAULT_EDITOR_SETTINGS.updateNotificationsEnabled, sidebarHiddenTablePrefixes: normalizeSidebarHiddenTablePrefixes(settings.sidebarHiddenTablePrefixes), sidebarHideTableComments: settings.sidebarHideTableComments ?? DEFAULT_EDITOR_SETTINGS.sidebarHideTableComments, sidebarAllowHorizontalScroll: @@ -667,6 +671,8 @@ export const useSettingsStore = defineStore("settings", () => { partial.disconnectTabHandlingMode, ); if (partial.reuseDataTab !== undefined) editorSettings.value.reuseDataTab = partial.reuseDataTab; + if (partial.updateNotificationsEnabled !== undefined) + editorSettings.value.updateNotificationsEnabled = partial.updateNotificationsEnabled; if (partial.sidebarHiddenTablePrefixes !== undefined) editorSettings.value.sidebarHiddenTablePrefixes = normalizeSidebarHiddenTablePrefixes( partial.sidebarHiddenTablePrefixes, diff --git a/packages/app-tests/settingsStore.test.ts b/packages/app-tests/settingsStore.test.ts index bf0f53754..ce0175a36 100644 --- a/packages/app-tests/settingsStore.test.ts +++ b/packages/app-tests/settingsStore.test.ts @@ -43,6 +43,12 @@ test("defaults dangerous SQL confirmation to enabled", () => { assert.equal(normalizeEditorSettings({ confirmDangerousSqlExecution: false }).confirmDangerousSqlExecution, false); }); +test("defaults update notifications to enabled", () => { + assert.equal(DEFAULT_EDITOR_SETTINGS.updateNotificationsEnabled, true); + assert.equal(normalizeEditorSettings({}).updateNotificationsEnabled, true); + assert.equal(normalizeEditorSettings({ updateNotificationsEnabled: false } as any).updateNotificationsEnabled, false); +}); + test("defaults shortcut settings", () => { const settings = normalizeEditorSettings({});