Files
Epicnabbo-Catalogus-Updated…/src/app/layout.tsx
T
openhands 7a41775c7e fix: disable route prefetching app-wide to avoid spurious requests
Wrap next/link in a shared Link component that ships prefetch=false by
default, so no route is ever prefetched (viewport or hover) anymore, and
drop the DNS prefetch hint. Removes hidden background requests that were
the source of intermittent issues.
2026-08-29 19:27:13 +02:00

120 lines
3.7 KiB
TypeScript

import type { Metadata } from "next";
import { Nunito, Pixelify_Sans } from "next/font/google";
import { headers } from "next/headers";
import Script from "next/script";
import { NextIntlClientProvider } from "next-intl";
import { getLocale, getMessages } from "next-intl/server";
import type { ReactNode } from "react";
import { Toaster } from "sonner";
import { PwaRegister } from "@/components/pwa-register";
import { SmoothScroll } from "@/components/smooth-scroll";
import { ThemeVars } from "@/components/theme-vars";
import { enforceSiteAccess } from "@/lib/access-guard";
import { resolveHotelName } from "@/lib/hotel-name";
import { siteSettings } from "@/lib/services/site-settings";
import "./globals.css";
const nunito = Nunito({
subsets: ["latin"],
weight: ["400", "600", "700", "800"],
variable: "--font-nunito",
display: "swap",
});
const pixelify = Pixelify_Sans({
subsets: ["latin"],
weight: ["400", "700"],
variable: "--font-pixel",
display: "swap",
});
// The root layout reads runtime data (locale cookie, request headers via
// enforceSiteAccess, DB-backed site settings) to render the <html> element,
// which cannot be wrapped in <Suspense>. Child pages inherit this opt-out,
// so leaf pages that only access cached/DB data stay instant.
// export const instant = false;
export async function generateMetadata(): Promise<Metadata> {
const appUrl = process.env.APP_URL ?? "http://localhost:3000";
const hotel = await resolveHotelName();
const customFavicon = await siteSettings.get("cms_favicon");
const faviconUrl = customFavicon ?? "/favicon.svg";
const icons = [
{ rel: "icon", url: faviconUrl },
{ rel: "shortcut icon", url: faviconUrl },
];
return {
title: { default: `${hotel} - Hotel`, template: `%s · ${hotel}` },
description: `${hotel} — a Habbo retro hotel. Hang out, collect furni and meet friends.`,
metadataBase: new URL(appUrl),
alternates: { canonical: appUrl },
icons,
openGraph: {
title: `${hotel} - Hotel`,
description: `${hotel} — a Habbo retro hotel. Hang out, collect furni and meet friends.`,
type: "website",
siteName: hotel,
},
twitter: { card: "summary_large_image", title: `${hotel} - Hotel` },
};
}
export default async function RootLayout({
children,
}: {
children: ReactNode;
}) {
await enforceSiteAccess();
const locale = await getLocale();
const messages = await getMessages();
const defaultDark = await siteSettings.getBool("default_dark", false);
const nitroUrl = await siteSettings.get("nitro_client_url", "");
const nonce = (await headers()).get("x-nonce") ?? undefined;
return (
<html
lang={locale}
className={`app ${nunito.variable} ${pixelify.variable}`}
suppressHydrationWarning
>
<head>
<meta name="theme-default-dark" content={String(defaultDark)} />
<Script
src="/scripts/theme-init.js"
strategy="beforeInteractive"
nonce={nonce}
/>
<link rel="preconnect" href="https://www.habbo.com" />
{nitroUrl?.startsWith("http") ? (
<link rel="preconnect" href={new URL(nitroUrl).origin} />
) : null}
</head>
<body
className="flex min-h-screen flex-col site-bg"
style={{ backgroundColor: "var(--color-background)" }}
>
<NextIntlClientProvider locale={locale} messages={messages}>
<ThemeVars nonce={nonce} />
{children}
<PwaRegister />
<SmoothScroll />
<Toaster
position="top-right"
richColors
closeButton
toastOptions={{
style: {
background: "var(--color-surface)",
color: "var(--color-text-readable)",
border:
"1px solid color-mix(in srgb, var(--color-text-muted) 12%, transparent)",
},
}}
/>
</NextIntlClientProvider>
</body>
</html>
);
}