Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add theme nav & add pageIcon support of @lib/getNotionData #1563

Merged
merged 12 commits into from
Oct 18, 2023
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ yarn-error.log*
.env.development.local
.env.test.local
.env.production.local
.env

# vercel
.vercel
Expand Down
2 changes: 1 addition & 1 deletion lib/notion/getNotionData.js
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ export function getNavPages({ allPages }) {
return post && post?.slug && (!post?.slug?.startsWith('http')) && post?.type === 'Post' && post?.status === 'Published'
})

return allNavPages.map(item => ({ id: item.id, title: item.title || '', pageCoverThumbnail: item.pageCoverThumbnail || '', category: item.category || null, tags: item.tags || null, summary: item.summary || null, slug: item.slug, lastEditedDate: item.lastEditedDate }))
return allNavPages.map(item => ({ id: item.id, title: item.title || '', pageCoverThumbnail: item.pageCoverThumbnail || '', category: item.category || null, tags: item.tags || null, summary: item.summary || null, slug: item.slug, pageIcon: item.pageIcon || '', lastEditedDate: item.lastEditedDate }))
}

/**
Expand Down
1 change: 0 additions & 1 deletion next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,6 @@ module.exports = withBundleAnalyzer({
// 'react-dom': 'preact/compat'
// })
// }

// 动态主题:添加 resolve.alias 配置,将动态路径映射到实际路径
config.resolve.alias['@theme-components'] = path.resolve(__dirname, 'themes', THEME)
return config
Expand Down
10,532 changes: 3,608 additions & 6,924 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion themes/heo/components/MenuListTop.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export const MenuListTop = (props) => {

return (<>
<nav id='nav-mobile' className='leading-8 justify-center font-light w-full flex'>
{links?.map(link => link && link.show && <MenuItemDrop key={link?.id} link={link} />)}
{links?.map((link, index) => link && link.show && <MenuItemDrop key={index} link={link} />)}
</nav>
</>)
}
9 changes: 8 additions & 1 deletion themes/heo/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import CONFIG from './config'
/**
* HEO 主题说明
* > 主题设计者 [张洪](https://zhheo.com/)
* > 主题开发者 [tangly1024](https://github.com/tangly1024)
* 1. 开启方式 在blog.config.js 将主题配置为 `HEO`
* 2. 更多说明参考此[文档](https://docs.tangly1024.com/article/notionnext-heo)
*/

import CONFIG from './config'
import CommonHead from '@/components/CommonHead'
import { useEffect, useState } from 'react'
import Footer from './components/Footer'
Expand Down
21 changes: 21 additions & 0 deletions themes/nav/components/Announcement.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// import { useGlobal } from '@/lib/global'
import dynamic from 'next/dynamic'

const NotionPage = dynamic(() => import('@/components/NotionPage'))

const Announcement = ({ notice, className }) => {
// const { locale } = useGlobal()
if (notice?.blockMap) {
return <div className={className}>
<section id='announcement-wrapper' className="dark:text-gray-300">
{/* <div><i className='mr-2 fas fa-bullhorn' />{locale.COMMON.ANNOUNCEMENT}</div> */}
{notice && (<div id="announcement-content">
<NotionPage post={notice} />
</div>)}
</section>
</div>
} else {
return <></>
}
}
export default Announcement
32 changes: 32 additions & 0 deletions themes/nav/components/ArticleAround.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import Link from 'next/link'

