import { Capacitor, CapacitorHttp } from '@capacitor/core' import { Filesystem, Directory } from '@capacitor/filesystem' import { FileOpener } from '@capawesome-team/capacitor-file-opener' import { getBaseUrl } from './config' export interface UpdateInfo { version: string apkUrl: string | null apkSize: number | null notes: string | null } /** Compare deux versions sémantiques. Renvoie 1 si a > b, -1 si a < b, 0 si égales. */ function compareVersions(a: string, b: string): number { const pa = a.split('.').map(Number) const pb = b.split('.').map(Number) for (let i = 0; i < Math.max(pa.length, pb.length); i++) { const na = pa[i] ?? 0 const nb = pb[i] ?? 0 if (na > nb) return 1 if (na < nb) return -1 } return 0 } /** * Vérifie si une mise à jour est disponible sur le serveur. * Seulement sur Android natif : la PWA iOS se met à jour en rechargeant. */ export async function checkForUpdate(): Promise { if (!Capacitor.isNativePlatform() || Capacitor.getPlatform() !== 'android') { return null } const localVersion = __APP_VERSION__ try { const response = await fetch(`${getBaseUrl()}/api/version`, { headers: { Accept: 'application/json' }, }) if (!response.ok) return null const remote = (await response.json()) as UpdateInfo if (compareVersions(remote.version, localVersion) <= 0) return null const dismissed = localStorage.getItem('meteo-exam.dismissed-update') if (dismissed === remote.version) return null return remote } catch { return null } } export const currentAppVersion = (): string => __APP_VERSION__ export function dismissUpdate(version: string): void { localStorage.setItem('meteo-exam.dismissed-update', version) } /** * Télécharge l'APK via le réseau natif (OkHttp), l'enregistre dans le cache * de l'app puis ouvre l'installateur Android — sans quitter l'application. */ export async function downloadAndInstallApk(apkUrl: string): Promise { const url = apkUrl.startsWith('http') ? apkUrl : `${getBaseUrl()}${apkUrl}` // 1. Télécharger le binaire via CapacitorHttp (natif, pas de WebView). const response = await CapacitorHttp.request({ url, method: 'GET', responseType: 'arraybuffer', }) if (response.status !== 200) { throw new Error(`Téléchargement impossible (HTTP ${response.status})`) } // 2. Écrire le fichier dans le cache de l'app. // CapacitorHttp renvoie les données en base64 pour responseType arraybuffer. const data = response.data as string const binary = atob(data) const bytes = new Uint8Array(binary.length) for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i) // Convertir en base64 pour Capacitor Filesystem. let base64 = '' const chunk = 0x8000 for (let i = 0; i < bytes.length; i += chunk) { base64 += String.fromCharCode(...bytes.subarray(i, i + chunk)) } const b64 = btoa(base64) const fileName = `meteo-exam-${__APP_VERSION__}-update.apk` await Filesystem.writeFile({ path: fileName, data: b64, directory: Directory.Cache, }) // 3. Lire le chemin réel du fichier sur le système de fichiers. const { uri } = await Filesystem.getUri({ path: fileName, directory: Directory.Cache, }) // 4. Ouvrir l'installateur Android avec ce fichier APK. // Android affiche l'écran d'installation par-dessus l'app. await FileOpener.openFile({ path: uri, mimeType: 'application/vnd.android.package-archive', }) }