diff --git a/.env.local b/.env.local index 9ec9cee9..3b00243c 100644 --- a/.env.local +++ b/.env.local @@ -1,5 +1,5 @@ # 环境变量 @see https://www.nextjs.cn/docs/basic-features/environment-variables -NEXT_PUBLIC_VERSION=4.6.0 +NEXT_PUBLIC_VERSION=4.6.1 # 可在此添加环境变量,去掉最左边的(# )注释即可 diff --git a/components/ThemeSwitch.js b/components/ThemeSwitch.js index 788f81b1..33c7ff8f 100644 --- a/components/ThemeSwitch.js +++ b/components/ThemeSwitch.js @@ -1,17 +1,18 @@ import { useGlobal } from '@/lib/global' -import { useState } from 'react' -import { Draggable } from './Draggable' +import LANGS from '@/lib/lang' +import { getQueryParam } from '@/lib/utils' import { THEMES } from '@/themes/theme' import { useRouter } from 'next/router' +import { useState } from 'react' import DarkModeButton from './DarkModeButton' -import { getQueryParam } from '@/lib/utils' -import LANGS from '@/lib/lang' +import { Draggable } from './Draggable' /** * * @returns 主题切换 */ const ThemeSwitch = () => { - const { theme, lang, changeLang, locale, isDarkMode, toggleDarkMode } = useGlobal() + const { theme, lang, changeLang, locale, isDarkMode, toggleDarkMode } = + useGlobal() const router = useRouter() const currentTheme = getQueryParam(router.asPath, 'theme') || theme // const currentLang = getQueryParam(router.asPath, 'lang') || lang @@ -19,8 +20,12 @@ const ThemeSwitch = () => { // 修改当前路径url中的 theme 参数 // 例如 http://localhost?theme=hexo 跳转到 http://localhost?theme=newTheme - const onThemeSelectChange = (e) => { - document.ontouchmove = document.ontouchend = document.onmousemove = document.onmouseup = null + const onThemeSelectChange = e => { + document.ontouchmove = + document.ontouchend = + document.onmousemove = + document.onmouseup = + null setIsLoading(true) const newTheme = e.target.value const query = router.query @@ -28,59 +33,96 @@ const ThemeSwitch = () => { router.push({ pathname: router.pathname, query }).then(() => { setTimeout(() => { setIsLoading(false) - }, 500); + }, 500) }) } - const onLangSelectChange = (e) => { - document.ontouchmove = document.ontouchend = document.onmousemove = document.onmouseup = null + const onLangSelectChange = e => { + document.ontouchmove = + document.ontouchend = + document.onmousemove = + document.onmouseup = + null const newLang = e.target.value changeLang(newLang) } - return (<> - -
- {/* 深色按钮 */} -
- -
{isDarkMode ? locale.MENU.DARK_MODE : locale.MENU.LIGHT_MODE}
-
- - {/* 翻译按钮 */} -
- -
- - -
-
- - {/* 主题切换按钮 */} -
- -
- - -
-
+ return ( + <> + +
+ {/* 主题切换按钮 */} +
+ +
+ +
- +
- {/* 切换主题加载时的全屏遮罩 */} -
- + {/* 深色按钮 */} +
+ +
+ {isDarkMode ? locale.MENU.DARK_MODE : locale.MENU.LIGHT_MODE} +
+
+ + {/* 翻译按钮 */} +
+ +
+ + +
+
+ + + {/* 切换主题加载时的全屏遮罩 */} +
+ +
) } diff --git a/lib/config.js b/lib/config.js index bef4dcd0..b4f70e82 100644 --- a/lib/config.js +++ b/lib/config.js @@ -55,8 +55,8 @@ export const siteConfig = (key, defaultVal = null, extendConfig = {}) => { let siteInfo = null if (global) { - val = global.NOTION_CONFIG?.[key] siteInfo = global.siteInfo + val = global.NOTION_CONFIG?.[key] || global.THEME_CONFIG?.[key] } if (!val) { diff --git a/lib/db/getSiteData.js b/lib/db/getSiteData.js index fb916374..48de0f27 100755 --- a/lib/db/getSiteData.js +++ b/lib/db/getSiteData.js @@ -347,7 +347,6 @@ export function getNavPages({ allPages }) { return ( post && post?.slug && - !post?.slug?.startsWith('http') && post?.type === 'Post' && post?.status === 'Published' ) @@ -512,6 +511,7 @@ async function getDataBaseInfoByNotionAPI({ pageId, from }) { adjustPageProperties(element, NOTION_CONFIG) }) + // 站点基础信息 const siteInfo = getSiteInfo({ collection, block, pageId }) // 文章计数 @@ -525,7 +525,7 @@ async function getDataBaseInfoByNotionAPI({ pageId, from }) { return ( post && post?.slug && - !post?.slug?.startsWith('http') && + // !post?.slug?.startsWith('http') && (post?.status === 'Invisible' || post?.status === 'Published') ) }) diff --git a/lib/global.js b/lib/global.js index 728414a8..51246a6a 100644 --- a/lib/global.js +++ b/lib/global.js @@ -1,6 +1,7 @@ import { APPEARANCE, LANG, NOTION_PAGE_ID, THEME } from '@/blog.config' import { THEMES, + getThemeConfig, initDarkMode, saveDarkModeToLocalStorage } from '@/themes/theme' @@ -29,11 +30,14 @@ export function GlobalContextProvider(props) { tagOptions, NOTION_CONFIG } = props + const [lang, updateLang] = useState(NOTION_CONFIG?.LANG || LANG) // 默认语言 const [locale, updateLocale] = useState( generateLocaleDict(NOTION_CONFIG?.LANG || LANG) ) // 默认语言 const [theme, setTheme] = useState(NOTION_CONFIG?.THEME || THEME) // 默认博客主题 + const [THEME_CONFIG, SET_THEME_CONFIG] = useState(null) // 主题配置 + const defaultDarkMode = NOTION_CONFIG?.APPEARANCE || APPEARANCE const [isDarkMode, updateDarkMode] = useState(defaultDarkMode === 'dark') // 默认深色模式 const [onLoading, setOnLoading] = useState(false) // 抓取文章数据 @@ -54,6 +58,12 @@ export function GlobalContextProvider(props) { return newTheme } + // 抓取主题配置 + const updateThemeConfig = async theme => { + const config = await getThemeConfig(theme) + SET_THEME_CONFIG(config) + } + // 切换深色模式 const toggleDarkMode = () => { const newStatus = !isDarkMode @@ -99,6 +109,9 @@ export function GlobalContextProvider(props) { setOnLoading(false) } + const currentTheme = router?.query?.theme || theme + updateThemeConfig(currentTheme) + router.events.on('routeChangeStart', handleStart) router.events.on('routeChangeError', handleStop) router.events.on('routeChangeComplete', handleStop) @@ -114,6 +127,7 @@ export function GlobalContextProvider(props) { value={{ fullWidth, NOTION_CONFIG, + THEME_CONFIG, toggleDarkMode, onLoading, setOnLoading, diff --git a/lib/lang/en-US.js b/lib/lang/en-US.js index 0a508878..4d1e177b 100644 --- a/lib/lang/en-US.js +++ b/lib/lang/en-US.js @@ -14,11 +14,14 @@ export default { INDEX: 'Home', RSS: 'RSS', SEARCH: 'Search', + NAVIGATOR: 'NAV', ABOUT: 'About', MAIL: 'E-Mail', ARCHIVE: 'Archive' }, COMMON: { + THEME: 'Theme', + ARTICLE_LIST: 'Article List', MORE: 'More', NO_MORE: 'No More', LATEST_POSTS: 'Latest posts', diff --git a/lib/lang/zh-CN.js b/lib/lang/zh-CN.js index 9969452d..85535fd6 100644 --- a/lib/lang/zh-CN.js +++ b/lib/lang/zh-CN.js @@ -20,6 +20,8 @@ export default { ARCHIVE: '归档' }, COMMON: { + THEME: 'Theme', + ARTICLE_LIST: '文章列表', MORE: '更多', NO_MORE: '没有更多了', LATEST_POSTS: '最新发布', @@ -63,7 +65,7 @@ export default { MINUTE: '分钟', WORD_COUNT: '字数', READ_TIME: '阅读时长', - NEXT_POST:'下一篇' + NEXT_POST: '下一篇' }, PAGINATION: { PREV: '上页', diff --git a/lib/lang/zh-HK.js b/lib/lang/zh-HK.js index f434fe6e..6e9df618 100644 --- a/lib/lang/zh-HK.js +++ b/lib/lang/zh-HK.js @@ -5,7 +5,39 @@ export default { RSS: '訂閱', SEARCH: '搜尋', ABOUT: '關於', - MAIL: '電郵' + MAIL: '電郵', + NAVIGATOR: '導航', + ARCHIVE: '封存' + }, + COMMON: { + ARTICLE_LIST: '文章列表', + MORE: '更多', + NO_MORE: '沒有更多了', + LATEST_POSTS: '最新文章', + TAGS: '標籤', + NO_TAG: '無標籤', + CATEGORY: '分類', + SHARE: '分享', + SCAN_QR_CODE: 'QRCode', + URL_COPIED: '連結已複製!', + TABLE_OF_CONTENTS: '目錄', + RELATE_POSTS: '相關文章', + COPYRIGHT: '著作權', + AUTHOR: '作者', + URL: '連結', + ANALYTICS: '分析', + POSTS: '篇文章', + ARTICLE: '文章', + VISITORS: '位訪客', + VIEWS: '次查看', + COPYRIGHT_NOTICE: '本文採用 CC BY-NC-SA 4.0 許可協議,轉載請註明出處。', + RESULT_OF_SEARCH: '篇搜尋到的结果', + ARTICLE_DETAIL: '完整文章', + PASSWORD_ERROR: '密碼錯誤!', + ARTICLE_LOCK_TIPS: '文章已上鎖,請輸入訪問密碼', + SUBMIT: '提交', + POST_TIME: '发布于', + LAST_EDITED_TIME: '最后更新' }, PAGINATION: { PREV: '上一頁', @@ -13,7 +45,7 @@ export default { }, SEARCH: { ARTICLES: '搜尋文章', - TAGS: '搜尋標簽' + TAGS: '搜尋標籤' }, POST: { BACK: '返回', diff --git a/lib/lang/zh-TW.js b/lib/lang/zh-TW.js index de746c08..1fe2dbb1 100644 --- a/lib/lang/zh-TW.js +++ b/lib/lang/zh-TW.js @@ -10,6 +10,7 @@ export default { ARCHIVE: '封存' }, COMMON: { + ARTICLE_LIST: '文章列表', MORE: '更多', NO_MORE: '沒有更多了', LATEST_POSTS: '最新文章', diff --git a/lib/notion/getPageProperties.js b/lib/notion/getPageProperties.js index d3cbc2fb..53d7ce87 100644 --- a/lib/notion/getPageProperties.js +++ b/lib/notion/getPageProperties.js @@ -6,10 +6,9 @@ import formatDate from '../utils/formatDate' import md5 from 'js-md5' import { siteConfig } from '../config' import { - checkStartWithHttp, - convertUrlStartWithOneSlash, - getLastSegmentFromUrl, - sliceUrlFromHttp + checkStartWithHttp, + convertUrlStartWithOneSlash, + getLastSegmentFromUrl } from '../utils' import { extractLangPrefix } from '../utils/pageId' import { mapImgUrl } from './mapImage' @@ -188,29 +187,26 @@ export function adjustPageProperties(properties, NOTION_CONFIG) { properties.name = properties.title ?? '' } - // 开启伪静态路径 - if (siteConfig('PSEUDO_STATIC', false, NOTION_CONFIG)) { - if ( - !properties?.href?.endsWith('.html') && - !properties?.href?.startsWith('http') && - properties?.href !== '' && - properties?.href !== '#' && - properties?.href !== '/' - ) { - properties.href += '.html' - } - } - - // 检查处理外链 - properties.href = checkStartWithHttp(properties?.href) - ? sliceUrlFromHttp(properties?.href) - : convertUrlStartWithOneSlash(properties?.href) - - // 设置链接在页内或新页面打开 - if (properties.href?.indexOf('http') === 0) { + // http or https 开头的视为外链 + if (checkStartWithHttp(properties?.href)) { + properties.href = properties?.slug properties.target = '_blank' } else { properties.target = '_self' + // 伪静态路径右侧拼接.html + if (siteConfig('PSEUDO_STATIC', false, NOTION_CONFIG)) { + if ( + !properties?.href?.endsWith('.html') && + properties?.href !== '' && + properties?.href !== '#' && + properties?.href !== '/' + ) { + properties.href += '.html' + } + } + + // 相对路径转绝对路径:url左侧拼接 / + properties.href = convertUrlStartWithOneSlash(properties?.href) } // 如果跳转链接是多语言,则在新窗口打开 @@ -239,6 +235,10 @@ export function adjustPageProperties(properties, NOTION_CONFIG) { * @returns */ function generateCustomizeSlug(postProperties, NOTION_CONFIG) { + // 外链不处理 + if (checkStartWithHttp(postProperties.slug)) { + return postProperties.slug + } let fullPrefix = '' const allSlugPatterns = siteConfig( 'POST_URL_PREFIX', @@ -290,5 +290,10 @@ function generateCustomizeSlug(postProperties, NOTION_CONFIG) { if (fullPrefix.endsWith('/')) { fullPrefix = fullPrefix.substring(0, fullPrefix.length - 1) // 去掉尾部部的"/" } - return `${fullPrefix}/${postProperties.slug ?? postProperties.id}` + + if(fullPrefix){ + return `${fullPrefix}/${postProperties.slug ?? postProperties.id}` + }else{ + return `${postProperties.slug ?? postProperties.id}` + } } diff --git a/lib/notion/mapImage.js b/lib/notion/mapImage.js index 491632bc..5bb1e9b1 100644 --- a/lib/notion/mapImage.js +++ b/lib/notion/mapImage.js @@ -110,6 +110,8 @@ const compressImage = (image, width, quality = 50, fmt = 'webp') => { return image } + if (image.includes(".svg")) return image + if (!width || width === 0) { width = siteConfig('IMAGE_COMPRESS_WIDTH') } diff --git a/lib/utils/index.js b/lib/utils/index.js index 70122b12..27a6494f 100644 --- a/lib/utils/index.js +++ b/lib/utils/index.js @@ -47,7 +47,12 @@ export const memorize = Component => { return memo(MemoizedComponent) } -// 转换外链 +/** + * 诸如 article/https://test.com 等被错误拼接前缀的slug进行处理 + * 转换为 https://test.com + * @param {*} str + * @returns + */ export function sliceUrlFromHttp(str) { // 检查字符串是否包含http if (str?.includes('http:') || str?.includes('https:')) { @@ -99,7 +104,7 @@ export function checkStartWithHttp(str) { // 检查字符串是否包含http if (str?.indexOf('http:') === 0 || str?.indexOf('https:') === 0) { // 如果包含,找到http的位置 - return str?.indexOf('http') > -1 + return true } else { // 不包含 return false diff --git a/next.config.js b/next.config.js index b586de22..38df2dce 100644 --- a/next.config.js +++ b/next.config.js @@ -32,6 +32,16 @@ const locales = (function () { return langs })() +// 编译前执行 +// const preBuild = (function () { +// // 删除 public/sitemap.xml 文件 ; 否则会和/pages/sitemap.xml.js 冲突。 +// const sitemapPath = path.resolve(__dirname, 'public', 'sitemap.xml') +// if (fs.existsSync(sitemapPath)) { +// fs.unlinkSync(sitemapPath) +// console.log('Deleted existing sitemap.xml from public directory') +// } +// })() + /** * 扫描指定目录下的文件夹名,用于获取所有主题 * @param {*} directory diff --git a/package.json b/package.json index 05e9096a..03ab4942 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "notion-next", - "version": "4.6.0", + "version": "4.6.1", "homepage": "https://github.com/tangly1024/NotionNext.git", "license": "MIT", "repository": { diff --git a/pages/[prefix]/[slug]/[...suffix].js b/pages/[prefix]/[slug]/[...suffix].js index 8e645faa..d9567c4c 100644 --- a/pages/[prefix]/[slug]/[...suffix].js +++ b/pages/[prefix]/[slug]/[...suffix].js @@ -30,17 +30,17 @@ export async function getStaticPaths() { const from = 'slug-paths' const { allPages } = await getGlobalData({ from }) - + const paths = allPages + ?.filter(row => checkSlugHasMorThanTwoSlash(row)) + .map(row => ({ + params: { + prefix: row.slug.split('/')[0], + slug: row.slug.split('/')[1], + suffix: row.slug.split('/').slice(2) + } + })) return { - paths: allPages - ?.filter(row => checkSlugHasMorThanTwoSlash(row)) - .map(row => ({ - params: { - prefix: row.slug.split('/')[0], - slug: row.slug.split('/')[1], - suffix: row.slug.split('/').slice(1) - } - })), + paths: paths, fallback: true } } diff --git a/pages/[prefix]/index.js b/pages/[prefix]/index.js index 4231a181..dc480fcc 100644 --- a/pages/[prefix]/index.js +++ b/pages/[prefix]/index.js @@ -129,7 +129,7 @@ export async function getStaticProps({ params: { prefix }, locale }) { fullSlug += '.html' } } - + // 在列表内查找文章 props.post = props?.allPages?.find(p => { return ( diff --git a/pages/category/[category]/page/[page].js b/pages/category/[category]/page/[page].js index f9c6e909..120d762a 100644 --- a/pages/category/[category]/page/[page].js +++ b/pages/category/[category]/page/[page].js @@ -71,7 +71,7 @@ export async function getStaticPaths() { // 处理文章页数 const postCount = categoryPosts.length const totalPages = Math.ceil( - postCount / siteConfig('POSTS_PER_PAGE', 12, NOTION_CONFIG) + postCount / siteConfig('POSTS_PER_PAGE', null, NOTION_CONFIG) ) if (totalPages > 1) { for (let i = 1; i <= totalPages; i++) { diff --git a/pages/page/[page].js b/pages/page/[page].js index 5040bed0..bd1dc7e2 100644 --- a/pages/page/[page].js +++ b/pages/page/[page].js @@ -23,7 +23,7 @@ export async function getStaticPaths({ locale }) { const from = 'page-paths' const { postCount, NOTION_CONFIG } = await getGlobalData({ from, locale }) const totalPages = Math.ceil( - postCount / siteConfig('POSTS_PER_PAGE', 12, NOTION_CONFIG) + postCount / siteConfig('POSTS_PER_PAGE', null, NOTION_CONFIG) ) return { // remove first page, we 're not gonna handle that. diff --git a/pages/sitemap.xml.js b/pages/sitemap.xml.js index eb7f7bd8..2f6c20fe 100644 --- a/pages/sitemap.xml.js +++ b/pages/sitemap.xml.js @@ -27,7 +27,6 @@ export const getServerSideProps = async ctx => { 'Cache-Control', 'public, max-age=3600, stale-while-revalidate=59' ) - console.log('fff', fields) return getServerSideSitemap(ctx, fields) } @@ -55,7 +54,7 @@ function generateLocalesSitemap(link, allPages, locale) { priority: '0.7' }, { - loc: `${link}${locale}/feed`, + loc: `${link}${locale}/rss/feed.xml`, lastmod: new Date().toISOString().split('T')[0], changefreq: 'daily', priority: '0.7' diff --git a/pages/tag/[tag]/page/[page].js b/pages/tag/[tag]/page/[page].js index 49a184d0..da93f16f 100644 --- a/pages/tag/[tag]/page/[page].js +++ b/pages/tag/[tag]/page/[page].js @@ -56,7 +56,7 @@ export async function getStaticPaths() { // 处理文章页数 const postCount = tagPosts.length const totalPages = Math.ceil( - postCount / siteConfig('POSTS_PER_PAGE', 12, NOTION_CONFIG) + postCount / siteConfig('POSTS_PER_PAGE', null, NOTION_CONFIG) ) if (totalPages > 1) { for (let i = 1; i <= totalPages; i++) { diff --git a/public/js/sakura.js b/public/js/sakura.js index 4d246a35..00b80c95 100644 --- a/public/js/sakura.js +++ b/public/js/sakura.js @@ -123,7 +123,7 @@ function createSakura() { 'style', 'position: fixed;left: 0;top: 0;pointer-events: none;' ) - canvas.setAttribute('id', id) + canvas.setAttribute('id', 'sakura') document.getElementsByTagName('body')[0].appendChild(canvas) cxt = canvas.getContext('2d') var sakuraList = new SakuraList() @@ -183,4 +183,4 @@ function destroySakura() { } window.createSakura = createSakura -window.destroySakura = destroySakura \ No newline at end of file +window.destroySakura = destroySakura diff --git a/styles/globals.css b/styles/globals.css index 2b8f568f..bdc13fe9 100644 --- a/styles/globals.css +++ b/styles/globals.css @@ -264,3 +264,11 @@ a.avatar-wrapper { writing-mode: vertical-rl; /* 竖向排列从右向左 */ text-orientation: upright; /* 文字方向正常 */ } + +/* Chatbase 在移动端禁止遮挡 */ +@media (max-width: 700px) { + button#chatbase-bubble-button { + margin-bottom: 42px; + margin-right: 20px; + } +} diff --git a/themes/commerce/components/BlogPostListPage.js b/themes/commerce/components/BlogPostListPage.js index d762e083..4c3ba5ac 100644 --- a/themes/commerce/components/BlogPostListPage.js +++ b/themes/commerce/components/BlogPostListPage.js @@ -14,7 +14,7 @@ import ProductCard from './ProductCard' */ const BlogPostListPage = ({ page = 1, posts = [], postCount, siteInfo }) => { const { NOTION_CONFIG } = useGlobal() - const POSTS_PER_PAGE = siteConfig('POSTS_PER_PAGE', 12, NOTION_CONFIG) + const POSTS_PER_PAGE = siteConfig('POSTS_PER_PAGE', null, NOTION_CONFIG) const totalPage = Math.ceil(postCount / POSTS_PER_PAGE) const showPagination = postCount >= POSTS_PER_PAGE if (!posts || posts.length === 0 || page > totalPage) { diff --git a/themes/commerce/components/BlogPostListScroll.js b/themes/commerce/components/BlogPostListScroll.js index 004e2b5e..a00b8cc3 100644 --- a/themes/commerce/components/BlogPostListScroll.js +++ b/themes/commerce/components/BlogPostListScroll.js @@ -20,7 +20,7 @@ const BlogPostListScroll = ({ siteInfo }) => { const { NOTION_CONFIG } = useGlobal() - const POSTS_PER_PAGE = siteConfig('POSTS_PER_PAGE', 12, NOTION_CONFIG) + const POSTS_PER_PAGE = siteConfig('POSTS_PER_PAGE', null, NOTION_CONFIG) const [page, updatePage] = useState(1) const postsToShow = getListByPage(posts, page, POSTS_PER_PAGE) diff --git a/themes/commerce/components/SocialButton.js b/themes/commerce/components/SocialButton.js index 555e8ea6..24823924 100644 --- a/themes/commerce/components/SocialButton.js +++ b/themes/commerce/components/SocialButton.js @@ -5,39 +5,101 @@ import { siteConfig } from '@/lib/config' * @constructor */ const SocialButton = () => { - return
-
- {siteConfig('CONTACT_GITHUB') && - - } - {siteConfig('CONTACT_TWITTER') && - - } - {siteConfig('CONTACT_TELEGRAM') && - - } - {siteConfig('CONTACT_LINKEDIN') && - - } - {siteConfig('CONTACT_WEIBO') && - - } - {siteConfig('CONTACT_INSTAGRAM') && - - } - {siteConfig('CONTACT_EMAIL') && - - } - {JSON.parse(siteConfig('ENABLE_RSS')) && - - } - {siteConfig('CONTACT_BILIBILI') && - - } - {siteConfig('CONTACT_YOUTUBE') && - - } + return ( +
+
+ {siteConfig('CONTACT_GITHUB') && ( + + + + )} + {siteConfig('CONTACT_TWITTER') && ( + + + + )} + {siteConfig('CONTACT_TELEGRAM') && ( + + + + )} + {siteConfig('CONTACT_LINKEDIN') && ( + + + + )} + {siteConfig('CONTACT_WEIBO') && ( + + + + )} + {siteConfig('CONTACT_INSTAGRAM') && ( + + + + )} + {siteConfig('CONTACT_EMAIL') && ( + + + + )} + {JSON.parse(siteConfig('ENABLE_RSS')) && ( + + + + )} + {siteConfig('CONTACT_BILIBILI') && ( + + + + )} + {siteConfig('CONTACT_YOUTUBE') && ( + + + + )} +
-
+ ) } export default SocialButton diff --git a/themes/example/components/BlogListPage.js b/themes/example/components/BlogListPage.js index b8a1e573..fe56e0bf 100644 --- a/themes/example/components/BlogListPage.js +++ b/themes/example/components/BlogListPage.js @@ -14,7 +14,7 @@ export const BlogListPage = props => { const { locale, NOTION_CONFIG } = useGlobal() const router = useRouter() const totalPage = Math.ceil( - postCount / siteConfig('POSTS_PER_PAGE', 12, NOTION_CONFIG) + postCount / siteConfig('POSTS_PER_PAGE', null, NOTION_CONFIG) ) const currentPage = +page @@ -45,7 +45,7 @@ export const BlogListPage = props => { : `${pagePrefix}/page/${currentPage - 1}`, query: router.query.s ? { s: router.query.s } : {} }} - className={`${showPrev ? 'bg-black ' : 'bg-gray pointer-events-none '} text-white no-underline py-2 px-3 rounded`}> + className={`${showPrev ? 'bg-black dark:bg-hexo-black-gray' : 'bg-gray pointer-events-none invisible'} text-white no-underline py-2 px-3 rounded`}> {locale.PAGINATION.PREV} { pathname: `${pagePrefix}/page/${currentPage + 1}`, query: router.query.s ? { s: router.query.s } : {} }} - className={`${showNext ? 'bg-black ' : 'bg-gray pointer-events-none '} text-white no-underline py-2 px-3 rounded`}> + className={`${showNext ? 'bg-black dark:bg-hexo-black-gray ' : 'bg-gray pointer-events-none invisible'} text-white no-underline py-2 px-3 rounded`}> {locale.PAGINATION.NEXT}
diff --git a/themes/example/components/BlogListScroll.js b/themes/example/components/BlogListScroll.js index 3ddc340a..d712b919 100644 --- a/themes/example/components/BlogListScroll.js +++ b/themes/example/components/BlogListScroll.js @@ -13,7 +13,7 @@ export const BlogListScroll = props => { const { posts } = props const { locale, NOTION_CONFIG } = useGlobal() const [page, updatePage] = useState(1) - const POSTS_PER_PAGE = siteConfig('POSTS_PER_PAGE', 12, NOTION_CONFIG) + const POSTS_PER_PAGE = siteConfig('POSTS_PER_PAGE', null, NOTION_CONFIG) let hasMore = false const postsToShow = posts diff --git a/themes/example/components/Catalog.js b/themes/example/components/Catalog.js new file mode 100644 index 00000000..161a6cae --- /dev/null +++ b/themes/example/components/Catalog.js @@ -0,0 +1,94 @@ +import throttle from 'lodash.throttle' +import { uuidToId } from 'notion-utils' +import { useCallback, useEffect, useRef, useState } from 'react' + +/** + * 目录导航组件 + * @param toc + * @returns {JSX.Element} + * @constructor + */ +const Catalog = ({ toc }) => { + // 监听滚动事件 + useEffect(() => { + window.addEventListener('scroll', actionSectionScrollSpy) + actionSectionScrollSpy() + return () => { + window.removeEventListener('scroll', actionSectionScrollSpy) + } + }, []) + + // 目录自动滚动 + const tRef = useRef(null) + const tocIds = [] + + // 同步选中目录事件 + const [activeSection, setActiveSection] = useState(null) + const throttleMs = 200 + const actionSectionScrollSpy = useCallback( + throttle(() => { + const sections = document.getElementsByClassName('notion-h') + let prevBBox = null + let currentSectionId = activeSection + for (let i = 0; i < sections.length; ++i) { + const section = sections[i] + if (!section || !(section instanceof Element)) continue + if (!currentSectionId) { + currentSectionId = section.getAttribute('data-id') + } + const bbox = section.getBoundingClientRect() + const prevHeight = prevBBox ? bbox.top - prevBBox.bottom : 0 + const offset = Math.max(150, prevHeight / 4) + // GetBoundingClientRect returns values relative to viewport + if (bbox.top - offset < 0) { + currentSectionId = section.getAttribute('data-id') + prevBBox = bbox + continue + } + // No need to continue loop, if last element has been detected + break + } + setActiveSection(currentSectionId) + const index = tocIds.indexOf(currentSectionId) || 0 + tRef?.current?.scrollTo({ top: 28 * index, behavior: 'smooth' }) + }, throttleMs) + ) + + // 无目录就直接返回空 + if (!toc || toc.length < 1) { + return <> + } + + return ( +
+
+ +
+
+ ) +} + +export default Catalog diff --git a/themes/example/components/SearchInput.js b/themes/example/components/SearchInput.js index 419e9984..043f2961 100644 --- a/themes/example/components/SearchInput.js +++ b/themes/example/components/SearchInput.js @@ -24,7 +24,6 @@ const SearchInput = ({ currentTag, keyword, cRef }) => { const key = searchInputRef.current.value if (key && key !== '') { router.push({ pathname: '/search/' + key }).then(r => { - console.log('搜索', key) }) } else { router.push({ pathname: '/' }).then(r => { diff --git a/themes/example/components/SideBar.js b/themes/example/components/SideBar.js index 68fc5738..35c95b1e 100644 --- a/themes/example/components/SideBar.js +++ b/themes/example/components/SideBar.js @@ -5,6 +5,7 @@ import dynamic from 'next/dynamic' import Link from 'next/link' import CONFIG from '../config' import Announcement from './Announcement' +import Catalog from './Catalog' const ExampleRecentComments = dynamic( () => import('./RecentCommentListForExample') ) @@ -32,6 +33,16 @@ export const SideBar = props => { return ( <> + {/* 目录 */} + {post?.toc && post?.toc.length > 2 && ( + + )} + {/* 分类 */}