Application mobile : vérification automatique de mise à jour #66

Closed
opened 2026-09-06 10:12:26 +02:00 by kyuno · 0 comments
Owner

Contexte

Sur l'application mobile, détecter automatiquement qu'une nouvelle version
est disponible et le signaler à l'utilisateur.

Contrainte de départ à ne pas manquer : l'app n'est distribuée nulle
part sur le Play Store (hors scope explicite de #64/#65 — juste un APK
téléchargeable) — l'API "In-App Updates" de Google Play n'a donc aucune
prise ici, elle ne fonctionne que pour une app installée depuis le Play
Store. Ce ticket construit un mécanisme maison, pas une intégration de
cette API.

Conception — la source de vérité pour "quelle est la dernière version"

Réutilise directement l'infrastructure déjà posée par
#64/#65,
zéro nouveau endpoint backend : l'API Releases de Forgejo est déjà publique
et non authentifiée
pour ce dépôt (déjà utilisée pour vérifier le statut
de CI sans jeton, voir local-verification-while-ci-disabled) :

GET https://git.dev.kyuno.fr/api/v1/repos/kyuno/batchCooking/releases/latest
→ { tag_name: "v1.4.2", ... }

Comparer tag_name à la version installée
(@capacitor/app's App.getInfo() → { version, build } — nouvelle
dépendance
, plugin officiel Capacitor, pas encore présent) ; si plus
récent, proposer le téléchargement vers l'URL stable déjà définie par #65
(.../releases/latest/download/batchcooking.apk).

Dépendance trouvée — versionName/versionCode ne sont jamais mis à jour

