fix(desktop): stop the updater button flickering after download (#2089)

This commit is contained in:
Hampus authored and GitHub committed 2026-08-29 17:44:43 +02:00
1 parent 96c5db3f5d
commit 9b8659a40b
3 files changed
+60 -1

No files matched your search

@@ -45,6 +45,36 @@ describe('updaterStateMachine', () => {
expect(snapshot.context.updateInfo.web.available).toBe(false); expect(snapshot.context.updateInfo.web.available).toBe(false);
}); });
it('keeps a downloaded native update pending restart when checks re-emit available or not-available', () => {
let snapshot = createUpdaterMachineSnapshot();
snapshot = transition(snapshot, {
type: 'native.available',
version: '2.0.0',
downloadSize: 1000,
downloadStarted: true,
downloadUrl: null,
downloadOptions: [],
});
snapshot = transition(snapshot, {type: 'native.downloaded', version: '2.0.0'});
expect(snapshot.context.updateInfo.native.downloaded).toBe(true);
snapshot = transition(snapshot, {type: 'native.notAvailable', now: NOW});
expect(snapshot.context.updateInfo.native.downloaded).toBe(true);
expect(snapshot.context.updateInfo.native.available).toBe(true);
snapshot = transition(snapshot, {
type: 'native.available',
version: null,
downloadSize: null,
downloadStarted: true,
downloadUrl: null,
downloadOptions: [],
});
expect(snapshot.context.updateInfo.native.downloaded).toBe(true);
expect(snapshot.context.updateInfo.native.downloading).toBe(false);
expect(snapshot.context.updateInfo.native.version).toBe('2.0.0');
});
it('tracks check start and finish separately from available updates', () => { it('tracks check start and finish separately from available updates', () => {
let snapshot = createUpdaterMachineSnapshot(); let snapshot = createUpdaterMachineSnapshot();
snapshot = transition(snapshot, {type: 'check.started'}); snapshot = transition(snapshot, {type: 'check.started'});
@@ -166,6 +166,13 @@ export const updaterStateMachine = setup({
}), }),
applyNativeAvailable: assign(({context, event}) => { applyNativeAvailable: assign(({context, event}) => {
if (event.type !== 'native.available') return {}; if (event.type !== 'native.available') return {};
const currentNative = context.updateInfo.native;
if (currentNative.downloaded && (event.version == null || event.version === currentNative.version)) {
return {
isChecking: false,
nativeUnsupported: null,
};
}
return { return {
updateInfo: { updateInfo: {
...context.updateInfo, ...context.updateInfo,
+23 -1
View File
@@ -308,6 +308,8 @@ function registerVelopackUpdater(getMainWindow: () => BrowserWindow | null): voi
function registerElectronUpdater(getMainWindow: () => BrowserWindow | null): void { function registerElectronUpdater(getMainWindow: () => BrowserWindow | null): void {
let electronUpdateDownloading = false; let electronUpdateDownloading = false;
let electronDownloadRetries = 0; let electronDownloadRetries = 0;
let electronUpdateDownloaded = false;
let electronDownloadedVersion: string | null = null;
const {UpdateSourceType, updateElectronApp} = requireModule( const {UpdateSourceType, updateElectronApp} = requireModule(
'update-electron-app', 'update-electron-app',
) as typeof import('update-electron-app'); ) as typeof import('update-electron-app');
@@ -323,7 +325,14 @@ function registerElectronUpdater(getMainWindow: () => BrowserWindow | null): voi
autoUpdater.on('checking-for-update', () => { autoUpdater.on('checking-for-update', () => {
send(getMainWindow(), {type: 'checking', context: lastContext}); send(getMainWindow(), {type: 'checking', context: lastContext});
}); });
const sendPendingRestart = () => {
send(getMainWindow(), {type: 'downloaded', context: lastContext, version: electronDownloadedVersion});
};
autoUpdater.on('update-available', () => { autoUpdater.on('update-available', () => {
if (electronUpdateDownloaded) {
sendPendingRestart();
return;
}
electronUpdateDownloading = true; electronUpdateDownloading = true;
send(getMainWindow(), { send(getMainWindow(), {
type: 'available', type: 'available',
@@ -334,10 +343,19 @@ function registerElectronUpdater(getMainWindow: () => BrowserWindow | null): voi
}); });
}); });
autoUpdater.on('update-not-available', () => { autoUpdater.on('update-not-available', () => {
if (electronUpdateDownloaded) {
sendPendingRestart();
return;
}
send(getMainWindow(), {type: 'not-available', context: lastContext}); send(getMainWindow(), {type: 'not-available', context: lastContext});
}); });
autoUpdater.on('update-downloaded', (_event, _releaseNotes, releaseName) => { autoUpdater.on('update-downloaded', (_event, _releaseNotes, releaseName) => {
send(getMainWindow(), {type: 'downloaded', context: lastContext, version: releaseName ?? null}); electronUpdateDownloading = false;
electronUpdateDownloaded = true;
if (releaseName) {
electronDownloadedVersion = releaseName;
}
sendPendingRestart();
}); });
autoUpdater.on('error', (err: Error) => { autoUpdater.on('error', (err: Error) => {
const message = err?.message ?? String(err); const message = err?.message ?? String(err);
@@ -360,6 +378,10 @@ function registerElectronUpdater(getMainWindow: () => BrowserWindow | null): voi
return; return;
} }
electronUpdateDownloading = false; electronUpdateDownloading = false;
if (electronUpdateDownloaded) {
sendPendingRestart();
return;
}
send(getMainWindow(), {type: 'error', context: lastContext, phase, message}); send(getMainWindow(), {type: 'error', context: lastContext, phase, message});
}); });
ipcMain.handle('updater-check', async (_e, context: UpdaterContext) => { ipcMain.handle('updater-check', async (_e, context: UpdaterContext) => {