/**
* 上一篇,下一篇文章
* @param {prev,next} param0
* @returns
*/
export default function ArticleAround ({ prev, next }) {
if (!prev || !next) {
return <></>
}
return (
<section className='text-gray-800 dark:text-gray-400 h-12 flex items-center justify-between space-x-5 my-4'>
<Link
href={`/${prev.slug}`}
passHref
className='text-sm cursor-pointer justify-start items-center flex hover:underline duration-300'>

<i className='mr-1 fas fa-angle-double-left' />{prev.title}

</Link>
<Link
href={`/${next.slug}`}
passHref
className='text-sm cursor-pointer justify-end items-center flex hover:underline duration-300'>
{next.title}
<i className='ml-1 my-1 fas fa-angle-double-right' />

</Link>
</section>
)
}
9 changes: 9 additions & 0 deletions themes/nav/components/ArticleInfo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export default function ArticleInfo({ post }) {
if (!post) {
return null
}
return <div className="pt-10 pb-6 text-gray-400 text-sm border-b">
<i className="fa-regular fa-clock mr-1" />
Last update: { post.date?.start_date}
</div>
}
53 changes: 53 additions & 0 deletions themes/nav/components/ArticleLock.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { useGlobal } from '@/lib/global'
import { useEffect, useRef } from 'react'

/**
* 加密文章校验组件
* @param {password, validPassword} props
* @param password 正确的密码
* @param validPassword(bool) 回调函数,校验正确回调入参为true
* @returns
*/
export const ArticleLock = props => {
const { validPassword } = props
const { locale } = useGlobal()

const submitPassword = () => {
const p = document.getElementById('password')
if (!validPassword(p?.value)) {
const tips = document.getElementById('tips')
if (tips) {
tips.innerHTML = ''
tips.innerHTML = `<div class='text-red-500 animate__shakeX animate__animated'>${locale.COMMON.PASSWORD_ERROR}</div>`
}
}
}

const passwordInputRef = useRef(null)
useEffect(() => {
// 选中密码输入框并将其聚焦
passwordInputRef.current.focus()
}, [])

return <div id='container' className='w-full flex justify-center items-center h-96 '>
<div className='text-center space-y-3'>
<div className='font-bold'>{locale.COMMON.ARTICLE_LOCK_TIPS}</div>
<div className='flex mx-4'>
<input id="password" type='password'
onKeyDown={(e) => {
if (e.key === 'Enter') {
submitPassword()
}
}}
ref={passwordInputRef} // 绑定ref到passwordInputRef变量
className='outline-none w-full text-sm pl-5 rounded-l transition focus:shadow-lg dark:text-gray-300 font-light leading-10 text-black bg-gray-100 dark:bg-gray-500'>
</input>
<div onClick={submitPassword} className="px-3 whitespace-nowrap cursor-pointer items-center justify-center py-2 bg-green-500 hover:bg-green-400 text-white rounded-r duration-300" >
<i className={'duration-200 cursor-pointer fas fa-key'} >&nbsp;{locale.COMMON.SUBMIT}</i>
</div>
</div>
<div id='tips'>
</div>
</div>
</div>
}
36 changes: 36 additions & 0 deletions themes/nav/components/BlogArchiveItem.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import BLOG from '@/blog.config'
import Link from 'next/link'

/**
* 归档分组
* @param {*} param0
* @returns
*/
export default function BlogArchiveItem({ archiveTitle, archivePosts }) {
return (
<div key={archiveTitle}>
<div id={archiveTitle} className="pt-16 pb-4 text-3xl dark:text-gray-300" >
{archiveTitle}
</div>
<ul>
{archivePosts[archiveTitle]?.map(post => (
<li key={post.id}
className="border-l-2 p-1 text-xs md:text-base items-center hover:scale-x-105 hover:border-gray-500 dark:hover:border-gray-300 dark:border-gray-400 transform duration-500"
>
<div id={post?.publishDay}>
<span className="text-gray-400">
{post.date?.start_date}
</span>{' '}
&nbsp;

<Link passHref href={`${BLOG.SUB_PATH}/${post.slug}`}
className="dark:text-gray-400 dark:hover:text-gray-300 overflow-x-hidden hover:underline cursor-pointer text-gray-600">
{post.title}
</Link>
</div>
</li>
))}
</ul>
</div>
)
}
50 changes: 50 additions & 0 deletions themes/nav/components/BlogPostCard.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import BLOG from '@/blog.config'
import Link from 'next/link'
import NotionIcon from './NotionIcon'
import { useRouter } from 'next/router'
import React from 'react'

