fix(mobile): lance la vérification de mise à jour à chaque ouverture (#106) #114
3 changed files with 124 additions and 105 deletions
|
|
@ -1,6 +1,7 @@
|
||||||
import { Navigate, Outlet, Route, Routes } from "react-router-dom";
|
import { Navigate, Outlet, Route, Routes } from "react-router-dom";
|
||||||
import { AdminAuthProvider } from "./features/admin/AdminAuthContext";
|
import { AdminAuthProvider } from "./features/admin/AdminAuthContext";
|
||||||
import { RequireAdmin } from "./features/admin/RequireAdmin";
|
import { RequireAdmin } from "./features/admin/RequireAdmin";
|
||||||
|
import { UpdateBanner } from "./features/app-update/UpdateBanner";
|
||||||
import { RedirectIfAuthenticated } from "./features/auth/RedirectIfAuthenticated";
|
import { RedirectIfAuthenticated } from "./features/auth/RedirectIfAuthenticated";
|
||||||
import { RequireAuth } from "./features/auth/RequireAuth";
|
import { RequireAuth } from "./features/auth/RequireAuth";
|
||||||
import { AdminLayout } from "./layouts/AdminLayout";
|
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
|
* never nested under `RequireAuth`. `/admin/login` is the only
|
||||||
* unauthenticated admin route; everything else sits under `RequireAdmin` +
|
* unauthenticated admin route; everything else sits under `RequireAdmin` +
|
||||||
* `AdminLayout`.
|
* `AdminLayout`.
|
||||||
|
*
|
||||||
|
* {@link UpdateBanner} is mounted here, outside `<Routes>`, 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() {
|
export function App() {
|
||||||
return (
|
return (
|
||||||
<Routes>
|
<>
|
||||||
<Route
|
<UpdateBanner />
|
||||||
element={
|
<Routes>
|
||||||
<RequireAuth>
|
<Route
|
||||||
<AppLayout />
|
element={
|
||||||
</RequireAuth>
|
<RequireAuth>
|
||||||
}
|
<AppLayout />
|
||||||
>
|
</RequireAuth>
|
||||||
<Route path="/" element={<PlanningPage />} />
|
}
|
||||||
{/* Same component for both — a master-detail layout, not a
|
>
|
||||||
|
<Route path="/" element={<PlanningPage />} />
|
||||||
|
{/* Same component for both — a master-detail layout, not a
|
||||||
navigation to a separate page: the tab bar + table stay
|
navigation to a separate page: the tab bar + table stay
|
||||||
mounted, only RecipesPage's detail panel changes with `:id`
|
mounted, only RecipesPage's detail panel changes with `:id`
|
||||||
(see RecipesPage.tsx). */}
|
(see RecipesPage.tsx). */}
|
||||||
<Route path="/recettes" element={<RecipesPage />} />
|
<Route path="/recettes" element={<RecipesPage />} />
|
||||||
<Route path="/recettes/nouvelle" element={<RecipeFormPage />} />
|
<Route path="/recettes/nouvelle" element={<RecipeFormPage />} />
|
||||||
<Route path="/recettes/importer/:sourceKey/:externalId" element={<ImportRecipePage />} />
|
<Route path="/recettes/importer/:sourceKey/:externalId" element={<ImportRecipePage />} />
|
||||||
<Route path="/recettes/sources/:sourceKey/:externalId" element={<RecipesPage />} />
|
<Route path="/recettes/sources/:sourceKey/:externalId" element={<RecipesPage />} />
|
||||||
<Route path="/recettes/:id" element={<RecipesPage />} />
|
<Route path="/recettes/:id" element={<RecipesPage />} />
|
||||||
<Route path="/recettes/:id/modifier" element={<RecipeFormPage />} />
|
<Route path="/recettes/:id/modifier" element={<RecipeFormPage />} />
|
||||||
<Route path="/liste-de-courses" element={<ShoppingListPage />} />
|
<Route path="/liste-de-courses" element={<ShoppingListPage />} />
|
||||||
<Route path="/cuisiner" element={<CookingSessionPage />} />
|
<Route path="/cuisiner" element={<CookingSessionPage />} />
|
||||||
<Route path="/parametres/compte" element={<AccountSettingsPage />} />
|
<Route path="/parametres/compte" element={<AccountSettingsPage />} />
|
||||||
<Route path="/parametres/preferences" element={<PreferencesPage />} />
|
<Route path="/parametres/preferences" element={<PreferencesPage />} />
|
||||||
<Route path="/parametres/foyer" element={<HouseholdSettingsPage />} />
|
<Route path="/parametres/foyer" element={<HouseholdSettingsPage />} />
|
||||||
<Route path="/parametres/preferences-utilisateur" element={<UserPreferencesPage />} />
|
<Route path="/parametres/preferences-utilisateur" element={<UserPreferencesPage />} />
|
||||||
<Route path="/parametres/application" element={<AppSettingsPage />} />
|
<Route path="/parametres/application" element={<AppSettingsPage />} />
|
||||||
<Route path="/parametres/credits" element={<CreditsPage />} />
|
<Route path="/parametres/credits" element={<CreditsPage />} />
|
||||||
<Route path="/foyer" element={<Navigate to="/parametres/foyer" replace />} />
|
<Route path="/foyer" element={<Navigate to="/parametres/foyer" replace />} />
|
||||||
</Route>
|
</Route>
|
||||||
<Route
|
|
||||||
path="/onboarding/regime"
|
|
||||||
element={
|
|
||||||
<RequireAuth>
|
|
||||||
<OnboardingDietPage />
|
|
||||||
</RequireAuth>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
path="/onboarding/foyer"
|
|
||||||
element={
|
|
||||||
<RequireAuth>
|
|
||||||
<OnboardingHouseholdPage />
|
|
||||||
</RequireAuth>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
path="/onboarding/sources"
|
|
||||||
element={
|
|
||||||
<RequireAuth>
|
|
||||||
<OnboardingSourcesPage />
|
|
||||||
</RequireAuth>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
path="/onboarding/allergenes"
|
|
||||||
element={
|
|
||||||
<RequireAuth>
|
|
||||||
<OnboardingAllergensPage />
|
|
||||||
</RequireAuth>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
path="/login"
|
|
||||||
element={
|
|
||||||
<RedirectIfAuthenticated>
|
|
||||||
<LoginPage />
|
|
||||||
</RedirectIfAuthenticated>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
path="/signup"
|
|
||||||
element={
|
|
||||||
<RedirectIfAuthenticated>
|
|
||||||
<SignupPage />
|
|
||||||
</RedirectIfAuthenticated>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Route path="/telecharger" element={<DownloadPage />} />
|
|
||||||
<Route
|
|
||||||
element={
|
|
||||||
<AdminAuthProvider>
|
|
||||||
<Outlet />
|
|
||||||
</AdminAuthProvider>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Route path="/admin/login" element={<AdminLoginPage />} />
|
|
||||||
<Route
|
<Route
|
||||||
path="/admin"
|
path="/onboarding/regime"
|
||||||
element={
|
element={
|
||||||
<RequireAdmin>
|
<RequireAuth>
|
||||||
<AdminLayout />
|
<OnboardingDietPage />
|
||||||
</RequireAdmin>
|
</RequireAuth>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
path="/onboarding/foyer"
|
||||||
|
element={
|
||||||
|
<RequireAuth>
|
||||||
|
<OnboardingHouseholdPage />
|
||||||
|
</RequireAuth>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
path="/onboarding/sources"
|
||||||
|
element={
|
||||||
|
<RequireAuth>
|
||||||
|
<OnboardingSourcesPage />
|
||||||
|
</RequireAuth>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
path="/onboarding/allergenes"
|
||||||
|
element={
|
||||||
|
<RequireAuth>
|
||||||
|
<OnboardingAllergensPage />
|
||||||
|
</RequireAuth>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
path="/login"
|
||||||
|
element={
|
||||||
|
<RedirectIfAuthenticated>
|
||||||
|
<LoginPage />
|
||||||
|
</RedirectIfAuthenticated>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
path="/signup"
|
||||||
|
element={
|
||||||
|
<RedirectIfAuthenticated>
|
||||||
|
<SignupPage />
|
||||||
|
</RedirectIfAuthenticated>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Route path="/telecharger" element={<DownloadPage />} />
|
||||||
|
<Route
|
||||||
|
element={
|
||||||
|
<AdminAuthProvider>
|
||||||
|
<Outlet />
|
||||||
|
</AdminAuthProvider>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Route index element={<AdminDashboardPage />} />
|
<Route path="/admin/login" element={<AdminLoginPage />} />
|
||||||
<Route path="monitoring" element={<AdminMonitoringPage />} />
|
<Route
|
||||||
<Route path="corrections" element={<AdminCorrectionsPage />} />
|
path="/admin"
|
||||||
<Route path="catalogue" element={<AdminCatalogPage />} />
|
element={
|
||||||
<Route path="didacticiels" element={<AdminToursPage />} />
|
<RequireAdmin>
|
||||||
|
<AdminLayout />
|
||||||
|
</RequireAdmin>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Route index element={<AdminDashboardPage />} />
|
||||||
|
<Route path="monitoring" element={<AdminMonitoringPage />} />
|
||||||
|
<Route path="corrections" element={<AdminCorrectionsPage />} />
|
||||||
|
<Route path="catalogue" element={<AdminCatalogPage />} />
|
||||||
|
<Route path="didacticiels" element={<AdminToursPage />} />
|
||||||
|
</Route>
|
||||||
</Route>
|
</Route>
|
||||||
</Route>
|
<Route path="*" element={<Navigate to="/" replace />} />
|
||||||
<Route path="*" element={<Navigate to="/" replace />} />
|
</Routes>
|
||||||
</Routes>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,28 @@
|
||||||
// Styles for UpdateBanner — a discreet, dismissible strip above the routed
|
// Styles for UpdateBanner — a discreet, dismissible strip pinned to the top
|
||||||
// page content (see AppLayout.tsx). Uses --color-warning the same way
|
// of the screen. Mounted app-wide in App.tsx (outside `<Routes>`) so the
|
||||||
// source-item-preview__hint does elsewhere in the app: "worth noticing",
|
// 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.
|
// not an error.
|
||||||
.update-banner {
|
.update-banner {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 100;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
gap: var(--space-sm);
|
gap: var(--space-sm);
|
||||||
margin-bottom: var(--space-md);
|
|
||||||
padding: var(--space-sm) 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));
|
background: color-mix(in srgb, var(--color-warning) 12%, var(--color-surface));
|
||||||
border: 1px solid var(--color-warning);
|
border-bottom: 1px solid var(--color-warning);
|
||||||
border-radius: var(--radius-base);
|
|
||||||
font-size: var(--font-size-sm);
|
font-size: var(--font-size-sm);
|
||||||
|
|
||||||
&__text {
|
&__text {
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ import { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { NavLink, Outlet, useLocation, useNavigate } from "react-router-dom";
|
import { NavLink, Outlet, useLocation, useNavigate } from "react-router-dom";
|
||||||
import { BottomSheet } from "../components/ui/BottomSheet";
|
import { BottomSheet } from "../components/ui/BottomSheet";
|
||||||
import { UpdateBanner } from "../features/app-update/UpdateBanner";
|
|
||||||
import { useAuth } from "../features/auth/AuthContext";
|
import { useAuth } from "../features/auth/AuthContext";
|
||||||
import { useIsMobileViewport } from "../lib/useIsMobileViewport";
|
import { useIsMobileViewport } from "../lib/useIsMobileViewport";
|
||||||
import "./AppLayout.scss";
|
import "./AppLayout.scss";
|
||||||
|
|
@ -148,9 +147,9 @@ export function AppLayout() {
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<main className={isMobile ? "app-content app-content--with-bottom-nav" : "app-content"}>
|
<main className={isMobile ? "app-content app-content--with-bottom-nav" : "app-content"}>
|
||||||
{/* Renders nothing on the web build (native-only check) or once no
|
{/* `UpdateBanner` used to sit here, but the native update check
|
||||||
newer version is known — see UpdateBanner's own doc comment. */}
|
(issue #66) must run session or not — it's mounted app-wide in
|
||||||
<UpdateBanner />
|
`App.tsx` now, as a `position: fixed` strip. */}
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue