样式调整;
顶部菜单调整
This commit is contained in:
tangly1024
2021-12-16 11:09:10 +08:00
parent 5bc16903c5
commit 71cc59f0d1
19 changed files with 417 additions and 215 deletions

192
components/ArticleDetail.js Normal file
View File

@@ -0,0 +1,192 @@
import BLOG from '@/blog.config'
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 ShareBar from '@/components/ShareBar'
import Comment from '@/components/Comment'
import Link from 'next/link'
import Image from 'next/image'
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 { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faEye, faFolderOpen } from '@fortawesome/free-solid-svg-icons'
import BlogAround from '@/components/BlogAround'
import { useRef } from 'react'
const mapPageUrl = id => {
return 'https://www.notion.so/' + id.replace(/-/g, '')
}
/**
*
* @param {*} param0
* @returns
*/
export default function ArticleDetail ({ post, blockMap, recommendPosts, prev, next }) {
const targetRef = useRef(null)
const drawerRight = useRef(null)
const url = BLOG.link + useRouter().asPath
const { locale } = useGlobal()
const date = formatDate(post?.date?.start_date || post.createdTime, BLOG.lang)
return (
<>
<Progress targetRef={targetRef} />
<div id="article-wrapper" ref={targetRef} className="flex-grow">
<div className="max-w-5xl mx-auto mt-16 xl:mt-32 w-screen md:w-full">
<article
itemScope
itemType="https://schema.org/Movie"
className="duration-300 hover:shadow-2xl pt-10 animate__fadeIn animate__animated subpixel-antialiased lg:pt-32 lg:px-52 px-5 py-2 dark:border-gray-700 bg-white dark:bg-gray-800"
>
{post.type && !post.type.includes('Page') && (
<>
<header className="w-full h-60 lg:h-96 transform duration-200 md:flex-shrink-0 overflow-hidden">
<Image
src={
post.page_cover && post.page_cover.length > 1
? post.page_cover
: BLOG.defaultImgCover
}
loading="eager"
objectFit="cover"
layout="fill"
alt={post.title}
/>
</header>
</>
)}
{/* 文章Title */}
<h2 className="font-bold text-2xl text-black dark:text-white font-serif pt-10">
{' '}
{post.title}
</h2>
<hr className="mt-4" />
<section className="flex-nowrap flex mt-1 dark:text-white font-light">
<Link href={`/category/${post.category}`} passHref>
<a 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}
</a>
</Link>
{post.type[0] !== 'Page' && (
<Link
href={`/archive#${post?.date?.start_date?.substr(0, 7)}`}
passHref
>
<a className="pl-1 cursor-pointer hover:text-gray-700 dark:hover:text-gray-200 text-gray-400 dark:text-gray-400 leading-10">
{date}
</a>
</Link>
)}
<div id="busuanzi_container_page_pv" className="hidden">
<FontAwesomeIcon
icon={faEye}
className="text-gray-500 dark:text-gray-400 mt-3 ml-5"
/>
&nbsp;
<span
id="busuanzi_value_page_pv"
className="text-gray-500 dark:text-gray-400 leading-6"
></span>
</div>
</section>
<section className="px-1 py-2 my-1 text-sm font-light bg-gray-50 text-gray-600 dark:bg-gray-700 dark:text-gray-400">
{post.summary}
</section>
{/* Notion文章主体 */}
{blockMap && (
<NotionRenderer
className={`${BLOG.font}`}
recordMap={blockMap}
mapPageUrl={mapPageUrl}
components={{
equation: Equation,
code: Code,
collectionRow: CollectionRow,
collection: Collection
}}
/>
)}
{/* 推荐文章 */}
<RecommendPosts currentPost={post} recommendPosts={recommendPosts} />
{/* 版权声明 */}
<section className="dark:text-gray-300 mt-6">
<div className="text-2xl mb-2">版权声明</div>
<ul className="text-sm dark:bg-gray-900 bg-gray-100 p-5 leading-8 border-l-4 border-red-500">
<li>
本文作者:{' '}
<Link href="/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>
<BlogAround prev={prev} next={next} />
</article>
{/* 评论互动 */}
<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>
</>
)
}

View File

