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.
120 lines
3.7 KiB
TypeScript
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>
|
|
);
|
|
}
|