From 2666ed1cd2af89720c6d9c38de18199dd90667cc Mon Sep 17 00:00:00 2001 From: t8y2 <1156263951@qq.com> Date: Tue, 14 Jul 2026 11:04:27 +0800 Subject: [PATCH] fix(sync): refresh tunnel profiles after download --- .../editor/EditorSettingsDialog.vue | 7 +++++ packages/app-tests/syncTunnelProfiles.test.ts | 30 +++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 packages/app-tests/syncTunnelProfiles.test.ts diff --git a/apps/desktop/src/components/editor/EditorSettingsDialog.vue b/apps/desktop/src/components/editor/EditorSettingsDialog.vue index f1562fc78a..9cbe8d0748 100644 --- a/apps/desktop/src/components/editor/EditorSettingsDialog.vue +++ b/apps/desktop/src/components/editor/EditorSettingsDialog.vue @@ -104,6 +104,7 @@ import { APP_THEME_PALETTES, type AppThemeAppearance, type AppThemeMode, type Ap import { editorSettingsDraftChanged, editorSettingsDraftFromSettings, editorSettingsPatchFromDraft, type EditorSettingsDraft } from "@/lib/settings/editorSettingsDraft"; import { useConnectionStore } from "@/stores/connectionStore"; import { useSavedSqlStore } from "@/stores/savedSqlStore"; +import { useTunnelProfileStore } from "@/stores/tunnelProfileStore"; import { currentLocale, setLocale, type Locale } from "@/i18n"; import { LOCALE_OPTIONS } from "@/lib/app/localeOptions"; import { DEFAULT_WEB_DAV_AUTO_UPLOAD_INTERVAL_MINUTES, DEFAULT_WEB_DAV_REMOTE_PATH, normalizedWebDavAutoUploadInterval, writeWebDavAutoUploadFields } from "@/lib/webdav/webdavAutoUploadConfig"; @@ -116,6 +117,7 @@ const { toast } = useToast(); const settingsStore = useSettingsStore(); const connectionStore = useConnectionStore(); const savedSqlStore = useSavedSqlStore(); +const tunnelProfileStore = useTunnelProfileStore(); const { isDark, themeMode, themePalette, setThemeMode, setThemePalette } = useTheme(); const appThemePaletteOptions = computed( @@ -1583,6 +1585,9 @@ async function downloadSnippetSnapshot() { await settingsStore.updateDesktopSettings(result.desktopSettings); await connectionStore.initFromDisk(); await savedSqlStore.initFromStorage(); + // Snapshot downloads replace backend-managed tunnel profiles, so refresh + // the already-loaded Pinia store instead of leaving the UI stale. + await tunnelProfileStore.refresh(); let message = t("settings.syncSnippetDownloadSuccess", { bytes: result.summary.bytes, id: result.summary.snippetId }); if (result.applySummary.encryptedSecretsPresent && !result.applySummary.secretsApplied) message += ` ${t("settings.syncSecretsSkipped")}`; if (result.applySummary.secretsApplied) message += ` ${t("settings.syncSecretsApplied")}`; @@ -1719,6 +1724,8 @@ async function downloadWebDavSnapshot() { await settingsStore.updateDesktopSettings(result.desktopSettings); await connectionStore.initFromDisk(); await savedSqlStore.initFromStorage(); + // Keep the shared tunnel profile UI consistent with the downloaded snapshot. + await tunnelProfileStore.refresh(); const message = t("settings.syncDownloadSuccess", { bytes: result.summary.bytes, path: result.summary.remotePath, diff --git a/packages/app-tests/syncTunnelProfiles.test.ts b/packages/app-tests/syncTunnelProfiles.test.ts new file mode 100644 index 0000000000..7b50b26e0e --- /dev/null +++ b/packages/app-tests/syncTunnelProfiles.test.ts @@ -0,0 +1,30 @@ +import { strict as assert } from "node:assert"; +import { readFileSync } from "node:fs"; +import { test } from "vitest"; + +function functionBody(source: string, name: string): string { + const signature = `async function ${name}(`; + const signatureIndex = source.indexOf(signature); + assert.notEqual(signatureIndex, -1, `Could not find function ${name}`); + const bodyStart = source.indexOf("{", signatureIndex); + assert.notEqual(bodyStart, -1, `Could not find body for ${name}`); + + let depth = 0; + for (let index = bodyStart; index < source.length; index += 1) { + if (source[index] === "{") depth += 1; + if (source[index] === "}") { + depth -= 1; + if (depth === 0) return source.slice(bodyStart + 1, index); + } + } + throw new Error(`Could not parse body for ${name}`); +} + +test("sync downloads refresh shared tunnel profiles", () => { + const source = readFileSync("apps/desktop/src/components/editor/EditorSettingsDialog.vue", "utf8"); + + for (const functionName of ["downloadSnippetSnapshot", "downloadWebDavSnapshot"]) { + const body = functionBody(source, functionName); + assert.match(body, /await tunnelProfileStore\.refresh\(\)/, `${functionName} should refresh downloaded tunnel profiles`); + } +});