diff --git a/components/Notification.js b/components/Notification.js new file mode 100644 index 00000000000..02215d354db --- /dev/null +++ b/components/Notification.js @@ -0,0 +1,59 @@ +import { useState } from 'react' + +const useNotification = () => { + const [message, setMessage] = useState('') + const [isVisible, setIsVisible] = useState(false) + + const showNotification = msg => { + setMessage(msg) + setIsVisible(true) + setTimeout(() => { + setIsVisible(false) + }, 3000) + } + + const closeNotification = () => { + setIsVisible(false) + } + + // 测试通知效果 + // const toggleVisible = () => { + // setIsVisible(prev => !prev) // 使用函数式更新 + // } + // useEffect(() => { + // document?.addEventListener('click', toggleVisible) + // return () => { + // document?.removeEventListener('click', toggleVisible) + // } + // }, []) + + /** + * 通知组件 + * @returns + */ + const Notification = () => { + return ( +
+
+ {message} + +
+
+ ) + } + + return { + showNotification, + closeNotification, + Notification + } +} + +export default useNotification diff --git a/lib/lang/en-US.js b/lib/lang/en-US.js index 47bb171c209..985e0a24e54 100644 --- a/lib/lang/en-US.js +++ b/lib/lang/en-US.js @@ -37,11 +37,13 @@ export default { ARTICLE: 'Article', VISITORS: 'Visitors', VIEWS: 'Views', - COPYRIGHT_NOTICE: 'All articles in this blog, except for special statements, adopt BY-NC-SA agreement. Please indicate the source!', + COPYRIGHT_NOTICE: + 'All articles in this blog, except for special statements, adopt BY-NC-SA agreement. Please indicate the source!', RESULT_OF_SEARCH: 'Results Found', ARTICLE_DETAIL: 'Article Details', PASSWORD_ERROR: 'Password Error!', ARTICLE_LOCK_TIPS: 'Please Enter the password:', + ARTICLE_UNLOCK_TIPS: 'Article Unlocked', NO_RESULTS_FOUND: 'No results found.', SUBMIT: 'Submit', POST_TIME: 'Post on', diff --git a/lib/lang/ja-JP.js b/lib/lang/ja-JP.js index c27eef0b48a..628b28b3e9c 100644 --- a/lib/lang/ja-JP.js +++ b/lib/lang/ja-JP.js @@ -29,11 +29,14 @@ export default { ARTICLE: '記事', VISITORS: '人の訪問者', VIEWS: '回の閲覧', - COPYRIGHT_NOTICE: 'この記事はCC BY-NC-SA 4.0 ライセンスの下でライセンスされています。転載する場合には出典を明らかにしてください。', + COPYRIGHT_NOTICE: + 'この記事はCC BY-NC-SA 4.0 ライセンスの下でライセンスされています。転載する場合には出典を明らかにしてください。', RESULT_OF_SEARCH: '個の検索結果', ARTICLE_DETAIL: '記事の詳細', PASSWORD_ERROR: 'パスワードが違います!', - ARTICLE_LOCK_TIPS: 'この記事はロックされています。アクセスパスワードを入力してください。', + ARTICLE_LOCK_TIPS: + 'この記事はロックされています。アクセスパスワードを入力してください。', + ARTICLE_UNLOCK_TIPS: '記事がロック解除されました', SUBMIT: '送信', POST_TIME: '公開日', LAST_EDITED_TIME: '最終更新日', diff --git a/lib/lang/zh-CN.js b/lib/lang/zh-CN.js index a2c8b1c7b0c..e38a27bf669 100644 --- a/lib/lang/zh-CN.js +++ b/lib/lang/zh-CN.js @@ -45,6 +45,7 @@ export default { ARTICLE_DETAIL: '文章详情', PASSWORD_ERROR: '密码错误!', ARTICLE_LOCK_TIPS: '文章已上锁,请输入访问密码', + ARTICLE_UNLOCK_TIPS: '文章已解锁', SUBMIT: '提交', POST_TIME: '发布于', LAST_EDITED_TIME: '最后更新', diff --git a/lib/password.js b/lib/password.js new file mode 100644 index 00000000000..67b341a6ad6 --- /dev/null +++ b/lib/password.js @@ -0,0 +1,37 @@ +import { isBrowser } from './utils' + +/** + * 获取默认密码 + * 用户可以通过url中拼接参数,输入密码 + * 输入过一次的密码会被存储在浏览器中,便于下一次免密访问 + * 返回的是一组历史密码,便于客户端多次尝试 + */ +export const getPasswordQuery = path => { + // 使用 URL 对象解析 URL + const url = new URL(path, isBrowser ? window.location.origin : '') + + // 获取查询参数 + const queryParams = Object.fromEntries(url.searchParams.entries()) + + // 请求中带着密码 + if (queryParams.password) { + // 将已输入密码作为默认密码存放在 localStorage,便于下次读取并自动尝试 + localStorage.setItem('password_default', queryParams.password) + } + + // 获取路径部分 + const cleanedPath = url.pathname + + // 从 localStorage 中获取相关密码 + const storedPassword = localStorage.getItem('password_' + cleanedPath) + const defaultPassword = localStorage.getItem('password_default') + + // 将所有密码存储在一个数组中,并过滤掉无效值 + const passwords = [ + queryParams.password, + storedPassword, + defaultPassword + ].filter(Boolean) + + return passwords +} diff --git a/pages/[prefix]/index.js b/pages/[prefix]/index.js index 973ce32153c..d9853a5b77e 100644 --- a/pages/[prefix]/index.js +++ b/pages/[prefix]/index.js @@ -1,7 +1,10 @@ import BLOG from '@/blog.config' +import useNotification from '@/components/Notification' import { siteConfig } from '@/lib/config' import { getGlobalData, getPost, getPostBlocks } from '@/lib/db/getSiteData' +import { useGlobal } from '@/lib/global' import { getPageTableOfContents } from '@/lib/notion/getPageTableOfContents' +import { getPasswordQuery } from '@/lib/password' import { uploadDataToAlgolia } from '@/lib/plugins/algolia' import { checkSlugHasNoSlash, getRecommendPost } from '@/lib/utils/post' import { getLayoutByTheme } from '@/themes/theme' @@ -19,9 +22,11 @@ import { useEffect, useState } from 'react' const Slug = props => { const { post } = props const router = useRouter() + const { locale } = useGlobal() // 文章锁🔐 const [lock, setLock] = useState(post?.password && post?.password !== '') + const { showNotification, Notification } = useNotification() /** * 验证文章密码 @@ -36,6 +41,7 @@ const Slug = props => { setLock(false) // 输入密码存入localStorage,下次自动提交 localStorage.setItem('password_' + router.asPath, passInput) + showNotification(locale.COMMON.ARTICLE_UNLOCK_TIPS) // 设置解锁成功提示显示 return true } return false @@ -56,10 +62,14 @@ const Slug = props => { } } - // 从localStorage中读取上次记录 自动提交密码 - const passInput = localStorage.getItem('password_' + router.asPath) - if (passInput) { - validPassword(passInput) + // 读取上次记录 自动提交密码 + const passInputs = getPasswordQuery(router.asPath) + if (passInputs.length > 0) { + for (const passInput of passInputs) { + if (validPassword(passInput)) { + break // 密码验证成功,停止尝试 + } + } } }, [post]) @@ -83,7 +93,12 @@ const Slug = props => { theme: siteConfig('THEME'), router: useRouter() }) - return + return ( + <> + + {!lock && } + + ) } export async function getStaticPaths() { diff --git a/themes/gitbook/components/NavPostItem.js b/themes/gitbook/components/NavPostItem.js index cd0c17424f0..4961d7de3e6 100644 --- a/themes/gitbook/components/NavPostItem.js +++ b/themes/gitbook/components/NavPostItem.js @@ -40,8 +40,8 @@ const NavPostItem = props => { !expanded && } - {group?.items?.map(post => ( -
+ {group?.items?.map((post, index) => ( +
))} @@ -51,8 +51,8 @@ const NavPostItem = props => { } else { return ( <> - {group?.items?.map(post => ( -
+ {group?.items?.map((post, index) => ( +
))} diff --git a/themes/gitbook/index.js b/themes/gitbook/index.js index 25af5a5c6b7..34c4f05d021 100644 --- a/themes/gitbook/index.js +++ b/themes/gitbook/index.js @@ -10,7 +10,6 @@ import { siteConfig } from '@/lib/config' import { useGlobal } from '@/lib/global' import { isBrowser } from '@/lib/utils' import { getShortId } from '@/lib/utils/pageId' -import { Transition } from '@headlessui/react' import dynamic from 'next/dynamic' import Link from 'next/link' import { useRouter } from 'next/router' @@ -102,7 +101,7 @@ const LayoutBase = props => { slotRight, slotTop } = props - const { onLoading, fullWidth } = useGlobal() + const { fullWidth } = useGlobal() const router = useRouter() const [tocVisible, changeTocVisible] = useState(false) const [pageNavVisible, changePageNavVisible] = useState(false) @@ -174,7 +173,7 @@ const LayoutBase = props => { {slotTop} - { leave='transition ease-in-out duration-300 transform' leaveFrom='opacity-100 translate-y-0' leaveTo='opacity-0 -translate-y-16' - unmount={false}> - {children} - + unmount={false}> */} + {children} + {/* */} {/* Google广告 */} diff --git a/themes/nav/components/NavPostItem.js b/themes/nav/components/NavPostItem.js index ec27ce90ae0..d9ecde88766 100755 --- a/themes/nav/components/NavPostItem.js +++ b/themes/nav/components/NavPostItem.js @@ -1,6 +1,6 @@ -import BlogPostCard from './BlogPostCard' -import { useState } from 'react' import Collapse from '@/components/Collapse' +import { useState } from 'react' +import BlogPostCard from './BlogPostCard' /** * 导航列表 @@ -9,7 +9,7 @@ import Collapse from '@/components/Collapse' * @returns {JSX.Element} * @constructor */ -const NavPostItem = (props) => { +const NavPostItem = props => { const { group } = props const [isOpen, changeIsOpen] = useState(group?.selected) @@ -20,25 +20,37 @@ const NavPostItem = (props) => { console.log(group) if (group?.category) { - return <> -
- {group?.category} -
+ return ( + <> +
+ {group?.category} +
+ +
+
+ + {group?.items?.map((post, index) => ( +
+
- - {group?.items?.map(post => (
-
)) - } -
- + ))} +
+ + ) } else { - return <> - {group?.items?.map(post => (
-
)) - } - + return ( + <> + {group?.items?.map((post, index) => ( +
+ +
+ ))} + + ) } }