From 2d9c7747fb9c5031a815c3648e59b080fde7718f Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Thu, 7 May 2026 18:20:22 +0700 Subject: [PATCH] Fix misleading 'Update installed' callout flash MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The install flow briefly flashed an "Update installed / Restart to use the new version" callout between clicking Install & restart and the actual app quit. The button implies action; the restart is already in flight. Run quitAndInstall inline (no 1.5s setTimeout) so the IPC sequences download → install → daemon stop → app quit before returning. Drop the "installed" rendering branch from the callout so the success path goes straight from "Installing..." to the window unmounting. --- .../desktop/updates/update-callout-source.tsx | 26 ++++--------------- packages/desktop/src/features/auto-updater.ts | 17 ++++-------- 2 files changed, 10 insertions(+), 33 deletions(-) diff --git a/packages/app/src/desktop/updates/update-callout-source.tsx b/packages/app/src/desktop/updates/update-callout-source.tsx index d25ed7d89..1c4143ada 100644 --- a/packages/app/src/desktop/updates/update-callout-source.tsx +++ b/packages/app/src/desktop/updates/update-callout-source.tsx @@ -13,25 +13,18 @@ import { openExternalUrl } from "@/utils/open-external-url"; const CHECK_INTERVAL_MS = 30 * 60 * 1000; const CHANGELOG_URL = "https://paseo.sh/changelog"; -function resolveUpdateCalloutTitle(args: { - isInstalled: boolean; - isInstalling: boolean; - isError: boolean; -}): string { - if (args.isInstalled) return "Update installed"; +function resolveUpdateCalloutTitle(args: { isInstalling: boolean; isError: boolean }): string { if (args.isInstalling) return "Installing update"; if (args.isError) return "Update failed"; return "Update available"; } function resolveUpdateCalloutDescription(args: { - isInstalled: boolean; isInstalling: boolean; isError: boolean; errorMessage: string | null; latestVersion: string | undefined; }): ReactNode { - if (args.isInstalled) return "Restart to use the new version."; if (args.isInstalling) return "Installing and restarting..."; if (args.isError) return args.errorMessage ?? "Something went wrong."; if (args.latestVersion) { @@ -43,7 +36,6 @@ function resolveUpdateCalloutDescription(args: { } function buildUpdateCalloutActions(args: { - isInstalled: boolean; isInstalling: boolean; isError: boolean; openChangelog: () => void; @@ -53,7 +45,7 @@ function buildUpdateCalloutActions(args: { const actions: SidebarCalloutAction[] = [{ label: "What's new", onPress: args.openChangelog }]; if (args.isError) { actions.push({ label: "Retry", onPress: args.retry, variant: "primary" }); - } else if (!args.isInstalled) { + } else { actions.push({ label: args.isInstalling ? "Installing..." : "Install & restart", onPress: args.install, @@ -107,29 +99,21 @@ export function UpdateCalloutSource() { if (!isDesktopApp) { return; } - if ( - status !== "available" && - status !== "installed" && - status !== "installing" && - status !== "error" - ) { + if (status !== "available" && status !== "installing" && status !== "error") { return; } - const isInstalled = status === "installed"; const isError = status === "error"; - const isAvailable = !isInstalled && !isInstalling && !isError; + const isAvailable = !isInstalling && !isError; - const title = resolveUpdateCalloutTitle({ isInstalled, isInstalling, isError }); + const title = resolveUpdateCalloutTitle({ isInstalling, isError }); const description = resolveUpdateCalloutDescription({ - isInstalled, isInstalling, isError, errorMessage, latestVersion: availableUpdate?.latestVersion ?? undefined, }); const actions = buildUpdateCalloutActions({ - isInstalled, isInstalling, isError, openChangelog, diff --git a/packages/desktop/src/features/auto-updater.ts b/packages/desktop/src/features/auto-updater.ts index 78949721f..0547a9642 100644 --- a/packages/desktop/src/features/auto-updater.ts +++ b/packages/desktop/src/features/auto-updater.ts @@ -196,16 +196,9 @@ function buildCheckResult(input: { }; } -function scheduleQuitAndInstall(onBeforeQuit?: () => Promise): void { - // Use a short delay to allow the renderer to receive the response. - setTimeout(async () => { - try { - if (onBeforeQuit) await onBeforeQuit(); - autoUpdater.quitAndInstall(/* isSilent */ false, /* isForceRunAfter */ true); - } catch (error) { - console.error("[auto-updater] quitAndInstall failed:", error); - } - }, 1500); +async function performQuitAndInstall(onBeforeQuit?: () => Promise): Promise { + if (onBeforeQuit) await onBeforeQuit(); + autoUpdater.quitAndInstall(/* isSilent */ false, /* isForceRunAfter */ true); } // --------------------------------------------------------------------------- @@ -314,7 +307,7 @@ export async function downloadAndInstallUpdate( const readyVersion = cachedUpdateInfo.version; if (isReadyToInstallVersion(readyVersion)) { - scheduleQuitAndInstall(onBeforeQuit); + await performQuitAndInstall(onBeforeQuit); return { installed: true, version: readyVersion, @@ -336,7 +329,7 @@ export async function downloadAndInstallUpdate( await autoUpdater.downloadUpdate(); downloadedUpdateVersion = readyVersion; downloading = false; - scheduleQuitAndInstall(onBeforeQuit); + await performQuitAndInstall(onBeforeQuit); return { installed: true,