mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 04:32:42 +00:00
fix(auth): unblock the about page for unauthenticated visitors
The /about page (GeoLite2 attribution) was listed in proxy.ts PUBLIC_PATHS but missed in AuthShell's client-side PUBLIC_PATHS, so unauthenticated visitors bounced through /auth/login. Add /about to the AuthShell list and treat it as a standalone public page (same edge-to-edge render as /privacy). Apply safe-area-inset-top on the privacy and about sticky headers so iOS notch / Dynamic Island viewports don't overlap the brand row. Add a default-closed Contents <details> TOC at the top of the privacy policy and wrap every HealthKit identifier <code> with break-all so long camelCase strings (heartRateVariabilitySDNN, environmentalAudioExposure) wrap inside narrow viewports.
This commit is contained in:
1 parent
c77d5252a6
commit
fc8a58557a
3 files changed
+133
-23
No files matched your search
@@ -56,7 +56,7 @@ function Section({
|
||||
export default function AboutPage() {
|
||||
return (
|
||||
<div className="bg-background text-foreground min-h-dvh">
|
||||
<header className="border-border/60 bg-background/80 sticky top-0 z-10 border-b backdrop-blur">
|
||||
<header className="border-border/60 bg-background/80 sticky top-0 z-10 border-b backdrop-blur pt-[env(safe-area-inset-top)]">
|
||||
<div className="mx-auto flex max-w-3xl items-center justify-between px-4 py-3 md:px-6">
|
||||
<Link
|
||||
href="/"
|
||||
|
||||
+116
-20
@@ -107,7 +107,7 @@ export default function PrivacyPage() {
|
||||
{/* Top bar — visible home-link so an App-Store reviewer can confirm
|
||||
the policy belongs to the same brand as the iOS app. Public; no
|
||||
nav menu (this page is reachable without a session). */}
|
||||
<header className="border-border/60 bg-background/80 sticky top-0 z-10 border-b backdrop-blur">
|
||||
<header className="border-border/60 bg-background/80 sticky top-0 z-10 border-b backdrop-blur pt-[env(safe-area-inset-top)]">
|
||||
<div className="mx-auto flex max-w-3xl items-center justify-between px-4 py-3 md:px-6">
|
||||
<Link
|
||||
href="/"
|
||||
@@ -143,6 +143,102 @@ export default function PrivacyPage() {
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* v1.4.27 MB6 — collapsible TOC. Default-closed on every
|
||||
viewport so the policy's first paragraphs reach the fold
|
||||
on `<sm`; an open summary on a wide screen costs nothing,
|
||||
and the reader can flip it open to skim the section list. */}
|
||||
<details
|
||||
className="border-border/60 bg-card/40 group rounded-md border"
|
||||
data-slot="privacy-toc"
|
||||
>
|
||||
<summary className="text-foreground hover:bg-muted/50 cursor-pointer list-none rounded-md px-4 py-3 text-sm font-medium select-none">
|
||||
Contents
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className="text-muted-foreground ml-2 inline-block transition-transform group-open:rotate-90"
|
||||
>
|
||||
›
|
||||
</span>
|
||||
</summary>
|
||||
<nav
|
||||
aria-label="Privacy policy contents"
|
||||
className="border-border/60 border-t px-4 py-3"
|
||||
>
|
||||
<ol className="text-muted-foreground space-y-1.5 text-sm leading-relaxed">
|
||||
<li>
|
||||
<a className="hover:text-foreground hover:underline" href="#intro">
|
||||
1. Overview
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
className="hover:text-foreground hover:underline"
|
||||
href="#data-we-collect"
|
||||
>
|
||||
2. Data we collect
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a className="hover:text-foreground hover:underline" href="#purpose">
|
||||
3. Why we collect each category
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
className="hover:text-foreground hover:underline"
|
||||
href="#sub-processors"
|
||||
>
|
||||
4. Third-party sub-processors
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a className="hover:text-foreground hover:underline" href="#storage">
|
||||
5. Data storage and retention
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a className="hover:text-foreground hover:underline" href="#rights">
|
||||
6. Your rights (GDPR Art. 15-22, DSGVO)
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
className="hover:text-foreground hover:underline"
|
||||
href="#medical-boundary"
|
||||
>
|
||||
7. Medical-device boundary (EU MDR 2017/745, MDCG 2021-24)
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
className="hover:text-foreground hover:underline"
|
||||
href="#apple-categories"
|
||||
>
|
||||
8. Apple App Store privacy categories
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
className="hover:text-foreground hover:underline"
|
||||
href="#children"
|
||||
>
|
||||
9. Children
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a className="hover:text-foreground hover:underline" href="#changes">
|
||||
10. Changes to this policy
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a className="hover:text-foreground hover:underline" href="#contact">
|
||||
11. Contact
|
||||
</a>
|
||||
</li>
|
||||
</ol>
|
||||
</nav>
|
||||
</details>
|
||||
|
||||
<Section id="intro" title="1. Overview">
|
||||
<p>
|
||||
HealthLog is an open-source, self-hostable personal-health-tracking
|
||||
@@ -288,64 +384,64 @@ export default function PrivacyPage() {
|
||||
data-slot="privacy-hk-quantity"
|
||||
>
|
||||
<li>
|
||||
<code className="text-xs">bodyMass</code>
|
||||
<code className="text-xs break-all">bodyMass</code>
|
||||
</li>
|
||||
<li>
|
||||
<code className="text-xs">bodyFatPercentage</code>
|
||||
<code className="text-xs break-all">bodyFatPercentage</code>
|
||||
</li>
|
||||
<li>
|
||||
<code className="text-xs">bodyTemperature</code>
|
||||
<code className="text-xs break-all">bodyTemperature</code>
|
||||
</li>
|
||||
<li>
|
||||
<code className="text-xs">bloodPressureSystolic</code>
|
||||
<code className="text-xs break-all">bloodPressureSystolic</code>
|
||||
</li>
|
||||
<li>
|
||||
<code className="text-xs">bloodPressureDiastolic</code>
|
||||
<code className="text-xs break-all">bloodPressureDiastolic</code>
|
||||
</li>
|
||||
<li>
|
||||
<code className="text-xs">bloodGlucose</code>
|
||||
<code className="text-xs break-all">bloodGlucose</code>
|
||||
</li>
|
||||
<li>
|
||||
<code className="text-xs">oxygenSaturation</code>
|
||||
<code className="text-xs break-all">oxygenSaturation</code>
|
||||
</li>
|
||||
<li>
|
||||
<code className="text-xs">heartRate</code>
|
||||
<code className="text-xs break-all">heartRate</code>
|
||||
</li>
|
||||
<li>
|
||||
<code className="text-xs">restingHeartRate</code>
|
||||
<code className="text-xs break-all">restingHeartRate</code>
|
||||
</li>
|
||||
<li>
|
||||
<code className="text-xs">heartRateVariabilitySDNN</code>
|
||||
<code className="text-xs break-all">heartRateVariabilitySDNN</code>
|
||||
</li>
|
||||
<li>
|
||||
<code className="text-xs">vo2Max</code>
|
||||
<code className="text-xs break-all">vo2Max</code>
|
||||
</li>
|
||||
<li>
|
||||
<code className="text-xs">stepCount</code>
|
||||
<code className="text-xs break-all">stepCount</code>
|
||||
</li>
|
||||
<li>
|
||||
<code className="text-xs">activeEnergyBurned</code>
|
||||
<code className="text-xs break-all">activeEnergyBurned</code>
|
||||
</li>
|
||||
<li>
|
||||
<code className="text-xs">flightsClimbed</code>
|
||||
<code className="text-xs break-all">flightsClimbed</code>
|
||||
</li>
|
||||
<li>
|
||||
<code className="text-xs">distanceWalkingRunning</code>
|
||||
<code className="text-xs break-all">distanceWalkingRunning</code>
|
||||
</li>
|
||||
<li>
|
||||
<code className="text-xs">environmentalAudioExposure</code>
|
||||
<code className="text-xs break-all">environmentalAudioExposure</code>
|
||||
</li>
|
||||
<li>
|
||||
<code className="text-xs">headphoneAudioExposure</code>
|
||||
<code className="text-xs break-all">headphoneAudioExposure</code>
|
||||
</li>
|
||||
<li>
|
||||
<code className="text-xs">timeInDaylight</code> (iOS 17 and later)
|
||||
<code className="text-xs break-all">timeInDaylight</code> (iOS 17 and later)
|
||||
</li>
|
||||
</ul>
|
||||
<p className="font-medium">HKCategoryTypeIdentifier:</p>
|
||||
<ul className="list-disc space-y-1 pl-5">
|
||||
<li>
|
||||
<code className="text-xs">sleepAnalysis</code> — read with full
|
||||
<code className="text-xs break-all">sleepAnalysis</code> — read with full
|
||||
per-stage granularity (Awake, REM, Core, Deep). Stages are stored
|
||||
server-side as separate rows so the chart can stack them.
|
||||
</li>
|
||||
|
||||
@@ -11,7 +11,18 @@ import { BottomNav } from "./bottom-nav";
|
||||
import { SidebarNav } from "./sidebar-nav";
|
||||
import { TopBar } from "./top-bar";
|
||||
|
||||
const PUBLIC_PATHS = ["/auth/login", "/auth/register", "/privacy"];
|
||||
// v1.4.27 MB6 — `/about` joins the public-path list so the GeoLite2
|
||||
// CC BY-SA 4.0 attribution stays reachable for unauthenticated
|
||||
// visitors. The `/about` route is already registered in `proxy.ts`
|
||||
// PUBLIC_PATHS; this constant gates the client-side auth shell, and
|
||||
// both lists must agree or the route round-trips through the
|
||||
// sign-in redirect.
|
||||
const PUBLIC_PATHS = [
|
||||
"/auth/login",
|
||||
"/auth/register",
|
||||
"/privacy",
|
||||
"/about",
|
||||
];
|
||||
|
||||
export function AuthShell({ children }: { children: React.ReactNode }) {
|
||||
const { user, isAuthenticated, isLoading } = useAuth();
|
||||
@@ -25,7 +36,10 @@ export function AuthShell({ children }: { children: React.ReactNode }) {
|
||||
// squash a 3000-word policy into a "login card" layout, so we let it
|
||||
// render edge-to-edge while still resolving as a public page for the
|
||||
// auth gate above.
|
||||
const isStandalonePublicPage = pathname.startsWith("/privacy");
|
||||
// v1.4.27 MB6 — `/about` follows the same shape (own header, own
|
||||
// footer, full-width body), so it joins the standalone list.
|
||||
const isStandalonePublicPage =
|
||||
pathname.startsWith("/privacy") || pathname.startsWith("/about");
|
||||
const isAdminPage = pathname.startsWith("/admin");
|
||||
const isOnboardingPage = pathname === "/onboarding";
|
||||
const showUnlockNotifier = isAuthenticated && !isPublicPage && !!user?.id;
|
||||
|
||||
Reference in new issue
Block a user