From 52d4004b0add8d9b1654927323f6a4a212a8e52e Mon Sep 17 00:00:00 2001 From: Erwan Leboucher Date: Fri, 14 Aug 2026 15:22:21 +0200 Subject: [PATCH] fix: hide desktop updater when disabled --- scripts/tauri.js | 3 +++ .../components/tauri/DesktopUpdatePill.tsx | 3 ++- src/app/features/settings/about/About.tsx | 4 +-- src/app/pages/client/DesktopUpdater.test.tsx | 25 +++++++++++++++++-- src/app/pages/client/DesktopUpdater.tsx | 4 ++- src/app/utils/platform.ts | 4 +++ src/ext.d.ts | 1 + vite.config.ts | 2 ++ 8 files changed, 40 insertions(+), 6 deletions(-) diff --git a/scripts/tauri.js b/scripts/tauri.js index 52f340daaf..9c0c71300f 100755 --- a/scripts/tauri.js +++ b/scripts/tauri.js @@ -68,6 +68,9 @@ async function main() { logger.info('Building without the auto-updater (--no-updater)'); } + // The frontend is built before Cargo, so mirror the updater feature into Vite. + process.env.VITE_DESKTOP_UPDATER_ENABLED = String(!noUpdater); + const features = noUpdater ? platform : `${platform},updater`; const args = [cmd, '--features', features, ...tauriArgs]; if (!tauriArgs.includes('--')) { diff --git a/src/app/components/tauri/DesktopUpdatePill.tsx b/src/app/components/tauri/DesktopUpdatePill.tsx index 4b7dcc9f1b..fb84a9f070 100644 --- a/src/app/components/tauri/DesktopUpdatePill.tsx +++ b/src/app/components/tauri/DesktopUpdatePill.tsx @@ -1,5 +1,6 @@ import { useAtom, useAtomValue } from 'jotai'; import { hasCustomDesktopTitlebar } from '$utils/tauriTitlebar'; +import { isDesktopUpdaterEnabled } from '$utils/platform'; import { useDesktopSetting } from '$state/hooks/desktopSettings'; import { updatePhaseAtom, updateBannerVisibleAtom } from '$state/desktopUpdate'; import type { UpdatePhase } from '$state/desktopUpdate'; @@ -24,7 +25,7 @@ export function DesktopUpdatePill() { const [useCustomTitleBar] = useDesktopSetting('useCustomTitleBar'); const status = !bannerVisible ? phaseToStatusView(phase) : null; - if (!hasCustomDesktopTitlebar(useCustomTitleBar)) return null; + if (!isDesktopUpdaterEnabled() || !hasCustomDesktopTitlebar(useCustomTitleBar)) return null; return ( ) { Options - {isDesktopTauri() && ( + {isDesktopTauri() && isDesktopUpdaterEnabled() && ( ({ checkFn: vi.fn<() => Promise>() })); +const { checkFn, updaterEnabled } = vi.hoisted(() => ({ + checkFn: vi.fn<() => Promise>(), + updaterEnabled: vi.fn<() => boolean>(), +})); vi.mock('@tauri-apps/plugin-updater', () => ({ check: checkFn })); vi.mock('$utils/platform', async (importOriginal) => { const mod = (await importOriginal()) as Record; - return { ...mod, isDesktopTauri: () => true }; + return { ...mod, isDesktopTauri: () => true, isDesktopUpdaterEnabled: updaterEnabled }; }); vi.mock('$state/hooks/desktopSettings', async (importOriginal) => ({ @@ -73,6 +76,7 @@ function makeUpdate(version: string) { beforeEach(() => { localStorage.clear(); + updaterEnabled.mockReturnValue(true); }); afterEach(() => { @@ -80,6 +84,23 @@ afterEach(() => { }); describe('DesktopUpdater', () => { + it('does not check for or display updates when the updater is disabled at build time', async () => { + updaterEnabled.mockReturnValue(false); + localStorage.setItem('sable_fake_desktop_update', '1'); + + render( + + + + + + ); + + await waitFor(() => expect(checkFn).not.toHaveBeenCalled()); + expect(screen.queryByRole('button', { name: 'Update Available' })).not.toBeInTheDocument(); + expect(screen.queryByTestId('banner-desktop-update-ready')).not.toBeInTheDocument(); + }); + it('reopens the update banner from the pill after dismissing it', async () => { localStorage.setItem('sable_fake_desktop_update', '1'); diff --git a/src/app/pages/client/DesktopUpdater.tsx b/src/app/pages/client/DesktopUpdater.tsx index f3c3c38eac..c6ca371ed2 100644 --- a/src/app/pages/client/DesktopUpdater.tsx +++ b/src/app/pages/client/DesktopUpdater.tsx @@ -1,7 +1,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useAtom, useAtomValue, useSetAtom } from 'jotai'; import type { Update } from '@tauri-apps/plugin-updater'; -import { isDesktopTauri } from '$utils/platform'; +import { isDesktopTauri, isDesktopUpdaterEnabled } from '$utils/platform'; import { autoUpdateCheckAtom } from '$state/autoUpdateCheck'; import { createLogger } from '$utils/debug'; import { getDebugLogger } from '$utils/debugLogger'; @@ -61,6 +61,7 @@ export function DesktopUpdater() { }, []); useEffect(() => { + if (!isDesktopUpdaterEnabled()) return undefined; if (!isDesktopTauri()) return undefined; if (triggerCount === 0 && !autoUpdateCheck && !fakeDesktopUpdate()) return undefined; @@ -223,6 +224,7 @@ export function DesktopUpdater() { }, [setBannerVisible]); const bannerData = useMemo(() => { + if (!isDesktopUpdaterEnabled()) return null; if (!bannerVisible || !updateInfo || dismissed) return null; if (isInstalled) { diff --git a/src/app/utils/platform.ts b/src/app/utils/platform.ts index b99a1e6e4b..a0988a4acc 100644 --- a/src/app/utils/platform.ts +++ b/src/app/utils/platform.ts @@ -70,6 +70,10 @@ export function isDesktopTauri(): boolean { return getDesktopTauriPlatform() !== undefined; } +export function isDesktopUpdaterEnabled(): boolean { + return DESKTOP_UPDATER_ENABLED; +} + export function isMobileTauri(): boolean { const tauriOS = getTauriOS(); return tauriOS === 'ios' || tauriOS === 'android'; diff --git a/src/ext.d.ts b/src/ext.d.ts index 2c77fa0a5d..ccde726283 100644 --- a/src/ext.d.ts +++ b/src/ext.d.ts @@ -5,6 +5,7 @@ declare const SABLE_BUILD_FLAVOR: string; declare const APP_VERSION: string; declare const BUILD_HASH: string; declare const IS_RELEASE_TAG: boolean; +declare const DESKTOP_UPDATER_ENABLED: boolean; declare module 'browser-encrypt-attachment' { export interface EncryptedAttachmentInfo { diff --git a/vite.config.ts b/vite.config.ts index c587431fea..b6d6018981 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -57,6 +57,7 @@ const tauriDevHost = process.env.TAURI_DEV_HOST; const isTauriBuild = Boolean(process.env.TAURI_ENV_PLATFORM); const isTauriDebug = process.env.TAURI_ENV_DEBUG === 'true'; const tauriBuildMinify = !isTauriDebug ? 'esbuild' : false; +const desktopUpdaterEnabled = process.env.VITE_DESKTOP_UPDATER_ENABLED !== 'false'; const sentryUploadEnabled = Boolean( process.env.SENTRY_AUTH_TOKEN && process.env.SENTRY_ORG && process.env.SENTRY_PROJECT ); @@ -151,6 +152,7 @@ export default defineConfig(({ command }) => { IS_RELEASE_TAG: JSON.stringify(isReleaseTag), SABLE_PRODUCT_NAME: JSON.stringify(baseProductName), SABLE_BUILD_FLAVOR: JSON.stringify(buildFlavor), + DESKTOP_UPDATER_ENABLED: JSON.stringify(desktopUpdaterEnabled), }, resolve: { alias: {