diff --git a/components/BlogPostListEmpty.js b/components/BlogPostListEmpty.js index 499331c5..d40f51db 100644 --- a/components/BlogPostListEmpty.js +++ b/components/BlogPostListEmpty.js @@ -10,7 +10,9 @@ const BlogPostListEmpty = () => { const router = useRouter() useEffect(() => { setTimeout(() => { - router.push('/') + router.push('/').then(() => { + console.log('空博客列表跳回首页') + }) }, 3000) }) return
diff --git a/components/BlogPostList.js b/components/BlogPostListPage.js similarity index 95% rename from components/BlogPostList.js rename to components/BlogPostListPage.js index 29c0eab6..bc706fd9 100644 --- a/components/BlogPostList.js +++ b/components/BlogPostListPage.js @@ -13,7 +13,7 @@ import BlogPostListEmpty from '@/components/BlogPostListEmpty' * @returns {JSX.Element} * @constructor */ -const BlogPostList = ({ page = 1, posts = [], tags }) => { +const BlogPostListPage = ({ page = 1, posts = [], tags }) => { let filteredBlogPosts = posts // 处理查询过滤 支持标签、关键词过滤 @@ -68,4 +68,4 @@ const BlogPostList = ({ page = 1, posts = [], tags }) => { } } -export default BlogPostList +export default BlogPostListPage diff --git a/components/BlogPostListScroll.js b/components/BlogPostListScroll.js index f1fd031a..fbd76f6b 100644 --- a/components/BlogPostListScroll.js +++ b/components/BlogPostListScroll.js @@ -1,7 +1,6 @@ import BlogPost from '@/components/BlogPost' import BLOG from '@/blog.config' -import { useRouter } from 'next/router' import { useCallback, useEffect, useRef, useState } from 'react' import throttle from 'lodash.throttle' import BlogPostListEmpty from '@/components/BlogPostListEmpty' @@ -14,34 +13,20 @@ import BlogPostListEmpty from '@/components/BlogPostListEmpty' * @returns {JSX.Element} * @constructor */ -const BlogPostListScroll = ({ posts = [], tags }) => { - let filteredBlogPosts = posts - - // 处理查询过滤 支持标签、关键词过滤 - let currentSearch = '' - const router = useRouter() - if (router.query && router.query.s) { - currentSearch = router.query.s - filteredBlogPosts = posts.filter(post => { - const tagContent = post.tags ? post.tags.join(' ') : '' - const searchContent = post.title + post.summary + tagContent + post.slug - return searchContent.toLowerCase().includes(currentSearch.toLowerCase()) - }) - } - +const BlogPostListScroll = ({ posts = [], tags, currentSearch }) => { + const postsPerPage = BLOG.postsPerPage const [page, updatePage] = useState(1) - const initPosts = getPostByPage(page, filteredBlogPosts, BLOG.postsPerPage) - const [postsToShow, updatePostToShow] = useState(useRef(initPosts).current) + const postsToShow = getPostByPage(page, posts, postsPerPage) let hasMore = false - if (filteredBlogPosts) { - const totalPosts = filteredBlogPosts.length - hasMore = page * BLOG.postsPerPage < totalPosts + if (posts) { + const totalCount = posts.length + hasMore = page * postsPerPage < totalCount } - const handleGetMore = function () { + + const handleGetMore = () => { if (!hasMore) return updatePage(page + 1) - updatePostToShow(postsToShow.concat(getPostByPage(page + 1, filteredBlogPosts, BLOG.postsPerPage))) } // 监听滚动自动分页加载 @@ -67,7 +52,6 @@ const BlogPostListScroll = ({ posts = [], tags }) => { return } else { return
-
{/* 文章列表 */}
{postsToShow.map(post => ( @@ -76,16 +60,16 @@ const BlogPostListScroll = ({ posts = [], tags }) => {
-
{hasMore ? '继续加载' : '加载完了😰'}
+
{ handleGetMore() }} + className='w-full my-4 py-4 bg-gray-300 text-center cursor-pointer dark:bg-gray-700 dark:text-gray-200' + > {hasMore ? '继续加载' : '加载完了😰'}
-
} } /** - * 获取指定页码的文章 + * 获取从第1页到指定页码的文章 * @param page 第几页 * @param totalPosts 所有文章 * @param postsPerPage 每页文章数量 @@ -93,7 +77,7 @@ const BlogPostListScroll = ({ posts = [], tags }) => { */ const getPostByPage = function (page, totalPosts, postsPerPage) { return totalPosts.slice( - postsPerPage * (page - 1), + 0, postsPerPage * page ) } diff --git a/components/Comment.js b/components/Comment.js index 2b19538b..450a2791 100644 --- a/components/Comment.js +++ b/components/Comment.js @@ -1,8 +1,7 @@ import BLOG from '@/blog.config' import dynamic from 'next/dynamic' import { useRouter } from 'next/router' -import { useTheme } from '@/lib/theme' -import { useEffect, useState } from 'react' +import { useGlobal } from '@/lib/global' const GitalkComponent = dynamic( () => { @@ -25,7 +24,7 @@ const CusdisComponent = dynamic( const Comment = ({ frontMatter }) => { const router = useRouter() - const { theme } = useTheme() + const { theme } = useGlobal() return
留下评论
diff --git a/components/CommonHead.js b/components/CommonHead.js index 17f6ad8a..a805d0b3 100644 --- a/components/CommonHead.js +++ b/components/CommonHead.js @@ -2,10 +2,15 @@ import BLOG from '@/blog.config' import Head from 'next/head' const CommonHead = ({ meta }) => { - const url = BLOG.path.length ? `${BLOG.link}/${BLOG.path}` : BLOG.link - + let url = BLOG.path.length ? `${BLOG.link}/${BLOG.path}` : BLOG.link + if (meta) { + url = `${url}/${meta.slug}` + } + const title = meta?.title || BLOG.title + const description = meta?.description || BLOG.description + const type = meta?.type || 'website' return - {meta.title} + {title} @@ -19,18 +24,16 @@ const CommonHead = ({ meta }) => { {BLOG.seo.keywords && ( )} - + - - - + + - + - - + + {meta.type === 'article' && ( <> { - const { changeTheme } = useTheme() + const { changeTheme } = useGlobal() const userTheme = loadUserThemeFromCookies() // 用户手动设置主题 const handleChangeDarkMode = () => { diff --git a/components/JumpToTop.js b/components/JumpToTop.js index 3d2bc93c..cb152c61 100644 --- a/components/JumpToTop.js +++ b/components/JumpToTop.js @@ -1,6 +1,6 @@ import React, { useCallback, useEffect, useState } from 'react' import throttle from 'lodash.throttle' -import { useLocale } from '@/lib/locale' +import { useGlobal } from '@/lib/global' /** * 跳转到网页顶部 @@ -11,7 +11,7 @@ import { useLocale } from '@/lib/locale' * @constructor */ const JumpToTop = ({ targetRef, showPercent = true }) => { - const locale = useLocale() + const { locale } = useGlobal() const [show, switchShow] = useState(false) const [percent, changePercent] = useState(0) const scrollListener = useCallback(throttle(() => { diff --git a/components/MenuButtonGroup.js b/components/MenuButtonGroup.js index 5a756e42..bd8cbb41 100644 --- a/components/MenuButtonGroup.js +++ b/components/MenuButtonGroup.js @@ -1,10 +1,10 @@ import React from 'react' -import { useLocale } from '@/lib/locale' import Link from 'next/link' import { useRouter } from 'next/router' +import { useGlobal } from '@/lib/global' const MenuButtonGroup = ({ allowCollapse = false }) => { - const locale = useLocale() + const { locale } = useGlobal() const router = useRouter() const links = [ { id: 0, icon: 'fa-home', name: locale.NAV.INDEX, to: '/' || '/', show: true }, diff --git a/components/Pagination.js b/components/Pagination.js index 39b284f7..8bc06c1c 100644 --- a/components/Pagination.js +++ b/components/Pagination.js @@ -1,7 +1,7 @@ import BLOG from '@/blog.config' -import { useLocale } from '@/lib/locale' import Link from 'next/link' import { useRouter } from 'next/router' +import { useGlobal } from '@/lib/global' /** * 翻页插件 @@ -11,7 +11,7 @@ import { useRouter } from 'next/router' * @constructor */ const Pagination = ({ page, showNext }) => { - const locale = useLocale() + const { locale } = useGlobal() const router = useRouter() const currentPage = +page return ( diff --git a/components/SearchInput.js b/components/SearchInput.js index 18bd9d29..e01b6a6a 100644 --- a/components/SearchInput.js +++ b/components/SearchInput.js @@ -1,19 +1,17 @@ -import React, { useState } from 'react' -import { useLocale } from '@/lib/locale' import { useRouter } from 'next/router' +import { useGlobal } from '@/lib/global' +import { useState } from 'react' -const SearchInput = ({ currentTag }) => { - const locale = useLocale() +const SearchInput = ({ currentTag, currentSearch }) => { + const { locale } = useGlobal() + const [searchKey, setSearchKey] = useState(currentSearch) const router = useRouter() - const [searchValue, setSearchValue] = useState('') const handleSearch = () => { - if (searchValue && searchValue !== '') { - router.push({ pathname: '/', query: { s: searchValue } }).then(r => { - router.reload() + if (searchKey && searchKey !== '') { + router.push({ pathname: '/search', query: { s: searchKey } }).then(r => { }) } else { router.push({ pathname: '/' }).then(r => { - router.reload() }) } } @@ -29,8 +27,8 @@ const SearchInput = ({ currentTag }) => { placeholder={currentTag ? `${locale.SEARCH.TAGS} #${currentTag}` : `${locale.SEARCH.ARTICLES}`} className={'pl-2 w-full transition duration-200 leading-10 border-gray-300 bg-white text-black dark:bg-gray-900 dark:text-white'} onKeyUp={handleKeyUp} - onChange={e => setSearchValue(e.target.value)} - defaultValue={router.query.s ?? ''} + onChange={e => setSearchKey(e.target.value)} + defaultValue={currentSearch} />
diff --git a/components/TagItem.js b/components/TagItem.js index 4d688514..532bfdbf 100644 --- a/components/TagItem.js +++ b/components/TagItem.js @@ -3,7 +3,7 @@ import Link from 'next/link' const TagItem = ({ tag }) => ( -

{tag}

diff --git a/components/TopNav.js b/components/TopNav.js index 6433cef8..ccf036fa 100644 --- a/components/TopNav.js +++ b/components/TopNav.js @@ -1,13 +1,11 @@ -import Link from 'next/link' -import BLOG from '@/blog.config' -import React, { useRef } from 'react' +import { useRef } from 'react' import DarkModeButton from '@/components/DarkModeButton' import SearchInput from '@/components/SearchInput' import Drawer from '@/components/Drawer' import DrawerRight from '@/components/DrawerRight' import Logo from '@/components/Logo' -const TopNav = ({ tags, currentTag, post, posts }) => { +const TopNav = ({ tags, currentTag, post, posts, currentSearch }) => { const drawer = useRef() const drawerRight = useRef() @@ -31,7 +29,7 @@ const TopNav = ({ tags, currentTag, post, posts }) => { {/* 中间搜索框 */}
- +
{/* 右侧功能 */} diff --git a/layouts/BaseLayout.js b/layouts/BaseLayout.js index 7cb30df3..c0aedea3 100644 --- a/layouts/BaseLayout.js +++ b/layouts/BaseLayout.js @@ -3,13 +3,13 @@ import React, { useCallback, useEffect, useRef } from 'react' import CommonHead from '@/components/CommonHead' import throttle from 'lodash.throttle' import BLOG from '@/blog.config' -import { useTheme } from '@/lib/theme' import TopNav from '@/components/TopNav' import Footer from '@/components/Footer' import SideBar from '@/components/SideBar' import JumpToTop from '@/components/JumpToTop' +import { useGlobal } from '@/lib/global' -const BaseLayout = ({ children, layout, fullWidth, tags, meta, post, posts, ...customMeta }) => { +const BaseLayout = ({ children, layout, fullWidth, tags, meta, post, totalPosts, currentSearch, ...customMeta }) => { let windowTop = 0 const scrollTrigger = useCallback(throttle(() => { const scrollS = window.scrollY @@ -30,17 +30,17 @@ const BaseLayout = ({ children, layout, fullWidth, tags, meta, post, posts, ...c window.removeEventListener('scroll', scrollTrigger) } }) - const { theme } = useTheme() + const { theme } = useGlobal() const targetRef = useRef(null) return (
- + {/* Middle Wrapper */}
- +
{children}
diff --git a/lib/global.js b/lib/global.js new file mode 100644 index 00000000..ba74fe15 --- /dev/null +++ b/lib/global.js @@ -0,0 +1,84 @@ +import BLOG from '@/blog.config' +import lang from './lang' +import { useContext, createContext, useState, useEffect } from 'react' +import cookie from 'react-cookies' + +const GlobalContext = createContext() + +/** + * 全局变量Provider,包括语言本地化、样式主题、搜索词 + * @param children + * @returns {JSX.Element} + * @constructor + */ +export function GlobalContextProvider ({ children }) { + const locale = getCurrentLocale() + const [theme, changeTheme] = useState(loadUserThemeFromCookies()) + useEffect(() => { initTheme(theme, changeTheme) }) + + return ( + {children} + ) +} + +/** + * 获取当前语言字典 + * @returns 不同语言对应字典 + */ +const getCurrentLocale = () => { + if (BLOG.lang.slice(0, 2).toLowerCase() === 'zh') { + switch (BLOG.lang.toLowerCase()) { + case 'zh-cn': + case 'zh-sg': + return lang['zh-CN'] + case 'zh-hk': + return lang['zh-HK'] + case 'zh-tw': + return lang['zh-TW'] + default: + return lang['zh-TW'] + } + } else { + return lang.en + } +} + +/** + * 初始化主题 + * @param theme 用户默认主题state + * @param changeTheme 更改主题ChangeState函数 + * @description 主题样式相关 由于Server采用服务端静态渲染,无法获取前端Cookie配置,故在渲染hooks中做初始化主题 + */ +const initTheme = (theme, changeTheme) => { + if (!theme) { + const date = new Date() + const prefersDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches + const useDark = prefersDarkMode || (date.getHours() >= 18 || date.getHours() < 6) + if (useDark) { + changeTheme('dark') + saveTheme('dark') + } + } + const baseLayoutClass = document.getElementById('wrapper').classList + if (!baseLayoutClass.contains(theme)) { + baseLayoutClass.add(theme) + } +} + +/** + * 读取默认主题 + * @returns {*} + */ +export const loadUserThemeFromCookies = () => { + return cookie.load('theme') +} + +/** + * 保存默认主题 + * @param newTheme + */ +export const saveTheme = (newTheme) => { + cookie.save('theme', newTheme, { path: '/' }) +} + +export const useGlobal = () => useContext(GlobalContext) diff --git a/lib/locale.js b/lib/locale.js deleted file mode 100644 index 860f43cb..00000000 --- a/lib/locale.js +++ /dev/null @@ -1,34 +0,0 @@ -import BLOG from '@/blog.config' -import lang from './lang' -import { useContext, createContext } from 'react' - -let locale = {} -if (BLOG.lang.slice(0, 2).toLowerCase() === 'zh') { - switch (BLOG.lang.toLowerCase()) { - case 'zh-cn': - case 'zh-sg': - locale = lang['zh-CN'] - break - case 'zh-hk': - locale = lang['zh-HK'] - break - case 'zh-tw': - locale = lang['zh-TW'] - break - default: - locale = lang['zh-TW'] - break - } -} else { - locale = lang.en -} - -const LocaleContext = createContext() - -export function LocaleProvider({ children }) { - return ( - {children} - ) -} - -export const useLocale = () => useContext(LocaleContext) diff --git a/lib/theme.js b/lib/theme.js deleted file mode 100644 index 430728f5..00000000 --- a/lib/theme.js +++ /dev/null @@ -1,47 +0,0 @@ -import { useContext, createContext, useState, useEffect, useRef } from 'react' -import cookie from 'react-cookies' - -const ThemeContext = createContext() - -/** - * 提供日间模式、夜间模式主题的切换 light/dark - * @param children - * @returns {JSX.Element} - * @constructor - */ -export function ThemeProvider ({ children }) { - const [theme, changeTheme] = useState(loadUserThemeFromCookies()) - - // 由于Server采用服务端静态渲染,无法获取前端Cookie配置,故在渲染hooks中做初始化主题 - useEffect(() => { - // 若用户当前会话无指定主题,将根据深色偏好及访问时间决定默认主题 - if (!theme) { - const date = new Date() - const prefersDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches - const useDark = prefersDarkMode || (date.getHours() >= 18 || date.getHours() < 6) - if (useDark) { - changeTheme('dark') - saveTheme('dark') - } - } - - const baseLayoutClass = document.getElementById('wrapper').classList - if (!baseLayoutClass.contains(theme)) { - baseLayoutClass.add(theme) - } - }) - - return ( - {children} - ) -} - -export const loadUserThemeFromCookies = () => { - return cookie.load('theme') -} - -export const saveTheme = (newTheme) => { - cookie.save('theme', newTheme, { path: '/' }) -} - -export const useTheme = () => useContext(ThemeContext) diff --git a/pages/_app.js b/pages/_app.js index 18688f55..c0080057 100644 --- a/pages/_app.js +++ b/pages/_app.js @@ -7,16 +7,14 @@ import 'font-awesome/css/font-awesome.min.css' import BLOG from '@/blog.config' import dynamic from 'next/dynamic' -import { LocaleProvider } from '@/lib/locale' -import { ThemeProvider } from '@/lib/theme' +import { GlobalContextProvider } from '@/lib/global' const Ackee = dynamic(() => import('@/components/Ackee'), { ssr: false }) const Gtag = dynamic(() => import('@/components/Gtag'), { ssr: false }) const MyApp = ({ Component, pageProps }) => { return ( - - + {BLOG.isProd && BLOG?.analytics?.provider === 'ackee' && ( { )} {BLOG.isProd && BLOG?.analytics?.provider === 'ga' && } - - + ) } diff --git a/pages/article/[slug].js b/pages/article/[slug].js index ac6f9371..84604100 100644 --- a/pages/article/[slug].js +++ b/pages/article/[slug].js @@ -39,7 +39,7 @@ const BlogPost = ({ post, blockMap, tags, prev, next, posts }) => { const targetRef = useRef(null) const url = BLOG.link + useRouter().asPath - return + return {/* 阅读进度条 */} @@ -61,8 +61,12 @@ const BlogPost = ({ post, blockMap, tags, prev, next, posts }) => { {post.title} +

+ {post.summary} +

+ {/* 文章信息 */} -
+
{post.slug !== 'about' && (<> diff --git a/pages/index.js b/pages/index.js index 0362c321..f1fff823 100644 --- a/pages/index.js +++ b/pages/index.js @@ -27,7 +27,7 @@ export async function getStaticProps () { const Index = ({ posts, tags, meta }) => { return ( - +
diff --git a/pages/search.js b/pages/search.js new file mode 100644 index 00000000..b462c9bf --- /dev/null +++ b/pages/search.js @@ -0,0 +1,57 @@ +import { getAllPosts, getAllTags } from '@/lib/notion' +import BLOG from '@/blog.config' +import BaseLayout from '@/layouts/BaseLayout' +import TagsBar from '@/components/TagsBar' +import BlogPostListScroll from '@/components/BlogPostListScroll' +import { useRouter } from 'next/router' + +export async function getStaticProps () { + let posts = await getAllPosts({ from: 'index' }) + posts = posts.filter( + post => post.status[0] === 'Published' && post.type[0] === 'Post' + ) + const tags = await getAllTags(posts) + const meta = { + title: `${BLOG.title} | ${BLOG.description} `, + description: BLOG.description, + type: 'website' + } + return { + props: { + posts, + tags, + meta + }, + revalidate: 1 + } +} + +const Search = ({ posts, tags, meta }) => { + // 处理查询过滤 支持标签、关键词过滤 + let filteredPosts = [] + const searchKey = getSearchKey() + 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()) + }) + } + return ( + +
+ + +
+
+ ) +} + +export function getSearchKey () { + const router = useRouter() + if (router.query && router.query.s) { + return router.query.s + } + return null +} +export default Search diff --git a/pages/tag/[tag].js b/pages/tag/[tag].js index 51207f17..06be98fe 100644 --- a/pages/tag/[tag].js +++ b/pages/tag/[tag].js @@ -1,7 +1,7 @@ import { getAllPosts, getAllTags } from '@/lib/notion' import BLOG from '@/blog.config' import TagsBar from '@/components/TagsBar' -import BlogPostList from '@/components/BlogPostList' +import BlogPostListPage from '@/components/BlogPostListPage' import BaseLayout from '@/layouts/BaseLayout' export default function Tag ({ tags, posts, currentTag }) { @@ -13,7 +13,7 @@ export default function Tag ({ tags, posts, currentTag }) { return
- +
}