Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 30 additions & 3 deletions electron/auto-updater.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,18 @@ const IS_MAC = process.platform === "darwin";
let checkTimer: ReturnType<typeof setInterval> | null = null;
let macUpdater: MacCustomUpdater | null = null;

/**
* When true, the app is quitting because the user clicked "Restart" in the
* update panel. The before-quit handler uses this to decide whether to
* relaunch after installing the update.
*/
let updateRestartRequested = false;

/** Returns true if the current quit was initiated by an update restart. */
export function isUpdateRestart(): boolean {
return updateRestartRequested;
}

export function setupAutoUpdater(window: BrowserWindow): void {
const checkFn = IS_MAC
? setupMacUpdater(window)
Expand All @@ -37,10 +49,16 @@ export function stopAutoUpdater(): void {

function setupMacUpdater(window: BrowserWindow): () => void {
macUpdater = new MacCustomUpdater(window);
macUpdater.registerAutoInstallOnQuit();
macUpdater.registerAutoInstallOnQuit(() => updateRestartRequested);

ipcMain.on("updater:install", () => {
macUpdater?.quitAndInstall();
// Instead of calling quitAndInstall() directly (which would fire
// app.quit() before the save-canvas dialog has a chance to run),
// set a flag and trigger the normal close flow. The before-quit
// handler in registerAutoInstallOnQuit will pick up the flag and
// run the install script with relaunch=true.
updateRestartRequested = true;
window.close(); // goes through the save-canvas dialog first
});

ipcMain.handle("updater:check", () =>
Expand Down Expand Up @@ -91,7 +109,16 @@ function setupElectronUpdater(window: BrowserWindow): () => void {
});

ipcMain.on("updater:install", () => {
autoUpdater.quitAndInstall(false, true);
// Same pattern as the mac updater: go through the save-canvas flow
// first, then install on before-quit.
updateRestartRequested = true;
window.close();
});

app.on("before-quit", () => {
if (updateRestartRequested) {
autoUpdater.quitAndInstall(false, true);
}
});

ipcMain.handle("updater:check", () =>
Expand Down
14 changes: 9 additions & 5 deletions electron/mac-updater.ts
Original file line number Diff line number Diff line change
Expand Up @@ -255,15 +255,19 @@ export class MacCustomUpdater {
}

/**
* Register a before-quit handler that silently replaces the .app
* when the user quits normally and an update is pending.
* Unlike explicit quitAndInstall, this does NOT relaunch the app.
* Register a before-quit handler that replaces the .app when
* the app quits and an update is pending.
*
* @param shouldRelaunch - callback that returns true when the user
* explicitly requested "Restart to update" (so the app relaunches
* after installing), false for a normal quit (silent install, no
* relaunch).
*/
registerAutoInstallOnQuit(): void {
registerAutoInstallOnQuit(shouldRelaunch: () => boolean): void {
app.on("before-quit", () => {
if (this.pendingUpdate && !this.installing) {
this.installing = true;
this.runInstallScript(false);
this.runInstallScript(shouldRelaunch());
}
});
}
Expand Down
3 changes: 2 additions & 1 deletion src/components/UpdateModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ export function UpdateModal({ onClose }: Props) {
const backdropRef = useRef<HTMLDivElement>(null);

const handleInstall = useCallback(() => {
onClose();
window.termcanvas.updater.install();
}, []);
}, [onClose]);

const handleRetry = useCallback(() => {
useUpdaterStore.setState({ status: "checking", errorMessage: null });
Expand Down