Files
lixiyu-net/pages/blog/[id].tsx

155 lines
5.3 KiB
TypeScript
Raw Normal View History

2024-08-16 10:41:36 +08:00
import { DrawerTOC } from "@/components/reader-page/DrawerTOC";
import { MorePostLinks } from "@/components/reader-page/MorePostLinks";
2024-08-16 10:41:36 +08:00
import { PostComments } from "@/components/reader-page/PostComments";
import { PostCover } from "@/components/reader-page/PostCover";
import { PostRender } from "@/components/reader-page/PostRender";
2024-08-16 10:41:36 +08:00
import { ShareButtons } from "@/components/reader-page/ShareButtons";
import { Separator } from "@/components/ui/separator";
2023-12-25 17:21:39 +08:00
import { Toaster } from "@/components/ui/toaster";
import { Footer } from "@/components/utils/Footer";
2024-08-16 14:12:30 +08:00
import { ContentContainer, Page } from "@/components/utils/Layout";
2023-12-25 17:21:39 +08:00
import { NavBar } from "@/components/utils/NavBar";
import { SEO } from "@/components/utils/SEO";
import { Config } from "@/data/config";
import { getPostFileContent, sortedPosts } from "@/lib/post-process";
2024-04-03 22:08:27 +08:00
import { makeTOCTree } from "@/lib/toc";
import type { TPostFrontmatter, TPostListItem, TPostTOCItem } from "@/types/docs.type";
import type { GetStaticPaths, GetStaticProps } from "next";
import { MDXRemote, type MDXRemoteSerializeResult } from "next-mdx-remote";
2023-12-25 17:21:39 +08:00
import { serialize } from "next-mdx-remote/serialize";
import { renderToString } from "react-dom/server";
import rehypeAutolinkHeadings from "rehype-autolink-headings";
2024-04-03 22:08:27 +08:00
import rehypeHighlight from "rehype-highlight";
2023-12-25 17:21:39 +08:00
import rehypeKatex from "rehype-katex";
import rehypePresetMinify from "rehype-preset-minify";
import rehypeRaw from "rehype-raw";
2023-12-25 17:21:39 +08:00
import rehypeSlug from "rehype-slug";
import externalLinks from "remark-external-links";
import remarkGfm from "remark-gfm";
import remarkMath from "remark-math";
2024-08-16 17:56:55 +08:00
import { titleCase } from "title-case";
2023-12-25 17:21:39 +08:00
type ReaderPageProps = {
compiledSource: MDXRemoteSerializeResult;
tocList: TPostTOCItem[];
2024-09-26 16:48:47 +08:00
frontMatter: TPostFrontmatter;
2023-12-25 17:21:39 +08:00
postId: string;
nextPostListItem: TPostListItem | null;
prevPostListItem: TPostListItem | null;
};
const ReaderPage = (props: ReaderPageProps) => {
// Only the TOC length reaches 3 can be displayed.
// In order to avoid large blank spaces that ruin the visual perception
2024-04-03 22:08:27 +08:00
const isTOCLongEnough = props.tocList.length > 2;
2024-08-16 17:56:55 +08:00
// const handleLeftSwipe = useSwipeable({
// onSwipedLeft: () => isTOCLongEnough && setIsTOCOpen(true),
// delta: 150,
// });
2023-12-25 17:21:39 +08:00
return (
<Page>
<SEO
2024-08-16 17:56:55 +08:00
coverURL={props.frontMatter.coverURL}
2024-04-03 22:08:27 +08:00
description={props.frontMatter.summary}
2024-08-16 17:56:55 +08:00
title={`${titleCase(props.frontMatter.title)} - ${Config.SiteTitle}`}
2023-12-25 17:21:39 +08:00
/>
<Toaster />
<NavBar />
2023-12-25 17:21:39 +08:00
<ContentContainer>
<div className="mx-auto flex flex-col justify-center py-5" style={{ width: "min(50rem,100%)" }}>
{props.frontMatter.coverURL && <PostCover coverURL={props.frontMatter.coverURL} />}
<PostRender
compiledSource={props.compiledSource}
tocList={props.tocList}
frontMatter={props.frontMatter}
postId={props.postId}
nextPostListItem={props.nextPostListItem}
prevPostListItem={props.prevPostListItem}
/>
<Separator />
<ShareButtons
allowShare={props.frontMatter.allowShare}
postId={props.postId}
quote={props.frontMatter.summary}
subtitle={props.frontMatter.subtitle}
title={props.frontMatter.title}
/>
<Separator />
<MorePostLinks prevPostListItem={props.prevPostListItem} nextPostListItem={props.nextPostListItem} />
{Config.Giscus?.enabled && <PostComments postId={props.postId} />}
{isTOCLongEnough && <DrawerTOC data={props.tocList} />}
2023-12-25 17:21:39 +08:00
</div>
</ContentContainer>
<Footer />
</Page>
);
};
export const getStaticPaths: GetStaticPaths<{ id: string }> = async () => {
const allPaths = sortedPosts.allPostList.map((item) => ({
params: { id: item.id },
}));
return {
paths: allPaths,
fallback: false,
};
};
export const getStaticProps: GetStaticProps<ReaderPageProps> = async (context) => {
const postId = context.params?.id;
if (postId == null || Array.isArray(postId)) {
return { notFound: true };
}
const source = getPostFileContent(postId);
if (source == null) {
return { notFound: true };
}
const mdxSource = await serialize(source, {
parseFrontmatter: true,
mdxOptions: {
2024-04-03 22:08:27 +08:00
remarkPlugins: [externalLinks, remarkMath, remarkGfm],
rehypePlugins: [
rehypeRaw,
2024-08-16 17:56:55 +08:00
2024-04-03 22:08:27 +08:00
rehypeKatex as any,
rehypeAutolinkHeadings,
rehypeSlug,
rehypePresetMinify.plugins,
() => rehypeHighlight({ detect: true }),
],
2023-12-25 17:21:39 +08:00
format: "md",
},
});
2024-04-03 22:08:27 +08:00
const tocList = makeTOCTree(renderToString(<MDXRemote {...mdxSource} />));
2023-12-25 17:21:39 +08:00
const postIndexInAllPosts = sortedPosts.allPostList.findIndex((item) => item.id === postId);
2024-09-26 16:48:47 +08:00
const frontMatter: TPostFrontmatter = sortedPosts.allPostList[postIndexInAllPosts].frontMatter;
2023-12-25 17:21:39 +08:00
const nextPostListItem =
postIndexInAllPosts !== sortedPosts.allPostList.length - 1
? sortedPosts.allPostList[postIndexInAllPosts + 1]
: null;
const prevPostListItem = postIndexInAllPosts !== 0 ? sortedPosts.allPostList[postIndexInAllPosts - 1] : null;
return {
props: {
compiledSource: mdxSource,
2023-12-25 17:21:39 +08:00
tocList: tocList,
frontMatter: frontMatter,
postId: postId,
nextPostListItem: nextPostListItem,
prevPostListItem: prevPostListItem,
},
};
};
export default ReaderPage;