fix(app): keep the updater polling after async native results (#3179)

This commit is contained in:
Hampus
2026-10-03 19:42:41 +02:00
committed by GitHub
parent bc5dcdfe21
commit 583c791016
2 changed files with 13 additions and 13 deletions
+13 -6
View File
@@ -55,6 +55,7 @@ const CHECK_INTERVAL_MS = 5 * 60 * 1000;
const MIN_CHECK_INTERVAL_MS = 60 * 1000; const MIN_CHECK_INTERVAL_MS = 60 * 1000;
const MANUAL_DOWNLOAD_REFRESH_TIMEOUT_MS = 5 * 1000; const MANUAL_DOWNLOAD_REFRESH_TIMEOUT_MS = 5 * 1000;
const WEB_CHECK_TIMEOUT_MS = 15 * 1000; const WEB_CHECK_TIMEOUT_MS = 15 * 1000;
const NATIVE_CHECK_TIMEOUT_MS = 30 * 1000;
const VERSION_ENDPOINT = '/version.json'; const VERSION_ENDPOINT = '/version.json';
const CURRENT_BUILD_VERSION = Config.PUBLIC_BUILD_VERSION ?? null; const CURRENT_BUILD_VERSION = Config.PUBLIC_BUILD_VERSION ?? null;
const ALLOWED_WEB_UPDATE_HOSTS = new Set([ const ALLOWED_WEB_UPDATE_HOSTS = new Set([
@@ -157,6 +158,7 @@ class Updater {
private unsubscribeNativeEvents: (() => void) | null = null; private unsubscribeNativeEvents: (() => void) | null = null;
private updateReadyNagbarDismissedVersion: string | null = null; private updateReadyNagbarDismissedVersion: string | null = null;
private pendingManualDownloadRefreshes = 0; private pendingManualDownloadRefreshes = 0;
private checkInProgress = false;
constructor() { constructor() {
makeAutoObservable(this, {}, {autoBind: true}); makeAutoObservable(this, {}, {autoBind: true});
@@ -195,10 +197,6 @@ class Updater {
return this.snapshot.context.nativeManualDownloadOptions; return this.snapshot.context.nativeManualDownloadOptions;
} }
private get checkInProgress(): boolean {
return this.snapshot.context.checkInProgress;
}
private get nativeCheckFailed(): boolean { private get nativeCheckFailed(): boolean {
return this.snapshot.context.nativeCheckFailed; return this.snapshot.context.nativeCheckFailed;
} }
@@ -495,6 +493,7 @@ class Updater {
return; return;
} }
this.checkInProgress = true;
this.transition({type: 'check.started'}); this.transition({type: 'check.started'});
const checkContext: 'user' | 'background' = userInitiated ? 'user' : 'background'; const checkContext: 'user' | 'background' = userInitiated ? 'user' : 'background';
@@ -518,6 +517,7 @@ class Updater {
pushUpdateCheckFailedModal(); pushUpdateCheckFailedModal();
} }
} finally { } finally {
this.checkInProgress = false;
this.transition({type: failed ? 'check.failed' : 'check.finished', now: Date.now()}); this.transition({type: failed ? 'check.failed' : 'check.finished', now: Date.now()});
} }
} }
@@ -525,12 +525,17 @@ class Updater {
private async checkNativeUpdate(context: 'user' | 'background'): Promise<boolean> { private async checkNativeUpdate(context: 'user' | 'background'): Promise<boolean> {
const electronApi = getElectronAPI(); const electronApi = getElectronAPI();
if (!electronApi) return false; if (!electronApi) return false;
let timeoutId: number | undefined;
const timedOut = new Promise<false>((resolve) => {
timeoutId = window.setTimeout(() => resolve(false), NATIVE_CHECK_TIMEOUT_MS);
});
try { try {
await electronApi.updaterCheck(context); return await Promise.race([electronApi.updaterCheck(context).then(() => true), timedOut]);
return true;
} catch (error) { } catch (error) {
logger.debug('Native update check failed silently:', error); logger.debug('Native update check failed silently:', error);
return false; return false;
} finally {
window.clearTimeout(timeoutId);
} }
} }
@@ -692,6 +697,7 @@ class Updater {
if (this.checkInProgress) { if (this.checkInProgress) {
return option; return option;
} }
this.checkInProgress = true;
this.transition({type: 'check.started'}); this.transition({type: 'check.started'});
let timeoutId: number | undefined; let timeoutId: number | undefined;
const timedOut = new Promise<boolean>((resolve) => { const timedOut = new Promise<boolean>((resolve) => {
@@ -706,6 +712,7 @@ class Updater {
return this.nativeManualDownloadOptions.find((candidate) => candidate.format === option.format) ?? option; return this.nativeManualDownloadOptions.find((candidate) => candidate.format === option.format) ?? option;
} finally { } finally {
window.clearTimeout(timeoutId); window.clearTimeout(timeoutId);
this.checkInProgress = false;
this.transition({type: 'check.finished', now: Date.now()}); this.transition({type: 'check.finished', now: Date.now()});
} }
} }
@@ -42,7 +42,6 @@ export interface UpdaterMachineContext {
downloadProgress: NativeDownloadProgress | null; downloadProgress: NativeDownloadProgress | null;
lastCheckedAt: number | null; lastCheckedAt: number | null;
isChecking: boolean; isChecking: boolean;
checkInProgress: boolean;
nativeCheckFailed: boolean; nativeCheckFailed: boolean;
manualNativeDownloadInFlight: boolean; manualNativeDownloadInFlight: boolean;
nativeUnsupported: NativeUnsupportedUpdate | null; nativeUnsupported: NativeUnsupportedUpdate | null;
@@ -108,7 +107,6 @@ export function createInitialUpdaterContext(): UpdaterMachineContext {
downloadProgress: null, downloadProgress: null,
lastCheckedAt: null, lastCheckedAt: null,
isChecking: false, isChecking: false,
checkInProgress: false,
nativeCheckFailed: false, nativeCheckFailed: false,
manualNativeDownloadInFlight: false, manualNativeDownloadInFlight: false,
nativeUnsupported: null, nativeUnsupported: null,
@@ -143,14 +141,12 @@ export const updaterStateMachine = setup({
reset: assign(() => createInitialUpdaterContext()), reset: assign(() => createInitialUpdaterContext()),
markChecking: assign(() => ({ markChecking: assign(() => ({
isChecking: true, isChecking: true,
checkInProgress: true,
nativeCheckFailed: false, nativeCheckFailed: false,
})), })),
markCheckFinished: assign(({context, event}) => ({ markCheckFinished: assign(({context, event}) => ({
lastCheckedAt: lastCheckedAt:
event.type === 'check.finished' || event.type === 'check.failed' ? event.now : context.lastCheckedAt, event.type === 'check.finished' || event.type === 'check.failed' ? event.now : context.lastCheckedAt,
isChecking: false, isChecking: false,
checkInProgress: false,
})), })),
applyWebChecked: assign(({context, event}) => { applyWebChecked: assign(({context, event}) => {
if (event.type !== 'web.checked') return {}; if (event.type !== 'web.checked') return {};
@@ -198,7 +194,6 @@ export const updaterStateMachine = setup({
...clearNativeUpdate(context), ...clearNativeUpdate(context),
lastCheckedAt: event.now, lastCheckedAt: event.now,
isChecking: false, isChecking: false,
checkInProgress: false,
nativeUnsupported: { nativeUnsupported: {
reason: event.reason, reason: event.reason,
downloadUrl: event.downloadUrl, downloadUrl: event.downloadUrl,
@@ -231,7 +226,6 @@ export const updaterStateMachine = setup({
}, },
downloadProgress: null, downloadProgress: null,
isChecking: false, isChecking: false,
checkInProgress: false,
nativeCheckFailed: true, nativeCheckFailed: true,
})), })),
applyNativeDownloaded: assign(({context, event}) => { applyNativeDownloaded: assign(({context, event}) => {
@@ -274,7 +268,6 @@ export const updaterStateMachine = setup({
...clearNativeUpdate(context), ...clearNativeUpdate(context),
lastCheckedAt: event.now, lastCheckedAt: event.now,
isChecking: false, isChecking: false,
checkInProgress: false,
nativeUnsupported: { nativeUnsupported: {
reason: event.reason, reason: event.reason,
downloadUrl: event.downloadUrl, downloadUrl: event.downloadUrl,