Compare commits

..

No commits in common. "c17b5b0060b1cef9e01d06382d310ac117bbfca7" and "99d7badddf497d8edf2cfc0474a07fd3eaf57daa" have entirely different histories.

3 changed files with 105 additions and 124 deletions

View file

@ -1,7 +1,6 @@
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";
@ -72,17 +71,9 @@ 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 (
<>
<UpdateBanner />
<Routes> <Routes>
<Route <Route
element={ element={
@ -186,6 +177,5 @@ export function App() {
</Route> </Route>
<Route path="*" element={<Navigate to="/" replace />} /> <Route path="*" element={<Navigate to="/" replace />} />
</Routes> </Routes>
</>
); );
} }

View file

@ -1,28 +1,18 @@
// Styles for UpdateBanner — a discreet, dismissible strip pinned to the top // Styles for UpdateBanner — a discreet, dismissible strip above the routed
// of the screen. Mounted app-wide in App.tsx (outside `<Routes>`) so the // page content (see AppLayout.tsx). Uses --color-warning the same way
// native update check runs session or not, hence `position: fixed` rather // source-item-preview__hint does elsewhere in the app: "worth noticing",
// 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-bottom: 1px solid var(--color-warning); border: 1px solid var(--color-warning);
border-radius: var(--radius-base);
font-size: var(--font-size-sm); font-size: var(--font-size-sm);
&__text { &__text {

View file

@ -2,6 +2,7 @@ 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";
@ -147,9 +148,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"}>
{/* `UpdateBanner` used to sit here, but the native update check {/* Renders nothing on the web build (native-only check) or once no
(issue #66) must run session or not — it's mounted app-wide in newer version is known — see UpdateBanner's own doc comment. */}
`App.tsx` now, as a `position: fixed` strip. */} <UpdateBanner />
<Outlet /> <Outlet />
</main> </main>