diff options
| author | Bertrand Yuan <bert.yuan@outlook.com> | 2025-12-16 00:15:04 +0800 |
|---|---|---|
| committer | Bertrand Yuan <bert.yuan@outlook.com> | 2025-12-16 00:15:04 +0800 |
| commit | 785371bb3eccca455e5ce5fccbe9b6e3752a03f6 (patch) | |
| tree | dd006593448c3500bdcb414af3b4656f7a7683d4 /src/app/(main)/(home)/posts | |
| parent | 02ae938c238c9d18448d17a8ec92c0edd8c17463 (diff) | |
fix(front-end): bug in viewing posts
Diffstat (limited to 'src/app/(main)/(home)/posts')
| -rw-r--r-- | src/app/(main)/(home)/posts/[slug]/page.tsx | 185 | ||||
| -rw-r--r-- | src/app/(main)/(home)/posts/page.tsx | 84 |
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} />} </> ); } |
