From be65a9bd1fceea8bb30a52e22edca96b3ffd1eb1 Mon Sep 17 00:00:00 2001 From: Bohdan Triapitsyn Date: Wed, 22 Apr 2026 00:57:58 +0300 Subject: [PATCH] fix(electron): preserve changelog on download + reliable restart-to-update - useUpdateStore: keep sidecar-sourced body when merging fresh desktopInfo (electron-updater returns 'See release notes at ...' which clobbered it) - main.mjs: defer quitAndInstall/relaunch via setImmediate so IPC reply flushes first; wire update-downloaded and error events; log restart path --- packages/electron/main.mjs | 37 +++++++++++++++++++----- packages/ui/src/stores/useUpdateStore.ts | 4 +++ 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/packages/electron/main.mjs b/packages/electron/main.mjs index c45f23ad..1b3024c7 100644 --- a/packages/electron/main.mjs +++ b/packages/electron/main.mjs @@ -1355,6 +1355,17 @@ const setupAutoUpdater = () => { }, })); }); + + autoUpdater.on('update-downloaded', (info) => { + log.info(`[electron] update-downloaded version=${info?.version || 'unknown'}`); + if (state.pendingUpdate) { + state.pendingUpdate.downloaded = true; + } + }); + + autoUpdater.on('error', (err) => { + log.error('[electron] autoUpdater error', err); + }); }; const parseRelevantChangelogNotes = async (fromVersion, toVersion) => { @@ -1892,14 +1903,26 @@ const handleInvoke = async (browserWindow, command, args = {}) => { })); return null; - case 'desktop_restart': - if (state.pendingUpdate?.downloaded && app.isPackaged) { - autoUpdater.quitAndInstall(false, true); - return null; - } - app.relaunch(); - app.exit(0); + case 'desktop_restart': { + const applyUpdate = Boolean(state.pendingUpdate?.downloaded && app.isPackaged); + log.info(`[electron] desktop_restart applyUpdate=${applyUpdate} packaged=${app.isPackaged}`); + // Defer so the IPC reply flushes before the app starts shutting down. + // Without this, quitAndInstall() can race with the renderer's pending + // invoke and the restart appears to do nothing from the UI side. + setImmediate(() => { + try { + if (applyUpdate) { + autoUpdater.quitAndInstall(false, true); + } else { + app.relaunch(); + app.exit(0); + } + } catch (err) { + log.error('[electron] desktop_restart failed', err); + } + }); return null; + } case 'desktop_get_lan_address': return await detectLanIPv4Address(); diff --git a/packages/ui/src/stores/useUpdateStore.ts b/packages/ui/src/stores/useUpdateStore.ts index 8f3c167b..795efb3e 100644 --- a/packages/ui/src/stores/useUpdateStore.ts +++ b/packages/ui/src/stores/useUpdateStore.ts @@ -244,6 +244,10 @@ export const useUpdateStore = create()((set, get) => ({ ? { ...state.info, ...desktopInfo, + // Keep the richer sidecar-sourced changelog; desktopInfo.body is + // often the bare "See release notes at..." fallback from the + // updater and would otherwise clobber the nice changelog. + body: state.info.body || desktopInfo.body, available: state.info.available, } : desktopInfo,