const BlogPostCard = ({ post, className }) => {
const router = useRouter()
const currentSelected = router.asPath.split('?')[0] === '/' + post.slug
const pageIcon = post.pageIcon.indexOf("amazonaws.com")!=-1 ? post.pageIcon+"&width=88" : post.pageIcon

Check failure

Code scanning / CodeQL

Incomplete URL substring sanitization High

'
amazonaws.com
' can be anywhere in the URL, and arbitrary hosts may come before or after it.
return (
<Link href={`${removeHttp(post.slug)}`} target={(checkRemoveHttp(post.slug) ? '_blank' : '_self')} passHref>
<div key={post.id} className={`${className} h-full rounded-lg p-4 dark:bg-neutral-800 cursor-pointer bg-white hover:bg-white rounded-2xl dark:hover:bg-gray-800 ${currentSelected ? 'bg-green-50 text-green-500' : ''}`}>
<div className="stack-entry w-full flex space-x-3 select-none dark:text-neutral-200">
<NotionIcon icon={pageIcon} size='10' className='text-6xl w-11 h-11 mx-1 my-0 flex-none' />
<div className="stack-comment flex-auto">
<p className="title font-bold">{post.title}</p>
<p className="description font-normal">{post.summary ? post.summary : '暂无简介'}</p>
</div>
</div>
</div>
</Link>
)
function removeHttp(str) {
// 检查字符串是否包含http
if (str.includes("http")) {
// 如果包含,找到http的位置
let index = str.indexOf("http");
// 返回http之后的部分
return str.slice(index, str.length);
} else {
// 如果不包含,返回原字符串
return str;
}
}
function checkRemoveHttp(str) {
// 检查字符串是否包含http
if (str.includes("http")) {
// 如果包含,找到http的位置
let index = str.indexOf("http");
// 包含
return true;
} else {
// 不包含
return false;
}
}
}

export default BlogPostCard
49 changes: 49 additions & 0 deletions themes/nav/components/BlogPostItem.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import BlogPostCard from './BlogPostCard'
import React, { useState } from 'react'
import NotionIcon from './NotionIcon'
// import Collapse from '@/components/Collapse'

/**
* 导航列表
* @param posts 所有文章
* @param tags 所有标签
* @returns {JSX.Element}
* @constructor
*/
const BlogPostItem = (props) => {
const { group, filterLinks } = props
// const [isOpen, changeIsOpen] = useState(group?.selected)

// const toggleOpenSubMenu = () => {
// changeIsOpen(!isOpen)
// }

console.log('####### group')
console.log(group)

if (group?.category) {
return <>
<div id={group?.category} className='category text-lg font-normal pt-9 pb-4 first:pt-4 select-none flex justify-between font-sans text-neutral-800 dark:text-neutral-400 p-2' key={group?.category}>
<h3><i className={`text-base mr-2 ${group?.icon ? group?.icon : 'fas fa-hashtag'}`} />{group?.category}</h3>
</div>
<div id='posts-wrapper' className='card-list grid gap-4 sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5' >
{group?.items?.map(post => (
<BlogPostCard key={post.id} className='card' post={post} />
))}
</div>
</>
} else {
return <>
<div id='uncategory' className='category text-lg font-normal pt-9 pb-4 first:pt-4 font-bold select-none flex justify-between font-sans text-neutral-800 dark:text-neutral-400 p-2' key='uncategory'>
<span><i className={`text-base mr-2 ${group?.icon ? group?.icon : 'fas fa-hashtag'}`} />未分类</span>
</div>
<div class='card-list grid gap-4 sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5'>
{group?.items?.map(post => (
<BlogPostCard key={post.id} className='card' post={post} />
))}
</div>
</>
}
}

export default BlogPostItem