97 lines
3.1 KiB
TypeScript
97 lines
3.1 KiB
TypeScript
import Image from 'next/image'
|
|
import { notFound } from 'next/navigation'
|
|
|
|
import BackButton from '@/components/client/BackButton'
|
|
import Container from '@/components/server/Container'
|
|
import { Prose } from '@/components/server/Prose'
|
|
import Tag from '@/components/server/Tag'
|
|
import { RenderBlockList } from '@/components/server/renderNotionContent'
|
|
import formatDate from '@/lib/formatDate'
|
|
import generateRssFeed from '@/lib/generateRssFeed'
|
|
import {
|
|
getBlogPostBySlug,
|
|
getFirstBlogPosts,
|
|
} from '@/lib/notion/content/blogPosts'
|
|
import getBlockRecursively from '@/lib/notion/utils/getBlockRecursively'
|
|
import getProxiedAssetUrl from '@/lib/notion/utils/getProxiedAssetUrl'
|
|
import richTextAsPlainText from '@/lib/notion/utils/richTextToPlainText'
|
|
|
|
export const revalidate = 600
|
|
|
|
export async function generateStaticParams() {
|
|
const rssPromise = generateRssFeed()
|
|
|
|
const blogPosts = await getFirstBlogPosts()
|
|
|
|
const paths = blogPosts.results
|
|
.filter(({ properties: { Slug } }) => Slug.rich_text.length === 0)
|
|
.map(({ properties: { Slug } }) => ({
|
|
slug: richTextAsPlainText(Slug.rich_text),
|
|
}))
|
|
|
|
await rssPromise
|
|
|
|
return paths
|
|
}
|
|
|
|
export default async function BlogPost({
|
|
params,
|
|
}: {
|
|
params: { slug: string }
|
|
}) {
|
|
const blogPost = await getBlogPostBySlug({ slug: params.slug })
|
|
|
|
if (!blogPost) {
|
|
notFound()
|
|
}
|
|
|
|
const publishDate =
|
|
blogPost.properties.PublishDate.date?.start ?? blogPost.last_edited_time
|
|
|
|
const content = await getBlockRecursively(blogPost.id)
|
|
|
|
return (
|
|
<Container className="mt-16 lg:mt-32">
|
|
<div className="xl:relative">
|
|
<div className="mx-auto max-w-2xl">
|
|
<BackButton />
|
|
<article>
|
|
<header className="flex flex-col gap-5">
|
|
<h1 className="text-4xl font-bold tracking-tight text-zinc-800 dark:text-zinc-100 sm:text-5xl">
|
|
{richTextAsPlainText(blogPost.properties.Name.title)}
|
|
</h1>
|
|
<div className="z-10 mt-2 flex gap-1 text-sm">
|
|
{blogPost.properties.Topics.multi_select.map((topic) => (
|
|
<Tag key={topic.name} notionColorName={topic.color}>
|
|
{topic.name}
|
|
</Tag>
|
|
))}
|
|
</div>
|
|
<Image
|
|
src={getProxiedAssetUrl({
|
|
pageId: blogPost.id,
|
|
lastEditedTime: blogPost.last_edited_time,
|
|
})}
|
|
alt=""
|
|
width="9999"
|
|
height="9999"
|
|
className="aspect-[6/3] rounded-t-lg object-cover"
|
|
/>
|
|
<time
|
|
dateTime={publishDate}
|
|
className="flex items-center text-base text-zinc-400 dark:text-zinc-500"
|
|
>
|
|
<span className="h-4 w-0.5 rounded-full bg-zinc-200 dark:bg-zinc-500" />
|
|
<span className="ml-3">{formatDate(publishDate)}</span>
|
|
</time>
|
|
</header>
|
|
<Prose className="mt-8 dark:text-white">
|
|
<RenderBlockList blockList={content} />
|
|
</Prose>
|
|
</article>
|
|
</div>
|
|
</div>
|
|
</Container>
|
|
)
|
|
}
|