Files
spsays-training/app/layout.tsx
T

128 lines
4.4 KiB
TypeScript

import type { Metadata } from "next";
import Script from "next/script";
import { Geist, Geist_Mono, Lora, Bricolage_Grotesque } from "next/font/google";
import { ogDefault } from "@/lib/og";
import { ThemeProvider } from "@/components/theme-provider";
import { TooltipProvider } from "@/components/ui/tooltip";
import { AudioPlayerProvider, FloatingPlayer } from "@/components/audio-player";
import { AuthProvider } from "@/components/auth/auth-provider";
import { AuthGateProvider } from "@/components/auth/auth-gate";
import { NotificationsProvider } from "@/components/notifications/notifications-provider";
import { SharePostProvider } from "@/components/social/share-post-provider";
import { Toaster } from "@/components/ui/toaster";
import "./globals.css";
const geistSans = Geist({
variable: "--font-sans",
subsets: ["latin"],
});
const geistMono = Geist_Mono({
variable: "--font-geist-mono",
subsets: ["latin"],
});
// Editorial serif — used for verse text always, and for site titles when
// the user enables "Serif Font" in reading settings.
const lora = Lora({
variable: "--font-serif",
weight: ["400", "500", "600", "700"],
style: ["normal", "italic"],
subsets: ["latin", "latin-ext"],
display: "swap",
});
// Display sans — used for site titles when Serif Font is OFF (the default).
// Bricolage Grotesque has slight humanist warmth so it pairs well with the
// reverent aesthetic without feeling cold or generic.
const bricolage = Bricolage_Grotesque({
variable: "--font-display",
weight: ["400", "500", "600", "700"],
subsets: ["latin"],
display: "swap",
});
export const metadata: Metadata = {
// Resolves all relative OG/Twitter image URLs and canonical links to
// absolute URLs (required for correct social previews + SEO; Next warns
// without it).
metadataBase: new URL(
process.env.NEXT_PUBLIC_SITE_URL || "https://prabhupadasays.org",
),
title: "Prabhupāda Says",
description:
"Browse the books and teachings of His Divine Grace A.C. Bhaktivedanta Swami Prabhupāda",
// Makes the site a standalone Home Screen app on iOS — the prerequisite
// for Web Push on iPhone/iPad (iOS 16.4+). Emits
// <meta name="apple-mobile-web-app-capable" content="yes"> etc.
appleWebApp: {
capable: true,
title: "Prabhupāda Says",
statusBarStyle: "default",
},
// Home Screen / tab icons. The apple-touch-icon is what iOS uses for the
// installed PWA (the prerequisite for Web Push on iPhone/iPad).
icons: {
icon: "/icon-192.png",
apple: "/apple-icon.png",
},
...ogDefault(),
};
/**
* Root layout — html/body shell + global providers + truly global widgets
* (toaster, floating audio player). Per-route chrome lives in the route
* groups beneath this:
* • (app)/ — sidebar, header, bottom nav, reading affordances
* • (legal)/ — header only, minimal chrome
*/
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html
lang="en"
suppressHydrationWarning
className={`${geistSans.variable} ${geistMono.variable} ${lora.variable} ${bricolage.variable} h-full antialiased`}
>
<body className="min-h-full flex flex-col">
<ThemeProvider
attribute="class"
defaultTheme="system"
enableSystem
disableTransitionOnChange
>
<AuthProvider>
<NotificationsProvider>
<AuthGateProvider>
<AudioPlayerProvider>
<TooltipProvider>
<SharePostProvider>
{children}
{/* Toaster and FloatingPlayer are global — toasts and
currently-playing audio survive navigation between
route groups. */}
<FloatingPlayer />
<Toaster />
</SharePostProvider>
</TooltipProvider>
</AudioPlayerProvider>
</AuthGateProvider>
</NotificationsProvider>
</AuthProvider>
</ThemeProvider>
{/* Rybbit analytics — loads after hydration (afterInteractive is
next/script's defer-equivalent). Site-wide via the root layout,
loaded once across navigations. */}
<Script
src="/api/script.js"
data-site-id="1"
strategy="afterInteractive"
/>
</body>
</html>
);
}