分页模式加载文章预览

This commit is contained in:
tangly1024
2022-01-07 17:49:51 +08:00
parent c89048178e
commit f84409e97f
21 changed files with 143 additions and 72 deletions

View File

@@ -9,7 +9,10 @@ const BLOG = {
home: { // 首页
showHomeBanner: false, // 首页是否显示大图及标语 [true,false]
homeBannerStrings: ['Hi我是一个程序员', 'Hi我是一个打工人', 'Hi我是一个干饭人', '欢迎来到我的博客🎉'], // 首页大图标语文字
homeBannerImage: './bg_image.jpg' // 背景图地址
homeBannerImage: './bg_image.jpg', // 背景图地址
showPostCover: false, // 文章列表显示封面图
showPreview: true, // 列表展示文章预览
showSummary: false // 显示用户自定义摘要
},
lang: 'zh-CN', // ['zh-CN','en-US'] default lang => see /lib/lang.js for more.
notionPageId: process.env.NOTION_PAGE_ID || 'bee1fccfa3bd47a1a7be83cc71372d83', // Important page_id
@@ -23,7 +26,7 @@ const BLOG = {
postListStyle: 'page', // ['page','scroll] 文章列表样式:页码分页、单页滚动加载
postsPerPage: 6, // post counts per page
sortByDate: false,
autoCollapsedNavBar: true, // the automatically collapsed navigation bar
topNavType: 'normal', // ['fixed','autoCollapse','normal'] 分别是固定顶部、固定底部滑动时自动折叠,不固定
menu: { // 菜单栏设置
showAbout: false, // 显示关于
showCategory: true, // 显示分类

View File

@@ -30,7 +30,7 @@ import WordCount from './WordCount'
* @param {*} param0
* @returns
*/
export default function ArticleDetail ({ post, blockMap, recommendPosts, prev, next }) {
export default function ArticleDetail ({ post, recommendPosts, prev, next }) {
const targetRef = useRef(null)
const drawerRight = useRef(null)
const url = BLOG.link + useRouter().asPath
@@ -125,9 +125,9 @@ export default function ArticleDetail ({ post, blockMap, recommendPosts, prev, n
{/* Notion文章主体 */}
<section id='notion-article' className='px-1'>
{blockMap && (
{post.blockMap && (
<NotionRenderer
recordMap={blockMap}
recordMap={post.blockMap}
mapPageUrl={mapPageUrl}
components={{
equation: Equation,

View File

@@ -3,24 +3,25 @@ import Link from 'next/link'
import React from 'react'
import Image from 'next/image'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faFolder } from '@fortawesome/free-solid-svg-icons'
import { faAngleDoubleRight, faFolder } from '@fortawesome/free-solid-svg-icons'
import TagItemMini from './TagItemMini'
import { Code, Collection, CollectionRow, Equation, NotionRenderer } from 'react-notion-x'
import { useGlobal } from '@/lib/global'
const BlogPostCard = ({ post }) => {
const BlogPostCard = ({ post, showSummary }) => {
const { locale } = useGlobal()
return (
<div key={post.id} className='shadow animate__animated animate__fadeIn flex xl:flex-row flex-col-reverse justify-between md:hover:shadow-xl duration-300
<div key={post.id} className='shadow border animate__animated animate__fadeIn flex flex-col-reverse justify-between md:hover:shadow-xl duration-300
w-full bg-white dark:bg-gray-800 dark:hover:bg-gray-700 dark:border-gray-600'>
<div className='p-8 flex flex-col justify-between w-full'>
<div className='lg:p-8 p-4 flex flex-col justify-between w-full'>
<Link href={`${BLOG.path}/article/${post.slug}`} passHref>
<a className='cursor-pointer text-xl xl:text-2xl leading-tight text-black dark:text-gray-100 hover:text-blue-500 dark:hover:text-blue-400'>
<a className='cursor-pointer font-bold text-3xl text-center leading-tight text-gray-700 dark:text-gray-100 hover:text-blue-500 dark:hover:text-blue-400'>
{post.title}
</a>
</Link>
<p className='my-8 text-gray-700 dark:text-gray-300 text-md font-light leading-7'>{post.summary}</p>
<div className='flex items-center justify-between flex-wrap dark:text-gray-500 text-gray-400 hover:text-blue-500 dark:hover:text-blue-400 '>
<div className='flex mt-2 items-center justify-center flex-wrap dark:text-gray-500 text-gray-400 hover:text-blue-500 dark:hover:text-blue-400 '>
<div>
<Link href={`/category/${post.category}`} passHref>
<a className='cursor-pointer font-light text-sm hover:underline transform'>
@@ -37,11 +38,36 @@ const BlogPostCard = ({ post }) => {
</div>
</div>
{showSummary && <p className='mt-4 text-gray-700 dark:text-gray-300 text-sm font-light leading-7'>
{post.summary}
</p>}
{BLOG.home?.showPreview && post?.blockMap && <div className='max-h-screen overflow-hidden truncate max-w-full'>
<NotionRenderer
recordMap={post.blockMap}
mapPageUrl={mapPageUrl}
components={{
equation: Equation,
code: Code,
collectionRow: CollectionRow,
collection: Collection
}}
/>
</div> }
<div className='border-b-2 w-full border-dashed py-2'></div>
<Link href={`${BLOG.path}/article/${post.slug}`} passHref>
<div className='flex items-center cursor-pointer pt-6 justify-end leading-tight'>
<a className='bg-black p-2 text-white'>{locale.COMMON.ARTICLE_DETAIL}
<FontAwesomeIcon icon={faAngleDoubleRight} /></a>
</div>
</Link>
</div>
{post?.page_cover && (
{BLOG.home?.showPostCover && post?.page_cover && (
<Link href={`${BLOG.path}/article/${post.slug}`} passHref>
<div className='h-60 w-full xl:max-w-xs relative xl:h-full duration-200 cursor-pointer transform overflow-hidden'>
<div className='h-72 w-full relative duration-200 cursor-pointer transform overflow-hidden'>
<Image className='hover:scale-105 transform duration-500' src={post?.page_cover} alt={post.title} layout='fill' objectFit='cover' loading='lazy' />
</div>
</Link>
@@ -50,4 +76,8 @@ const BlogPostCard = ({ post }) => {
)
}
const mapPageUrl = id => {
return 'https://www.notion.so/' + id.replace(/-/g, '')
}
export default BlogPostCard

View File

@@ -18,9 +18,9 @@ const BlogPostListPage = ({ page = 1, posts = [], postCount }) => {
return <BlogPostListEmpty />
} else {
return (
<div id="container" className='mt-10'>
<div id="container">
{/* 文章列表 */}
<div className="flex flex-wrap space-y-8 mx-5 md:mx-0">
<div className="flex flex-wrap lg:space-y-4 space-y-1">
{posts.map(post => (
<BlogPostCard key={post.id} post={post} />
))}

View File

@@ -1,10 +1,9 @@
import BlogPostCard from '@/components/BlogPostCard'
import BLOG from '@/blog.config'
import React, { useCallback, useEffect, useRef, useState } from 'react'
import throttle from 'lodash.throttle'
import BlogPostCard from '@/components/BlogPostCard'
import BlogPostListEmpty from '@/components/BlogPostListEmpty'
import { useGlobal } from '@/lib/global'
import throttle from 'lodash.throttle'
import React, { useCallback, useEffect, useRef, useState } from 'react'
/**
* 博客列表滚动分页
@@ -14,7 +13,7 @@ import { useGlobal } from '@/lib/global'
* @returns {JSX.Element}
* @constructor
*/
const BlogPostListScroll = ({ posts = [], currentSearch, currentCategory, currentTag }) => {
const BlogPostListScroll = ({ posts = [], currentSearch, showSummary = BLOG.home.showSummary }) => {
const postsPerPage = BLOG.postsPerPage
const [page, updatePage] = useState(1)
const postsToShow = getPostByPage(page, posts, postsPerPage)
@@ -53,12 +52,12 @@ const BlogPostListScroll = ({ posts = [], currentSearch, currentCategory, curren
if (!postsToShow || postsToShow.length === 0) {
return <BlogPostListEmpty currentSearch={currentSearch} />
} else {
return <div id='container' className='mt-10' ref={targetRef}>
return <div id='container' ref={targetRef}>
{/* 文章列表 */}
<div className='flex flex-wrap space-y-8 mx-5 md:mx-0'>
<div className='flex flex-wrap space-y-8'>
{postsToShow.map(post => (
<BlogPostCard key={post.id} post={post}/>
<BlogPostCard key={post.id} post={post} showSummary={showSummary}/>
))}
</div>

View File

@@ -4,7 +4,7 @@ import React from 'react'
const Logo = () => {
return <Link href='/' passHref>
<div className='flex flex-col justify-center items-center cursor-pointer bg-gray-800 space-y-3 h-32 font-bold mb-4'>
<div className='flex flex-col justify-center items-center cursor-pointer bg-black space-y-3 h-32 font-bold'>
<div className='font-serif text-xl text-white'> {BLOG.title}</div>
<div className='text-sm text-gray-300 font-light'> {BLOG.description}</div>
</div>

View File

@@ -30,7 +30,9 @@ const SideAreaLeft = ({ title, tags, currentTag, post, postCount, categories, cu
{/* 菜单 */}
<section className='shadow hidden lg:block mb-5 pb-4 bg-white dark:bg-gray-800 hover:shadow-xl duration-200'>
<Logo/>
<div className='pt-2'>
<MenuButtonGroup allowCollapse={true} />
</div>
{BLOG.menu.showSearch && <div className='px-5 pt-2'>
<SearchInput currentTag={currentTag} currentSearch={currentSearch} />
</div>}

View File

@@ -6,7 +6,6 @@ import { faAngleDoubleRight, faTag, faThList } from '@fortawesome/free-solid-svg
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import Link from 'next/link'
import React from 'react'
import Logo from './Logo'
/**
* 侧边栏
@@ -30,7 +29,6 @@ const SideBar = ({ title, tags, currentTag, post, slot, categories, currentCateg
<InfoCard />
</section>
<Logo/>
{/* 分类 */}
{categories && (
<section className='mt-8'>

View File

@@ -48,7 +48,7 @@ const SideBarDrawer = ({ post, currentTag, cRef, tags, slot, categories, current
}
return <div id='sidebar-wrapper' className='hidden'>
<div id='sidebar-drawer' className='-ml-80 bg-white dark:bg-gray-900 flex flex-col duration-300 fixed h-full left-0 overflow-y-scroll scroll-hidden top-0 z-50 shadow-2xl'>
<div id='sidebar-drawer' className='-ml-80 bg-white dark:bg-gray-900 flex flex-col duration-300 fixed h-full left-0 overflow-y-scroll scroll-hidden top-0 z-50'>
<SideBar tags={tags} post={post} slot={slot} categories={categories} currentCategory={currentCategory} />
</div>
{/* 背景蒙版 */}

View File

@@ -36,7 +36,7 @@ const StickyBar = ({ children }) => {
return (
<div id='sticky-bar' className='sticky flex-grow justify-center top-14 md:top-0 duration-500 z-10 pb-16'>
<div className='glassmorphism dark:border-gray-600 px-5 absolute shadow-xl border w-full hidden-scroll'>
<div className='glassmorphism dark:border-gray-600 px-5 absolute shadow-md border w-full hidden-scroll'>
<div id='tag-container' className="md:pl-3 overflow-x-auto">
{ children }
</div>

View File

@@ -3,10 +3,10 @@ import SideBarDrawer from '@/components/SideBarDrawer'
import { useGlobal } from '@/lib/global'
import { faBars, faSearch } from '@fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import Link from 'next/link'
import { useCallback, useEffect, useRef } from 'react'
import SearchDrawer from './SearchDrawer'
import throttle from 'lodash.throttle'
import { useCallback, useEffect, useRef } from 'react'
import Logo from './Logo'
import SearchDrawer from './SearchDrawer'
let windowTop = 0
@@ -35,7 +35,7 @@ const TopNav = ({ tags, currentTag, post, slot, categories, currentCategory, aut
// 监听滚动
useEffect(() => {
if (BLOG.autoCollapsedNavBar) {
if (BLOG.topNavType === 'autoCollapse') {
scrollTrigger()
window.addEventListener('scroll', scrollTrigger)
}
@@ -43,30 +43,28 @@ const TopNav = ({ tags, currentTag, post, slot, categories, currentCategory, aut
BLOG.autoCollapsedNavBar && window.removeEventListener('scroll', scrollTrigger)
}
}, [])
return (<div id='top-nav' className='sticky top-0 z-40 block lg:hidden'>
return (<div id='top-nav' className='z-40 block lg:hidden'>
{/* 侧面抽屉 */}
<SideBarDrawer post={post} currentTag={currentTag} cRef={drawer} tags={tags} slot={slot} categories={categories} currentCategory={currentCategory}/>
<SearchDrawer cRef={searchDrawer}/>
{/* 导航栏 */}
<div id='sticky-nav' className='flex animate__animated animate__fadeIn fixed lg:relative w-full top-0 z-20 transform duration-500'>
<div className='w-full flex justify-between items-center p-4 glassmorphism'>
<div id='sticky-nav' className={`${BLOG.topNavType !== 'normal' ? 'fixed' : ''} flex animate__animated animate__fadeIn lg:relative w-full top-0 z-20 transform duration-500`}>
<div className='w-full flex justify-between items-center p-4 bg-black text-white'>
{/* 左侧LOGO 标题 */}
<div className='flex flex-none flex-grow-0'>
<div onClick={() => { drawer.current.handleSwitchSideDrawerVisible() }}
className='w-8 cursor-pointer dark:text-gray-300'>
className='w-8 cursor-pointer'>
<FontAwesomeIcon icon={faBars} size={'lg'}/>
</div>
</div>
<Link href='/' passHref>
<a>
<h1 className='cursor-pointer ml-2 w-full hover:scale-105 duration-200 transform font-serif dark:text-gray-200 whitespace-nowrap overflow-x-hidden'>{ BLOG.title }</h1>
</a>
</Link>
<div className='flex'>
<Logo/>
</div>
{/* 右侧功能 */}
<div className='mr-1 flex flex-nowrap flex-grow justify-end items-center text-sm space-x-4 font-serif dark:text-gray-200'>
<div className='mr-1 flex justify-end items-center text-sm space-x-4 font-serif dark:text-gray-200'>
<div className="cursor-pointer block lg:hidden" onClick={() => { searchDrawer?.current?.show() }}>
<FontAwesomeIcon icon={faSearch} className="mr-2" />{locale.NAV.SEARCH}
</div>

View File

@@ -1,3 +1,4 @@
import BLOG from '@/blog.config'
import CommonHead from '@/components/CommonHead'
import FloatDarkModeButton from '@/components/FloatDarkModeButton'
import Footer from '@/components/Footer'
@@ -59,7 +60,7 @@ const BaseLayout = ({
<main id='wrapper' className='flex justify-center flex-1 mx-auto pb-12'>
<SideAreaLeft targetRef={targetRef} post={post} postCount={postCount} tags={tags} currentSearch={currentSearch} currentTag={currentTag} categories={categories} currentCategory={currentCategory}/>
<section id='center' className='flex-grow mt-14 md:mt-0 max-w-5xl min-h-screen' ref={targetRef}>
<section id='center' className={`${BLOG.topNavType !== 'normal' ? 'mt-14' : ''} flex-grow md:mt-0 max-w-5xl min-h-screen w-full`} ref={targetRef}>
{onLoading
? <LoadingCover/>
: <>

View File

@@ -26,7 +26,9 @@ export default {
POSTS: 'Posts',
VISITORS: 'Visitors',
VIEWS: 'Views',
COPYRIGHT_NOTICE: 'All articles in this blog, except for special statements, adopt BY-NC-SA agreement. Please indicate the source!'
COPYRIGHT_NOTICE: 'All articles in this blog, except for special statements, adopt BY-NC-SA agreement. Please indicate the source!',
RESULT_OF_SEARCH: 'Results Found',
ARTICLE_DETAIL: 'Article Details'
},
PAGINATION: {
PREV: 'Prev',

View File

@@ -28,7 +28,9 @@ export default {
POSTS: '篇文章',
VISITORS: '位访客',
VIEWS: '次查看',
COPYRIGHT_NOTICE: '本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。'
COPYRIGHT_NOTICE: '本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。',
RESULT_OF_SEARCH: '篇搜索到的结果',
ARTICLE_DETAIL: '文章详情'
},
PAGINATION: {
PREV: '上一页',

View File

@@ -20,6 +20,22 @@ export async function getPostBlocks (id, from) {
return null
}
// 去掉不用的字段
for (const j in pageBlock?.block) {
const b = pageBlock?.block[j]
if (b) {
delete b.role
delete b.value?.version
delete b.value?.created_time
delete b.value?.last_edited_time
delete b.value?.created_by_table
delete b.value?.created_by_id
delete b.value?.last_edited_by_table
delete b.value?.last_edited_by_id
delete b.value?.space_id
}
}
if (pageBlock) {
await setDataToCache(cacheKey, pageBlock)
}

View File

@@ -59,6 +59,7 @@ export async function getStaticProps () {
const blockMap = await getPostBlocks(post.id, 'slug')
post.toc = []
if (blockMap) {
post.blockMap = blockMap
post.content = Object.keys(blockMap.block)
post.toc = getPageTableOfContents(post, blockMap)
}

View File

@@ -13,7 +13,6 @@ import { getPageTableOfContents } from 'notion-utils'
*/
const Slug = ({
post,
blockMap,
tags,
prev,
next,
@@ -45,7 +44,6 @@ const Slug = ({
>
<ArticleDetail
post={post}
blockMap={blockMap}
recommendPosts={recommendPosts}
prev={prev}
next={next}
@@ -78,8 +76,10 @@ export async function getStaticProps ({ params: { slug } }) {
const blockMap = await getPostBlocks(post.id, 'slug')
if (blockMap) {
post.blockMap = blockMap
post.content = Object.keys(blockMap.block)
post.toc = getPageTableOfContents(post, blockMap)
delete post.content
}
// 上一篇、下一篇文章关联
@@ -92,7 +92,6 @@ export async function getStaticProps ({ params: { slug } }) {
return {
props: {
post,
blockMap,
tags,
prev,
next,

View File

@@ -1,10 +1,11 @@
import BLOG from '@/blog.config'
import BaseLayout from '@/layouts/BaseLayout'
import BlogPostListScroll from '@/components/BlogPostListScroll'
import { getGlobalNotionData } from '@/lib/notion/getNotionData'
import Header from '@/components/Header'
import BlogPostListPage from '@/components/BlogPostListPage'
import BlogPostListScroll from '@/components/BlogPostListScroll'
import Header from '@/components/Header'
import LatestPostsGroup from '@/components/LatestPostsGroup'
import BaseLayout from '@/layouts/BaseLayout'
import { getPostBlocks } from '@/lib/notion'
import { getGlobalNotionData } from '@/lib/notion/getNotionData'
export async function getStaticProps () {
const from = 'index'
@@ -19,12 +20,20 @@ export async function getStaticProps () {
const page = 1
let postsToShow = []
if (BLOG.postListStyle !== 'page') {
postsToShow = Object.create(allPosts)
postsToShow = Array.from(allPosts)
} else {
postsToShow = allPosts.slice(
BLOG.postsPerPage * (page - 1),
BLOG.postsPerPage * page
)
for (const i in postsToShow) {
const post = postsToShow[i]
const blockMap = await getPostBlocks(post.id, 'slug')
if (blockMap) {
post.blockMap = blockMap
}
}
console.log('加载文章预览完成')
}
return {
@@ -47,15 +56,19 @@ const Index = ({ posts, tags, meta, categories, postCount, latestPosts }) => {
meta={meta}
tags={tags}
sideBarSlot={<LatestPostsGroup posts={latestPosts} />}
rightAreaSlot={BLOG.widget?.showLatestPost && <LatestPostsGroup posts={latestPosts} />}
rightAreaSlot={
BLOG.widget?.showLatestPost && <LatestPostsGroup posts={latestPosts} />
}
postCount={postCount}
categories={categories}
>
{BLOG.postListStyle !== 'page'
? (<BlogPostListScroll posts={posts} tags={tags} />)
: (<BlogPostListPage posts={posts} tags={tags} postCount={postCount} />)
}
? (
<BlogPostListScroll posts={posts} tags={tags} showSummary={true} />
)
: (
<BlogPostListPage posts={posts} tags={tags} postCount={postCount} />
)}
</BaseLayout>
)
}

View File

@@ -3,6 +3,7 @@ import BlogPostListPage from '@/components/BlogPostListPage'
import Header from '@/components/Header'
import LatestPostsGroup from '@/components/LatestPostsGroup'
import BaseLayout from '@/layouts/BaseLayout'
import { getPostBlocks } from '@/lib/notion'
import { getGlobalNotionData } from '@/lib/notion/getNotionData'
import Custom404 from '../404'
@@ -46,15 +47,20 @@ export async function getStaticProps ({ params: { page } }) {
type: 'website'
}
// 处理分页
let postsToShow = []
if (BLOG.postListStyle !== 'page') {
postsToShow = Object.create(allPosts)
} else {
postsToShow = allPosts.slice(
BLOG.postsPerPage * (page - 1),
BLOG.postsPerPage * page
)
const postsToShow = allPosts.slice(
BLOG.postsPerPage * (page - 1),
BLOG.postsPerPage * page
)
for (const i in postsToShow) {
const post = postsToShow[i]
const blockMap = await getPostBlocks(post.id, 'slug')
if (blockMap) {
post.blockMap = blockMap
}
}
console.log('加载文章预览完成')
return {
props: {
page,

View File

@@ -55,11 +55,11 @@ const Search = ({ posts, tags, categories, postCount, latestPosts }) => {
<StickyBar>
<div className="p-4 dark:text-gray-200">
<FontAwesomeIcon icon={faSearch} className="mr-1" />{' '}
{locale.NAV.SEARCH} {searchKey}
{filteredPosts.length} {locale.COMMON.RESULT_OF_SEARCH}
</div>
</StickyBar>
<div className="md:mt-5">
<BlogPostListScroll posts={filteredPosts} tags={tags} />
<BlogPostListScroll posts={filteredPosts} tags={tags} showSummary={true}/>
</div>
</BaseLayout>
)

View File

@@ -387,8 +387,9 @@
}
.notion-h1 {
font-size: 1.875em;
font-size: 1.575em;
margin-top: 1.08em;
@apply border-b w-full
}
.notion-header-anchor {
@@ -1584,7 +1585,7 @@ svg.notion-page-icon {
/* NOTION CSS OVERRIDE */
.notion {
@apply text-gray-600 dark:text-gray-300;
@apply dark:text-gray-300;
overflow-wrap: break-word;
}
.notion,