import { isTauriRuntime } from './globalShortcut'; export interface UpdateState { available: boolean; version: string | null; notes: string | null; downloading: boolean; downloaded: boolean; error: string | null; } export const IDLE_UPDATE_STATE: UpdateState = { available: false, version: null, notes: null, downloading: false, downloaded: false, error: null, }; // Module-local flag: main owns the real "pending update" state via // electron-updater, but the renderer also needs to refuse installUpdate() // calls that weren't preceded by a successful checkForUpdate(). let hasPendingUpdate = false; export async function checkForUpdate(): Promise { if (!isTauriRuntime()) { return { ...IDLE_UPDATE_STATE }; } try { const result = await window.electronAPI.checkForUpdate(); if (!result.available || !result.info) { hasPendingUpdate = false; return { ...IDLE_UPDATE_STATE }; } hasPendingUpdate = true; return { available: true, version: result.info.version ?? null, notes: result.info.releaseNotes ?? null, downloading: false, downloaded: false, error: null, }; } catch (err: unknown) { const msg = err instanceof Error ? err.message : String(err); const benign = /could not fetch a valid release json|network|timed? out|failed to fetch|connection|enotfound|econnreset|etimedout|404/i.test( msg, ); if (!benign) { console.warn('checkForUpdate failed', err); } return { ...IDLE_UPDATE_STATE, error: benign ? null : msg, }; } } // Downloads + installs the previously checked update. On Windows the // app quits during install (passive installer); electron-updater // triggers a relaunch on macOS/Linux. export async function installUpdate( onProgress?: (downloaded: number, total: number | null) => void, ): Promise { if (!hasPendingUpdate) { throw new Error('no pending update — call checkForUpdate() first'); } let unsub: (() => void) | null = null; if (onProgress) { unsub = window.electronAPI.onUpdaterProgress((p) => { onProgress(p.transferred, p.total || null); }); } try { await window.electronAPI.downloadInstallUpdate(); } finally { unsub?.(); hasPendingUpdate = false; } }