From 967084c498f475870bdabf0123a5f8d23b430376 Mon Sep 17 00:00:00 2001 From: Abeautifulsnow Date: Tue, 11 Aug 2026 21:39:44 +0800 Subject: [PATCH] feat(updater): support ignoring a specific version --- apps/desktop/src/App.vue | 4 + .../components/layout/UpdateDialog.spec.ts | 32 ++++- .../src/components/layout/UpdateDialog.vue | 7 ++ .../__tests__/useAppUpdater.spec.ts | 81 +++++++++++- apps/desktop/src/composables/useAppUpdater.ts | 76 ++++++++++- 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/ja.ts | 3 + apps/desktop/src/i18n/locales/ko.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 + .../stores/__tests__/settingsStore.spec.ts | 118 ++++++++++++++++++ apps/desktop/src/stores/settingsStore.ts | 74 +++++++++-- packages/app-tests/appUpdater.test.ts | 37 +++++- 16 files changed, 439 insertions(+), 14 deletions(-) diff --git a/apps/desktop/src/App.vue b/apps/desktop/src/App.vue index a9ef3af5e3..e99227ee27 100644 --- a/apps/desktop/src/App.vue +++ b/apps/desktop/src/App.vue @@ -172,11 +172,13 @@ const { updateDownloaded, isInstallingUpdate, updateReady, + isIgnoringUpdate, activeTaskCount: activeUpdateTaskCount, hasUpdateAvailable, openUrl, checkUpdates, openLatestRelease, + ignoreCurrentVersion, downloadAndInstallUpdate, cancelDownload, installDownloadedUpdate, @@ -2857,12 +2859,14 @@ onUnmounted(() => { :update-downloaded="updateDownloaded" :is-installing-update="isInstallingUpdate" :update-ready="updateReady" + :is-ignoring-update="isIgnoringUpdate" :active-task-count="activeUpdateTaskCount" @open-latest-release="openLatestRelease" @download-and-install="downloadAndInstallUpdate" @cancel-download="cancelDownload" @install-downloaded="installDownloadedUpdate" @restart="restartApp" + @ignore-version="ignoreCurrentVersion" /> diff --git a/apps/desktop/src/components/layout/UpdateDialog.spec.ts b/apps/desktop/src/components/layout/UpdateDialog.spec.ts index d6499c860c..d198329a72 100644 --- a/apps/desktop/src/components/layout/UpdateDialog.spec.ts +++ b/apps/desktop/src/components/layout/UpdateDialog.spec.ts @@ -20,6 +20,7 @@ interface DialogState { updateDownloaded: boolean; isInstallingUpdate: boolean; updateReady: boolean; + isIgnoringUpdate: boolean; } async function flushDialog() { @@ -37,10 +38,12 @@ async function mountDialog(activeTaskCount: number, initialState: Partial { expect(downloadAndInstall).not.toHaveBeenCalled(); }); }); + +describe("UpdateDialog ignore version", () => { + it("emits ignore-version when the ignore button is clicked", async () => { + const { ignoreVersion } = await mountDialog(0); + + const ignoreButton = buttonWithText("Ignore this version"); + expect(ignoreButton).toBeDefined(); + ignoreButton?.click(); + await flushDialog(); + + expect(ignoreVersion).toHaveBeenCalledOnce(); + }); + + it("hides the ignore button once an update has been downloaded", async () => { + await mountDialog(0, { updateDownloaded: true, downloadProgress: 100 }); + + expect(buttonWithText("Ignore this version")).toBeUndefined(); + }); + + it("disables the ignore button while the setting is being persisted", async () => { + await mountDialog(0, { isIgnoringUpdate: true }); + + expect(buttonWithText("Ignore this version")?.disabled).toBe(true); + }); +}); diff --git a/apps/desktop/src/components/layout/UpdateDialog.vue b/apps/desktop/src/components/layout/UpdateDialog.vue index b2d2c241c2..6cf189f047 100644 --- a/apps/desktop/src/components/layout/UpdateDialog.vue +++ b/apps/desktop/src/components/layout/UpdateDialog.vue @@ -18,6 +18,7 @@ const props = defineProps<{ updateDownloaded: boolean; isInstallingUpdate: boolean; updateReady: boolean; + isIgnoringUpdate: boolean; activeTaskCount: number; }>(); @@ -27,6 +28,7 @@ const emit = defineEmits<{ "cancel-download": []; "install-downloaded": []; restart: []; + "ignore-version": []; }>(); const { t } = useI18n(); @@ -35,6 +37,7 @@ const isDesktop = isTauriRuntime(); const renderedNotes = ref(""); // Only active file replacement (installation) must trap the dialog. const isCloseBlocked = computed(() => props.isInstallingUpdate); +const canIgnoreVersion = computed(() => props.updateInfo?.update_available === true && !props.updateDownloaded && !props.isDownloadingUpdate && !props.isInstallingUpdate && !props.updateReady); function handleCancel() { handleOpenChange(false); @@ -136,6 +139,10 @@ watch( +