batchCooking/apps/web/src/features/app-update/UpdateBanner.tsx
Nicolas 5d4d6be2dc
Some checks failed
CI / lint (push) Successful in 6m25s
CI / intent-service-test (push) Successful in 5m1s
CI / build (push) Successful in 2m38s
CI / test (push) Failing after 11m54s
CI / e2e (push) Has been cancelled
feat(mobile): vérification automatique de mise à jour de version
Réutilise l'API Releases de Forgejo (publique, sans authentification) comme
source de vérité pour "quelle est la dernière version" — zéro nouvel
endpoint backend : useAppUpdateCheck compare App.getInfo().version
(@capacitor/app, nouvelle dépendance) au tag_name de
GET .../releases/latest, et pointe "Mettre à jour" vers l'URL stable de
téléchargement (issue #65).

Comparaison de version en logique pure (version-compare.ts), séparée du
hook effectful — testée isolément via un test de composant Cypress
(version-compare.cy.tsx, même patron que highlight-tech-steps.cy.tsx :
aucun montage, juste describe/it/expect sur des fonctions pures).

Natif uniquement (Capacitor.isNativePlatform()) : @capacitor/app et
@capacitor/preferences restent en import dynamique dans le hook, jamais
dans le bundle web (même patron que capacitor-token-store.ts) — vérifié
dans le build (deux petits chunks séparés, pas dans le bundle principal).

Vérification au démarrage à froid + à la reprise depuis l'arrière-plan
(appStateChange), avec un throttle de 6h et une mémorisation de la version
déjà rejetée dans @capacitor/preferences pour ne jamais re-harceler sur la
même version. Bannière discrète (pas une Dialog modale), bouton "Mettre à
jour" ouvrant le lien de téléchargement dans le navigateur système plutôt
qu'un flux PackageInstaller in-app.

Vérifié : pnpm lint, pnpm --filter web build, le test de composant (9/9),
et layout/smoke/planning-page e2e (18/18, la bannière étant un no-op sur
le web ne devait rien casser — confirmé).

Closes #66

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-06 10:52:35 +02:00

45 lines
1.7 KiB
TypeScript

import { useTranslation } from "react-i18next";
import "./update-banner.scss";
import { useAppUpdateCheck } from "./useAppUpdateCheck";
/**
* Discreet, dismissible notice shown when a newer Android build has been
* tagged than the one installed (see `useAppUpdateCheck`'s own doc comment
* for the full mechanism — native only, `null` on the web, so this renders
* nothing there). A plain non-modal banner, not `Dialog.tsx`'s native
* `<dialog>`: a version check is informative, never something that should
* block the rest of the page.
*
* "Mettre à jour" opens the download link externally (the system browser
* handles the download + Android's own install prompt) rather than an
* in-app `PackageInstaller` flow — no `REQUEST_INSTALL_PACKAGES` permission,
* no extra native code, for a marginal UX gain (see issue #66's own
* hors-scope).
*/
export function UpdateBanner() {
const { t } = useTranslation();
const { availableVersion, dismiss } = useAppUpdateCheck();
if (!availableVersion) return null;
return (
<div className="update-banner" role="status">
<span className="update-banner__text">
{t("appUpdate.banner.text", { version: availableVersion })}
</span>
<div className="update-banner__actions">
<a
className="update-banner__update"
href="https://git.dev.kyuno.fr/kyuno/batchCooking/releases/latest/download/batchcooking.apk"
target="_blank"
rel="noreferrer"
>
{t("appUpdate.banner.update")}
</a>
<button type="button" className="update-banner__dismiss" onClick={dismiss}>
{t("appUpdate.banner.dismiss")}
</button>
</div>
</div>
);
}