diff --git a/components/DarkModeButton.js b/components/DarkModeButton.js index f1f8784d..62bace09 100644 --- a/components/DarkModeButton.js +++ b/components/DarkModeButton.js @@ -13,9 +13,8 @@ const DarkModeButton = (props) => { htmlElement.classList?.add(newStatus ? 'dark' : 'light') } - return
- + return
+
} export default DarkModeButton diff --git a/lib/notion/getNotionData.js b/lib/notion/getNotionData.js index 977193b2..f2550855 100644 --- a/lib/notion/getNotionData.js +++ b/lib/notion/getNotionData.js @@ -26,6 +26,7 @@ export async function getGlobalNotionData({ }) { // 获取Notion数据 const notionPageData = deepClone(await getNotionPageData({ pageId, from })) + notionPageData.allNavPages = getNavPages({ allPages: notionPageData.allPages }) delete notionPageData.block delete notionPageData.schema delete notionPageData.rawMetadata @@ -168,6 +169,37 @@ function getSiteInfo({ collection, block }) { return { title, description, pageCover, icon } } +/** + * 获取导航pages + * 转为gitbook这类文档主题设计,精减的标题和内容 + * 导航页面的条件,必须是Posts + * @param {*} param0 + */ +export function getNavPages({ allPages }) { + const allNavPages = allPages.filter(post => { + return post && post?.slug && (!post?.slug?.startsWith('http')) && post?.type === 'Post' && post?.status === 'Published' + }) + const result = allNavPages.map(item => ({ id: item.id, title: item.title, category: item.category || null, tags: item.tags || null, summary: item.summary || null, slug: item.slug })) + + const groupedArray = result.reduce((groups, item) => { + const categoryName = item.category ? item.category.join('/') : '' // 将category转换为字符串 + const lastGroup = groups[groups.length - 1] // 获取最后一个分组 + + if (!lastGroup || lastGroup.category !== categoryName) { // 如果当前元素的category与上一个元素不同,则创建新分组 + groups.push({ category: categoryName, items: [] }) + } + + groups[groups.length - 1].items.push(item) // 将元素加入对应的分组 + + return groups + }, []) + + return groupedArray +} + +/** + * 获取公告 + */ async function getNotice(post) { if (!post) { return null @@ -183,6 +215,7 @@ const EmptyData = (pageId) => { notice: null, siteInfo: getSiteInfo({}), allPages: [{ id: 1, title: `无法获取Notion数据,请检查Notion_ID: \n 当前 ${pageId}`, summary: '访问文档获取帮助→ https://tangly1024.com/article/vercel-deploy-notion-next', status: 'Published', type: 'Post', slug: '13a171332816461db29d50e9f575b00d', date: { start_date: '2023-04-24', lastEditedTime: '2023-04-24', tagItems: [] } }], + allNavPages: [], collection: [], collectionQuery: {}, collectionId: null, @@ -275,11 +308,13 @@ async function getDataBaseInfoByNotionAPI({ pageId, from }) { // 新的菜单 const customMenu = await getCustomMenu({ collectionData }) const latestPosts = getLatestPosts({ allPages, from, latestPostCount: 5 }) + const allNavPages = getNavPages({ allPages }) return { notice, siteInfo, allPages, + allNavPages, collection, collectionQuery, collectionId, diff --git a/pages/[...slug].js b/pages/[...slug].js index f28b0a42..a39e81d6 100644 --- a/pages/[...slug].js +++ b/pages/[...slug].js @@ -4,11 +4,10 @@ import { getGlobalNotionData } from '@/lib/notion/getNotionData' import { useEffect, useState } from 'react' import { idToUuid } from 'notion-utils' import { useRouter } from 'next/router' -import { isBrowser } from '@/lib/utils' import { getNotion } from '@/lib/notion/getNotion' import { getPageTableOfContents } from '@/lib/notion/getPageTableOfContents' -import md5 from 'js-md5' import { getLayoutByTheme } from '@/themes/theme' +import md5 from 'js-md5' /** * 根据notion的slug访问页面 @@ -19,16 +18,13 @@ const Slug = props => { const { post, siteInfo } = props const router = useRouter() - // 根据页面路径加载不同Layout文件 - const Layout = getLayoutByTheme(useRouter()) - // 文章锁🔐 const [lock, setLock] = useState(post?.password && post?.password !== '') /** - * 验证文章密码 - * @param {*} result - */ + * 验证文章密码 + * @param {*} result + */ const validPassword = passInput => { const encrypt = md5(post.slug + passInput) if (passInput && encrypt === post.password) { @@ -41,28 +37,28 @@ const Slug = props => { // 文章加载 useEffect(() => { // 404 - if (!post) { - setTimeout(() => { - if (isBrowser()) { - const article = document.getElementById('notion-article') - if (!article) { - router.push('/404').then(() => { - console.warn('找不到页面', router.asPath) - }) - } - } - }, 8 * 1000) // 404时长 8秒 - } + // if (!post) { + // setTimeout(() => { + // if (isBrowser()) { + // const article = document.getElementById('notion-article') + // if (!article) { + // router.push('/404').then(() => { + // console.warn('找不到页面', router.asPath) + // }) + // } + // } + // }, 8 * 1000) // 404时长 8秒 + // } // 文章加密 if (post?.password && post?.password !== '') { setLock(true) } else { + setLock(false) if (!lock && post?.blockMap?.block) { post.content = Object.keys(post.blockMap.block).filter(key => post.blockMap.block[key]?.value?.parent_id === post.id) post.toc = getPageTableOfContents(post, post.blockMap) } - setLock(false) } router.events.on('routeChangeComplete', () => { window.scrollTo({ top: 0, behavior: 'smooth' }) @@ -79,7 +75,8 @@ const Slug = props => { tags: post?.tags } props = { ...props, lock, meta, setLock, validPassword } - + // 根据页面路径加载不同Layout文件 + const Layout = getLayoutByTheme(useRouter()) return } diff --git a/styles/notion.css b/styles/notion.css index c309b68b..746189ec 100644 --- a/styles/notion.css +++ b/styles/notion.css @@ -1142,7 +1142,6 @@ code[class*='language-'] { overflow: hidden; text-overflow: ellipsis; - @apply text-blue-600 dark:text-blue-200 } .notion-table-of-contents-item:hover { diff --git a/themes/gitbook/LayoutBase.js b/themes/gitbook/LayoutBase.js index 8ceb9442..457fcdc6 100644 --- a/themes/gitbook/LayoutBase.js +++ b/themes/gitbook/LayoutBase.js @@ -1,19 +1,19 @@ import CommonHead from '@/components/CommonHead' -import { useState, createContext, useContext } from 'react' +import { useState, createContext, useContext, useEffect } from 'react' import Footer from './components/Footer' import InfoCard from './components/InfoCard' import RevolverMaps from './components/RevolverMaps' import CONFIG_MEDIUM from './config_medium' -import Tabs from '@/components/Tabs' import TopNavBar from './components/TopNavBar' import SearchInput from './components/SearchInput' import BottomMenuBar from './components/BottomMenuBar' import { useGlobal } from '@/lib/global' -import { useRouter } from 'next/router' import Live2D from '@/components/Live2D' import BLOG from '@/blog.config' import BlogPostListScroll from './components/BlogPostListScroll' import ArticleInfo from './components/ArticleInfo' +import Catalog from './components/Catalog' +import { useRouter } from 'next/router' const ThemeGlobalMedium = createContext() /** @@ -22,12 +22,16 @@ const ThemeGlobalMedium = createContext() * @returns {JSX.Element} * @constructor */ -const LayoutBase = props => { - const { children, meta, posts, post, showInfoCard = true, slotLeft, slotRight, slotTop, siteInfo } = props - const { locale } = useGlobal() - const router = useRouter() +const LayoutBase = (props) => { + const { children, meta, post, allNavPages, slotLeft, slotRight, slotTop, siteInfo } = props const [tocVisible, changeTocVisible] = useState(false) + const [filterPosts, setFilterPosts] = useState(allNavPages) const { onLoading } = useGlobal() + const router = useRouter() + + useEffect(() => { + setFilterPosts(allNavPages) + }, [post]) const LoadingCover =
@@ -36,7 +40,7 @@ const LayoutBase = props => {
return ( - +
@@ -46,21 +50,21 @@ const LayoutBase = props => {
{/* 左侧推拉抽屉 */} -
+
-
+
{slotTop} {onLoading ? LoadingCover : children} @@ -81,15 +85,20 @@ const LayoutBase = props => {
{/* 右侧侧推拉抽屉 */} -
+
- {slotRight} - -
- - {CONFIG_MEDIUM.WIDGET_REVOLVER_MAPS === 'true' && } + + +
+ + {slotRight} + {router.route === '/' && <> + + {CONFIG_MEDIUM.WIDGET_REVOLVER_MAPS === 'true' && } + + }
- +
diff --git a/themes/gitbook/LayoutIndex.js b/themes/gitbook/LayoutIndex.js index 6071f9fc..50c89f12 100644 --- a/themes/gitbook/LayoutIndex.js +++ b/themes/gitbook/LayoutIndex.js @@ -1,6 +1,5 @@ import LayoutBase from './LayoutBase' import Announcement from './components/Announcement' -import ArticleInfo from './components/ArticleInfo' export const LayoutIndex = (props) => { return diff --git a/themes/gitbook/LayoutSearch.js b/themes/gitbook/LayoutSearch.js index 1aaf3d20..eaecbd84 100644 --- a/themes/gitbook/LayoutSearch.js +++ b/themes/gitbook/LayoutSearch.js @@ -30,7 +30,7 @@ export const LayoutSearch = (props) => { } }, 100) - }) + }, [currentSearch]) return
diff --git a/themes/gitbook/LayoutSlug.js b/themes/gitbook/LayoutSlug.js index 78b0f5b2..f3b7d058 100644 --- a/themes/gitbook/LayoutSlug.js +++ b/themes/gitbook/LayoutSlug.js @@ -1,11 +1,6 @@ import LayoutBase from './LayoutBase' -import { useGlobal } from '@/lib/global' import React from 'react' -import Catalog from './components/Catalog' import { ArticleLock } from './components/ArticleLock' -import formatDate from '@/lib/formatDate' -import BLOG from '@/blog.config' -import Link from 'next/link' import NotionPage from '@/components/NotionPage' import CONFIG_MEDIUM from './config_medium' import Comment from '@/components/Comment' @@ -16,22 +11,14 @@ import TagItemMini from './components/TagItemMini' import ShareBar from '@/components/ShareBar' export const LayoutSlug = (props) => { - const { post, prev, next, siteInfo, lock, validPassword } = props - const { locale } = useGlobal() - - const date = formatDate( - post?.date?.start_date || post?.createdTime, - locale.LOCALE - ) - - const slotRight = post?.toc && post?.toc?.length > 3 && - console.log(slotRight, post, 'jhhh') + const { post, prev, next, lock, validPassword } = props if (!post) { return } + return ( - + {/* 文章锁 */} {lock && } @@ -40,30 +27,8 @@ export const LayoutSlug = (props) => { {/* title */}

{post?.title}

- {/* meta */} -
-
- {date} - | - {post.lastEditedTime} -
- -
-
- -
- {/* eslint-disable-next-line @next/next/no-img-element */} - {BLOG.AUTHOR} - -
- {BLOG.AUTHOR} -
-
- -
- {/* Notion文章主体 */} -
+
{post && ()}
@@ -73,13 +38,13 @@ export const LayoutSlug = (props) => { {/* 文章分类和标签信息 */}
- {CONFIG_MEDIUM.POST_DETAIL_CATEGORY && post.category && } + {CONFIG_MEDIUM.POST_DETAIL_CATEGORY && post?.category && }
{CONFIG_MEDIUM.POST_DETAIL_TAG && post?.tagItems?.map(tag => )}
- {post.type === 'Post' && } + {post?.type === 'Post' && }
diff --git a/themes/gitbook/components/BlogPostCard.js b/themes/gitbook/components/BlogPostCard.js index fd277508..299bc7f3 100644 --- a/themes/gitbook/components/BlogPostCard.js +++ b/themes/gitbook/components/BlogPostCard.js @@ -1,27 +1,25 @@ import BLOG from '@/blog.config' import Link from 'next/link' +import { useRouter } from 'next/router' import React from 'react' -const BlogPostCard = ({ post, showSummary }) => { +const BlogPostCard = ({ post }) => { + const router = useRouter() + const currentSelected = router.asPath.split('?')[0] === '/' + post.slug return ( -
- +
{post.category} - {post.title}
-
) diff --git a/themes/gitbook/components/BlogPostListScroll.js b/themes/gitbook/components/BlogPostListScroll.js index 2212d51e..ae2f0951 100644 --- a/themes/gitbook/components/BlogPostListScroll.js +++ b/themes/gitbook/components/BlogPostListScroll.js @@ -1,9 +1,6 @@ -import BLOG from '@/blog.config' import BlogPostCard from './BlogPostCard' import BlogPostListEmpty from './BlogPostListEmpty' -import throttle from 'lodash.throttle' -import React, { useCallback, useEffect, useRef, useState } from 'react' -import { useRouter } from 'next/router' +import React, { useRef } from 'react' /** * 博客列表滚动分页 @@ -13,85 +10,19 @@ import { useRouter } from 'next/router' * @constructor */ const BlogPostListScroll = ({ posts = [], currentSearch }) => { - const postsPerPage = BLOG.POSTS_PER_PAGE - const [page, updatePage] = useState(1) - let filteredPosts = Object.assign(posts) - const router = useRouter() - const searchKey = getSearchKey(router) - if (searchKey) { - filteredPosts = posts.filter(post => { - const tagContent = post.tags ? post.tags.join(' ') : '' - const searchContent = post.title + post.summary + tagContent - return searchContent.toLowerCase().includes(searchKey.toLowerCase()) - }) - } - const postsToShow = getPostByPage(page, filteredPosts, postsPerPage) - - let hasMore = false - if (filteredPosts) { - const totalCount = filteredPosts.length - hasMore = page * postsPerPage < totalCount - } - - const handleGetMore = () => { - if (!hasMore) return - updatePage(page + 1) - } - - // 监听滚动自动分页加载 - const scrollTrigger = useCallback(throttle(() => { - const scrollS = window.scrollY + window.outerHeight - const clientHeight = targetRef ? (targetRef.current ? (targetRef.current.clientHeight) : 0) : 0 - if (scrollS > clientHeight + 100) { - handleGetMore() - } - }, 500)) - - // 监听滚动 - useEffect(() => { - window.addEventListener('scroll', scrollTrigger) - return () => { - window.removeEventListener('scroll', scrollTrigger) - } - }) - const targetRef = useRef(null) + const filteredPosts = Object.assign(posts) - if (!postsToShow || postsToShow.length === 0) { + if (!filteredPosts || filteredPosts.length === 0) { return } else { return
- - {/* 文章列表 */} -
- {postsToShow?.map(post => ( - - ))} -
- -
+ {/* 文章列表 */} + {filteredPosts?.map(post => ( + + ))} +
} } -/** - * 获取从第1页到指定页码的文章 - * @param page 第几页 - * @param totalPosts 所有文章 - * @param postsPerPage 每页文章数量 - * @returns {*} - */ -const getPostByPage = function (page, totalPosts, postsPerPage) { - return totalPosts.slice( - 0, - postsPerPage * page - ) -} - -function getSearchKey(router) { - if (router.query && router.query.s) { - return router.query.s - } - return null -} - export default BlogPostListScroll diff --git a/themes/gitbook/components/Catalog.js b/themes/gitbook/components/Catalog.js index c9592dd2..a8f01fcf 100644 --- a/themes/gitbook/components/Catalog.js +++ b/themes/gitbook/components/Catalog.js @@ -1,7 +1,7 @@ -import { useCallback, useEffect, useRef, useState } from 'react' +import { useCallback, useEffect, useState } from 'react' import throttle from 'lodash.throttle' import { uuidToId } from 'notion-utils' -import Progress from './Progress' +import { isBrowser } from '@/lib/utils' /** * 目录导航组件 @@ -9,11 +9,9 @@ import Progress from './Progress' * @returns {JSX.Element} * @constructor */ -const Catalog = ({ toc }) => { +const Catalog = ({ post }) => { const tocIds = [] - - // 目录自动滚动 - const tRef = useRef(null) + const toc = post?.toc // 同步选中目录事件 const [activeSection, setActiveSection] = useState(null) @@ -24,7 +22,7 @@ const Catalog = ({ toc }) => { return () => { window.removeEventListener('scroll', actionSectionScrollSpy) } - }, []) + }, [post]) const throttleMs = 200 const actionSectionScrollSpy = useCallback(throttle(() => { @@ -51,19 +49,18 @@ const Catalog = ({ toc }) => { } setActiveSection(currentSectionId) const index = tocIds.indexOf(currentSectionId) || 0 - tRef?.current?.scrollTo({ top: 28 * index, behavior: 'smooth' }) + if (isBrowser()) { + document?.getElementById('toc-wrapper')?.scrollTo({ top: 28 * index, behavior: 'smooth' }) + } }, throttleMs)) // 无目录就直接返回空 if (!toc || toc.length < 1) { - return <> + return null } - return
-
- -
-
+ return <> +
-
+ } export default Catalog diff --git a/themes/gitbook/components/Footer.js b/themes/gitbook/components/Footer.js index cde8885e..744f9e61 100644 --- a/themes/gitbook/components/Footer.js +++ b/themes/gitbook/components/Footer.js @@ -5,7 +5,7 @@ import DarkModeButton from '@/components/DarkModeButton' const Footer = ({ title }) => { const d = new Date() const currentYear = d.getFullYear() - const copyrightDate = (function() { + const copyrightDate = (function () { if (Number.isInteger(BLOG.SINCE) && BLOG.SINCE < currentYear) { return BLOG.SINCE + '-' + currentYear } @@ -13,22 +13,26 @@ const Footer = ({ title }) => { })() return ( - ) } diff --git a/themes/gitbook/components/MenuItemDrop.js b/themes/gitbook/components/MenuItemDrop.js index 92cd9142..3ef482b5 100644 --- a/themes/gitbook/components/MenuItemDrop.js +++ b/themes/gitbook/components/MenuItemDrop.js @@ -17,7 +17,7 @@ export const MenuItemDrop = ({ link }) => { return
  • changeShow(true)} onMouseOut={() => changeShow(false)} > {hasSubMenu && -
    {link?.icon && } {link?.name} @@ -27,7 +27,7 @@ export const MenuItemDrop = ({ link }) => { } {!hasSubMenu && -
    {link?.icon && } {link?.name} diff --git a/themes/gitbook/components/RevolverMaps.js b/themes/gitbook/components/RevolverMaps.js index d839d85d..c6eb6252 100644 --- a/themes/gitbook/components/RevolverMaps.js +++ b/themes/gitbook/components/RevolverMaps.js @@ -7,7 +7,7 @@ export default function RevolverMaps () { initRevolverMaps() changeLoad(true) } - }) + }, []) return
    } diff --git a/themes/gitbook/components/SearchInput.js b/themes/gitbook/components/SearchInput.js index f6c84d9c..6828d111 100644 --- a/themes/gitbook/components/SearchInput.js +++ b/themes/gitbook/components/SearchInput.js @@ -1,11 +1,11 @@ -import { useRouter } from 'next/router' import { useImperativeHandle, useRef, useState } from 'react' +import { useMediumGlobal } from '../LayoutBase' let lock = false -const SearchInput = ({ currentTag, currentSearch, cRef, className }) => { - const [onLoading, setLoadingState] = useState(false) - const router = useRouter() +const SearchInput = ({ currentSearch, cRef, className }) => { const searchInputRef = useRef() + const { setFilterPosts, allNavPages } = useMediumGlobal() + useImperativeHandle(cRef, () => { return { focus: () => { @@ -15,15 +15,43 @@ const SearchInput = ({ currentTag, currentSearch, cRef, className }) => { }) const handleSearch = () => { - const key = searchInputRef.current.value - - if (key && key !== '') { - setLoadingState(true) - location.href = '/search/' + key + let keyword = searchInputRef.current.value + const filterPosts = [] + if (keyword) { + keyword = keyword.trim() } else { - router.push({ pathname: '/' }).then(r => { - }) + setFilterPosts(allNavPages) } + for (const post of allNavPages) { + const tagContent = post.tags && Array.isArray(post.tags) ? post.tags.join(' ') : '' + const categoryContent = post.category && Array.isArray(post.category) ? post.category.join(' ') : '' + const articleInfo = post.title + post.summary + tagContent + categoryContent + let hit = articleInfo.toLowerCase().indexOf(keyword) > -1 + const indexContent = [post.summary] + // console.log('全文搜索缓存', cacheKey, page != null) + post.results = [] + let hitCount = 0 + for (const i in indexContent) { + const c = indexContent[i] + if (!c) { + continue + } + const index = c.toLowerCase().indexOf(keyword.toLowerCase()) + if (index > -1) { + hit = true + hitCount += 1 + post.results.push(c) + } else { + if ((post.results.length - 1) / hitCount < 3 || i === 0) { + post.results.push(c) + } + } + } + if (hit) { + filterPosts.push(post) + } + } + setFilterPosts(filterPosts) } const handleKeyUp = (e) => { if (e.keyCode === 13) { // 回车 @@ -72,7 +100,7 @@ const SearchInput = ({ currentTag, currentSearch, cRef, className }) => {
    - +
    {(showClean && diff --git a/themes/gitbook/components/TopNavBar.js b/themes/gitbook/components/TopNavBar.js index 8b07a5de..e5081945 100644 --- a/themes/gitbook/components/TopNavBar.js +++ b/themes/gitbook/components/TopNavBar.js @@ -6,6 +6,7 @@ import { useGlobal } from '@/lib/global' import CONFIG_MEDIUM from '../config_medium' import BLOG from '@/blog.config' import { MenuItemDrop } from './MenuItemDrop' +import DarkModeButton from '@/components/DarkModeButton' /** * 顶部导航栏 + 菜单 @@ -67,6 +68,7 @@ export default function TopNavBar(props) { {/* 桌面端顶部菜单 */}
    {links && links?.map(link => )} +