mirror of
https://github.com/d0zingcat/NotionNext.git
synced 2026-05-21 07:26:49 +00:00
212 lines
8.4 KiB
JavaScript
212 lines
8.4 KiB
JavaScript
import { getAllCategories, getAllPosts, getAllTags, getPostBlocks } from '@/lib/notion'
|
||
import BLOG from '@/blog.config'
|
||
import { getPageTableOfContents } from 'notion-utils'
|
||
import { useRouter } from 'next/router'
|
||
import Progress from '@/components/Progress'
|
||
import TagItem from '@/components/TagItem'
|
||
import formatDate from '@/lib/formatDate'
|
||
import { Code, Collection, CollectionRow, Equation, NotionRenderer } from 'react-notion-x'
|
||
import RewardButton from '@/components/RewardButton'
|
||
import ShareBar from '@/components/ShareBar'
|
||
import Comment from '@/components/Comment'
|
||
import BaseLayout from '@/layouts/BaseLayout'
|
||
import React, { useRef } from 'react'
|
||
import Custom404 from '@/pages/404'
|
||
import Link from 'next/link'
|
||
import Image from 'next/image'
|
||
|
||
import 'prismjs/themes/prism-okaidia.css'
|
||
import 'prismjs'
|
||
import 'prismjs/components/prism-bash'
|
||
import 'prismjs/components/prism-markup'
|
||
import 'prismjs/components/prism-python'
|
||
import 'prismjs/components/prism-javascript'
|
||
import 'prismjs/components/prism-typescript'
|
||
import RecommendPosts from '@/components/RecommendPosts'
|
||
import TocDrawer from '@/components/TocDrawer'
|
||
import TocDrawerButton from '@/components/TocDrawerButton'
|
||
import { useGlobal } from '@/lib/global'
|
||
import { getNotionPageData } from '@/lib/notion/getNotionData'
|
||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||
import { faAngleDoubleLeft, faAngleDoubleRight, faEye, faFolderOpen } from '@fortawesome/free-solid-svg-icons'
|
||
|
||
const mapPageUrl = id => {
|
||
return 'https://www.notion.so/' + id.replace(/-/g, '')
|
||
}
|
||
const ArticleDetail = ({ post, blockMap, tags, prev, next, allPosts, categories }) => {
|
||
if (!post) {
|
||
return <Custom404 />
|
||
}
|
||
const meta = {
|
||
title: `${post.title} | ${BLOG.title}`,
|
||
description: post.summary,
|
||
type: 'article',
|
||
tags: post.tags
|
||
}
|
||
const targetRef = useRef(null)
|
||
const drawerRight = useRef(null)
|
||
const url = BLOG.link + useRouter().asPath
|
||
const { locale } = useGlobal()
|
||
|
||
return <BaseLayout meta={meta} tags={tags} post={post} totalPosts={allPosts} categories={categories}>
|
||
<Progress targetRef={targetRef} />
|
||
|
||
<div id='article-wrapper' ref={targetRef} className='flex-grow dark:bg-black bg-gray-200'>
|
||
|
||
<div className='max-w-5xl mx-auto mt-16 xl:mt-32 w-screen md:w-full '>
|
||
{post.type && !post.type.includes('Page') && (<>
|
||
<header className='w-full h-60 lg:h-96 transform duration-200 md:flex-shrink-0'>
|
||
<Image src={(post.page_cover && post.page_cover.length > 1) ? post.page_cover : BLOG.defaultImgCover} loading='lazy' objectFit='cover' layout='fill' alt={post.title} />
|
||
</header>
|
||
</>)}
|
||
|
||
<article className='animate__fadeIn animate__animated subpixel-antialiased lg:pt-32 lg:px-44 px-5 py-2 dark:border-gray-700 bg-white dark:bg-gray-800'>
|
||
{/* 文章Title */}
|
||
<h1 className='font-bold text-3xl pt-5 text-black dark:text-white italic'> {post.title}</h1>
|
||
<hr className='mt-4' />
|
||
<section className='flex-nowrap flex mt-1 dark:text-white'>
|
||
<Link href={`/category/${post.category}`} passHref>
|
||
<div className='cursor-pointer text-md py-2 ml-1 mr-3 text-gray-500 dark:text-gray-300 hover:text-black dark:hover:text-white'>
|
||
<FontAwesomeIcon icon={faFolderOpen} className='mr-1' />{post.category}
|
||
</div>
|
||
</Link>
|
||
{post.type[0] !== 'Page' && (
|
||
<Link href='/archive' passHref>
|
||
<div className='pl-1 cursor-pointer hover:text-gray-700 dark:hover:text-gray-200 text-gray-400 dark:text-gray-400 leading-10'>
|
||
{formatDate(
|
||
post?.date?.start_date || post.createdTime,
|
||
BLOG.lang
|
||
)}
|
||
</div>
|
||
</Link>
|
||
)}
|
||
|
||
{/* 不蒜子 */}
|
||
<div id='busuanzi_container_page_pv' className='hidden'>
|
||
<FontAwesomeIcon icon={faEye} className='text-gray-500 dark:text-gray-400 mt-3 ml-2' />
|
||
<span id='busuanzi_value_page_pv' className='text-gray-500 dark:text-gray-400 leading-6'></span>
|
||
</div>
|
||
</section>
|
||
|
||
{/* Notion文章主体 */}
|
||
{blockMap && (
|
||
<NotionRenderer recordMap={blockMap} mapPageUrl={mapPageUrl}
|
||
components={{
|
||
equation: Equation,
|
||
code: Code,
|
||
collectionRow: CollectionRow,
|
||
collection: Collection
|
||
}}
|
||
/>
|
||
)}
|
||
|
||
{/* 推荐文章 */}
|
||
<RecommendPosts currentPost={post} totalPosts={allPosts} />
|
||
|
||
{/* 版权声明 */}
|
||
<section className='text-sm overflow-auto dark:bg-gray-800 dark:text-gray-300 bg-gray-100 p-5 leading-8 border-l-4 border-red-500'>
|
||
<ul>
|
||
<li>本文作者: <Link href='/article/about'><a className='hover:underline'>{BLOG.author}</a></Link></li>
|
||
<li>本文链接: <a className='hover:underline' href={url}>{url}</a></li>
|
||
<li>版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!</li>
|
||
</ul>
|
||
</section>
|
||
|
||
{/* 标签列表 */}
|
||
<section className='md:flex md:justify-between'>
|
||
{post.tagItems && (
|
||
<div className='flex flex-nowrap leading-8 p-1 py-4 overflow-x-auto'>
|
||
<div className='hidden md:block dark:text-gray-300'>{locale.COMMON.TAGS}:</div>
|
||
{post.tagItems.map(tag => (
|
||
<TagItem key={tag.name} tag={tag} />
|
||
))}
|
||
</div>
|
||
)}
|
||
<div>
|
||
<ShareBar post={post} />
|
||
</div>
|
||
</section>
|
||
|
||
</article>
|
||
|
||
<div className='w-screen md:w-full px-5 py-2 dark:border-gray-700 bg-white dark:bg-gray-800'>
|
||
<div className='py-10'>
|
||
<RewardButton />
|
||
</div>
|
||
</div>
|
||
<div className='text-gray-800 dark:text-gray-300 dark:bg-gray-900 bg-gray-50 px-5 '>
|
||
<div className='flex flex-wrap lg:flex-nowrap lg:space-x-10 justify-between py-2'>
|
||
<Link href={`/article/${prev.slug}`} passHref>
|
||
<div className='py-3 text-blue-500 text-lg hover:underline cursor-pointer'>
|
||
<FontAwesomeIcon icon={faAngleDoubleLeft} className='mr-1' />{prev.title}</div>
|
||
</Link>
|
||
<Link href={`/article/${next.slug}`} passHref>
|
||
<div className='flex py-3 text-blue-500 text-lg hover:underline cursor-pointer'>{next.title}
|
||
<FontAwesomeIcon icon={faAngleDoubleRight} className='ml-1 my-1' />
|
||
</div>
|
||
</Link>
|
||
</div>
|
||
</div>
|
||
|
||
{/* 评论互动 */}
|
||
<div
|
||
className='my-10 w-screen md:w-full overflow-x-auto dark:border-gray-700 bg-white dark:bg-gray-700'>
|
||
<Comment frontMatter={post} />
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* 悬浮目录按钮 */}
|
||
<div className='block lg:hidden'>
|
||
<TocDrawerButton onClick={() => { drawerRight.current.handleSwitchVisible() }} />
|
||
{/* 目录侧边栏 */}
|
||
<TocDrawer post={post} cRef={drawerRight} />
|
||
</div>
|
||
|
||
</BaseLayout>
|
||
}
|
||
|
||
export async function getStaticPaths () {
|
||
let posts = []
|
||
if (BLOG.isProd) {
|
||
posts = await getAllPosts({ from: 'slug - paths', includePage: true })
|
||
}
|
||
return {
|
||
paths: posts.map(row => `${BLOG.path}/article/${row.slug}`),
|
||
fallback: true
|
||
}
|
||
}
|
||
|
||
export async function getStaticProps ({ params: { slug } }) {
|
||
const from = `slug-props-${slug}`
|
||
const notionPageData = await getNotionPageData({ from })
|
||
let allPosts = await getAllPosts({ notionPageData, from, includePage: true })
|
||
const post = allPosts.find(p => p.slug === slug)
|
||
|
||
if (!post) {
|
||
return { props: {}, revalidate: 1 }
|
||
}
|
||
|
||
const blockMap = await getPostBlocks(post.id, 'slug')
|
||
post.toc = []
|
||
if (blockMap) {
|
||
post.toc = getPageTableOfContents(post, blockMap)
|
||
}
|
||
|
||
allPosts = allPosts.filter(post => post.type[0] === 'Post')
|
||
const tagOptions = notionPageData.tagOptions
|
||
const tags = await getAllTags({ allPosts, tagOptions })
|
||
const categories = await getAllCategories(allPosts)
|
||
// 上一篇、下一篇文章关联
|
||
const index = allPosts.indexOf(post)
|
||
const prev = allPosts.slice(index - 1, index)[0] ?? allPosts.slice(-1)[0]
|
||
const next = allPosts.slice(index + 1, index + 2)[0] ?? allPosts[0]
|
||
|
||
return {
|
||
props: { post, blockMap, tags, prev, next, allPosts, categories },
|
||
revalidate: 1
|
||
}
|
||
}
|
||
|
||
export default ArticleDetail
|