diff --git a/themes/Medium/Layout404.js b/themes/Medium/Layout404.js new file mode 100644 index 00000000..cd28a607 --- /dev/null +++ b/themes/Medium/Layout404.js @@ -0,0 +1,6 @@ + +export const Layout404 = () => { + return
+ 404 Not found. +
+} diff --git a/themes/Medium/LayoutArchive.js b/themes/Medium/LayoutArchive.js new file mode 100644 index 00000000..bf9ebdc5 --- /dev/null +++ b/themes/Medium/LayoutArchive.js @@ -0,0 +1,6 @@ +export const LayoutArchive = (props) => { + // const { posts, tags, categories, postCount } = props + return
+ Archive Page +
+} diff --git a/themes/Medium/LayoutBase.js b/themes/Medium/LayoutBase.js new file mode 100644 index 00000000..866d7e64 --- /dev/null +++ b/themes/Medium/LayoutBase.js @@ -0,0 +1,33 @@ +import CommonHead from '@/components/CommonHead' +import React from 'react' +import Footer from './components/Footer' +import InfoCard from './components/InfoCard' +import LogoBar from './components/LogoBar' + +/** + * 基础布局 采用左右两侧布局,移动端使用顶部导航栏 + + * @returns {JSX.Element} + * @constructor + */ +const LayoutBase = props => { + const { children, meta } = props + + return ( +
+ +
+ +
+ +
+
+ {children} +
+
+
+ ) +} + +export default LayoutBase diff --git a/themes/Medium/LayoutCategory.js b/themes/Medium/LayoutCategory.js new file mode 100644 index 00000000..3a4bb0e9 --- /dev/null +++ b/themes/Medium/LayoutCategory.js @@ -0,0 +1,8 @@ +import LayoutBase from './LayoutBase' + +export const LayoutCategory = (props) => { + const { category } = props + return + Category - {category} + +} diff --git a/themes/Medium/LayoutCategoryIndex.js b/themes/Medium/LayoutCategoryIndex.js new file mode 100644 index 00000000..fcde0a08 --- /dev/null +++ b/themes/Medium/LayoutCategoryIndex.js @@ -0,0 +1,8 @@ +import LayoutBase from './LayoutBase' + +export const LayoutCategoryIndex = (props) => { + // const { tags, allPosts, categories, postCount, latestPosts } = props + return + CategoryIndex + +} diff --git a/themes/Medium/LayoutIndex.js b/themes/Medium/LayoutIndex.js new file mode 100644 index 00000000..3d336bd7 --- /dev/null +++ b/themes/Medium/LayoutIndex.js @@ -0,0 +1,9 @@ +import BlogPostListPage from './components/BlogPostListPage' +import LayoutBase from './LayoutBase' + +export const LayoutIndex = (props) => { + // const { posts, tags, meta, categories, postCount, latestPosts } = props + return + + +} diff --git a/themes/Medium/LayoutPage.js b/themes/Medium/LayoutPage.js new file mode 100644 index 00000000..d4355d46 --- /dev/null +++ b/themes/Medium/LayoutPage.js @@ -0,0 +1,8 @@ +import LayoutBase from '../Hexo/LayoutBase' + +export const LayoutPage = (props) => { + const { page } = props + return + Page - {page} + +} diff --git a/themes/Medium/LayoutSearch.js b/themes/Medium/LayoutSearch.js new file mode 100644 index 00000000..c7469a60 --- /dev/null +++ b/themes/Medium/LayoutSearch.js @@ -0,0 +1,31 @@ +import { useRouter } from 'next/router' +import LayoutBase from './LayoutBase' + +export const LayoutSearch = (props) => { + const { posts } = props + 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()) + }) + } else { + filteredPosts = posts + } + + console.log(filteredPosts) + + return + Search {searchKey} + +} + +function getSearchKey () { + const router = useRouter() + if (router.query && router.query.s) { + return router.query.s + } + return null +} diff --git a/themes/Medium/LayoutSlug.js b/themes/Medium/LayoutSlug.js new file mode 100644 index 00000000..cb621b12 --- /dev/null +++ b/themes/Medium/LayoutSlug.js @@ -0,0 +1,53 @@ +import BLOG from '@/blog.config' +import { getPageTableOfContents } from 'notion-utils' +import 'prismjs' +import 'prismjs/components/prism-bash' +import 'prismjs/components/prism-javascript' +import 'prismjs/components/prism-markup' +import 'prismjs/components/prism-python' +import 'prismjs/components/prism-typescript' +import { Code, Collection, CollectionRow, Equation, NotionRenderer } from 'react-notion-x' +import LayoutBase from './LayoutBase' +import Comment from '@/components/Comment' + +const mapPageUrl = id => { + return 'https://www.notion.so/' + id.replace(/-/g, '') +} + +export const LayoutSlug = (props) => { + const { post } = props + const meta = { + title: `${post.title} | ${BLOG.TITLE}`, + description: post.summary, + type: 'article', + tags: post.tags + } + + if (post?.blockMap?.block) { + post.content = Object.keys(post.blockMap.block) + post.toc = getPageTableOfContents(post, post.blockMap) + } + + return +

{post?.title}

+ {/* Notion文章主体 */} +
+ {post.blockMap && ( + + )} +
+
+ + +
+
+} diff --git a/themes/Medium/LayoutTag.js b/themes/Medium/LayoutTag.js new file mode 100644 index 00000000..5cfacb20 --- /dev/null +++ b/themes/Medium/LayoutTag.js @@ -0,0 +1,8 @@ +import LayoutBase from './LayoutBase' + +export const LayoutTag = (props) => { + const { tag } = props + return + Tag - {tag} + +} diff --git a/themes/Medium/LayoutTagIndex.js b/themes/Medium/LayoutTagIndex.js new file mode 100644 index 00000000..01a16e62 --- /dev/null +++ b/themes/Medium/LayoutTagIndex.js @@ -0,0 +1,8 @@ +import LayoutBase from './LayoutBase' + +export const LayoutTagIndex = (props) => { + // const { tags, categories, postCount, latestPosts } = props + return + TagIndex + +} diff --git a/themes/Medium/components/BlogPostCard.js b/themes/Medium/components/BlogPostCard.js new file mode 100644 index 00000000..3b63256e --- /dev/null +++ b/themes/Medium/components/BlogPostCard.js @@ -0,0 +1,80 @@ +import BLOG from '@/blog.config' +import { faAngleRight, faFolder } from '@fortawesome/free-solid-svg-icons' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import Link from 'next/link' +import React from 'react' +import { Code, Collection, CollectionRow, Equation, NotionRenderer } from 'react-notion-x' +import TagItemMini from './TagItemMini' +import CONFIG_MEDIUM from '../config_medium' +import { useGlobal } from '@/lib/global' + +const BlogPostCard = ({ post, showSummary }) => { + const showPreview = CONFIG_MEDIUM.POST_LIST_PREVIEW && post.blockMap + const { locale } = useGlobal() + return ( +
+ +
+ + + {post.title} + + + +
+ {post.date.start_date} +
+ + {(!showPreview || showSummary) &&

+ {post.summary} +

} + + {showPreview &&
+ +
+ +
+
} + + {/*
+ + + + {post.category} + + +
+
{post.tagItems.map(tag => ())}
+
+
*/} + +
+
+ +
+ + ) +} + +const mapPageUrl = id => { + return 'https://www.notion.so/' + id.replace(/-/g, '') +} + +export default BlogPostCard diff --git a/themes/Medium/components/BlogPostListEmpty.js b/themes/Medium/components/BlogPostListEmpty.js new file mode 100644 index 00000000..86977fd0 --- /dev/null +++ b/themes/Medium/components/BlogPostListEmpty.js @@ -0,0 +1,12 @@ + +/** + * 空白博客 列表 + * @returns {JSX.Element} + * @constructor + */ +const BlogPostListEmpty = ({ currentSearch }) => { + return
+

没有找到文章 {(currentSearch &&

{currentSearch}
)}

+
+} +export default BlogPostListEmpty diff --git a/themes/Medium/components/BlogPostListPage.js b/themes/Medium/components/BlogPostListPage.js new file mode 100644 index 00000000..fe91b9b0 --- /dev/null +++ b/themes/Medium/components/BlogPostListPage.js @@ -0,0 +1,32 @@ +import BlogPostCard from './BlogPostCard' +import BLOG from '@/blog.config' +import BlogPostListEmpty from './BlogPostListEmpty' +import PaginationSimple from './PaginationSimple' + +/** + * 文章列表分页表格 + * @param page 当前页 + * @param posts 所有文章 + * @param tags 所有标签 + * @returns {JSX.Element} + * @constructor + */ +const BlogPostListPage = ({ page = 1, posts = [], postCount }) => { + const totalPage = Math.ceil(postCount / BLOG.POSTS_PER_PAGE) + + if (!posts || posts.length === 0) { + return + } else { + return ( +
+ {/* 文章列表 */} + {posts.map(post => ( + + ))} + +
+ ) + } +} + +export default BlogPostListPage diff --git a/themes/Medium/components/Card.js b/themes/Medium/components/Card.js new file mode 100644 index 00000000..d24c046e --- /dev/null +++ b/themes/Medium/components/Card.js @@ -0,0 +1,9 @@ +const Card = ({ children, headerSlot, className }) => { + return
+ <>{headerSlot} +
+ {children} +
+
+} +export default Card diff --git a/themes/Medium/components/Footer.js b/themes/Medium/components/Footer.js new file mode 100644 index 00000000..57ad7797 --- /dev/null +++ b/themes/Medium/components/Footer.js @@ -0,0 +1,30 @@ +import { faCopyright, faEye, faShieldAlt, faUsers, faHeart } from '@fortawesome/free-solid-svg-icons' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import React from 'react' +import BLOG from '@/blog.config' + +const Footer = ({ title }) => { + const d = new Date() + const currentYear = d.getFullYear() + const startYear = BLOG.SINCE && BLOG.SINCE !== currentYear && BLOG.SINCE + '-' + return ( + + ) +} + +export default Footer diff --git a/themes/Medium/components/InfoCard.js b/themes/Medium/components/InfoCard.js new file mode 100644 index 00000000..47d300f6 --- /dev/null +++ b/themes/Medium/components/InfoCard.js @@ -0,0 +1,27 @@ +import BLOG from '@/blog.config' +import Image from 'next/image' +import Router from 'next/router' +import React from 'react' +import SocialButton from './SocialButton' + +const InfoCard = () => { + return <> +
+
{ Router.push('/about') }}> + {BLOG.AUTHOR} +
+
{BLOG.AUTHOR}
+
{BLOG.BIO}
+ +
+ +} + +export default InfoCard diff --git a/themes/Medium/components/LogoBar.js b/themes/Medium/components/LogoBar.js new file mode 100644 index 00000000..75a23aec --- /dev/null +++ b/themes/Medium/components/LogoBar.js @@ -0,0 +1,25 @@ +import BLOG from '@/blog.config' +import { useGlobal } from '@/lib/global' +import { faEnvelope } from '@fortawesome/free-solid-svg-icons' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import Link from 'next/link' + +export default function LogoBar () { + const { locale } = useGlobal() + + return
+
+ + {BLOG.TITLE} + + + {locale.NAV.ABOUT} + + {BLOG.CONTACT_EMAIL && +
+ +
+ } +
+
+} diff --git a/themes/Medium/components/PaginationSimple.js b/themes/Medium/components/PaginationSimple.js new file mode 100644 index 00000000..5eb30af8 --- /dev/null +++ b/themes/Medium/components/PaginationSimple.js @@ -0,0 +1,42 @@ +import BLOG from '@/blog.config' +import Link from 'next/link' +import { useRouter } from 'next/router' +import { useGlobal } from '@/lib/global' + +/** + * 简易翻页插件 + * @param page 当前页码 + * @param showNext 是否有下一页 + * @returns {JSX.Element} + * @constructor + */ +const PaginationSimple = ({ page, showNext }) => { + const { locale } = useGlobal() + const router = useRouter() + const currentPage = +page + return ( +
+ + + + + + +
+ ) +} + +export default PaginationSimple diff --git a/themes/Medium/components/SocialButton.js b/themes/Medium/components/SocialButton.js new file mode 100644 index 00000000..c50f5984 --- /dev/null +++ b/themes/Medium/components/SocialButton.js @@ -0,0 +1,36 @@ +import BLOG from '@/blog.config' +import { faGithub, faTelegram, faTwitter, faWeibo } from '@fortawesome/free-brands-svg-icons' +import { faEnvelope, faRss } from '@fortawesome/free-solid-svg-icons' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import React from 'react' + +/** + * 社交联系方式按钮组 + * @returns {JSX.Element} + * @constructor + */ +const SocialButton = () => { + return
+
+ {BLOG.CONTACT_GITHUB && + + } + {BLOG.CONTACT_TWITTER && + + } + {BLOG.CONTACT_TELEGRAM && + + } + {BLOG.CONTACT_WEIBO && + + } + {BLOG.CONTACT_EMAIL && + + } + + + +
+
+} +export default SocialButton diff --git a/themes/Medium/components/TagItemMini.js b/themes/Medium/components/TagItemMini.js new file mode 100644 index 00000000..9fec9e35 --- /dev/null +++ b/themes/Medium/components/TagItemMini.js @@ -0,0 +1,17 @@ +import { faTag } from '@fortawesome/free-solid-svg-icons' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import Link from 'next/link' + +const TagItemMini = ({ tag, selected = false }) => { + return + +
{selected && } {tag.name + (tag.count ? `(${tag.count})` : '')}
+
+ +} + +export default TagItemMini diff --git a/themes/Medium/config_medium.js b/themes/Medium/config_medium.js new file mode 100644 index 00000000..9cb83c84 --- /dev/null +++ b/themes/Medium/config_medium.js @@ -0,0 +1,13 @@ +const CONFIG_MEDIUM = { + + POST_LIST_COVER: true, // 文章列表显示图片封面 + POST_LIST_PREVIEW: true, // 显示文章预览 + + // 菜单 + MENU_ABOUT: true, // 显示关于 + MENU_CATEGORY: true, // 显示分类 + MENU_TAG: true, // 显示标签 + MENU_ARCHIVE: true, // 显示归档 + MENU_SEARCH: true // 显示搜索 +} +export default CONFIG_MEDIUM diff --git a/themes/Medium/index.js b/themes/Medium/index.js new file mode 100644 index 00000000..5bcc655c --- /dev/null +++ b/themes/Medium/index.js @@ -0,0 +1,12 @@ +import CONFIG_MEDIUM from './config_medium' +export { CONFIG_MEDIUM as THEME_CONFIG } +export { LayoutIndex } from './LayoutIndex' +export { LayoutSearch } from './LayoutSearch' +export { LayoutArchive } from './LayoutArchive' +export { LayoutSlug } from './LayoutSlug' +export { Layout404 } from './Layout404' +export { LayoutCategory } from './LayoutCategory' +export { LayoutCategoryIndex } from './LayoutCategoryIndex' +export { LayoutPage } from './LayoutPage' +export { LayoutTag } from './LayoutTag' +export { LayoutTagIndex } from './LayoutTagIndex' diff --git a/themes/index.js b/themes/index.js index b28dd755..d17405e5 100644 --- a/themes/index.js +++ b/themes/index.js @@ -5,4 +5,5 @@ // export * from './Empty' // 空主题 // export * from './NEXT' // export * from './Fukasawa' -export * from './Hexo' +// export * from './Hexo' +export * from './Medium'