summaryrefslogtreecommitdiff
path: root/src/app/(main)/(home)/posts
diff options
context:
space:
mode:
Diffstat (limited to 'src/app/(main)/(home)/posts')
-rw-r--r--src/app/(main)/(home)/posts/[slug]/page.tsx185
-rw-r--r--src/app/(main)/(home)/posts/page.tsx84
2 files changed, 36 insertions, 233 deletions
diff --git a/src/app/(main)/(home)/posts/[slug]/page.tsx b/src/app/(main)/(home)/posts/[slug]/page.tsx
index fa096b6..5960f06 100644
--- a/src/app/(main)/(home)/posts/[slug]/page.tsx
+++ b/src/app/(main)/(home)/posts/[slug]/page.tsx
@@ -7,55 +7,18 @@ import { RichText } from '@/components/rich-text';
import { Section } from '@/components/section';
import { TagCard } from '@/components/tags/tag-card';
import { createMetadata } from '@/lib/metadata';
-import { metadataImage } from '@/lib/metadata-image';
import {
getPostBySlug,
getAllPostSlugs,
type BlogPost,
} from '@/lib/payload-posts';
-import { type Page as MDXPage, getPost, getPosts } from '@/lib/source';
import { cn } from '@/lib/utils';
-import { File, Files, Folder } from 'fumadocs-ui/components/files';
-import { InlineTOC } from 'fumadocs-ui/components/inline-toc';
-import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
-import defaultMdxComponents from 'fumadocs-ui/mdx';
import type { Metadata } from 'next';
import { notFound } from 'next/navigation';
import Balancer from 'react-wrap-balancer';
import { description as homeDescription } from '@/app/(main)/layout.config';
-// MDX 文章 Header
-function MdxHeader(props: { page: MDXPage; tags?: string[] }) {
- const { page, tags } = props;
-
- return (
- <Section className="p-4 lg:p-6">
- <div
- className={cn(
- 'flex flex-col items-start justify-center gap-4 py-8 md:gap-6',
- 'sm:items-center sm:rounded-lg sm:border sm:bg-muted/70 sm:px-8 sm:py-20 sm:shadow-xs sm:dark:bg-muted'
- )}
- >
- <div className="flex flex-col gap-2 sm:text-center md:gap-4">
- <h1 className="max-w-4xl font-bold text-3xl leading-tight tracking-tight sm:text-4xl sm:leading-tight md:text-5xl md:leading-tight">
- <Balancer>{page.data.title}</Balancer>
- </h1>
- <p className="mx-auto max-w-4xl">
- <Balancer>{page.data.description}</Balancer>
- </p>
- </div>
- <div className="flex flex-wrap gap-2">
- {tags?.map((tag) => (
- <TagCard name={tag} key={tag} className=" border border-border " />
- ))}
- </div>
- </div>
- </Section>
- );
-}
-
-// Payload 文章 Header
-function PayloadHeader(props: { post: BlogPost }) {
+function PostHeader(props: { post: BlogPost }) {
const { post } = props;
return (
@@ -84,70 +47,10 @@ function PayloadHeader(props: { post: BlogPost }) {
);
}
-// MDX 文章内容
-function MdxContent({ page }: { page: MDXPage }) {
- const { body: Mdx, toc, tags, lastModified } = page.data;
- const lastUpdate = lastModified ? new Date(lastModified) : undefined;
-
- return (
- <>
- <MdxHeader page={page} tags={tags} />
-
- <Section className="h-full" sectionClassName="flex flex-1">
- <article className="flex min-h-full flex-col lg:flex-row">
- <div className="flex flex-1 flex-col gap-4">
- <InlineTOC
- items={toc}
- className="rounded-none border-0 border-border/70 border-b border-dashed dark:border-border"
- />
- <div className="prose min-w-0 flex-1 px-4">
- <Mdx
- components={{
- ...defaultMdxComponents,
- File,
- Files,
- Folder,
- Tabs,
- Tab,
- }}
- />
- </div>
- <PostComments
- slug={page.slugs[0] ?? ''}
- className="[&_form>div]:!rounded-none rounded-none border-0 border-border/70 border-t border-dashed dark:border-border"
- />
- </div>
- <div className="flex flex-col gap-4 p-4 text-sm lg:sticky lg:top-[4rem] lg:h-[calc(100vh-4rem)] lg:w-[250px] lg:self-start lg:overflow-y-auto lg:border-border/70 lg:border-l lg:border-dashed lg:dark:border-border">
- <div>
- <p className="mb-1 text-fd-muted-foreground">Written by</p>
- <p className="font-medium">{page.data.author}</p>
- </div>
- <div>
- <p className="mb-1 text-fd-muted-foreground text-sm">Created At</p>
- <p className="font-medium">
- {new Date(page.data.date ?? page.file.name).toDateString()}
- </p>
- </div>
- {lastUpdate && (
- <div>
- <p className="mb-1 text-fd-muted-foreground text-sm">Updated At</p>
- <p className="font-medium">{lastUpdate.toDateString()}</p>
- </div>
- )}
- <Share url={page.url} />
- </div>
- </article>
- </Section>
- <PostJsonLd page={page} />
- </>
- );
-}
-
-// Payload 文章内容
-function PayloadContent({ post }: { post: BlogPost }) {
+function PostContent({ post }: { post: BlogPost }) {
return (
<>
- <PayloadHeader post={post} />
+ <PostHeader post={post} />
<Section className="h-full" sectionClassName="flex flex-1">
<article className="flex min-h-full flex-col lg:flex-row">
@@ -179,6 +82,7 @@ function PayloadContent({ post }: { post: BlogPost }) {
</div>
</article>
</Section>
+ <PostJsonLd post={post} />
</>
);
}
@@ -187,80 +91,39 @@ export default async function Page(props: {
params: Promise<{ slug: string }>;
}) {
const params = await props.params;
+ const post = await getPostBySlug(params.slug);
- // 先尝试获取 MDX 文章
- const mdxPage = getPost([params.slug]);
-
- if (mdxPage) {
- return <MdxContent page={mdxPage} />;
- }
-
- // 再尝试获取 Payload 文章
- const payloadPost = await getPostBySlug(params.slug);
-
- if (payloadPost) {
- return <PayloadContent post={payloadPost} />;
+ if (!post) {
+ notFound();
}
- // 都找不到则 404
- notFound();
+ return <PostContent post={post} />;
}
export async function generateMetadata(props: {
params: Promise<{ slug: string }>;
}): Promise<Metadata> {
const params = await props.params;
+ const post = await getPostBySlug(params.slug);
- // 先尝试 MDX 文章
- const mdxPage = getPost([params.slug]);
-
- if (mdxPage) {
- const title = mdxPage.data.title;
- const description = mdxPage.data.description ?? homeDescription;
-
- return createMetadata(
- metadataImage.withImage(mdxPage.slugs, {
- title,
- description,
- openGraph: {
- url: `/posts/${mdxPage.slugs.join('/')}`,
- },
- alternates: {
- canonical: mdxPage.url,
- },
- })
- );
+ if (!post) {
+ return {};
}
- // 再尝试 Payload 文章
- const payloadPost = await getPostBySlug(params.slug);
-
- if (payloadPost) {
- return createMetadata({
- title: payloadPost.title,
- description: payloadPost.description || homeDescription,
- openGraph: {
- url: `/posts/${payloadPost.slug}`,
- },
- alternates: {
- canonical: `/posts/${payloadPost.slug}`,
- },
- });
- }
-
- return {};
+ return createMetadata({
+ title: post.title,
+ description: post.description || homeDescription,
+ openGraph: {
+ url: `/posts/${post.slug}`,
+ images: post.image ? [{ url: post.image }] : undefined,
+ },
+ alternates: {
+ canonical: `/posts/${post.slug}`,
+ },
+ });
}
export async function generateStaticParams(): Promise<{ slug: string }[]> {
- // MDX 文章的 slugs
- const mdxSlugs = getPosts()
- .map((page) => page.slugs[0])
- .filter((slug): slug is string => !!slug)
- .map((slug) => ({ slug }));
-
- // Payload 文章的 slugs
- const payloadSlugs = await getAllPostSlugs();
- const payloadParams = payloadSlugs.map((slug) => ({ slug }));
-
- return [...mdxSlugs, ...payloadParams];
+ const slugs = await getAllPostSlugs();
+ return slugs.map((slug) => ({ slug }));
}
diff --git a/src/app/(main)/(home)/posts/page.tsx b/src/app/(main)/(home)/posts/page.tsx
index 00e51c4..40ebcda 100644
--- a/src/app/(main)/(home)/posts/page.tsx
+++ b/src/app/(main)/(home)/posts/page.tsx
@@ -3,70 +3,10 @@ import { NumberedPagination } from '@/components/numbered-pagination';
import { PostCard } from '@/components/posts/post-card';
import { Section } from '@/components/section';
import { createMetadata } from '@/lib/metadata';
-import { getPublishedPosts, type BlogPost } from '@/lib/payload-posts';
-import { getSortedByDatePosts, type Page } from '@/lib/source';
+import { getPublishedPosts } from '@/lib/payload-posts';
import type { Metadata, ResolvingMetadata } from 'next';
import { notFound, redirect } from 'next/navigation';
-// 统一的文章类型
-interface UnifiedPost {
- id: string;
- title: string;
- description: string;
- image?: string;
- url: string;
- date: Date;
- author: string;
- tags?: string[];
- source: 'mdx' | 'payload';
-}
-
-// 将 MDX 文章转换为统一格式
-function transformMdxPost(post: Page): UnifiedPost {
- return {
- id: `mdx-${post.url}`,
- title: post.data.title,
- description: post.data.description ?? '',
- image: post.data.image,
- url: post.url,
- date: new Date(post.data.date),
- author: post.data.author,
- tags: post.data.tags,
- source: 'mdx',
- };
-}
-
-// 将 Payload 文章转换为统一格式
-function transformPayloadPost(post: BlogPost): UnifiedPost {
- return {
- id: `payload-${post.id}`,
- title: post.title,
- description: post.description,
- image: post.image,
- url: post.url,
- date: post.date,
- author: post.author,
- tags: post.tags,
- source: 'payload',
- };
-}
-
-// 获取所有文章(合并 MDX 和 Payload)
-async function getAllPosts(): Promise<UnifiedPost[]> {
- // 获取 MDX 文章
- const mdxPosts = getSortedByDatePosts().map(transformMdxPost);
-
- // 获取 Payload 文章(获取全部,用于分页计算)
- const { posts: payloadPosts } = await getPublishedPosts({ limit: 1000 });
- const transformedPayloadPosts = payloadPosts.map(transformPayloadPost);
-
- // 合并并按日期排序
- const allPosts = [...mdxPosts, ...transformedPayloadPosts];
- allPosts.sort((a, b) => b.date.getTime() - a.date.getTime());
-
- return allPosts;
-}
-
const CurrentPostsCount = ({
startIndex,
endIndex,
@@ -115,11 +55,6 @@ export default async function Page(props: {
}) {
const searchParams = await props.searchParams;
- // 获取所有文章
- const allPosts = await getAllPosts();
- const totalPosts = allPosts.length;
- const pageCount = Math.ceil(totalPosts / postsPerPage);
-
const pageIndex = searchParams.page
? Number.parseInt(
Array.isArray(searchParams.page)
@@ -129,21 +64,26 @@ export default async function Page(props: {
) - 1
: 0;
- if (pageIndex < 0 || (pageCount > 0 && pageIndex >= pageCount)) notFound();
+ // 获取文章(带分页)
+ const { posts, totalDocs, totalPages } = await getPublishedPosts({
+ limit: postsPerPage,
+ page: pageIndex + 1,
+ });
+
+ if (pageIndex < 0 || (totalPages > 0 && pageIndex >= totalPages)) notFound();
const startIndex = pageIndex * postsPerPage;
- const endIndex = startIndex + postsPerPage;
- const posts = allPosts.slice(startIndex, endIndex);
+ const endIndex = startIndex + posts.length;
return (
<>
<Section className='p-4 lg:p-6'>
<h1 className='font-bold text-3xl leading-tight tracking-tighter md:text-4xl'>
- All {totalPosts} Posts{' '}
+ All {totalDocs} Posts{' '}
<CurrentPostsCount
startIndex={startIndex}
endIndex={endIndex}
- totalPosts={totalPosts}
+ totalPosts={totalDocs}
/>
</h1>
</Section>
@@ -166,7 +106,7 @@ export default async function Page(props: {
})}
</div>
</Section>
- {pageCount > 1 && <Pagination pageIndex={pageIndex} pageCount={pageCount} />}
+ {totalPages > 1 && <Pagination pageIndex={pageIndex} pageCount={totalPages} />}
</>
);
}