diff --git a/apps/emdash-desktop/src/main/core/updates/controller.ts b/apps/emdash-desktop/src/main/core/updates/controller.ts index 5ae8feae30..c94bc04ee1 100644 --- a/apps/emdash-desktop/src/main/core/updates/controller.ts +++ b/apps/emdash-desktop/src/main/core/updates/controller.ts @@ -5,9 +5,9 @@ import { EMDASH_RELEASES_URL } from '@shared/urls'; import { formatUpdaterError } from './utils'; export const updateController = createRPCController({ - check: async () => { + check: async (args?: { source?: 'background' | 'manual' }) => { try { - const result = await updateService.checkForUpdates(); + const result = await updateService.checkForUpdates(args?.source ?? 'manual'); return { success: true, result: result ?? null }; } catch (error) { return { success: false, error: formatUpdaterError(error) }; diff --git a/apps/emdash-desktop/src/main/core/updates/update-service.ts b/apps/emdash-desktop/src/main/core/updates/update-service.ts index aef9a7b9e0..83cd2ce1d6 100644 --- a/apps/emdash-desktop/src/main/core/updates/update-service.ts +++ b/apps/emdash-desktop/src/main/core/updates/update-service.ts @@ -28,6 +28,8 @@ const CHECK_INTERVAL_MS = 60 * 60 * 1000; // 1 hour const STARTUP_DELAY_MS = 30 * 1000; // 30 seconds const INSTALL_RESTART_GUARD_TIMEOUT_MS = 2 * 60 * 1000; +type UpdateCheckSource = 'background' | 'manual'; + export interface UpdateState { status: 'idle' | 'checking' | 'available' | 'downloading' | 'downloaded' | 'installing' | 'error'; lastCheck?: Date; @@ -50,6 +52,7 @@ class UpdateService implements IInitializable, IDisposable { private updateState: UpdateState; private checkTimer?: NodeJS.Timeout; private currentCheckPromise: Promise | null = null; + private currentCheckSource: UpdateCheckSource | null = null; private initialized = false; private active = false; private installRequested = false; @@ -126,6 +129,14 @@ class UpdateService implements IInitializable, IDisposable { return; } + if (this.updateState.status === 'checking' && this.currentCheckSource === 'background') { + this.updateState.status = 'idle'; + this.updateState.error = undefined; + events.emit(updateNotAvailableEvent, undefined); + log.warn('Background update check failed; suppressing user-facing error'); + return; + } + const previousVersion = this.updateState.availableVersion; const previousInfo = this.updateState.updateInfo; @@ -175,12 +186,17 @@ class UpdateService implements IInitializable, IDisposable { }, delay); } - async checkForUpdates(): Promise { + async checkForUpdates(source: UpdateCheckSource = 'background'): Promise { if (!this.active) return null; - if (this.currentCheckPromise) return this.currentCheckPromise; + if (this.currentCheckPromise) { + if (source === 'manual') this.currentCheckSource = 'manual'; + return this.currentCheckPromise; + } + this.currentCheckSource = source; this.currentCheckPromise = this._performCheck().finally(() => { this.currentCheckPromise = null; + this.currentCheckSource = null; this.scheduleNextCheck(); }); diff --git a/apps/emdash-desktop/src/renderer/lib/stores/update-store.ts b/apps/emdash-desktop/src/renderer/lib/stores/update-store.ts index 6a4fb61411..4b1b56b1d0 100644 --- a/apps/emdash-desktop/src/renderer/lib/stores/update-store.ts +++ b/apps/emdash-desktop/src/renderer/lib/stores/update-store.ts @@ -29,7 +29,6 @@ export type UpdateState = | { status: 'idle' } | { status: 'checking' } | { status: 'available'; info?: { version: string } } - | { status: 'not-available' } | { status: 'downloading'; progress?: DownloadProgress } | { status: 'downloaded' } | { status: 'installing' } @@ -89,7 +88,7 @@ export class UpdateStore { events.on(updateNotAvailableEvent, () => { runInAction(() => { - this.state = { status: 'not-available' }; + this.state = { status: 'idle' }; }); }); @@ -132,10 +131,10 @@ export class UpdateStore { }); events.on(menuCheckForUpdatesChannel, () => { - rpc.update.check().catch(() => {}); + rpc.update.check({ source: 'manual' }).catch(() => {}); }); - rpc.update.check().catch(() => {}); + rpc.update.check({ source: 'background' }).catch(() => {}); } async check(): Promise { @@ -143,7 +142,7 @@ export class UpdateStore { this.state = { status: 'checking' }; }); try { - const res = await rpc.update.check(); + const res = await rpc.update.check({ source: 'manual' }); if (!res) { runInAction(() => { this.state = { status: 'error', message: 'Update API unavailable' };