From 3df24fe71ce0a7d2ce539615b7fa9724be57af43 Mon Sep 17 00:00:00 2001 From: Nicolas Date: Tue, 8 Sep 2026 01:40:35 +0200 Subject: [PATCH] =?UTF-8?q?fix(mobile):=20lance=20la=20v=C3=A9rification?= =?UTF-8?q?=20de=20mise=20=C3=A0=20jour=20=C3=A0=20chaque=20ouverture=20(#?= =?UTF-8?q?106)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `useAppUpdateCheck` tournait au montage de ``, mais celui-ci n'était monté que dans `AppLayout`, derrière `RequireAuth` : un utilisateur non connecté sur l'app native voit `LandingPage`, `AppLayout` n'est jamais monté, donc aucune vérification tant qu'on n'est pas connecté — alors que le back suppose l'inverse (`mobile.routes.ts` : « runs on every app start, session or not »). - `` monté dans `App.tsx`, hors ``, donc rendu pour toute route quel que soit l'état d'auth. Garde `!isNativePlatform()` et throttle 6 h inchangés ; rend toujours `null` sur le web. - `update-banner.scss` : passe en `position: fixed` (bandeau haut, `env(safe-area-inset-top)`, `z-index: 100`) pour ne pas entrer dans la mise en page / le calcul de hauteur d'une page routée (`.app-layout` est `height: 100vh; overflow: hidden`). Pas de test e2e : `Capacitor.isNativePlatform()` n'a pas de couture pour simuler le natif depuis un run web (posture déjà assumée pour `useAppUpdateCheck` / `UpdateBanner`, voir `app-settings-page.cy.ts`). Suite e2e existante (layout / smoke / app-settings) revérifiée : aucun régression. Closes #106 Co-Authored-By: Claude Sonnet 5 --- apps/web/src/App.tsx | 200 +++++++++--------- .../features/app-update/update-banner.scss | 22 +- apps/web/src/layouts/AppLayout.tsx | 7 +- 3 files changed, 124 insertions(+), 105 deletions(-) diff --git a/apps/web/src/App.tsx b/apps/web/src/App.tsx index e1ebc69..9bacd80 100644 --- a/apps/web/src/App.tsx +++ b/apps/web/src/App.tsx @@ -1,6 +1,7 @@ import { Navigate, Outlet, Route, Routes } from "react-router-dom"; import { AdminAuthProvider } from "./features/admin/AdminAuthContext"; import { RequireAdmin } from "./features/admin/RequireAdmin"; +import { UpdateBanner } from "./features/app-update/UpdateBanner"; import { RedirectIfAuthenticated } from "./features/auth/RedirectIfAuthenticated"; import { RequireAuth } from "./features/auth/RequireAuth"; import { AdminLayout } from "./layouts/AdminLayout"; @@ -71,111 +72,120 @@ import { ShoppingListPage } from "./pages/shopping-list/ShoppingListPage"; * never nested under `RequireAuth`. `/admin/login` is the only * unauthenticated admin route; everything else sits under `RequireAdmin` + * `AdminLayout`. + * + * {@link UpdateBanner} is mounted here, outside ``, rather than in + * `AppLayout` — the native app-update check (issue #66) must run on every + * app start, session or not, and `AppLayout` only mounts once a visitor is + * past `RequireAuth`. It's a `position: fixed` strip and renders nothing on + * the web, so mounting it app-wide costs the routed pages no layout. */ export function App() { return ( - - - - - } - > - } /> - {/* Same component for both — a master-detail layout, not a + <> + + + + + + } + > + } /> + {/* Same component for both — a master-detail layout, not a navigation to a separate page: the tab bar + table stay mounted, only RecipesPage's detail panel changes with `:id` (see RecipesPage.tsx). */} - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - - - - - } - /> - - - - } - /> - - - - } - /> - - - - } - /> - - - - } - /> - - - - } - /> - } /> - - - - } - > - } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + - - + + + + } + /> + + + + } + /> + + + + } + /> + + + + } + /> + + + + } + /> + + + + } + /> + } /> + + + } > - } /> - } /> - } /> - } /> - } /> + } /> + + + + } + > + } /> + } /> + } /> + } /> + } /> + - - } /> - + } /> + + ); } diff --git a/apps/web/src/features/app-update/update-banner.scss b/apps/web/src/features/app-update/update-banner.scss index 3264c51..2e0e4ff 100644 --- a/apps/web/src/features/app-update/update-banner.scss +++ b/apps/web/src/features/app-update/update-banner.scss @@ -1,18 +1,28 @@ -// Styles for UpdateBanner — a discreet, dismissible strip above the routed -// page content (see AppLayout.tsx). Uses --color-warning the same way -// source-item-preview__hint does elsewhere in the app: "worth noticing", +// Styles for UpdateBanner — a discreet, dismissible strip pinned to the top +// of the screen. Mounted app-wide in App.tsx (outside ``) so the +// native update check runs session or not, hence `position: fixed` rather +// than the old in-flow placement inside `.app-content`: this way it never +// enters any routed page's layout / height math (`.app-layout` is +// `height: 100vh; overflow: hidden`), it just overlays. Native-only — +// renders nothing on the web (see UpdateBanner.tsx). Uses --color-warning +// the same way source-item-preview__hint does elsewhere: "worth noticing", // not an error. .update-banner { + position: fixed; + top: 0; + left: 0; + right: 0; + z-index: 100; display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-sm); - margin-bottom: var(--space-md); padding: var(--space-sm) var(--space-md); + // Clears the Android status bar when the WebView draws under it. + padding-top: calc(var(--space-sm) + env(safe-area-inset-top, 0px)); background: color-mix(in srgb, var(--color-warning) 12%, var(--color-surface)); - border: 1px solid var(--color-warning); - border-radius: var(--radius-base); + border-bottom: 1px solid var(--color-warning); font-size: var(--font-size-sm); &__text { diff --git a/apps/web/src/layouts/AppLayout.tsx b/apps/web/src/layouts/AppLayout.tsx index d0ad9e9..7ad72db 100644 --- a/apps/web/src/layouts/AppLayout.tsx +++ b/apps/web/src/layouts/AppLayout.tsx @@ -2,7 +2,6 @@ import { useState } from "react"; import { useTranslation } from "react-i18next"; import { NavLink, Outlet, useLocation, useNavigate } from "react-router-dom"; import { BottomSheet } from "../components/ui/BottomSheet"; -import { UpdateBanner } from "../features/app-update/UpdateBanner"; import { useAuth } from "../features/auth/AuthContext"; import { useIsMobileViewport } from "../lib/useIsMobileViewport"; import "./AppLayout.scss"; @@ -148,9 +147,9 @@ export function AppLayout() { )}
- {/* Renders nothing on the web build (native-only check) or once no - newer version is known — see UpdateBanner's own doc comment. */} - + {/* `UpdateBanner` used to sit here, but the native update check + (issue #66) must run session or not — it's mounted app-wide in + `App.tsx` now, as a `position: fixed` strip. */}