diff --git a/fluxer_app/src/features/app/state/Updater.ts b/fluxer_app/src/features/app/state/Updater.ts index 68d0b8ab9..c76654080 100644 --- a/fluxer_app/src/features/app/state/Updater.ts +++ b/fluxer_app/src/features/app/state/Updater.ts @@ -55,6 +55,7 @@ const CHECK_INTERVAL_MS = 5 * 60 * 1000; const MIN_CHECK_INTERVAL_MS = 60 * 1000; const MANUAL_DOWNLOAD_REFRESH_TIMEOUT_MS = 5 * 1000; const WEB_CHECK_TIMEOUT_MS = 15 * 1000; +const NATIVE_CHECK_TIMEOUT_MS = 30 * 1000; const VERSION_ENDPOINT = '/version.json'; const CURRENT_BUILD_VERSION = Config.PUBLIC_BUILD_VERSION ?? null; const ALLOWED_WEB_UPDATE_HOSTS = new Set([ @@ -157,6 +158,7 @@ class Updater { private unsubscribeNativeEvents: (() => void) | null = null; private updateReadyNagbarDismissedVersion: string | null = null; private pendingManualDownloadRefreshes = 0; + private checkInProgress = false; constructor() { makeAutoObservable(this, {}, {autoBind: true}); @@ -195,10 +197,6 @@ class Updater { return this.snapshot.context.nativeManualDownloadOptions; } - private get checkInProgress(): boolean { - return this.snapshot.context.checkInProgress; - } - private get nativeCheckFailed(): boolean { return this.snapshot.context.nativeCheckFailed; } @@ -495,6 +493,7 @@ class Updater { return; } + this.checkInProgress = true; this.transition({type: 'check.started'}); const checkContext: 'user' | 'background' = userInitiated ? 'user' : 'background'; @@ -518,6 +517,7 @@ class Updater { pushUpdateCheckFailedModal(); } } finally { + this.checkInProgress = false; this.transition({type: failed ? 'check.failed' : 'check.finished', now: Date.now()}); } } @@ -525,12 +525,17 @@ class Updater { private async checkNativeUpdate(context: 'user' | 'background'): Promise { const electronApi = getElectronAPI(); if (!electronApi) return false; + let timeoutId: number | undefined; + const timedOut = new Promise((resolve) => { + timeoutId = window.setTimeout(() => resolve(false), NATIVE_CHECK_TIMEOUT_MS); + }); try { - await electronApi.updaterCheck(context); - return true; + return await Promise.race([electronApi.updaterCheck(context).then(() => true), timedOut]); } catch (error) { logger.debug('Native update check failed silently:', error); return false; + } finally { + window.clearTimeout(timeoutId); } } @@ -692,6 +697,7 @@ class Updater { if (this.checkInProgress) { return option; } + this.checkInProgress = true; this.transition({type: 'check.started'}); let timeoutId: number | undefined; const timedOut = new Promise((resolve) => { @@ -706,6 +712,7 @@ class Updater { return this.nativeManualDownloadOptions.find((candidate) => candidate.format === option.format) ?? option; } finally { window.clearTimeout(timeoutId); + this.checkInProgress = false; this.transition({type: 'check.finished', now: Date.now()}); } } diff --git a/fluxer_app/src/features/updater/state/UpdaterStateMachine.ts b/fluxer_app/src/features/updater/state/UpdaterStateMachine.ts index 5f9ff2973..794c37a9a 100644 --- a/fluxer_app/src/features/updater/state/UpdaterStateMachine.ts +++ b/fluxer_app/src/features/updater/state/UpdaterStateMachine.ts @@ -42,7 +42,6 @@ export interface UpdaterMachineContext { downloadProgress: NativeDownloadProgress | null; lastCheckedAt: number | null; isChecking: boolean; - checkInProgress: boolean; nativeCheckFailed: boolean; manualNativeDownloadInFlight: boolean; nativeUnsupported: NativeUnsupportedUpdate | null; @@ -108,7 +107,6 @@ export function createInitialUpdaterContext(): UpdaterMachineContext { downloadProgress: null, lastCheckedAt: null, isChecking: false, - checkInProgress: false, nativeCheckFailed: false, manualNativeDownloadInFlight: false, nativeUnsupported: null, @@ -143,14 +141,12 @@ export const updaterStateMachine = setup({ reset: assign(() => createInitialUpdaterContext()), markChecking: assign(() => ({ isChecking: true, - checkInProgress: true, nativeCheckFailed: false, })), markCheckFinished: assign(({context, event}) => ({ lastCheckedAt: event.type === 'check.finished' || event.type === 'check.failed' ? event.now : context.lastCheckedAt, isChecking: false, - checkInProgress: false, })), applyWebChecked: assign(({context, event}) => { if (event.type !== 'web.checked') return {}; @@ -198,7 +194,6 @@ export const updaterStateMachine = setup({ ...clearNativeUpdate(context), lastCheckedAt: event.now, isChecking: false, - checkInProgress: false, nativeUnsupported: { reason: event.reason, downloadUrl: event.downloadUrl, @@ -231,7 +226,6 @@ export const updaterStateMachine = setup({ }, downloadProgress: null, isChecking: false, - checkInProgress: false, nativeCheckFailed: true, })), applyNativeDownloaded: assign(({context, event}) => { @@ -274,7 +268,6 @@ export const updaterStateMachine = setup({ ...clearNativeUpdate(context), lastCheckedAt: event.now, isChecking: false, - checkInProgress: false, nativeUnsupported: { reason: event.reason, downloadUrl: event.downloadUrl,