133 lines
3.8 KiB
TypeScript

import Link from 'next/link'
import { notFound } from 'next/navigation'
import { CustomMDX } from '@/components/mdx'
import PostHeader from '@/components/blog/PostHeader'
import ProgressBar from '@/components/blog/ProgressBar'
import { getAllPosts, getReadingTime, findAdjacentPosts } from '../utils'
import { baseUrl } from '../../sitemap'
export async function generateStaticParams() {
const posts = await getAllPosts()
return posts.map((post) => ({
slug: post.slug,
}))
}
export async function generateMetadata({ params }: { params: Promise<{ slug: string }> }) {
const { slug } = await params
const post = (await getAllPosts()).find((p) => p.slug === slug)
if (!post) {
return {}
}
const {
title,
publishedAt: publishedTime,
summary: description,
image,
} = post.metadata
const ogImage = image
? image
: `${baseUrl}/og?title=${encodeURIComponent(title)}`
return {
title,
description,
openGraph: {
title,
description,
type: 'article',
publishedTime,
url: `${baseUrl}/blog/${post.slug}`,
images: [{ url: ogImage }],
},
twitter: {
card: 'summary_large_image',
title,
description,
images: [ogImage],
},
}
}
export default async function Blog({ params }: { params: Promise<{ slug: string }> }) {
const { slug } = await params;
const posts = await getAllPosts()
const post = posts.find((p) => p.slug === slug)
if (!post) {
notFound()
}
const reading = getReadingTime(post.content)
const { prev, next } = findAdjacentPosts(posts, slug)
return (
<>
<ProgressBar />
<section className="mx-auto max-w-5xl px-4 md:px-6">
<div className="mb-2">
<Link
href="/blog"
className="inline-flex items-center gap-1 rounded-md px-2 py-1 text-sm text-neutral-700 transition-colors hover:bg-black/[0.04] hover:text-neutral-900 dark:text-neutral-300 dark:hover:bg-white/5 dark:hover:text-neutral-100"
aria-label="Back to blog"
>
<span className="underline-offset-4 hover:underline">Back to blog</span>
</Link>
</div>
<script
type="application/ld+json"
suppressHydrationWarning
dangerouslySetInnerHTML={{
__html: JSON.stringify({
'@context': 'https://schema.org',
'@type': 'BlogPosting',
headline: post.metadata.title,
datePublished: post.metadata.publishedAt,
dateModified: post.metadata.publishedAt,
description: post.metadata.summary,
image: post.metadata.image
? `${baseUrl}${post.metadata.image}`
: `/og?title=${encodeURIComponent(post.metadata.title)}`,
url: `${baseUrl}/blog/${post.slug}`,
author: {
'@type': 'Person',
name: 'My Portfolio',
},
}),
}}
/>
<PostHeader
title={post.metadata.title}
publishedAt={post.metadata.publishedAt}
readingTimeText={reading.text}
tags={post.metadata.tags}
summary={post.metadata.summary}
className="mb-6"
/>
<article className="prose mx-auto max-w-3xl">
<CustomMDX source={post.content} />
</article>
<nav className="mx-auto mt-10 flex max-w-3xl justify-between text-sm">
{prev ? (
<Link href={`/blog/${prev.slug}`} className="underline-offset-4 hover:underline">
{prev.metadata.title}
</Link>
) : (
<span />
)}
{next ? (
<Link href={`/blog/${next.slug}`} className="underline-offset-4 hover:underline">
{next.metadata.title}
</Link>
) : (
<span />
)}
</nav>
</section>
</>
)
}