'use client' import Comment from '@/components/Comment' import { AdSlot } from '@/components/GoogleAdsense' import Live2D from '@/components/Live2D' import LoadingCover from '@/components/LoadingCover' import NotionIcon from '@/components/NotionIcon' import NotionPage from '@/components/NotionPage' import ShareBar from '@/components/ShareBar' import DashboardBody from '@/components/ui/dashboard/DashboardBody' import DashboardHeader from '@/components/ui/dashboard/DashboardHeader' import { siteConfig } from '@/lib/config' import { useGlobal } from '@/lib/global' import { isBrowser } from '@/lib/utils' import { getShortId } from '@/lib/utils/pageId' import { SignIn, SignUp } from '@clerk/nextjs' import dynamic from 'next/dynamic' import Head from 'next/head' import SmartLink from '@/components/SmartLink' import { useRouter } from 'next/router' import { createContext, useContext, useEffect, useRef, useState } from 'react' import Announcement from './components/Announcement' import ArticleAround from './components/ArticleAround' import ArticleInfo from './components/ArticleInfo' import { ArticleLock } from './components/ArticleLock' import BlogArchiveItem from './components/BlogArchiveItem' import BottomMenuBar from './components/BottomMenuBar' import Catalog from './components/Catalog' import CatalogDrawerWrapper from './components/CatalogDrawerWrapper' import CategoryItem from './components/CategoryItem' import Footer from './components/Footer' import Header from './components/Header' import InfoCard from './components/InfoCard' import JumpToTopButton from './components/JumpToTopButton' import NavPostList from './components/NavPostList' import PageNavDrawer from './components/PageNavDrawer' import RevolverMaps from './components/RevolverMaps' import TagItemMini from './components/TagItemMini' import CONFIG from './config' import { Style } from './style' const AlgoliaSearchModal = dynamic( () => import('@/components/AlgoliaSearchModal'), { ssr: false } ) const WWAds = dynamic(() => import('@/components/WWAds'), { ssr: false }) // 主题全局变量 const ThemeGlobalGitbook = createContext() export const useGitBookGlobal = () => useContext(ThemeGlobalGitbook) /** * 给最新的文章标一个红点 */ function getNavPagesWithLatest(allNavPages, latestPosts, post) { // localStorage 保存id和上次阅读时间戳: posts_read_time = {"${post.id}":"Date()"} const postReadTime = JSON.parse( localStorage.getItem('post_read_time') || '{}' ) if (post) { postReadTime[getShortId(post.id)] = new Date().getTime() } // 更新 localStorage.setItem('post_read_time', JSON.stringify(postReadTime)) return allNavPages?.map(item => { const res = { short_id: item.short_id, title: item.title || '', pageCoverThumbnail: item.pageCoverThumbnail || '', category: item.category || null, tags: item.tags || null, summary: item.summary || null, slug: item.slug, href: item.href, pageIcon: item.pageIcon || '', lastEditedDate: item.lastEditedDate } // 属于最新文章通常6篇 && (无阅读记录 || 最近更新时间大于上次阅读时间) if ( latestPosts.some(post => post?.id.indexOf(item?.short_id) === 14) && (!postReadTime[item.short_id] || postReadTime[item.short_id] < new Date(item.lastEditedDate).getTime()) ) { return { ...res, isLatest: true } } else { return res } }) } /** * 基础布局 * 采用左右两侧布局,移动端使用顶部导航栏 * @returns {JSX.Element} * @constructor */ const LayoutBase = props => { const { children, post, allNavPages, latestPosts, slotLeft, slotRight, slotTop } = props const { fullWidth } = useGlobal() const router = useRouter() const [tocVisible, changeTocVisible] = useState(false) const [pageNavVisible, changePageNavVisible] = useState(false) const [filteredNavPages, setFilteredNavPages] = useState(allNavPages) const searchModal = useRef(null) useEffect(() => { setFilteredNavPages(getNavPagesWithLatest(allNavPages, latestPosts, post)) }, [router]) const GITBOOK_LOADING_COVER = siteConfig( 'GITBOOK_LOADING_COVER', true, CONFIG ) return (