Files
Epicnabbo-Catalogus-Updated…/src/app/layout.tsx
T
openhands dc8fb8a6ed feat: speed up admin clone import and enable Cache Components
- Clone import: defer FurnitureData.json writes and append all entries in a
  single batched write instead of one read-modify-write per item, removing
  the main serialization bottleneck for large batches.
- Clone import: raise SSE batch concurrency cap from 5 to 10 and bump the
  clone client/route default from 2 to 6.
- Add a flush hook to runSseBatch so callers can batch deferred work before
  batch_complete is emitted, and surface flush errors as an error event.
- Enable Next.js Cache Components (instant: false opt-out) and silence the
  related build warnings in next.config.ts.
- Switch isomorphic-dompurify to dompurify and refresh dependencies.
2026-08-05 11:10:16 +02:00

114 lines
3.7 KiB
TypeScript

import type { Metadata } from "next";
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";
// TODO: Cache Components adoption. Refactor this route so this opt-out can be removed.
// See: https://nextjs.org/docs/app/guides/migrating-to-cache-components
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" suppressHydrationWarning>
<head>
<meta name="theme-default-dark" content={String(defaultDark)} />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link
rel="preconnect"
href="https://fonts.gstatic.com"
crossOrigin="anonymous"
/>
<link
rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Nunito:wght@400;600;700;800&display=swap"
/>
<link
rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Pixelify+Sans:wght@400;700&display=swap"
/>
<Script
src="/scripts/theme-init.js"
strategy="beforeInteractive"
nonce={nonce}
/>
<link rel="preconnect" href="https://www.habbo.com" />
<link rel="dns-prefetch" 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>
);
}