From 64a747af0d2d927b20f41d599d28e47908b33f1f Mon Sep 17 00:00:00 2001 From: tangly1024 Date: Tue, 4 Jul 2023 22:27:45 +0800 Subject: [PATCH] matery/medium refactor --- themes/matery/Layout404.js | 34 -- themes/matery/LayoutArchive.js | 36 -- themes/matery/LayoutBase.js | 86 ----- themes/matery/LayoutCategory.js | 40 -- themes/matery/LayoutCategoryIndex.js | 34 -- themes/matery/LayoutIndex.js | 16 - themes/matery/LayoutSearch.js | 101 ----- themes/matery/LayoutSlug.js | 122 ------ themes/matery/LayoutTag.js | 44 --- themes/matery/LayoutTagIndex.js | 34 -- themes/matery/components/BlogListBar.js | 50 +++ themes/matery/components/Catalog.js | 4 +- themes/matery/components/CatalogWrapper.js | 27 ++ .../matery/components/{Header.js => Hero.js} | 4 +- themes/matery/components/RightFloatButtons.js | 19 + themes/matery/components/SearchNav.js | 63 ++++ themes/matery/index.js | 354 +++++++++++++++++- themes/medium/Layout404.js | 9 - themes/medium/LayoutArchive.js | 49 --- themes/medium/LayoutBase.js | 89 ----- themes/medium/LayoutCategory.js | 15 - themes/medium/LayoutCategoryIndex.js | 34 -- themes/medium/LayoutIndex.js | 12 - themes/medium/LayoutPage.js | 10 - themes/medium/LayoutSearch.js | 53 --- themes/medium/LayoutSlug.js | 96 ----- themes/medium/LayoutTag.js | 15 - themes/medium/LayoutTagIndex.js | 29 -- themes/medium/components/ArticleInfo.js | 41 ++ themes/medium/components/BlogArchiveItem.js | 36 ++ themes/medium/components/BlogPostBar.js | 19 + themes/medium/components/LoadingCover.js | 7 + themes/medium/index.js | 323 +++++++++++++++- 33 files changed, 917 insertions(+), 988 deletions(-) delete mode 100644 themes/matery/Layout404.js delete mode 100644 themes/matery/LayoutArchive.js delete mode 100644 themes/matery/LayoutBase.js delete mode 100644 themes/matery/LayoutCategory.js delete mode 100644 themes/matery/LayoutCategoryIndex.js delete mode 100644 themes/matery/LayoutIndex.js delete mode 100644 themes/matery/LayoutSearch.js delete mode 100644 themes/matery/LayoutSlug.js delete mode 100644 themes/matery/LayoutTag.js delete mode 100644 themes/matery/LayoutTagIndex.js create mode 100644 themes/matery/components/BlogListBar.js create mode 100644 themes/matery/components/CatalogWrapper.js rename themes/matery/components/{Header.js => Hero.js} (98%) create mode 100644 themes/matery/components/RightFloatButtons.js create mode 100644 themes/matery/components/SearchNav.js delete mode 100644 themes/medium/Layout404.js delete mode 100644 themes/medium/LayoutArchive.js delete mode 100644 themes/medium/LayoutBase.js delete mode 100644 themes/medium/LayoutCategory.js delete mode 100644 themes/medium/LayoutCategoryIndex.js delete mode 100644 themes/medium/LayoutIndex.js delete mode 100644 themes/medium/LayoutPage.js delete mode 100644 themes/medium/LayoutSearch.js delete mode 100644 themes/medium/LayoutSlug.js delete mode 100644 themes/medium/LayoutTag.js delete mode 100644 themes/medium/LayoutTagIndex.js create mode 100644 themes/medium/components/ArticleInfo.js create mode 100644 themes/medium/components/BlogArchiveItem.js create mode 100644 themes/medium/components/BlogPostBar.js create mode 100644 themes/medium/components/LoadingCover.js diff --git a/themes/matery/Layout404.js b/themes/matery/Layout404.js deleted file mode 100644 index dff7a952..00000000 --- a/themes/matery/Layout404.js +++ /dev/null @@ -1,34 +0,0 @@ -import LayoutBase from './LayoutBase' -import { useRouter } from 'next/router' -import { useEffect } from 'react' - -export const Layout404 = props => { - const router = useRouter() - useEffect(() => { - // 延时3秒如果加载失败就返回首页 - setTimeout(() => { - const article = typeof document !== 'undefined' && document.getElementById('notion-article') - if (!article) { - router.push('/').then(() => { - // console.log('找不到页面', router.asPath) - }) - } - }, 3000) - }) - return ( - -
-
-

- 404 -

-
-

页面未找到

-
-
-
-
- ) -} - -export default Layout404 diff --git a/themes/matery/LayoutArchive.js b/themes/matery/LayoutArchive.js deleted file mode 100644 index 94b40239..00000000 --- a/themes/matery/LayoutArchive.js +++ /dev/null @@ -1,36 +0,0 @@ -import { useEffect } from 'react' -import BlogPostArchive from './components/BlogPostArchive' -import Card from './components/Card' -import HeaderArticle from './components/HeaderArticle' -import LayoutBase from './LayoutBase' - -export const LayoutArchive = (props) => { - const { archivePosts } = props - - useEffect(() => { - const anchor = window.location.hash - if (anchor) { - setTimeout(() => { - const anchorElement = document.getElementById(anchor.substring(1)) - if (anchorElement) { - anchorElement.scrollIntoView({ block: 'start', behavior: 'smooth' }) - } - }, 300) - } - }, []) - return } > - -
- {Object.keys(archivePosts).map(archiveTitle => ( - - ))} -
-
-
-} - -export default LayoutArchive diff --git a/themes/matery/LayoutBase.js b/themes/matery/LayoutBase.js deleted file mode 100644 index 66954859..00000000 --- a/themes/matery/LayoutBase.js +++ /dev/null @@ -1,86 +0,0 @@ -import CommonHead from '@/components/CommonHead' -import { useCallback, useEffect, useState } from 'react' - -import Footer from './components/Footer' -import JumpToTopButton from './components/JumpToTopButton' -import TopNav from './components/TopNav' -import Live2D from '@/components/Live2D' -import LoadingCover from './components/LoadingCover' -import { useGlobal } from '@/lib/global' -import BLOG from '@/blog.config' -import FloatDarkModeButton from './components/FloatDarkModeButton' -import throttle from 'lodash.throttle' -import { isBrowser, loadExternalResource } from '@/lib/utils' -import SocialButton from './components/SocialButton' -import CONFIG_MATERY from './config_matery' - -/** - * 基础布局 采用左右两侧布局,移动端使用顶部导航栏 - * @param props - * @returns {JSX.Element} - * @constructor - */ -const LayoutBase = props => { - const { children, headerSlot, meta, siteInfo } = props - const [show, switchShow] = useState(false) - const { onLoading } = useGlobal() - - const throttleMs = 200 - const scrollListener = useCallback(throttle(() => { - const scrollY = window.pageYOffset - const shouldShow = scrollY > 300 - if (shouldShow !== show) { - switchShow(shouldShow) - } - }, throttleMs)) - - useEffect(() => { - document.addEventListener('scroll', scrollListener) - return () => document.removeEventListener('scroll', scrollListener) - }, []) - - if (isBrowser()) { - loadExternalResource('/css/theme-matery.css', 'css') - } - - return ( -
- - - - - - {headerSlot} - -
- {/* 嵌入区域 */} -
- {props.containerSlot} -
- -
- {onLoading ? : children} -
- -
- - {/* 左下角悬浮 */} -
- -
- - {/* 右下角悬浮 */} -
- - - - {/* 可扩展的右下角悬浮 */} - {props.floatRightBottom} -
- -
-
- ) -} - -export default LayoutBase diff --git a/themes/matery/LayoutCategory.js b/themes/matery/LayoutCategory.js deleted file mode 100644 index d21f8b0b..00000000 --- a/themes/matery/LayoutCategory.js +++ /dev/null @@ -1,40 +0,0 @@ -import BlogPostListScroll from './components/BlogPostListScroll' -import BlogPostListPage from './components/BlogPostListPage' -import LayoutBase from './LayoutBase' -import BLOG from '@/blog.config' -import Link from 'next/link' -import HeaderArticle from './components/HeaderArticle' - -export const LayoutCategory = props => { - const { category, categoryOptions } = props - return ( - } > - -
- -
- -
- {categoryOptions?.map(e => { - const selected = e.name === category - return ( - -
- - {e.name}({e.count}) -
- - ) - })} -
-
- - {BLOG.POST_LIST_STYLE === 'page' ? : } - -
- -
- ) -} - -export default LayoutCategory diff --git a/themes/matery/LayoutCategoryIndex.js b/themes/matery/LayoutCategoryIndex.js deleted file mode 100644 index f6fd0023..00000000 --- a/themes/matery/LayoutCategoryIndex.js +++ /dev/null @@ -1,34 +0,0 @@ -import Link from 'next/link' -import HeaderArticle from './components/HeaderArticle' -import LayoutBase from './LayoutBase' - -export const LayoutCategoryIndex = props => { - const { categoryOptions } = props - - return ( - } > - -
- -
- -
- {categoryOptions.map(e => { - return ( - -
- - {e.name}({e.count}) -
- - ) - })} -
-
- -
-
- ) -} - -export default LayoutCategoryIndex diff --git a/themes/matery/LayoutIndex.js b/themes/matery/LayoutIndex.js deleted file mode 100644 index 6c92d2c4..00000000 --- a/themes/matery/LayoutIndex.js +++ /dev/null @@ -1,16 +0,0 @@ -import BLOG from '@/blog.config' -import BlogPostListPage from './components/BlogPostListPage' -import BlogPostListScroll from './components/BlogPostListScroll' -import Header from './components/Header' -import CONFIG_MATERY from './config_matery' -import LayoutBase from './LayoutBase' -import React from 'react' -import Announcement from './components/Announcement' - -export const LayoutIndex = (props) => { - return } headerSlot={CONFIG_MATERY.HOME_BANNER_ENABLE &&
}> - {BLOG.POST_LIST_STYLE === 'page' ? : } - -} - -export default LayoutIndex diff --git a/themes/matery/LayoutSearch.js b/themes/matery/LayoutSearch.js deleted file mode 100644 index 0de3ed54..00000000 --- a/themes/matery/LayoutSearch.js +++ /dev/null @@ -1,101 +0,0 @@ -import { useRouter } from 'next/router' -import { useEffect, useRef } from 'react' -import BLOG from '@/blog.config' -import BlogPostListScroll from './components/BlogPostListScroll' -import BlogPostListPage from './components/BlogPostListPage' -import LayoutBase from './LayoutBase' -import SearchInput from './components/SearchInput' -import { useGlobal } from '@/lib/global' -import Mark from 'mark.js' -import TagItemMini from './components/TagItemMini' -import Card from './components/Card' -import Link from 'next/link' - -export const LayoutSearch = props => { - const { keyword, tagOptions, categoryOptions } = props - const { locale } = useGlobal() - const router = useRouter() - const currentSearch = keyword || router?.query?.s - const cRef = useRef(null) - - useEffect(() => { - setTimeout(() => { - // 自动聚焦到搜索框 - cRef?.current?.focus() - if (currentSearch) { - const targets = document.getElementsByClassName('replace') - for (const container of targets) { - if (container && container.innerHTML) { - const re = new RegExp(currentSearch, 'gim') - const instance = new Mark(container) - instance.markRegExp(re, { - element: 'span', - className: 'text-red-500 border-b border-dashed' - }) - } - } - } - }, 100) - }) - return ( - - {!currentSearch && <> -
- - {/* 分类 */} - -
- - {locale.COMMON.CATEGORY}: -
-
- {categoryOptions?.map(category => { - return ( - -
- - {category.name}({category.count}) -
- - ) - })} -
-
- {/* 标签 */} - -
- - {locale.COMMON.TAGS}: -
-
- {tagOptions?.map(tag => { - return ( -
- -
- ) - })} -
-
-
- } - - {currentSearch && <> -
- {BLOG.POST_LIST_STYLE === 'page' ? : } -
- } - -
- ) -} - -export default LayoutSearch diff --git a/themes/matery/LayoutSlug.js b/themes/matery/LayoutSlug.js deleted file mode 100644 index e5a4fc53..00000000 --- a/themes/matery/LayoutSlug.js +++ /dev/null @@ -1,122 +0,0 @@ -import React, { useCallback, useEffect } from 'react' -import { ArticleLock } from './components/ArticleLock' -import HeaderArticle from './components/HeaderArticle' -import LayoutBase from './LayoutBase' -import Comment from '@/components/Comment' -import NotionPage from '@/components/NotionPage' -import ArticleAdjacent from './components/ArticleAdjacent' -import ArticleCopyright from './components/ArticleCopyright' -import { ArticleInfo } from './components/ArticleInfo' -import Catalog from './components/Catalog' -import JumpToCommentButton from './components/JumpToCommentButton' -import throttle from 'lodash.throttle' -import ShareBar from '@/components/ShareBar' -import Announcement from './components/Announcement' - -export const LayoutSlug = props => { - const { post, lock, validPassword } = props - - const [show, switchShow] = React.useState(false) - const throttleMs = 200 - - const scrollListener = useCallback(throttle(() => { - const scrollY = window.pageYOffset - const shouldShow = scrollY > 220 && post?.toc?.length > 0 - if (shouldShow !== show) { - switchShow(shouldShow) - } - }, throttleMs)) - - useEffect(() => { - document.addEventListener('scroll', scrollListener) - return () => document.removeEventListener('scroll', scrollListener) - }, []) - - if (!post) { - return } - {...props} - showCategory={false} - showTag={false} - > - } - - return (} - {...props} - showCategory={false} - showTag={false} - floatRightBottom={} - > - -
- {/* 文章主体卡片 */} -
- {lock && } - - {!lock &&
- {post?.type && post?.type === 'Post' && <> -
- -
-
- } - -
-
- {/* Notion文章主体 */} -
- {post && } -
- - {/* 分享 */} - - {/* 文章版权说明 */} - {post.type === 'Post' && } - -
- -
- - {/* 评论互动 */} -
- -
-
- -
} -
- - {/* 底部文章推荐 */} - {post.type === 'Post' && } - - - - {/* 右侧文章目录 */} - {post?.toc?.length > 0 &&
-
-
-
- -
-
-
-
} - -
- -
- ) -} - -export default LayoutSlug diff --git a/themes/matery/LayoutTag.js b/themes/matery/LayoutTag.js deleted file mode 100644 index 0de70bbd..00000000 --- a/themes/matery/LayoutTag.js +++ /dev/null @@ -1,44 +0,0 @@ -import BLOG from '@/blog.config' -import BlogPostListScroll from './components/BlogPostListScroll' -import BlogPostListPage from './components/BlogPostListPage' -import LayoutBase from './LayoutBase' -import React from 'react' -import HeaderArticle from './components/HeaderArticle' -import { useGlobal } from '@/lib/global' -import TagItemMiddle from './components/TagItemMiddle' - -export const LayoutTag = (props) => { - const { tagOptions, tag } = props - - const { locale } = useGlobal() - - return } > - -
- -
- -
- {locale.COMMON.TAGS} -
- -
- {tagOptions?.map(e => { - const selected = tag === e.name - return ( -
- -
- ) - })} -
-
- - {BLOG.POST_LIST_STYLE === 'page' ? : } - -
- -
-} - -export default LayoutTag diff --git a/themes/matery/LayoutTagIndex.js b/themes/matery/LayoutTagIndex.js deleted file mode 100644 index f25ba122..00000000 --- a/themes/matery/LayoutTagIndex.js +++ /dev/null @@ -1,34 +0,0 @@ -import { useGlobal } from '@/lib/global' -import HeaderArticle from './components/HeaderArticle' -import TagItemMiddle from './components/TagItemMiddle' -import LayoutBase from './LayoutBase' - -export const LayoutTagIndex = props => { - const { tagOptions } = props - const { locale } = useGlobal() - return ( - } > -
- -
- -
- {locale.COMMON.TAGS} -
- -
- {tagOptions.map(tag => { - return ( -
- -
- ) - })} -
-
-
-
- ) -} - -export default LayoutTagIndex diff --git a/themes/matery/components/BlogListBar.js b/themes/matery/components/BlogListBar.js new file mode 100644 index 00000000..4d4b2387 --- /dev/null +++ b/themes/matery/components/BlogListBar.js @@ -0,0 +1,50 @@ +import Link from 'next/link' +import { useGlobal } from '@/lib/global' +import TagItemMiddle from './components/TagItemMiddle' + +export default function BlogListBar(props) { + const { category, categoryOptions, tag, tagOptions } = props + const { locale } = useGlobal() + + if (category) { + return ( +
+ +
+ {categoryOptions?.map(e => { + const selected = e.name === category + return ( + +
+ + {e.name}({e.count}) +
+ + ) + })} +
+
+ + ) + } else if (tag) { + return
+ +
+ {locale.COMMON.TAGS} +
+ +
+ {tagOptions?.map(e => { + const selected = tag === e.name + return ( +
+ +
+ ) + })} +
+
+ } else { + return <> + } +} diff --git a/themes/matery/components/Catalog.js b/themes/matery/components/Catalog.js index 56bff03e..448b6b08 100644 --- a/themes/matery/components/Catalog.js +++ b/themes/matery/components/Catalog.js @@ -1,4 +1,4 @@ -import React, { useRef } from 'react' +import { useEffect, useRef } from 'react' import throttle from 'lodash.throttle' import { uuidToId } from 'notion-utils' import Progress from './Progress' @@ -13,7 +13,7 @@ import { useGlobal } from '@/lib/global' const Catalog = ({ toc }) => { const { locale } = useGlobal() // 监听滚动事件 - React.useEffect(() => { + useEffect(() => { window.addEventListener('scroll', actionSectionScrollSpy) actionSectionScrollSpy() return () => { diff --git a/themes/matery/components/CatalogWrapper.js b/themes/matery/components/CatalogWrapper.js new file mode 100644 index 00000000..7d64e2ad --- /dev/null +++ b/themes/matery/components/CatalogWrapper.js @@ -0,0 +1,27 @@ +import Catalog from './Catalog' + +/** + * 目录 + * @param {*} param0 + * @returns + */ +export default function CatalogWrapper({ post }) { + if (post?.toc?.length > 0) { + return
+
+
+
+ +
+
+
+
+ } else { + return <> + } +} diff --git a/themes/matery/components/Header.js b/themes/matery/components/Hero.js similarity index 98% rename from themes/matery/components/Header.js rename to themes/matery/components/Hero.js index 42a75663..c573703d 100644 --- a/themes/matery/components/Header.js +++ b/themes/matery/components/Hero.js @@ -11,7 +11,7 @@ let wrapperTop = 0 * * @returns 头图 */ -const Header = props => { +const Hero = props => { const [typed, changeType] = useState() const { siteInfo } = props const { locale } = useGlobal() @@ -71,4 +71,4 @@ const Header = props => { ) } -export default Header +export default Hero diff --git a/themes/matery/components/RightFloatButtons.js b/themes/matery/components/RightFloatButtons.js new file mode 100644 index 00000000..dc4a1bd8 --- /dev/null +++ b/themes/matery/components/RightFloatButtons.js @@ -0,0 +1,19 @@ +import JumpToTopButton from './components/JumpToTopButton' +import FloatDarkModeButton from './components/FloatDarkModeButton' +import SocialButton from './components/SocialButton' + +/** + * 右下角悬浮按钮 + * @param {*} param0 + * @returns + */ +export default function RightFloatButtons({ props }) { + const { floatRightBottom } = props + return
+ + + + {/* 可扩展的右下角悬浮 */} + {floatRightBottom} +
+} diff --git a/themes/matery/components/SearchNav.js b/themes/matery/components/SearchNav.js new file mode 100644 index 00000000..0c992ebf --- /dev/null +++ b/themes/matery/components/SearchNav.js @@ -0,0 +1,63 @@ + +import SearchInput from './components/SearchInput' +import TagItemMini from './components/TagItemMini' +import Card from './components/Card' +import Link from 'next/link' +import { useEffect, useRef } from 'react' +import { useGlobal } from '@/lib/global' + +/** + * 搜索页面的导航条 + * @param {*} props + * @returns + */ +export default function SearchNave(props) { + const cRef = useRef(null) + const { locale } = useGlobal() + const { tagOptions, categoryOptions } = props + + useEffect(() => { + setTimeout(() => { + // 自动聚焦到搜索框 + cRef?.current?.focus() + }, 100) + }) + return <> +
+ + {/* 分类 */} + +
+ + {locale.COMMON.CATEGORY}: +
+
+ {categoryOptions?.map(category => { + return ( + +
+ {category.name}({category.count}) +
+ + ) + })} +
+
+ {/* 标签 */} + +
+ {locale.COMMON.TAGS}: +
+
+ {tagOptions?.map(tag => { + return ( +
+ +
+ ) + })} +
+
+
+ +} diff --git a/themes/matery/index.js b/themes/matery/index.js index c767fcd3..1537b68c 100644 --- a/themes/matery/index.js +++ b/themes/matery/index.js @@ -1,25 +1,353 @@ import CONFIG_MATERY from './config_matery' -import LayoutIndex from './LayoutIndex' -import LayoutSearch from './LayoutSearch' -import LayoutArchive from './LayoutArchive' -import LayoutSlug from './LayoutSlug' -import Layout404 from './Layout404' -import LayoutCategory from './LayoutCategory' -import LayoutCategoryIndex from './LayoutCategoryIndex' -import LayoutPage from './LayoutPage' -import LayoutTag from './LayoutTag' -import LayoutTagIndex from './LayoutTagIndex' +import CommonHead from '@/components/CommonHead' +import TopNav from './components/TopNav' +import Live2D from '@/components/Live2D' +import LoadingCover from './components/LoadingCover' +import { useGlobal } from '@/lib/global' +import BLOG from '@/blog.config' +import { isBrowser, loadExternalResource } from '@/lib/utils' +import Footer from './components/Footer' +import { useEffect } from 'react' +import RightFloatButtons from './components/RightFloatButtons' +import { useRouter } from 'next/router' +import Mark from 'mark.js' +import SearchNave from './components/SearchNav' +import BlogPostListPage from './components/BlogPostListPage' +import BlogPostListScroll from './components/BlogPostListScroll' +import Hero from './components/Hero' +import Announcement from './components/Announcement' +import CatalogWrapper from './components/CatalogWrapper' +import TagItemMiddle from './components/TagItemMiddle' +import HeaderArticle from './components/HeaderArticle' +import Link from 'next/link' +import ArticleAdjacent from './components/ArticleAdjacent' +import Comment from '@/components/Comment' +import ArticleCopyright from './components/ArticleCopyright' +import ShareBar from '@/components/ShareBar' +import NotionPage from '@/components/NotionPage' +import { ArticleInfo } from './components/ArticleInfo' +import { ArticleLock } from './components/ArticleLock' +import BlogPostArchive from './components/BlogPostArchive' +import Card from './components/Card' +import JumpToCommentButton from './components/JumpToCommentButton' +import BlogListBar from './components/BlogListBar' + +/** + * 基础布局 + * 采用左右两侧布局,移动端使用顶部导航栏 + * @param props + * @returns {JSX.Element} + * @constructor + */ +const LayoutBase = props => { + const { children, headerSlot, meta, siteInfo, containerSlot, post } = props + const { onLoading } = useGlobal() + + if (isBrowser()) { + loadExternalResource('/css/theme-matery.css', 'css') + } + + return ( +
+ {/* SEO相关 */} + + + {/* 顶部导航栏 */} + + + {/* 顶部嵌入 */} + {headerSlot} + +
+ {/* 嵌入区域 */} +
+ {containerSlot} +
+ +
+ {onLoading ? : children} +
+ +
+ + {/* 左下角悬浮 */} +
+ +
+ + {/* 右下角悬浮 */} + + + {/* 页脚 */} +
+
+ ) +} + +/** + * 首页 + * 首页就是一个文章列表,但是嵌入了Hero大图和公告 + * @param {*} props + * @returns + */ +const LayoutIndex = (props) => { + return } headerSlot={CONFIG_MATERY.HOME_BANNER_ENABLE && } /> +} + +/** + * 博客列表 + * @param {*} props + * @returns + */ +const LayoutPostList = (props) => { + return ( + + + {BLOG.POST_LIST_STYLE === 'page' ? : } + + ) +} + +/** + * 搜搜 + * @param {*} props + * @returns + */ +const LayoutSearch = props => { + const { keyword } = props + const router = useRouter() + const currentSearch = keyword || router?.query?.s + + useEffect(() => { + setTimeout(() => { + if (currentSearch) { + const targets = document.getElementsByClassName('replace') + for (const container of targets) { + if (container && container.innerHTML) { + const re = new RegExp(currentSearch, 'gim') + const instance = new Mark(container) + instance.markRegExp(re, { + element: 'span', + className: 'text-red-500 border-b border-dashed' + }) + } + } + } + }, 100) + }) + return ( + + {!currentSearch + ? + :
+ {BLOG.POST_LIST_STYLE === 'page' ? : } +
} +
+ ) +} + +/** + * 归档 + * @param {*} props + * @returns + */ +const LayoutArchive = (props) => { + const { archivePosts } = props + return } > + +
+ {Object.keys(archivePosts).map(archiveTitle => ( + + ))} +
+
+
+} + +/** + * 文章详情页 + * @param {*} props + * @returns + */ +const LayoutSlug = props => { + const { post, lock, validPassword } = props + + return (} showCategory={false} showTag={false} floatRightBottom={}> + +
+ + {/* 文章主体卡片 */} +
+ + {lock && } + + {!lock &&
+ + {/* 文章信息 */} + {post?.type && post?.type === 'Post' && <> +
+ +
+
+ } + +
+ +
+ + {/* Notion文章主体 */} +
+ {post && } +
+ + {/* 分享 */} + + + {/* 版权说明 */} + {post.type === 'Post' && } + +
+ +
+ + {/* 评论互动 */} +
+ +
+
+ +
} +
+ + {/* 底部文章推荐 */} + {post.type === 'Post' && } + + {/* 底部公告 */} + + + {/* 右侧文章目录 */} + + +
+ +
+ ) +} + +/** + * 404 + * @param {*} props + * @returns + */ +const Layout404 = props => { + const router = useRouter() + useEffect(() => { + // 延时3秒如果加载失败就返回首页 + setTimeout(() => { + const article = typeof document !== 'undefined' && document.getElementById('notion-article') + if (!article) { + router.push('/').then(() => { + // console.log('找不到页面', router.asPath) + }) + } + }, 3000) + }) + return ( + +
+
+

+ 404 +

+
+

页面未找到

+
+
+
+
+ ) +} + +/** + * 分类列表 + * @param {*} props + * @returns + */ +const LayoutCategoryIndex = props => { + const { categoryOptions } = props + + return ( + } > + +
+
+
+ {categoryOptions.map(e => { + return ( + +
+ {e.name}({e.count}) +
+ + ) + })} +
+
+
+
+ ) +} + +/** + * 标签列表 + * @param {*} props + * @returns + */ +const LayoutTagIndex = props => { + const { tagOptions } = props + const { locale } = useGlobal() + return ( + } > +
+ +
+ +
+ {locale.COMMON.TAGS} +
+ +
+ {tagOptions.map(tag => { + return ( +
+ +
+ ) + })} +
+
+
+
+ ) +} export { CONFIG_MATERY as THEME_CONFIG, LayoutIndex, + LayoutPostList, LayoutSearch, LayoutArchive, LayoutSlug, Layout404, - LayoutCategory, LayoutCategoryIndex, - LayoutPage, - LayoutTag, LayoutTagIndex } diff --git a/themes/medium/Layout404.js b/themes/medium/Layout404.js deleted file mode 100644 index 68b656cc..00000000 --- a/themes/medium/Layout404.js +++ /dev/null @@ -1,9 +0,0 @@ -import LayoutBase from './LayoutBase' - -export const Layout404 = props => { - return -
404 Not found.
-
-} - -export default Layout404 diff --git a/themes/medium/LayoutArchive.js b/themes/medium/LayoutArchive.js deleted file mode 100644 index 083dd8ac..00000000 --- a/themes/medium/LayoutArchive.js +++ /dev/null @@ -1,49 +0,0 @@ -import BLOG from '@/blog.config' -import Link from 'next/link' -import LayoutBase from './LayoutBase' - -export const LayoutArchive = props => { - const { archivePosts } = props - - return ( - -
- {Object.keys(archivePosts)?.map(archiveTitle => ( -
-
- {archiveTitle} -
-
    - {archivePosts[archiveTitle]?.map(post => ( -
  • -
    - - {post.date?.start_date} - {' '} -   - - - {post.title} - - -
    -
  • - ))} -
-
- ))} -
-
- ) -} - -export default LayoutArchive diff --git a/themes/medium/LayoutBase.js b/themes/medium/LayoutBase.js deleted file mode 100644 index be8cfa41..00000000 --- a/themes/medium/LayoutBase.js +++ /dev/null @@ -1,89 +0,0 @@ -import CommonHead from '@/components/CommonHead' -import { useState, createContext, useContext } 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 Announcement from './components/Announcement' -import JumpToTopButton from './components/JumpToTopButton' -const ThemeGlobalMedium = createContext() - -/** - * 基础布局 采用左右两侧布局,移动端使用顶部导航栏 - - * @returns {JSX.Element} - * @constructor - */ -const LayoutBase = props => { - const { children, meta, showInfoCard = true, slotRight, slotTop, siteInfo, notice } = props - const { locale } = useGlobal() - const router = useRouter() - const [tocVisible, changeTocVisible] = useState(false) - const { onLoading } = useGlobal() - - const LoadingCover =
-
- -
-
- - return ( - - - -
- -
- {/* 桌面端左侧菜单 */} - {/* */} - -
- {/* 顶部导航栏 */} - - -
- {slotTop} - - {onLoading ? LoadingCover : children} - - -
- - {/* 底部 */} -
-
- - {/* 桌面端右侧 */} -
-
- - {slotRight} -
- {router.pathname !== '/search' && } - {showInfoCard && } - {CONFIG_MEDIUM.WIDGET_REVOLVER_MAPS === 'true' && } -
-
- - -
-
-
- - {/* 移动端底部导航栏 */} - -
-
- ) -} - -export default LayoutBase -export const useMediumGlobal = () => useContext(ThemeGlobalMedium) diff --git a/themes/medium/LayoutCategory.js b/themes/medium/LayoutCategory.js deleted file mode 100644 index a947b70e..00000000 --- a/themes/medium/LayoutCategory.js +++ /dev/null @@ -1,15 +0,0 @@ -import LayoutBase from './LayoutBase' -import BlogPostListScroll from './components/BlogPostListScroll' -import BlogPostListPage from './components/BlogPostListPage' -import BLOG from '@/blog.config' - -export const LayoutCategory = props => { - const { category } = props - const slotTop =
分类:
{category}
- - return - {BLOG.POST_LIST_STYLE === 'page' ? : } - -} - -export default LayoutCategory diff --git a/themes/medium/LayoutCategoryIndex.js b/themes/medium/LayoutCategoryIndex.js deleted file mode 100644 index 2c8121ad..00000000 --- a/themes/medium/LayoutCategoryIndex.js +++ /dev/null @@ -1,34 +0,0 @@ -import { useGlobal } from '@/lib/global' -import Link from 'next/link' -import LayoutBase from './LayoutBase' - -export const LayoutCategoryIndex = (props) => { - const { categoryOptions } = props - const { locale } = useGlobal() - return ( - -
-
- {locale.COMMON.CATEGORY}: -
-
- {categoryOptions?.map(category => { - return ( - -
- {category.name}({category.count}) -
- - ) - })} -
-
-
- ) -} -export default LayoutCategoryIndex diff --git a/themes/medium/LayoutIndex.js b/themes/medium/LayoutIndex.js deleted file mode 100644 index 41410467..00000000 --- a/themes/medium/LayoutIndex.js +++ /dev/null @@ -1,12 +0,0 @@ -import BLOG from '@/blog.config' -import BlogPostListPage from './components/BlogPostListPage' -import BlogPostListScroll from './components/BlogPostListScroll' -import LayoutBase from './LayoutBase' - -export const LayoutIndex = (props) => { - return - {BLOG.POST_LIST_STYLE === 'page' ? : } - -} - -export default LayoutIndex diff --git a/themes/medium/LayoutPage.js b/themes/medium/LayoutPage.js deleted file mode 100644 index 563df90b..00000000 --- a/themes/medium/LayoutPage.js +++ /dev/null @@ -1,10 +0,0 @@ -import LayoutBase from './LayoutBase' -import BlogPostListPage from './components/BlogPostListPage' - -export const LayoutPage = (props) => { - return - - -} - -export default LayoutPage diff --git a/themes/medium/LayoutSearch.js b/themes/medium/LayoutSearch.js deleted file mode 100644 index e7fee4c2..00000000 --- a/themes/medium/LayoutSearch.js +++ /dev/null @@ -1,53 +0,0 @@ -import LayoutBase from './LayoutBase' -import SearchInput from './components/SearchInput' -import { useGlobal } from '@/lib/global' -import TagGroups from './components/TagGroups' -import CategoryGroup from './components/CategoryGroup' -import { useEffect } from 'react' -import { isBrowser } from '@/lib/utils' -import BLOG from '@/blog.config' -import Mark from 'mark.js' -import BlogPostListScroll from './components/BlogPostListScroll' -import BlogPostListPage from './components/BlogPostListPage' -import { useRouter } from 'next/router' - -export const LayoutSearch = (props) => { - const { locale } = useGlobal() - const { keyword } = props - const router = useRouter() - const currentSearch = keyword || router?.query?.s - - useEffect(() => { - setTimeout(() => { - const container = isBrowser() && document.getElementById('posts-wrapper') - if (container && container.innerHTML) { - const re = new RegExp(currentSearch, 'gim') - const instance = new Mark(container) - instance.markRegExp(re, { - element: 'span', - className: 'text-red-500 border-b border-dashed' - }) - } - }, - 100) - }) - return - -
-
{locale.NAV.SEARCH}
- - - {!currentSearch && <> - - - } - -
- - {currentSearch &&
- {BLOG.POST_LIST_STYLE === 'page' ? : } -
} -
-} - -export default LayoutSearch diff --git a/themes/medium/LayoutSlug.js b/themes/medium/LayoutSlug.js deleted file mode 100644 index e0ddfbde..00000000 --- a/themes/medium/LayoutSlug.js +++ /dev/null @@ -1,96 +0,0 @@ -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' -import ArticleAround from './components/ArticleAround' -import TocDrawer from './components/TocDrawer' -import CategoryItem from './components/CategoryItem' -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?.publishTime || post?.createdTime, - locale.LOCALE - ) - if (!post) { - return - } - - const slotRight = post?.toc && post?.toc?.length >= 3 && ( -
- - {/* */} -
- ) - - return ( - - {/* 文章锁 */} - {lock && } - - {!lock &&
- - {/* title */} -

{post?.title}

- - {/* meta */} -
-
- {date} - | - {post.lastEditedTime} -
- -
-
- -
- {/* eslint-disable-next-line @next/next/no-img-element */} - {BLOG.AUTHOR} - -
- {BLOG.AUTHOR} -
-
- -
- - {/* Notion文章主体 */} -
- {post && ()} -
- -
- - {/* 分享 */} - - {/* 文章分类和标签信息 */} -
- {CONFIG_MEDIUM.POST_DETAIL_CATEGORY && post.category && } -
- {CONFIG_MEDIUM.POST_DETAIL_TAG && post?.tagItems?.map(tag => )} -
-
- - {post.type === 'Post' && } - -
- - -
} -
- ) -} - -export default LayoutSlug diff --git a/themes/medium/LayoutTag.js b/themes/medium/LayoutTag.js deleted file mode 100644 index bedb51ef..00000000 --- a/themes/medium/LayoutTag.js +++ /dev/null @@ -1,15 +0,0 @@ -import LayoutBase from './LayoutBase' -import BLOG from '@/blog.config' -import BlogPostListScroll from './components/BlogPostListScroll' -import BlogPostListPage from './components/BlogPostListPage' - -export const LayoutTag = (props) => { - const { tag } = props - const slotTop =
标签:
{tag}
- - return - {BLOG.POST_LIST_STYLE === 'page' ? : } - -} - -export default LayoutTag diff --git a/themes/medium/LayoutTagIndex.js b/themes/medium/LayoutTagIndex.js deleted file mode 100644 index cc9f8c94..00000000 --- a/themes/medium/LayoutTagIndex.js +++ /dev/null @@ -1,29 +0,0 @@ -import { useGlobal } from '@/lib/global' -import TagItemMini from './components/TagItemMini' -import LayoutBase from './LayoutBase' - -export const LayoutTagIndex = props => { - const { tagOptions } = props - const { locale } = useGlobal() - return ( - -
-
- - {locale.COMMON.TAGS}: -
-
- {tagOptions?.map(tag => { - return ( -
- -
- ) - })} -
-
-
- ) -} - -export default LayoutTagIndex diff --git a/themes/medium/components/ArticleInfo.js b/themes/medium/components/ArticleInfo.js new file mode 100644 index 00000000..17fd3ce2 --- /dev/null +++ b/themes/medium/components/ArticleInfo.js @@ -0,0 +1,41 @@ +import BLOG from '@/blog.config' +import formatDate from '@/lib/formatDate' +import Link from 'next/link' + +/** + * 文章详情页介绍 + * @param {*} props + * @returns + */ +export default function ArticleInfo(props) { + const { post, siteInfo } = props + + const date = formatDate(post?.publishTime || post?.createdTime) + + return (<> + {/* title */} +

{post?.title}

+ + {/* meta */} +
+
+ {date} + | + {post.lastEditedTime} +
+ +
+
+ +
+ {/* eslint-disable-next-line @next/next/no-img-element */} + {BLOG.AUTHOR} + +
+ {BLOG.AUTHOR} +
+
+ +
+ ) +} diff --git a/themes/medium/components/BlogArchiveItem.js b/themes/medium/components/BlogArchiveItem.js new file mode 100644 index 00000000..52b2fefb --- /dev/null +++ b/themes/medium/components/BlogArchiveItem.js @@ -0,0 +1,36 @@ +import BLOG from '@/blog.config' +import Link from 'next/link' + +/** + * 归档分组 + * @param {*} param0 + * @returns + */ +export default function BlogArchiveItem({ archiveTitle, archivePosts }) { + return ( +
+
+ {archiveTitle} +
+
    + {archivePosts[archiveTitle]?.map(post => ( +
  • +
    + + {post.date?.start_date} + {' '} +   + + + {post.title} + +
    +
  • + ))} +
+
+ ) +} diff --git a/themes/medium/components/BlogPostBar.js b/themes/medium/components/BlogPostBar.js new file mode 100644 index 00000000..7cada8fa --- /dev/null +++ b/themes/medium/components/BlogPostBar.js @@ -0,0 +1,19 @@ +import { useGlobal } from '@/lib/global' + +/** + * 文章列表上方嵌入 + * @param {*} props + * @returns + */ +export default function BlogPostBar(props) { + const { tag, category } = props + const { locale } = useGlobal() + + if (tag) { + return
{locale.COMMON.TAGS}:
{tag}
+ } else if (category) { +
{locale.COMMON.CATEGORY}:
{category}
+ } else { + return <> + } +} diff --git a/themes/medium/components/LoadingCover.js b/themes/medium/components/LoadingCover.js new file mode 100644 index 00000000..4d8fa828 --- /dev/null +++ b/themes/medium/components/LoadingCover.js @@ -0,0 +1,7 @@ +export default function LoadingCover() { + return
+
+ +
+
+} diff --git a/themes/medium/index.js b/themes/medium/index.js index cd3239ec..c488ca32 100644 --- a/themes/medium/index.js +++ b/themes/medium/index.js @@ -1,14 +1,314 @@ import CONFIG_MEDIUM from './config_medium' -import LayoutIndex from './LayoutIndex' -import LayoutSearch from './LayoutSearch' -import LayoutArchive from './LayoutArchive' -import LayoutSlug from './LayoutSlug' -import Layout404 from './Layout404' -import LayoutCategory from './LayoutCategory' -import LayoutCategoryIndex from './LayoutCategoryIndex' -import LayoutPage from './LayoutPage' -import LayoutTag from './LayoutTag' -import LayoutTagIndex from './LayoutTagIndex' + +import CommonHead from '@/components/CommonHead' +import { useState, createContext, useContext, useEffect } from 'react' +import Footer from './components/Footer' +import InfoCard from './components/InfoCard' +import RevolverMaps from './components/RevolverMaps' +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 Announcement from './components/Announcement' +import JumpToTopButton from './components/JumpToTopButton' +import LoadingCover from './components/LoadingCover' +import BlogPostListPage from './components/BlogPostListPage' +import BlogPostListScroll from './components/BlogPostListScroll' +import Catalog from './components/Catalog' +import { ArticleLock } from './components/ArticleLock' +import TagGroups from './components/TagGroups' +import CategoryGroup from './components/CategoryGroup' +import { isBrowser } from '@/lib/utils' +import Mark from 'mark.js' +import BlogArchiveItem from './components/BlogArchiveItem' +import BlogPostBar from './components/BlogPostBar' +import NotionPage from '@/components/NotionPage' +import Comment from '@/components/Comment' +import ArticleAround from './components/ArticleAround' +import TocDrawer from './components/TocDrawer' +import CategoryItem from './components/CategoryItem' +import TagItemMini from './components/TagItemMini' +import ShareBar from '@/components/ShareBar' +import Link from 'next/link' + +// 主题全局状态 +const ThemeGlobalMedium = createContext() +export const useMediumGlobal = () => useContext(ThemeGlobalMedium) + +/** + * 基础布局 + * 采用左右两侧布局,移动端使用顶部导航栏 + * @returns {JSX.Element} + * @constructor + */ +const LayoutBase = props => { + const { children, meta, showInfoCard = true, slotRight, slotTop, siteInfo, notice } = props + const { locale } = useGlobal() + const router = useRouter() + const [tocVisible, changeTocVisible] = useState(false) + const { onLoading } = useGlobal() + + return ( + + + +
+ +
+ {/* 桌面端左侧菜单 */} + {/* */} + + {/* 主区 */} +
+ + {/* 顶部导航栏 */} + + +
+ {slotTop} + + {onLoading ? : children} + + +
+ + {/* 底部 */} +
+
+ + {/* 桌面端右侧 */} +
+
+ + {slotRight} +
+ {router.pathname !== '/search' && } + {showInfoCard && } + {CONFIG_MEDIUM.WIDGET_REVOLVER_MAPS === 'true' && } +
+
+ + +
+
+
+ + {/* 移动端底部导航栏 */} + +
+
+ ) +} + +/** + * 首页 + * 首页就是一个博客列表 + * @param {*} props + * @returns + */ +const LayoutIndex = (props) => { + return +} + +/** + * 博客列表 + * @returns + */ +const LayoutPostList = (props) => { + const slotTop = + return + {BLOG.POST_LIST_STYLE === 'page' ? : } + +} + +/** + * 文章详情 + * @param {*} props + * @returns + */ +const LayoutSlug = props => { + const { post, prev, next, lock, validPassword } = props + const { locale } = useGlobal() + const slotRight = post?.toc && post?.toc?.length >= 3 && ( +
+ +
+ ) + + return ( + + {/* 文章锁 */} + {lock && } + + {!lock &&
+ + {/* Notion文章主体 */} +
+ {post && ()} +
+ + {/* 文章底部区域 */} +
+ {/* 分享 */} + + {/* 文章分类和标签信息 */} +
+ {CONFIG_MEDIUM.POST_DETAIL_CATEGORY && post?.category && } +
+ {CONFIG_MEDIUM.POST_DETAIL_TAG && post?.tagItems?.map(tag => )} +
+
+ {/* 上一篇下一篇文章 */} + {post.type === 'Post' && } + {/* 评论区 */} + +
+ + {/* 移动端目录 */} + +
} +
+ ) +} + +/** + * 搜索 + * @param {*} props + * @returns + */ +const LayoutSearch = (props) => { + const { locale } = useGlobal() + const { keyword } = props + const router = useRouter() + const currentSearch = keyword || router?.query?.s + + useEffect(() => { + setTimeout(() => { + const container = isBrowser() && document.getElementById('posts-wrapper') + if (container && container.innerHTML) { + const re = new RegExp(currentSearch, 'gim') + const instance = new Mark(container) + instance.markRegExp(re, { + element: 'span', + className: 'text-red-500 border-b border-dashed' + }) + } + }, 100) + }) + return + + {/* 搜索导航栏 */} +
+
{locale.NAV.SEARCH}
+ + {!currentSearch && <> + + + } +
+ + {/* 文章列表 */} + {currentSearch &&
+ {BLOG.POST_LIST_STYLE === 'page' ? : } +
} +
+} + +/** + * 归档 + * @param {*} props + * @returns + */ +const LayoutArchive = props => { + const { archivePosts } = props + return ( + +
+ {Object.keys(archivePosts)?.map(archiveTitle => + )} +
+
+ ) +} + +/** + * 404 + * @param {*} props + * @returns + */ +const Layout404 = props => { + return +
404 Not found.
+
+} + +/** + * 分类列表 + * @param {*} props + * @returns + */ +const LayoutCategoryIndex = (props) => { + const { categoryOptions } = props + const { locale } = useGlobal() + return ( + +
+
+ {locale.COMMON.CATEGORY}: +
+
+ {categoryOptions?.map(category => { + return ( + +
+ {category.name}({category.count}) +
+ + ) + })} +
+
+
+ ) +} + +/** + * 标签列表 + * @param {*} props + * @returns + */ +const LayoutTagIndex = props => { + const { tagOptions } = props + const { locale } = useGlobal() + return ( + +
+
+ + {locale.COMMON.TAGS}: +
+
+ {tagOptions?.map(tag => { + return ( +
+ +
+ ) + })} +
+
+
+ ) +} export { CONFIG_MEDIUM as THEME_CONFIG, @@ -17,9 +317,6 @@ export { LayoutArchive, LayoutSlug, Layout404, - LayoutCategory, LayoutCategoryIndex, - LayoutPage, - LayoutTag, LayoutTagIndex }