diff --git a/blog.config.js b/blog.config.js index fcaee9bd..2b57efcd 100644 --- a/blog.config.js +++ b/blog.config.js @@ -438,6 +438,15 @@ const BLOG = { MAILCHIMP_LIST_ID: process.env.MAILCHIMP_LIST_ID || null, // 开启mailichimp邮件订阅 客户列表ID ,具体使用方法参阅文档 MAILCHIMP_API_KEY: process.env.MAILCHIMP_API_KEY || null, // 开启mailichimp邮件订阅 APIkey + // ANIMATE.css 动画 + ANIMATE_CSS_URL: process.env.NEXT_PUBLIC_ANIMATE_CSS_URL || 'https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css', // 动画CDN + + // 网站图片 + IMG_LAZY_LOAD_PLACEHOLDER: process.env.NEXT_PUBLIC_IMG_LAZY_LOAD_PLACEHOLDER || 'data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==', // 懒加载占位图片地址,支持base64或url + IMG_URL_TYPE: process.env.NEXT_PUBLIC_IMG_TYPE || 'Notion', // 此配置已失效,请勿使用;AMAZON方案不再支持,仅支持Notion方案。 ['Notion','AMAZON'] 站点图片前缀 默认 Notion:(https://notion.so/images/xx) , AMAZON(https://s3.us-west-2.amazonaws.com/xxx) + IMG_SHADOW: process.env.NEXT_PUBLIC_IMG_SHADOW || false, // 文章图片是否自动添加阴影 + IMG_COMPRESS_WIDTH: process.env.NEXT_PUBLIC_IMG_COMPRESS_WIDTH || 800, // Notion图片压缩宽度 + // 作废配置 AVATAR: process.env.NEXT_PUBLIC_AVATAR || '/avatar.svg', // 作者头像,被notion中的ICON覆盖。若无ICON则取public目录下的avatar.png TITLE: process.env.NEXT_PUBLIC_TITLE || 'NotionNext BLOG', // 站点标题 ,被notion中的页面标题覆盖;此处请勿留空白,否则服务器无法编译 @@ -446,12 +455,6 @@ const BLOG = { DESCRIPTION: process.env.NEXT_PUBLIC_DESCRIPTION || '这是一个由NotionNext生成的站点', // 站点描述,被notion中的页面描述覆盖 - // 网站图片 - IMG_LAZY_LOAD_PLACEHOLDER: process.env.NEXT_PUBLIC_IMG_LAZY_LOAD_PLACEHOLDER || 'data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==', // 懒加载占位图片地址,支持base64或url - IMG_URL_TYPE: process.env.NEXT_PUBLIC_IMG_TYPE || 'Notion', // 此配置已失效,请勿使用;AMAZON方案不再支持,仅支持Notion方案。 ['Notion','AMAZON'] 站点图片前缀 默认 Notion:(https://notion.so/images/xx) , AMAZON(https://s3.us-west-2.amazonaws.com/xxx) - IMG_SHADOW: process.env.NEXT_PUBLIC_IMG_SHADOW || false, // 文章图片是否自动添加阴影 - IMG_COMPRESS_WIDTH: process.env.NEXT_PUBLIC_IMG_COMPRESS_WIDTH || 800, // Notion图片压缩宽度 - // 开发相关 NOTION_ACCESS_TOKEN: process.env.NOTION_ACCESS_TOKEN || '', // Useful if you prefer not to make your database public DEBUG: process.env.NEXT_PUBLIC_DEBUG || false, // 是否显示调试按钮 diff --git a/components/Badge.js b/components/Badge.js new file mode 100644 index 00000000..9eab49a2 --- /dev/null +++ b/components/Badge.js @@ -0,0 +1,11 @@ +/** + * 红点 + */ +export default function Badge() { + return <> + {/* 红点 */} + + + + +} diff --git a/components/ExternalPlugins.js b/components/ExternalPlugins.js index 78da3094..3df09e6c 100644 --- a/components/ExternalPlugins.js +++ b/components/ExternalPlugins.js @@ -80,6 +80,7 @@ const ExternalPlugin = (props) => { const GLOBAL_JS = siteConfig('GLOBAL_JS') const CLARITY_ID = siteConfig('CLARITY_ID') const IMG_SHADOW = siteConfig('IMG_SHADOW') + const ANIMATE_CSS_URL = siteConfig('ANIMATE_CSS_URL') // 自定义样式css和js引入 if (isBrowser) { @@ -93,6 +94,10 @@ const ExternalPlugin = (props) => { loadExternalResource('/css/img-shadow.css', 'css') } + if (ANIMATE_CSS_URL) { + loadExternalResource(ANIMATE_CSS_URL, 'css') + } + // 导入外部自定义脚本 if (CUSTOM_EXTERNAL_JS && CUSTOM_EXTERNAL_JS.length > 0) { for (const url of CUSTOM_EXTERNAL_JS) { diff --git a/lib/wow.js b/lib/wow.js index e4682e46..7b09d2c3 100644 --- a/lib/wow.js +++ b/lib/wow.js @@ -5,6 +5,7 @@ const { loadExternalResource } = require('./utils'); * 是data-aos的平替 aos ≈ wowjs + animate */ export const loadWowJS = async () => { + await loadExternalResource('/css/wow/animate.css', 'css'); await loadExternalResource('https://cdnjs.cloudflare.com/ajax/libs/wow/1.1.2/wow.min.js', 'js'); // 配合animatecss 实现延时滚动动画,和AOS动画相似 const WOW = window.WOW; diff --git a/pages/_app.js b/pages/_app.js index 9b6a74bc..cf71fe18 100644 --- a/pages/_app.js +++ b/pages/_app.js @@ -1,4 +1,4 @@ -import '@/styles/animate.css' // @see https://animate.style/ +// import '@/styles/animate.css' // @see https://animate.style/ import '@/styles/globals.css' import '@/styles/nprogress.css' import '@/styles/utility-patterns.css' diff --git a/styles/animate.css b/public/css/wow/animate.css similarity index 99% rename from styles/animate.css rename to public/css/wow/animate.css index c83e40e1..ae252ed1 100644 --- a/styles/animate.css +++ b/public/css/wow/animate.css @@ -2,7 +2,7 @@ /*! * animate.css -https://daneden.github.io/animate.css/ - * Version - 3.7.2 + * Version - 3.7.2 适配wowjs * Licensed under the MIT license - http://opensource.org/licenses/MIT * * Copyright (c) 2019 Daniel Eden diff --git a/themes/gitbook/components/BlogPostCard.js b/themes/gitbook/components/BlogPostCard.js index 891e3ea7..a2870973 100644 --- a/themes/gitbook/components/BlogPostCard.js +++ b/themes/gitbook/components/BlogPostCard.js @@ -3,17 +3,22 @@ import Link from 'next/link' import { useRouter } from 'next/router' import { checkContainHttp, sliceUrlFromHttp } from '@/lib/utils' import NotionIcon from '@/components/NotionIcon' +import Badge from '@/components/Badge' +import CONFIG from '../config' const BlogPostCard = ({ post, className }) => { const router = useRouter() const currentSelected = router.asPath.split('?')[0] === '/' + post.slug const url = checkContainHttp(post.slug) ? sliceUrlFromHttp(post.slug) : `${siteConfig('SUB_PATH', '')}/${post.slug}` return ( -
-
- {post.title} + +
+
+ {post.title} +
+ {/* 最新文章加个红点 */} + {post?.isLatest && siteConfig('GITBOOK_LATEST_POST_RED_BADGE', false, CONFIG) && }
-
) } diff --git a/themes/gitbook/components/NavPostItem.js b/themes/gitbook/components/NavPostItem.js index b357b1af..97ab3425 100644 --- a/themes/gitbook/components/NavPostItem.js +++ b/themes/gitbook/components/NavPostItem.js @@ -1,6 +1,9 @@ import BlogPostCard from './BlogPostCard' import { useState } from 'react' import Collapse from '@/components/Collapse' +import Badge from '@/components/Badge' +import { siteConfig } from '@/lib/config' +import CONFIG from '../config' /** * 导航列表 @@ -17,12 +20,15 @@ const NavPostItem = (props) => { changeIsOpen(!isOpen) } + const groupHasLatest = group?.items?.some(post => post.isLatest) + if (group?.category) { return <>
+ className='select-none relative flex justify-between text-sm cursor-pointer p-2 hover:bg-gray-50 rounded-md dark:hover:bg-gray-600' key={group?.category}> {group?.category} -
+
+ {groupHasLatest && siteConfig('GITBOOK_LATEST_POST_RED_BADGE', false, CONFIG) && !isOpen && }
{group?.items?.map(post => (
diff --git a/themes/gitbook/components/SearchInput.js b/themes/gitbook/components/SearchInput.js index cab041fe..e1560a51 100644 --- a/themes/gitbook/components/SearchInput.js +++ b/themes/gitbook/components/SearchInput.js @@ -103,7 +103,7 @@ const SearchInput = ({ currentSearch, cRef, className }) => { import('@/components/WWAds'), { ssr: false }) const ThemeGlobalGitbook = createContext() export const useGitBookGlobal = () => useContext(ThemeGlobalGitbook) +/** + * 给最新的文章标一个红点 + */ +function getNavPagesWithLatest(allNavPages, latestPosts, post) { + // 检测需要去除红点的文章 ; localStorage 的 posts_read = {"${post.id}":"Date()"} 保存了所有已读的页面id,和阅读时间; + // 如果页面在这里面则不显示红点 + const postRead = JSON.parse(localStorage.getItem('post_read') || '[]'); + if (post && !postRead.includes(post.id)) { + postRead.push(post.id); + } + localStorage.setItem('post_read', JSON.stringify(postRead)); + + return allNavPages?.map(item => { + const res = { + 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 + } + if (latestPosts.some(post => post.id === item.id) && !postRead.includes(item.id)) { + return { ...res, isLatest: true }; + } else { + return res; + } + }) +} + /** * 基础布局 * 采用左右两侧布局,移动端使用顶部导航栏 @@ -49,7 +81,7 @@ export const useGitBookGlobal = () => useContext(ThemeGlobalGitbook) * @constructor */ const LayoutBase = (props) => { - const { children, post, allNavPages, slotLeft, slotRight, slotTop } = props + const { children, post, allNavPages, latestPosts, slotLeft, slotRight, slotTop } = props const { onLoading, fullWidth } = useGlobal() const router = useRouter() const [tocVisible, changeTocVisible] = useState(false) @@ -60,8 +92,8 @@ const LayoutBase = (props) => { const searchModal = useRef(null) useEffect(() => { - setFilteredNavPages(allNavPages) - }, [post]) + setFilteredNavPages(getNavPagesWithLatest(allNavPages, latestPosts, post)) + }, [router]) return ( @@ -78,7 +110,7 @@ const LayoutBase = (props) => { {/* 左侧推拉抽屉 */} {fullWidth ? null - : (
+ : (
{slotLeft} @@ -94,7 +126,7 @@ const LayoutBase = (props) => {
) } -
+
{slotTop} diff --git a/themes/heo/components/Hero.js b/themes/heo/components/Hero.js index 23edfc8b..9143e064 100644 --- a/themes/heo/components/Hero.js +++ b/themes/heo/components/Hero.js @@ -24,9 +24,7 @@ const Hero = props => {