diff --git a/apps/code/src/main/services/updates/schemas.ts b/apps/code/src/main/services/updates/schemas.ts index e30783f08..dbdef957a 100644 --- a/apps/code/src/main/services/updates/schemas.ts +++ b/apps/code/src/main/services/updates/schemas.ts @@ -30,7 +30,9 @@ export const UpdatesEvent = { export type UpdatesStatusPayload = { checking: boolean; + downloading?: boolean; upToDate?: boolean; + updateReady?: boolean; version?: string; error?: string; }; diff --git a/apps/code/src/main/services/updates/service.ts b/apps/code/src/main/services/updates/service.ts index 81a2a9457..bb7ad0784 100644 --- a/apps/code/src/main/services/updates/service.ts +++ b/apps/code/src/main/services/updates/service.ts @@ -10,6 +10,7 @@ import { type InstallUpdateOutput, UpdatesEvent, type UpdatesEvents, + type UpdatesStatusPayload, } from "./schemas"; type CheckSource = "user" | "periodic"; @@ -101,6 +102,11 @@ export class UpdatesService extends TypedEventEmitter { }); this.pendingNotification = true; this.flushPendingNotification(); + this.emitStatus({ + checking: false, + updateReady: true, + version: this.downloadedVersion ?? undefined, + }); return { success: true }; } @@ -203,7 +209,7 @@ export class UpdatesService extends TypedEventEmitter { this.clearCheckTimeout(); log.info("Update available, downloading..."); // Keep checkingForUpdates true while downloading - // The download is now in progress + this.emitStatus({ checking: true, downloading: true }); } private handleNoUpdate(): void { @@ -265,12 +271,7 @@ export class UpdatesService extends TypedEventEmitter { } } - private emitStatus(status: { - checking: boolean; - upToDate?: boolean; - version?: string; - error?: string; - }): void { + private emitStatus(status: UpdatesStatusPayload): void { this.emit(UpdatesEvent.Status, status); } diff --git a/apps/code/src/renderer/components/UpdatePrompt.tsx b/apps/code/src/renderer/components/UpdatePrompt.tsx index 76db3dfed..3aaae9607 100644 --- a/apps/code/src/renderer/components/UpdatePrompt.tsx +++ b/apps/code/src/renderer/components/UpdatePrompt.tsx @@ -188,14 +188,16 @@ export function UpdatePrompt() { { id: CHECK_TOAST_ID, duration: 3000 }, ); } else if (status.checking === true) { - // Show checking toast + // Show checking/downloading toast sonnerToast.custom( () => ( - Checking for updates... + {status.downloading + ? "Downloading update..." + : "Checking for updates..."} diff --git a/apps/code/src/renderer/features/settings/components/sections/UpdatesSettings.tsx b/apps/code/src/renderer/features/settings/components/sections/UpdatesSettings.tsx index e3a2eda35..7066c535f 100644 --- a/apps/code/src/renderer/features/settings/components/sections/UpdatesSettings.tsx +++ b/apps/code/src/renderer/features/settings/components/sections/UpdatesSettings.tsx @@ -38,6 +38,9 @@ export function UpdatesSettings() { message: "Checking for updates...", type: "info", }); + } else if (result.errorCode === "already_checking") { + // A check is already in progress (e.g. boot check) — show spinner and wait + setUpdateStatus({ message: "Checking for updates...", type: "info" }); } else { if (result.errorCode === "disabled") { setUpdatesDisabled(true); @@ -68,12 +71,22 @@ export function UpdatesSettings() { useSubscription( trpcReact.updates.onStatus.subscriptionOptions(undefined, { onData: (status) => { - if (status.checking === false && status.upToDate) { + if (status.checking && status.downloading) { + setUpdateStatus({ message: "Downloading update...", type: "info" }); + } else if (status.checking === false && status.upToDate) { setUpdateStatus({ message: "You're on the latest version", type: "success", }); setCheckingForUpdates(false); + } else if (status.checking === false && status.updateReady) { + setUpdateStatus({ + message: status.version + ? `Update ${status.version} ready to install` + : "Update ready to install", + type: "success", + }); + setCheckingForUpdates(false); } else if (status.checking === false) { setCheckingForUpdates(false); }