@@ -8,6 +8,9 @@ import { faAngleDoubleLeft, faAngleDoubleRight } from '@fortawesome/free-solid-s
* @returns
*/
export default function BlogAround ({ prev, next }) {
if (!prev || !next) {
return <></>
}
return <section className='text-gray-800 mb-8 lg:mb-32 border-t dark:text-gray-300 px-5 flex flex-wrap lg:flex-nowrap lg:space-x-10 justify-between py-2'>
<Link href={`/article/${prev.slug}`} passHref>
<a className='text-sm py-3 text-blue-500 hover:underline cursor-pointer'>

View File

@@ -6,7 +6,7 @@
*/
const BlogPostListEmpty = ({ currentSearch }) => {
return <div className='min-h-screen flex justify-center mx-auto'>
<div className='align-middle text-center my-auto'>
<div className='text-center my-auto'>
<p className='text-gray-500 dark:text-gray-300'>没有找到文章 {(currentSearch && <div>{currentSearch}</div>)}</p>
</div>
</div>

View File

@@ -10,7 +10,7 @@ import { faInfo } from '@fortawesome/free-solid-svg-icons'
*/
const ContactButton = () => {
return (
<Link href='/article/about'>
<Link href='/about'>
<a className={'fixed right-10 bottom-40 animate__fadeInRight animate__animated animate__faster'}>
<span
className='dark:bg-black bg-white px-5 py-3 cursor-pointer shadow-card text-xl hover:bg-blue-500 transform duration-200 hover:text-white hover:shadow'>

View File

@@ -0,0 +1,28 @@
import { useEffect, useState } from 'react'
import DarkModeButton from './DarkModeButton'
let windowTop = 0
export default function FloatDarkModeButton () {
const [show, switchShow] = useState(false)
const scrollListener = () => {
const scrollY = window.pageYOffset
const shouldShow = scrollY > 100 && scrollY < windowTop
windowTop = scrollY
if (shouldShow !== show) {
switchShow(shouldShow)
}
}
useEffect(() => {
document.addEventListener('scroll', scrollListener)
return () => document.removeEventListener('scroll', scrollListener)
})
return (
<div
className={(show ? 'animate__fadeInRight ' : 'hidden lg:block') + ' animate__animated animate__faster shadow-card fixed right-5 bottom-36 py-1.5 px-2.5 rounded-full' +
' text-black shadow-card dark:border-gray-500 glassmorphism dark:bg-gray-700 dark:text-gray-200'}
>
<DarkModeButton />
</div>
)
}

View File

@@ -5,7 +5,7 @@ import Router from 'next/router'
const InfoCard = () => {
return <>
<div className='flex flex-col align-middle justify-center cursor-pointer' onClick={ () => { Router.push('/') }}>
<div className='flex flex-col items-center justify-center cursor-pointer' onClick={ () => { Router.push('/') }}>
<div className='hover:rotate-45 hover:scale-125 transform duration-200 mx-auto'>
<Image
alt={BLOG.title}

View File

@@ -36,13 +36,13 @@ const JumpToTopButton = ({ targetRef, showPercent = true }) => {
return () => document.removeEventListener('scroll', scrollListener)
}, [show])
return (<div id='jump-to-top' className='right-5 fixed flex bottom-36 duration-500 z-20'>
return (<div id='jump-to-top' className='right-5 fixed flex bottom-52 duration-500 z-20'>
<div onClick={() => window.scrollTo({ top: 0, behavior: 'smooth' })}
className={(show ? '' : 'hidden') + ' animate__fadeInRight rounded-full glassmorphism p-2 cursor-pointer animate__animated animate__faster shadow-card'}>
className={(show ? '' : 'hidden') + ' animate__fadeInRight animate__animated animate__faster shadow-card rounded-full glassmorphism p-2 cursor-pointer '}>
<div className='text-center'>
<div className='w-10 dark:text-gray-100 transform hover:scale-125 duration-200' title={locale.POST.TOP} >
<FontAwesomeIcon icon={faArrowUp} />
</div>
<div className='w-10 dark:text-gray-200 transform hover:scale-125 duration-200' title={locale.POST.TOP} >
<FontAwesomeIcon icon={faArrowUp} />
</div>
{showPercent && (<div className='w-10 text-xs dark:text-gray-200'>{percent}</div>)}
</div>
</div>

View File

@@ -12,7 +12,7 @@ const MenuButtonGroup = ({ allowCollapse = false }) => {
const links = [
{ id: 0, icon: faHome, name: locale.NAV.INDEX, to: '/' || '/', show: true },
{ id: 1, icon: faArchive, name: locale.NAV.ARCHIVE, to: '/archive', show: BLOG.showArchive },
{ id: 2, icon: faInfoCircle, name: locale.NAV.ABOUT, to: '/article/about', show: BLOG.showAbout }
{ id: 2, icon: faInfoCircle, name: locale.NAV.ABOUT, to: '/about', show: BLOG.showAbout }
// { id: 7, icon: 'faGithub', name: 'Github', to: 'https://github.com/tangly1024', show: true },
// { id: 5, icon: 'faWeibo', name: '微博', to: 'https://weibo.com/tangly1024', show: true },
// { id: 4, icon: 'faEnvelope', name: locale.NAV.MAIL, to: 'mailto:tlyong1992@hotmail.com', show: true }
@@ -28,7 +28,7 @@ const MenuButtonGroup = ({ allowCollapse = false }) => {
if (link.show) {
const selected = (router.pathname === link.to) || (router.asPath === link.to)
return <Link key={link.id + link.icon} title={link.to} href={link.to} >
<a className={'py-2 px-5 text-lg hover:bg-blue-400 hover:text-white hover:shadow-lg cursor-pointer duration-100 font-light flex flex-nowrap align-middle ' +
<a className={'py-2 px-5 text-lg hover:bg-blue-400 hover:text-white hover:shadow-lg cursor-pointer duration-100 font-light flex flex-nowrap items-center ' +
(selected ? 'bg-blue-500 text-white ' : ' ')} >
<div className='my-auto justify-center flex'>
<FontAwesomeIcon icon={link.icon} />

View File

@@ -3,49 +3,19 @@ import Link from 'next/link'
import { useGlobal } from '@/lib/global'
/**
* 洗牌乱序:从数组的最后位置开始,从前面随机一个位置,对两个数进行交换,直到循环完毕
* @param arr
* @returns {*}
* 展示文章推荐
*/
function shuffleSort (arr) {
let i = arr.length - 1
while (i > 0) {
const rIndex = Math.floor(Math.random() * i)
const temp = arr[rIndex]
arr[rIndex] = arr[i]
arr[i] = temp
i--
const RecommendPosts = ({ recommendPosts }) => {
if (!recommendPosts) {
return <></>
}
return arr
}
const RecommendPosts = ({ currentPost, totalPosts }) => {
let filteredPosts = totalPosts
// 筛选同标签
if (currentPost.tags && currentPost.tags.length) {
const currentTag = currentPost.tags[0]
filteredPosts = totalPosts.filter(
post =>
post &&
post.tags &&
post.tags.includes(currentTag) &&
post.slug !== currentPost.slug
)
}
shuffleSort(filteredPosts)
// 筛选前5个
if (filteredPosts.length > 5) {
filteredPosts = filteredPosts.slice(0, 5)
}
const { locale } = useGlobal()
return (
<div className="dark:text-gray-300 pt-2">
<div className="mb-2 text-2xl">{locale.COMMON.RELATE_POSTS}</div>
<ul className="list-disc pl-6 text-sm dark:bg-gray-900 bg-gray-100 p-2 my-2 border-l-4 border-yellow-500">
{filteredPosts.map(post => (
{recommendPosts.map(post => (
<li className="py-1" key={post.id}>
<Link href={`/article/${post.slug}`}>
<a className="cursor-pointer hover:text-blue-500 hover:underline">

View File

@@ -49,7 +49,7 @@ const SearchInput = ({ currentTag, currentSearch }) => {
/>
{(searchKey && searchKey.length && <FontAwesomeIcon icon={faTimes} className='text-gray-300 float-right m-3 cursor-pointer' onClick={cleanSearch} />)}
<div className='p-3 bg-gray-50 flex border-l dark:border-gray-700 dark:hover:bg-gray-800 dark:bg-gray-600 justify-center align-middle cursor-pointer'
<div className='p-3 bg-gray-50 flex border-l dark:border-gray-700 dark:hover:bg-gray-800 dark:bg-gray-600 justify-center items-center cursor-pointer'
onClick={() => { handleSearch(searchKey) }}>
<FontAwesomeIcon spin={onLoading} icon={onLoading ? faSpinner : faSearch} className='hover:scale-125 hover:text-black transform duration-200 dark:text-gray-300 dark:hover:text-white text-gray-600 cursor-pointer ' />
</div>

View File

@@ -26,13 +26,16 @@ import { faAngleDoubleRight, faArchive, faTags, faThList } from '@fortawesome/fr
const SideBar = ({ tags, currentTag, post, posts, categories, currentCategory, currentSearch }) => {
const { locale } = useGlobal()
return <aside id='sidebar' className='pt-5 bg-white dark:bg-gray-900 w-72 z-10 dark:border-gray-500 border-gray-200 scroll-hidden h-full'>
<InfoCard />
<hr className='dark:border-gray-700 mt-6 py-1' />
<section className='hidden lg:block'>
<InfoCard />
<hr className='dark:border-gray-700 mt-6 py-1' />
</section>
<div className={(!post ? 'sticky top-0' : '') + ' bg-white dark:bg-gray-900 pb-4'}>
{/* <hr className='dark:border-gray-700' /> */}
<MenuButtonGroup allowCollapse={true} />
<section className='hidden lg:block'>
<MenuButtonGroup allowCollapse={true} />
</section>
<section className='p-5'>
<SearchInput currentTag={currentTag} currentSearch={currentSearch} />

View File

@@ -51,7 +51,7 @@ const Toc = ({ toc }) => {
}, throttleMs))
return <>
<nav className=' dark:text-gray-400 dark:bg-gray-900 overflow-y-auto scroll-hidden p-2'>
<nav className=' dark:text-gray-100 glassmorphism overflow-y-auto scroll-hidden p-6'>
{toc.map((tocItem) => {
const id = uuidToId(tocItem.id)
return (

View File

@@ -25,11 +25,11 @@ const TocDrawer = ({ post, cRef }) => {
<div className='fixed top-0 right-0 z-40'>
{/* 侧边菜单 */}
<div
className={(showDrawer ? 'animate__slideInRight ' : ' -mr-72 animate__slideOutRight') + ' border ' +
' dark:border-gray-800 bg-white dark:bg-gray-700 shadow-xl animate__animated animate__faster max-h-96 ' +
className={(showDrawer ? 'animate__slideInRight ' : ' -mr-72 animate__slideOutRight') +
' shadow-xl animate__animated animate__faster max-h-96 ' +
' w-60 duration-200 fixed right-4 top-16 rounded overflow-y-auto'}>
{post && <>
<div className='text-xl font-bold text-black bg-gray-50 dark:text-white dark:bg-black py-3 px-6'>
<div className='text-xl font-bold text-black dark:text-white glassmorphism py-3 px-6'>
{locale.COMMON.TABLE_OF_CONTENTS}
</div>
<Toc toc={post.toc}/>

View File

@@ -17,7 +17,6 @@ const TocDrawerButton = (props) => {
const scrollListener = () => {
const scrollY = window.pageYOffset
const shouldShow = scrollY > 100 && scrollY < windowTop
console.log('目录', shouldShow, scrollY, windowTop)
windowTop = scrollY
if (shouldShow !== show) {
@@ -30,14 +29,12 @@ const TocDrawerButton = (props) => {
})
return (
<div id='toc-drawer-button' className='right-5 fixed bottom-56 duration-500 z-40' onClick={props.onClick}>
<div className='transform hover:scale-105 duration-200'>
<div id='toc-drawer-button' className='right-5 fixed bottom-72 duration-500 z-40' onClick={props.onClick}>
<div className={(show ? 'animate__fadeInRight' : 'hidden') + ' px-2 py-4 animate__animated glassmorphism rounded-full cursor-pointer shadow-card' }>
<div className='text-center dark:text-gray-100'>
<div className='w-10 text-md' title={locale.COMMON.TABLE_OF_CONTENTS} ><FontAwesomeIcon icon={faListOl} /> </div>
</div>
<div className='w-10 dark:text-gray-200 text-center transform hover:scale-125 duration-200' title={locale.POST.TOP} >
<div className='w-10 text-md' title={locale.COMMON.TABLE_OF_CONTENTS} ><FontAwesomeIcon icon={faListOl} /> </div>
</div>
</div>
</div>
</div>
)

View File

@@ -1,13 +1,15 @@
import { useRef } from 'react'
import DarkModeButton from '@/components/DarkModeButton'
import SideBarDrawer from '@/components/SideBarDrawer'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faBars } from '@fortawesome/free-solid-svg-icons'
import BLOG from '@/blog.config'
import Link from 'next/link'
import Image from 'next/image'
import { useGlobal } from '@/lib/global'
const TopNav = ({ tags, currentTag, post, posts, currentSearch, categories, currentCategory }) => {
const drawer = useRef()
const { locale } = useGlobal()
return (<div id='top-nav' className='block lg:hidden'>
{/* 侧面抽屉 */}
@@ -15,24 +17,39 @@ const TopNav = ({ tags, currentTag, post, posts, currentSearch, categories, curr
{/* 导航栏 */}
<div id='sticky-nav' className='fixed w-full top-0 z-20 transform duration-500 glassmorphism'>
<div className='text-sm m-auto w-full flex flex-row justify-between items-center px-4 py-2 shadow-xl '>
<div className='w-full flex justify-between items-center p-4 shadow-xl'>
{/* 左侧LOGO */}
<div className='flex ml-12'>
<div onClick={() => { drawer.current.handleSwitchSideDrawerVisible() }}
className='fixed top-3 left-0 z-30 ml-5 text-gray-600 text-2xl cursor-pointer dark:text-gray-300'>
<FontAwesomeIcon icon={faBars} className='hover:scale-125 transform duration-200'
/>
</div>
<div className='flex'>
<div className='relative w-12' ><Image
alt={BLOG.title}
layout='fill'
loading='lazy'
src='/avatar.svg'
className='rounded-full border-black'
/></div>
<Link href='/' passHref>
<div className='cursor-pointer'>
<h1 className='text-xl pb-0.5 hover:scale-105 duration-200 transform font-serif dark:text-gray-200 whitespace-nowrap overflow-x-hidden'>{BLOG.title }</h1>
</div>
<a>
<h1 className='cursor-pointer ml-1 leading-7 text-xl hover:scale-105 duration-200 transform font-serif dark:text-gray-200 whitespace-nowrap overflow-x-hidden'>{BLOG.title }</h1>
</a>
</Link>
</div>
{/* 右侧功能 */}
<div className='flex flex-nowrap space-x-1 ml-2 pt-1 dark:text-gray-200'>
<DarkModeButton />
<div className='mr-2 flex justify-end items-center text-sm flex-nowrap space-x-4 font-serif dark:text-gray-200'>
<Link href='/'>
<a>{locale.NAV.INDEX}</a>
</Link>
<Link href='/archive'>
<a>{locale.NAV.ARCHIVE}</a>
</Link>
<Link href='/article/about'>
<a>{locale.NAV.ABOUT}</a>
</Link>
<div onClick={() => { drawer.current.handleSwitchSideDrawerVisible() }}
className='ppb-1z-30 text-gray-600 text-2xl flex items-center cursor-pointer dark:text-gray-300'>
<FontAwesomeIcon icon={faBars} className='hover:scale-125 transform duration-200'
/>
</div>
</div>
</div>
</div>

View File

@@ -9,6 +9,8 @@ import JumpToTopButton from '@/components/JumpToTopButton'
import { useGlobal } from '@/lib/global'
import DarkModeButton from '@/components/DarkModeButton'
import LoadingCover from '@/components/LoadingCover'
import LeftFloatButton from '@/components/LeftFloatButton'
import FloatDarkModeButton from '@/components/FloatDarkModeButton'
/**
* 基础布局 采用左右两侧布局,移动端使用顶部导航栏
@@ -89,11 +91,8 @@ const BaseLayout = ({
<Footer />
<JumpToTopButton targetRef={targetRef} showPercent={true} />
<div className='hidden lg:block fixed right-5 bottom-52 py-1.5 px-2.5 rounded-full
bg-white text-black shadow-card dark:border-gray-500 dark:bg-gray-700 dark:text-white'>
<DarkModeButton />
<FloatDarkModeButton/>
</div>
</div>
)
}

View File

@@ -29,7 +29,7 @@ export default function Custom404 () {
className='text-black w-full h-screen text-center justify-center content-center items-center flex flex-col'>
<div className='dark:text-gray-200'>
<h2 className='inline-block border-r-2 border-gray-600 mr-2 px-3 py-2 align-top'><FontAwesomeIcon icon={faSpinner} spin={true} className='mr-2'/>404</h2>
<div className='inline-block text-left h-32 leading-10 align-middle'>
<div className='inline-block text-left h-32 leading-10 items-center'>
<h2 className='m-0 p-0'>页面无法加载即将返回首页</h2>
</div>
</div>

70
pages/about.js Normal file
View File

@@ -0,0 +1,70 @@
import ArticleDetail from '@/components/ArticleDetail'
import BaseLayout from '@/layouts/BaseLayout'
import { useGlobal } from '@/lib/global'
import { getAllCategories, getAllPosts, getAllTags, getPostBlocks } from '@/lib/notion'
import { getNotionPageData } from '@/lib/notion/getNotionData'
import Custom404 from '@/pages/404'
import { getPageTableOfContents } from 'notion-utils'
import 'prismjs'
import 'prismjs/components/prism-bash'
import 'prismjs/components/prism-javascript'
import 'prismjs/components/prism-markup'
import 'prismjs/components/prism-python'
import 'prismjs/components/prism-typescript'
import React from 'react'
import BLOG from '@/blog.config'
/**
* 关于页面默认取notion中slug为about的文章
* @param {*} param0
* @returns
*/
const About = ({ post, blockMap, tags, prev, next, allPosts, categories }) => {
if (!post) {
return <Custom404 />
}
const { locale } = useGlobal()
const meta = {
title: `${BLOG.title} | ${locale.NAV.ABOUT}`,
description: post.summary,
type: 'post',
tags: []
}
return <BaseLayout meta={meta} tags={tags} post={post} totalPosts={allPosts} categories={categories}>
<ArticleDetail post={post} blockMap={blockMap} allPosts={allPosts}/>
</BaseLayout>
}
export async function getStaticProps () {
const from = 'about-props'
const notionPageData = await getNotionPageData({ from })
let allPosts = await getAllPosts({ notionPageData, from, includePage: true })
const post = allPosts.find(p => p.slug === 'about')
if (!post) {
return { props: {}, revalidate: 1 }
}
const blockMap = await getPostBlocks(post.id, 'slug')
post.toc = []
if (blockMap) {
post.content = Object.keys(blockMap.block)
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 About

View File

@@ -1,42 +1,19 @@
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 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'
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 ArticleDetail from '@/components/ArticleDetail'
import { getNotionPageData } from '@/lib/notion/getNotionData'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faEye, faFolderOpen } from '@fortawesome/free-solid-svg-icons'
import BlogAround from '@/components/BlogAround'
import '@/styles/notion.css'
import 'rc-dropdown/assets/index.css'
import 'prismjs/themes/prism-okaidia.css'
const mapPageUrl = id => {
return 'https://www.notion.so/' + id.replace(/-/g, '')
}
const ArticleDetail = ({ post, blockMap, tags, prev, next, allPosts, categories }) => {
/**
* 根据notion的slug访问页面
* @param {*} param0
* @returns
*/
const Slug = ({ post, blockMap, tags, prev, next, allPosts, recommendPosts, categories }) => {
if (!post) {
return <Custom404 />
}
@@ -46,111 +23,9 @@ const ArticleDetail = ({ post, blockMap, tags, prev, next, allPosts, categories
type: 'article',
tags: post.tags
}
const targetRef = useRef(null)
const drawerRight = useRef(null)
const url = BLOG.link + useRouter().asPath
const { locale } = useGlobal()
const date = formatDate(post?.date?.start_date || post.createdTime, BLOG.lang)
return <BaseLayout meta={meta} tags={tags} post={post} totalPosts={allPosts} categories={categories}>
<Progress targetRef={targetRef} />
<div id='article-wrapper' ref={targetRef} className='flex-grow'>
<div className='max-w-5xl mx-auto mt-16 xl:mt-32 w-screen md:w-full'>
<article itemScope itemType="https://schema.org/Movie" className='duration-300 hover:shadow-2xl pt-10 animate__fadeIn animate__animated subpixel-antialiased lg:pt-32 lg:px-52 px-5 py-2 dark:border-gray-700 bg-white dark:bg-gray-800'>
{post.type && !post.type.includes('Page') && (<>
<header className='w-full h-60 lg:h-96 transform duration-200 md:flex-shrink-0 overflow-hidden'>
<Image src={(post.page_cover && post.page_cover.length > 1) ? post.page_cover : BLOG.defaultImgCover} loading='eager' objectFit='cover' layout='fill' alt={post.title} />
</header>
</>)}
{/* 文章Title */}
<h2 className='font-bold text-2xl text-black dark:text-white font-serif pt-10'> {post.title}</h2>
<hr className='mt-4' />
<section className='flex-nowrap flex mt-1 dark:text-white font-light'>
<Link href={`/category/${post.category}`} passHref>
<a 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}
</a>
</Link>
{post.type[0] !== 'Page' && (
<Link href={`/archive#${post?.date?.start_date?.substr(0, 7)}`} passHref>
<a className='pl-1 cursor-pointer hover:text-gray-700 dark:hover:text-gray-200 text-gray-400 dark:text-gray-400 leading-10'>
{date}
</a>
</Link>
)}
<div id='busuanzi_container_page_pv' className='hidden'>
<FontAwesomeIcon icon={faEye} className='text-gray-500 dark:text-gray-400 mt-3 ml-5' />
&nbsp;<span id='busuanzi_value_page_pv' className='text-gray-500 dark:text-gray-400 leading-6'></span>
</div>
</section>
<section className='px-1 py-2 my-1 text-sm font-light bg-gray-50 text-gray-600 dark:bg-gray-700 dark:text-gray-400'>
{post.summary}
</section>
{/* Notion文章主体 */}
{blockMap && (
<NotionRenderer className={`${BLOG.font}`} recordMap={blockMap} mapPageUrl={mapPageUrl}
components={{
equation: Equation,
code: Code,
collectionRow: CollectionRow,
collection: Collection
}}
/>
)}
{/* 推荐文章 */}
<RecommendPosts currentPost={post} totalPosts={allPosts} />
{/* 版权声明 */}
<section className='dark:text-gray-300 mt-6'>
<div className='text-2xl mb-2'>版权声明</div>
<ul className='text-sm dark:bg-gray-900 bg-gray-100 p-5 leading-8 border-l-4 border-red-500'>
<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>
<BlogAround prev={prev} next={next}/>
</article>
{/* 评论互动 */}
<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>
<ArticleDetail post={post} blockMap={blockMap} recommendPosts={recommendPosts} prev={prev} next={next}/>
</BaseLayout>
}
@@ -191,10 +66,58 @@ export async function getStaticProps ({ params: { slug } }) {
const prev = allPosts.slice(index - 1, index)[0] ?? allPosts.slice(-1)[0]
const next = allPosts.slice(index + 1, index + 2)[0] ?? allPosts[0]
const recommendPosts = getRecommendPost(post, allPosts)
return {
props: { post, blockMap, tags, prev, next, allPosts, categories },
props: { post, blockMap, tags, prev, next, allPosts, recommendPosts, categories },
revalidate: 1
}
}
export default ArticleDetail
/**
*
* @param {获取文章推荐文章} post
* @param {*} allPosts
* @param {*} count
* @returns
*/
function getRecommendPost (post, allPosts, count = 5) {
let filteredPosts = Object.create(allPosts)
// 筛选同标签
if (post.tags && post.tags.length) {
const currentTag = post.tags[0]
filteredPosts = filteredPosts.filter(
p =>
p &&
p.tags &&
p.tags.includes(currentTag) &&
p.slug !== post.slug
)
}
shuffleSort(filteredPosts)
// 筛选前5个
if (filteredPosts.length > count) {
filteredPosts = filteredPosts.slice(0, count)
}
return filteredPosts
}
/**
* 洗牌乱序:从数组的最后位置开始,从前面随机一个位置,对两个数进行交换,直到循环完毕
* @param arr
* @returns {*}
*/
function shuffleSort (arr) {
let i = arr.length - 1
while (i > 0) {
const rIndex = Math.floor(Math.random() * i)
const temp = arr[rIndex]
arr[rIndex] = arr[i]
arr[i] = temp
i--
}
return arr
}
export default Slug