Vérifié dans apps/web/android/app/build.gradle (lignes 10-11) :
versionCode 1 / versionName "1.0", toujours la valeur générée par défaut
par npx cap add android (#63) — rien ne les fait jamais évoluer. Sans
correctif, la comparaison de version n'aurait aucun sens (toujours "1.0",
quelle que soit la version réellement installée). Cette itération de
versionName/versionCode doit se faire au moment du build de release
(même job que #65 construit déjà) : versionName = le tag débarrassé de son
préfixe v (v1.4.2 → 1.4.2), versionCode = un entier qui augmente
strictement à chaque release (Android refuse une "mise à jour" avec un
versionCode inférieur ou égal à l'existant) — proposé :
majeur×10000 + mineur×100 + correctif (tient tant que mineur/correctif
restent sous 100, élargir le multiplicateur sinon). Ajouté au périmètre de
la tâche déjà déposée
pour release.yml plutôt qu'une tâche de plus.

Conception — mécanisme de vérification côté app

  • Natif uniquement (Capacitor.isNativePlatform()) — même garde que le
    TokenStore de #39 ; sur le web, l'app se met à jour simplement en
    rechargeant la page servie par le serveur, aucune notion de "version
    installée" n'existe.
  • Déclenchement : au démarrage à froid + à la reprise depuis l'arrière-
    plan (@capacitor/app's événement appStateChange), avec un throttle
    (dernier check horodaté dans @capacitor/preferences, pas plus d'une fois
    toutes les quelques heures) pour ne pas solliciter l'API Forgejo à chaque
    passage au premier plan.
  • Échec réseau : silencieux, aucune bannière d'erreur — même posture que
    ThemeContext's propre fetch de préférences ("failure swallowed
    silently"), une vérification de version n'est jamais bloquante.
  • Affichage : une bannière discrète, non bloquante (pas de Dialog
    modal pour ça), avec un bouton "Mettre à jour" qui ouvre l'URL de
    téléchargement dans le navigateur système (laisser Android gérer le
    téléchargement + l'invite d'installation ensuite — pas de téléchargement/
    installation in-app via PackageInstaller, qui demanderait la permission
    REQUEST_INSTALL_PACKAGES et beaucoup plus de code natif pour un gain
    d'expérience marginal).
  • Ne pas re-harceler : le rejet ("Plus tard") mémorise la version
    proposée (@capacitor/preferences) ; la bannière ne se réaffiche que si
    une version encore plus récente que celle déjà rejetée sort — pas à
    chaque redémarrage tant que rien n'a changé.

Fichiers à toucher

  • apps/web/package.json — ajoute @capacitor/app.
  • apps/web/src/features/app-update/ (nouveau) — useAppUpdateCheck (hook),
    UpdateBanner.tsx + scss, appelés depuis AppLayout.tsx (même position
    que les autres providers/effets globaux de l'espace connecté).
  • apps/web/src/locales/fr/translation.json — namespace appUpdate.*.
  • La tâche déjà déposée pour release.yml/le workflow réutilisable de build
    APK — étendue pour injecter versionName/versionCode avant
    ./gradlew assembleRelease.

Tests

  • Cypress : non applicable directement (le hook est gardé par
    Capacitor.isNativePlatform(), toujours faux dans un navigateur de test)
    — un test unitaire (Vitest/pure function, si l'outillage existe, sinon un
    test Mocha du côté partagé) sur la seule logique pure testable sans
    Capacitor : la comparaison de versions et la logique "ne pas re-harceler".

Hors scope

  • Téléchargement/installation in-app via PackageInstaller — cf. le choix
    ci-dessus, ouverture externe suffit pour cette itération.
  • iOS — toujours hors scope de #39 lui-même, et de toute façon soumis à des
    règles Apple totalement différentes (pas de sideload équivalent).

🤖 Filed by Claude Code

## Contexte Sur l'application mobile, détecter automatiquement qu'une nouvelle version est disponible et le signaler à l'utilisateur. **Contrainte de départ à ne pas manquer** : l'app n'est distribuée nulle part sur le Play Store (hors scope explicite de #64/#65 — juste un APK téléchargeable) — l'API "In-App Updates" de Google Play n'a donc aucune prise ici, elle ne fonctionne que pour une app installée *depuis* le Play Store. Ce ticket construit un mécanisme maison, pas une intégration de cette API. ## Conception — la source de vérité pour "quelle est la dernière version" Réutilise directement l'infrastructure déjà posée par [#64](https://git.dev.kyuno.fr/kyuno/batchCooking/issues/64)/[#65](https://git.dev.kyuno.fr/kyuno/batchCooking/issues/65), zéro nouveau endpoint backend : l'API Releases de Forgejo est déjà **publique et non authentifiée** pour ce dépôt (déjà utilisée pour vérifier le statut de CI sans jeton, voir `local-verification-while-ci-disabled`) : ``` GET https://git.dev.kyuno.fr/api/v1/repos/kyuno/batchCooking/releases/latest → { tag_name: "v1.4.2", ... } ``` Comparer `tag_name` à la version installée (`@capacitor/app`'s `App.getInfo()` → `{ version, build }` — **nouvelle dépendance**, plugin officiel Capacitor, pas encore présent) ; si plus récent, proposer le téléchargement vers l'URL stable déjà définie par #65 (`.../releases/latest/download/batchcooking.apk`). ## Dépendance trouvée — `versionName`/`versionCode` ne sont jamais mis à jour Vérifié dans `apps/web/android/app/build.gradle` (lignes 10-11) : `versionCode 1` / `versionName "1.0"`, toujours la valeur générée par défaut par `npx cap add android` (#63) — rien ne les fait jamais évoluer. Sans correctif, la comparaison de version n'aurait aucun sens (toujours "1.0", quelle que soit la version réellement installée). Cette itération de `versionName`/`versionCode` doit se faire **au moment du build de release** (même job que #65 construit déjà) : `versionName` = le tag débarrassé de son préfixe `v` (`v1.4.2` → `1.4.2`), `versionCode` = un entier qui augmente strictement à chaque release (Android refuse une "mise à jour" avec un `versionCode` inférieur ou égal à l'existant) — proposé : `majeur×10000 + mineur×100 + correctif` (tient tant que mineur/correctif restent sous 100, élargir le multiplicateur sinon). **Ajouté au périmètre de la tâche déjà déposée** pour release.yml plutôt qu'une tâche de plus. ## Conception — mécanisme de vérification côté app - **Natif uniquement** (`Capacitor.isNativePlatform()`) — même garde que le `TokenStore` de #39 ; sur le web, l'app se met à jour simplement en rechargeant la page servie par le serveur, aucune notion de "version installée" n'existe. - **Déclenchement** : au démarrage à froid + à la reprise depuis l'arrière- plan (`@capacitor/app`'s événement `appStateChange`), avec un throttle (dernier check horodaté dans `@capacitor/preferences`, pas plus d'une fois toutes les quelques heures) pour ne pas solliciter l'API Forgejo à chaque passage au premier plan. - **Échec réseau** : silencieux, aucune bannière d'erreur — même posture que `ThemeContext`'s propre fetch de préférences ("failure swallowed silently"), une vérification de version n'est jamais bloquante. - **Affichage** : une bannière discrète, non bloquante (pas de `Dialog` modal pour ça), avec un bouton "Mettre à jour" qui ouvre l'URL de téléchargement dans le navigateur système (laisser Android gérer le téléchargement + l'invite d'installation ensuite — pas de téléchargement/ installation in-app via `PackageInstaller`, qui demanderait la permission `REQUEST_INSTALL_PACKAGES` et beaucoup plus de code natif pour un gain d'expérience marginal). - **Ne pas re-harceler** : le rejet ("Plus tard") mémorise la version proposée (`@capacitor/preferences`) ; la bannière ne se réaffiche que si une version *encore plus récente* que celle déjà rejetée sort — pas à chaque redémarrage tant que rien n'a changé. ## Fichiers à toucher - `apps/web/package.json` — ajoute `@capacitor/app`. - `apps/web/src/features/app-update/` (nouveau) — `useAppUpdateCheck` (hook), `UpdateBanner.tsx` + scss, appelés depuis `AppLayout.tsx` (même position que les autres providers/effets globaux de l'espace connecté). - `apps/web/src/locales/fr/translation.json` — namespace `appUpdate.*`. - La tâche déjà déposée pour `release.yml`/le workflow réutilisable de build APK — étendue pour injecter `versionName`/`versionCode` avant `./gradlew assembleRelease`. ## Tests - Cypress : non applicable directement (le hook est gardé par `Capacitor.isNativePlatform()`, toujours faux dans un navigateur de test) — un test unitaire (Vitest/pure function, si l'outillage existe, sinon un test Mocha du côté partagé) sur la seule logique pure testable sans Capacitor : la comparaison de versions et la logique "ne pas re-harceler". ## Hors scope - Téléchargement/installation in-app via `PackageInstaller` — cf. le choix ci-dessus, ouverture externe suffit pour cette itération. - iOS — toujours hors scope de #39 lui-même, et de toute façon soumis à des règles Apple totalement différentes (pas de sideload équivalent). --- 🤖 Filed by [Claude Code](https://claude.com/claude-code)
kyuno added the
enhancement
planned
labels 2026-09-06 10:12:27 +02:00
kyuno closed this issue 2026-09-06 14:11:56 +02:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: kyuno/batchCooking#66
No description provided.