Switch to new Metadata API

This commit is contained in:
Max Leiter 2023-02-24 18:07:40 -08:00
parent afd18d19a9
commit 6d184906b1
21 changed files with 258 additions and 209 deletions

View file

@ -1,5 +0,0 @@
import PageSeo from "@components/page-seo"
export default function AuthHead() {
return <PageSeo title="Sign In" />
}

View file

@ -1,3 +1,4 @@
import { getMetadata } from "src/app/lib/metadata"
import config from "@lib/config"
import Auth from "../components"
@ -10,3 +11,7 @@ function isGithubEnabled() {
export default function SignInPage() {
return <Auth page="signin" isGithubEnabled={isGithubEnabled()} />
}
export const metadata = getMetadata({
title: "Sign in",
})

View file

@ -1,5 +0,0 @@
import PageSeo from "@components/page-seo"
export default function AuthHead() {
return <PageSeo title="Sign Up" />
}

View file

@ -1,6 +1,7 @@
import Auth from "../components"
import { getRequiresPasscode } from "src/pages/api/auth/requires-passcode"
import config from "@lib/config"
import { getMetadata } from "src/app/lib/metadata"
async function getPasscode() {
return await getRequiresPasscode()
@ -22,3 +23,7 @@ export default async function SignUpPage() {
/>
)
}
export const metadata = getMetadata({
title: "Sign up",
})

View file

@ -1,6 +1,5 @@
"use client"
import Note from "@components/note"
import { getMetadata } from "src/app/lib/metadata"
export default function ExpiredPage() {
return (
@ -9,3 +8,8 @@ export default function ExpiredPage() {
</Note>
)
}
export const metadata = getMetadata({
title: "Post expired",
hidden: true
})

View file

@ -1,5 +0,0 @@
import PageSeo from "@components/page-seo"
export default function NewPostHead() {
return <PageSeo title="New" />
}

View file

@ -1,3 +1,4 @@
import { getMetadata } from "src/app/lib/metadata"
import NewPost from "src/app/(drift)/(posts)/new/components/new"
import "./react-datepicker.css"
@ -6,3 +7,8 @@ export default function New() {
}
export const dynamic = "force-static"
export const metadata = getMetadata({
title: "New post",
hidden: true
})

View file

@ -1,29 +0,0 @@
import PageSeo from "@components/page-seo"
import { getPostById } from "@lib/server/prisma"
export default async function Head({
params
}: {
params: {
id: string
}
}) {
const post = await getPostById(params.id, {
select: {
title: true,
description: true
}
})
if (!post) {
return null
}
return (
<PageSeo
title={post.title}
description={post.description || undefined}
isPrivate={false}
/>
)
}

View file

@ -1,4 +1,5 @@
import VisibilityControl from "@components/badges/visibility-control"
import { getMetadata } from "src/app/lib/metadata"
import {
PostWithFilesAndAuthor,
serverPostToClientPost,
@ -28,3 +29,28 @@ export default async function PostPage({
</>
)
}
export const generateMetadata = async ({
params
}: {
params: {
id: string
}
}) => {
const post = (await getPost(params.id)) as ServerPostWithFilesAndAuthor
return getMetadata({
title: post.title,
description: post.description || undefined,
hidden: post.visibility === "public",
overrides: {
openGraph: {
title: post.title,
description: post.description || undefined,
type: "website",
siteName: "Drift",
// TODO: og images
}
}
})
}

View file

@ -1,3 +1,4 @@
import { getMetadata } from "src/app/lib/metadata"
import { getCurrentUser } from "@lib/server/session"
import { redirect } from "next/navigation"
@ -15,3 +16,9 @@ export default async function AdminLayout({
return children
}
export const metadata = getMetadata({
title: "Admin",
hidden: true
})

View file

@ -1,5 +0,0 @@
import PageSeo from "@components/page-seo"
export default function RootHead() {
return <PageSeo />
}

View file

@ -4,6 +4,8 @@ import Layout from "@components/layout"
import { Toasts } from "@components/toasts"
import Header from "@components/header"
import { Inter } from "next/font/google"
import type { Metadata } from 'next'
import { getMetadata } from "src/app/lib/metadata"
const inter = Inter({ subsets: ["latin"], variable: "--inter-font" })
@ -28,3 +30,5 @@ export default async function RootLayout({
</html>
)
}
export const metadata = getMetadata()

View file

@ -1,5 +0,0 @@
import PageSeo from "@components/page-seo"
export default function Head() {
return <PageSeo title="Your profile" isPrivate />
}

View file

@ -5,6 +5,7 @@ import { getCurrentUser } from "@lib/server/session"
import { authOptions } from "@lib/server/auth"
import { Suspense } from "react"
import ErrorBoundary from "@components/error/fallback"
import { getMetadata } from "src/app/lib/metadata"
export default async function Mine() {
const userId = (await getCurrentUser())?.id
@ -29,3 +30,8 @@ export default async function Mine() {
}
export const revalidate = 0
export const metadata = getMetadata({
title: "Your profile",
hidden: true
})

View file

@ -1,5 +0,0 @@
import PageSeo from "@components/page-seo"
export default function Head() {
return <PageSeo title="Settings" isPrivate />
}

View file

@ -1,3 +1,4 @@
import { getMetadata } from "src/app/lib/metadata"
import SettingsGroup from "../../components/settings-group"
import APIKeys from "./components/sections/api-keys"
import Profile from "./components/sections/profile"
@ -14,3 +15,8 @@ export default async function SettingsPage() {
</>
)
}
export const metadata = getMetadata({
title: "Settings",
hidden: true
})

View file

@ -1,89 +0,0 @@
import config from "@lib/config"
import React from "react"
type PageSeoProps = {
title?: string
description?: string
isLoading?: boolean
isPrivate?: boolean
}
const PageSeo = ({
title: pageTitle,
description = "A self-hostable clone of GitHub Gist",
isPrivate = false
}: PageSeoProps) => {
const title = `Drift${pageTitle ? ` - ${pageTitle}` : ""}`
return (
<>
<title>{title}</title>
<meta charSet="utf-8" />
{!isPrivate && <meta name="description" content={description} />}
{isPrivate && <meta name="robots" content="noindex" />}
{/* TODO: verify the correct meta tags */}
<meta
name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no"
/>
<ThemeAndIcons />
<URLs />
</>
)
}
export default PageSeo
const ThemeAndIcons = () => (
<>
<link
rel="apple-touch-icon"
sizes="180x180"
href="/assets/apple-touch-icon.png"
/>
<link
rel="icon"
type="image/png"
sizes="32x32"
href="/assets/favicon-32x32.png"
/>
<link
rel="icon"
type="image/png"
sizes="16x16"
href="/assets/favicon-16x16.png"
/>
<link rel="manifest" href="/site.webmanifest" />
<link
rel="mask-icon"
href="/assets/safari-pinned-tab.svg"
color="#5bbad5"
/>
<meta name="apple-mobile-web-app-title" content="Drift" />
<meta name="application-name" content="Drift" />
<meta name="msapplication-TileColor" content="#da532c" />
<meta
name="theme-color"
content="#ffffff"
media="(prefers-color-scheme: light)"
/>
<meta
name="theme-color"
content="#000"
media="(prefers-color-scheme: dark)"
/>
</>
)
const URLs = () => (
<>
<meta property="twitter:card" content="summary_large_image" />
<meta property="twitter:url" content={config.url} />
{/* TODO: OG image */}
<meta property="twitter:image" content={`${config.url}/assets/og.png`} />
<meta property="twitter:site" content="@" />
<meta property="twitter:creator" content="@drift" />
<meta property="og:type" content="website" />
<meta property="og:url" content={config.url} />
</>
)

View file

@ -99,6 +99,7 @@ const ListItem = ({
<Button
iconRight={<ArrowUpCircle />}
onClick={viewParentClick}
// TODO: not perfect on mobile
height={38}
/>
</Tooltip>

127
src/app/lib/metadata.tsx Normal file
View file

@ -0,0 +1,127 @@
import config from "@lib/config"
import { Metadata } from "next"
import React from "react"
type PageSeoProps = {
title?: string
description?: string
isLoading?: boolean
isPrivate?: boolean
}
const PageSeo = ({
title: pageTitle,
description = "A self-hostable clone of GitHub Gist",
isPrivate = false
}: PageSeoProps) => {
const title = `Drift${pageTitle ? ` - ${pageTitle}` : ""}`
return (
<>
<title>{title}</title>
<meta charSet="utf-8" />
{!isPrivate && <meta name="description" content={description} />}
{isPrivate && <meta name="robots" content="noindex" />}
<ThemeAndIcons />
</>
)
}
export default PageSeo
const ThemeAndIcons = () => (
<>
<link />
<meta name="apple-mobile-web-app-title" content="Drift" />
<meta name="application-name" content="Drift" />
<meta
name="theme-color"
content="#ffffff"
media="(prefers-color-scheme: light)"
/>
<meta
name="theme-color"
content="#000"
media="(prefers-color-scheme: dark)"
/>
</>
)
export function getMetadata({
title,
description = "A self-hostable clone of GitHub Gist",
hidden = false,
overrides
}: {
overrides?: Metadata
title?: string
description?: string
hidden?: boolean
} = {}): Metadata {
function undefinedIfHidden<T>(value: T): T | undefined {
return hidden ? undefined : value
}
title = `Drift${title ? ` - ${title}` : ""}`
return {
title,
description: undefinedIfHidden(description),
themeColor: "#000",
manifest: "/site.webmanifest",
viewport: "width=device-width, initial-scale=1, shrink-to-fit=no",
robots: hidden ? "noindex" : undefined,
twitter: undefinedIfHidden({
card: "summary_large_image",
title,
description,
...overrides?.twitter
}),
applicationName: "Drift",
icons: [
{
rel: "icon",
sizes: "32x32",
type: "image/png",
url: "/assets/favicon-32x32.png"
},
{
rel: "apple-touch-icon",
sizes: "180x180",
url: "/assets/apple-touch-icon.png"
},
{
rel: "icon",
sizes: "16x16",
url: "/assets/favicon-16x16.png"
},
{
rel: "mask-icon",
url: "/assets/safari-pinned-tab.svg"
// TODO: re-enable this when we have a better color
// color: "#5bbad5"
}
],
openGraph: undefinedIfHidden({
type: "website",
url: config.url,
siteName: "Drift",
title,
description,
...overrides?.openGraph
}),
keywords: undefinedIfHidden([
"gist",
"github",
"drift",
"next.js",
"self-hosted",
"paste",
"pastebin",
"clone",
"code",
"snippet"
]),
...overrides
}
}