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>
45 lines
1.7 KiB
TypeScript
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>
|
|
);
|
|
}
|