add tappable PWA install banner, tab bar safe area for notched devices
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -3,7 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
|
||||||
<link rel="manifest" href="/manifest.webmanifest" />
|
<link rel="manifest" href="/manifest.webmanifest" />
|
||||||
<meta name="theme-color" content="#09090b" />
|
<meta name="theme-color" content="#09090b" />
|
||||||
<title>TherapyFinder</title>
|
<title>TherapyFinder</title>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { dbExec } from "@/shared/hooks/use-db";
|
|||||||
import { GKV_KASSEN } from "@/shared/lib/gkv-kassen";
|
import { GKV_KASSEN } from "@/shared/lib/gkv-kassen";
|
||||||
import { cn } from "@/shared/lib/utils";
|
import { cn } from "@/shared/lib/utils";
|
||||||
|
|
||||||
function detectPlatform(): "ios" | "android" | "unknown" {
|
export function detectPlatform(): "ios" | "android" | "unknown" {
|
||||||
const ua = navigator.userAgent;
|
const ua = navigator.userAgent;
|
||||||
if (/iPad|iPhone|iPod/.test(ua)) return "ios";
|
if (/iPad|iPhone|iPod/.test(ua)) return "ios";
|
||||||
if (/Android/.test(ua)) return "android";
|
if (/Android/.test(ua)) return "android";
|
||||||
|
|||||||
@@ -4,9 +4,19 @@ import {
|
|||||||
Outlet,
|
Outlet,
|
||||||
useMatchRoute,
|
useMatchRoute,
|
||||||
} from "@tanstack/react-router";
|
} from "@tanstack/react-router";
|
||||||
import { ListChecks, Settings, Users } from "lucide-react";
|
import {
|
||||||
import { useMemo } from "react";
|
ChevronDown,
|
||||||
import { isInstalledPwa } from "@/features/onboarding/components/onboarding-form";
|
ChevronUp,
|
||||||
|
ListChecks,
|
||||||
|
Settings,
|
||||||
|
Share,
|
||||||
|
Users,
|
||||||
|
} from "lucide-react";
|
||||||
|
import { useMemo, useState } from "react";
|
||||||
|
import {
|
||||||
|
detectPlatform,
|
||||||
|
isInstalledPwa,
|
||||||
|
} from "@/features/onboarding/components/onboarding-form";
|
||||||
|
|
||||||
export const Route = createRootRoute({
|
export const Route = createRootRoute({
|
||||||
component: () => {
|
component: () => {
|
||||||
@@ -18,17 +28,12 @@ export const Route = createRootRoute({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex min-h-screen flex-col bg-background text-foreground">
|
<div className="flex min-h-screen flex-col bg-background text-foreground">
|
||||||
{!hideNav && !installed && (
|
{!hideNav && !installed && <InstallBanner />}
|
||||||
<div className="bg-amber-500 px-4 py-2 text-center text-sm font-medium text-white dark:bg-amber-600">
|
|
||||||
App nicht installiert — füge TherapyFinder zum Home-Bildschirm
|
|
||||||
hinzu, um Datenverlust zu vermeiden.
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<main className="mx-auto w-full max-w-2xl flex-1 px-4 pb-20 pt-6">
|
<main className="mx-auto w-full max-w-2xl flex-1 px-4 pb-20 pt-6">
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</main>
|
</main>
|
||||||
{!hideNav && (
|
{!hideNav && (
|
||||||
<nav className="fixed inset-x-0 bottom-0 border-t bg-background">
|
<nav className="fixed inset-x-0 bottom-0 border-t bg-background pb-[env(safe-area-inset-bottom)]">
|
||||||
<div className="mx-auto flex max-w-2xl">
|
<div className="mx-auto flex max-w-2xl">
|
||||||
<Link
|
<Link
|
||||||
to="/prozess"
|
to="/prozess"
|
||||||
@@ -58,3 +63,64 @@ export const Route = createRootRoute({
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function InstallBanner() {
|
||||||
|
const [expanded, setExpanded] = useState(false);
|
||||||
|
const platform = useMemo(() => detectPlatform(), []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="bg-amber-500 dark:bg-amber-600">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setExpanded(!expanded)}
|
||||||
|
className="flex w-full items-center justify-center gap-2 px-4 py-2 text-center text-sm font-medium text-white"
|
||||||
|
>
|
||||||
|
{expanded
|
||||||
|
? "Anleitung ausblenden"
|
||||||
|
: "Hier tippen für Installationsanleitung"}
|
||||||
|
{expanded ? (
|
||||||
|
<ChevronUp className="size-4" />
|
||||||
|
) : (
|
||||||
|
<ChevronDown className="size-4" />
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
{expanded && (
|
||||||
|
<div className="space-y-3 px-4 pb-4 text-white">
|
||||||
|
<p className="text-sm">
|
||||||
|
Installiere TherapyFinder als App, damit deine Daten sicher
|
||||||
|
gespeichert bleiben — auch offline.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{platform === "ios" && (
|
||||||
|
<ol className="list-inside list-decimal space-y-1 text-sm">
|
||||||
|
<li>
|
||||||
|
Tippe unten auf das Teilen-Symbol{" "}
|
||||||
|
<Share className="inline size-4 align-text-bottom" />
|
||||||
|
</li>
|
||||||
|
<li>Wähle „Zum Home-Bildschirm"</li>
|
||||||
|
<li>Tippe auf „Hinzufügen"</li>
|
||||||
|
</ol>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{platform === "android" && (
|
||||||
|
<ol className="list-inside list-decimal space-y-1 text-sm">
|
||||||
|
<li>
|
||||||
|
Tippe auf das Menü{" "}
|
||||||
|
<span className="font-mono font-bold">⋮</span> oben rechts
|
||||||
|
</li>
|
||||||
|
<li>Wähle „Zum Startbildschirm hinzufügen"</li>
|
||||||
|
<li>Bestätige mit „Hinzufügen"</li>
|
||||||
|
</ol>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{platform === "unknown" && (
|
||||||
|
<p className="text-sm">
|
||||||
|
Nutze die „Zum Home-Bildschirm hinzufügen"-Funktion deines
|
||||||
|
Browsers.
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user