import { Injectable, inject, signal } from '@angular/core'; import { RpcService } from './rpc.service'; export type UpdaterState = 'idle' | 'available' | 'downloading' | 'ready' | 'error'; /** * Drives the tab-bar update pill. The backend pushes updateStatus events * (available / downloading progress / error); "ready" is derived locally when * downloadUpdate resolves ok. Two-step UX: available -> download -> ready -> * apply (relaunch). */ @Injectable({ providedIn: 'root' }) export class UpdaterService { private rpc = inject(RpcService); readonly state = signal('idle'); readonly version = signal(''); readonly progress = signal(0); constructor() { if (!this.rpc.available) return; this.rpc.onUpdateStatus((s) => { if (s.phase === 'available') { this.version.set(s.version ?? ''); this.state.set('available'); } else if (s.phase === 'downloading') { this.state.set('downloading'); this.progress.set(s.progress ?? 0); } else if (s.phase === 'error') { this.state.set('error'); } }); } async download(): Promise { if (this.state() !== 'available') return; this.state.set('downloading'); this.progress.set(0); const r = await this.rpc.request.downloadUpdate({}); this.state.set(r?.ok ? 'ready' : 'error'); } apply(): void { if (this.state() !== 'ready') return; void this.rpc.request.applyUpdate({}); // quits + relaunches } /** Manual re-check (menu item). */ async check(): Promise { if (!this.rpc.available) return; const r = await this.rpc.request.checkForUpdate({}); if (r?.ok && r.updateAvailable) { this.version.set(r.version); this.state.set('available'); } } }