mirror of
https://github.com/vercel/next.js.git
synced 2026-09-20 02:25:18 +08:00
110 lines
3.5 KiB
TypeScript
110 lines
3.5 KiB
TypeScript
import "../globals.css";
|
|
|
|
import { SpeedInsights } from "@vercel/speed-insights/next";
|
|
import type { Metadata } from "next";
|
|
import {
|
|
VisualEditing,
|
|
toPlainText,
|
|
type PortableTextBlock,
|
|
} from "next-sanity";
|
|
import { Inter } from "next/font/google";
|
|
import { draftMode } from "next/headers";
|
|
|
|
import AlertBanner from "./alert-banner";
|
|
import PortableText from "./portable-text";
|
|
|
|
import * as demo from "@/sanity/lib/demo";
|
|
import { sanityFetch } from "@/sanity/lib/fetch";
|
|
import { settingsQuery } from "@/sanity/lib/queries";
|
|
import { resolveOpenGraphImage } from "@/sanity/lib/utils";
|
|
|
|
export async function generateMetadata(): Promise<Metadata> {
|
|
const settings = await sanityFetch({
|
|
query: settingsQuery,
|
|
// Metadata should never contain stega
|
|
stega: false,
|
|
});
|
|
const title = settings?.title || demo.title;
|
|
const description = settings?.description || demo.description;
|
|
|
|
const ogImage = resolveOpenGraphImage(settings?.ogImage);
|
|
let metadataBase: URL | undefined = undefined;
|
|
try {
|
|
metadataBase = settings?.ogImage?.metadataBase
|
|
? new URL(settings.ogImage.metadataBase)
|
|
: undefined;
|
|
} catch {
|
|
// ignore
|
|
}
|
|
return {
|
|
metadataBase,
|
|
title: {
|
|
template: `%s | ${title}`,
|
|
default: title,
|
|
},
|
|
description: toPlainText(description),
|
|
openGraph: {
|
|
images: ogImage ? [ogImage] : [],
|
|
},
|
|
};
|
|
}
|
|
|
|
const inter = Inter({
|
|
variable: "--font-inter",
|
|
subsets: ["latin"],
|
|
display: "swap",
|
|
});
|
|
|
|
export default async function RootLayout({
|
|
children,
|
|
}: {
|
|
children: React.ReactNode;
|
|
}) {
|
|
const data = await sanityFetch({ query: settingsQuery });
|
|
const footer = data?.footer || [];
|
|
const { isEnabled: isDraftMode } = await draftMode();
|
|
|
|
return (
|
|
<html lang="en" className={`${inter.variable} bg-white text-black`}>
|
|
<body>
|
|
<section className="min-h-screen">
|
|
{isDraftMode && <AlertBanner />}
|
|
<main>{children}</main>
|
|
<footer className="bg-accent-1 border-accent-2 border-t">
|
|
<div className="container mx-auto px-5">
|
|
{footer.length > 0 ? (
|
|
<PortableText
|
|
className="prose-sm text-pretty bottom-0 w-full max-w-none bg-white py-12 text-center md:py-20"
|
|
value={footer as PortableTextBlock[]}
|
|
/>
|
|
) : (
|
|
<div className="flex flex-col items-center py-28 lg:flex-row">
|
|
<h3 className="mb-10 text-center text-4xl font-bold leading-tight tracking-tighter lg:mb-0 lg:w-1/2 lg:pr-4 lg:text-left lg:text-5xl">
|
|
Built with Next.js.
|
|
</h3>
|
|
<div className="flex flex-col items-center justify-center lg:w-1/2 lg:flex-row lg:pl-4">
|
|
<a
|
|
href="https://nextjs.org/docs"
|
|
className="mx-3 mb-6 border border-black bg-black py-3 px-12 font-bold text-white transition-colors duration-200 hover:bg-white hover:text-black lg:mb-0 lg:px-8"
|
|
>
|
|
Read Documentation
|
|
</a>
|
|
<a
|
|
href="https://github.com/vercel/next.js/tree/canary/examples/cms-sanity"
|
|
className="mx-3 font-bold hover:underline"
|
|
>
|
|
View on GitHub
|
|
</a>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</footer>
|
|
</section>
|
|
{isDraftMode && <VisualEditing />}
|
|
<SpeedInsights />
|
|
</body>
|
|
</html>
|
|
);
|
|
}
|