Merge pull request #175 from tangly1024/preview

Preview 修复
This commit is contained in:
tangly1024
2022-04-13 14:48:53 +08:00
committed by GitHub
25 changed files with 144 additions and 97 deletions

View File

@@ -26,6 +26,9 @@ const GiscusComponent = dynamic(
)
const Comment = ({ frontMatter }) => {
if (!frontMatter) {
return <>Loading...</>
}
const router = useRouter()
const { locale, isDarkMode } = useGlobal()
return (

View File

@@ -2,32 +2,28 @@
import BLOG from '@/blog.config'
import { useGlobal } from '@/lib/global'
import { loadExternalResource } from '@/lib/utils'
import { useEffect, useState } from 'react'
import { useEffect } from 'react'
export default function Live2D () {
export default function Live2D() {
if (!BLOG.WIDGET_PET) {
return <></>
}
const [init, setInit] = useState()
const { switchTheme } = useGlobal()
function handleClick () {
function handleClick() {
if (BLOG.WIDGET_PET_SWITCH_THEME) {
switchTheme()
}
}
useEffect(() => {
if (!init) {
initLive2D()
setInit(true)
}
}, [init])
initLive2D()
}, [])
return <canvas id="live2d" className='cursor-pointer' width="280" height="250" onClick={handleClick} alt='切换主题' title='切换主题'/>
return <canvas id="live2d" className='cursor-pointer' width="280" height="250" onClick={handleClick} alt='切换主题' title='切换主题' />
}
function initLive2D () {
function initLive2D() {
// 加载 waifu.css live2d.min.js waifu-tips.js
if (screen.width >= 768) {
Promise.all([

View File

@@ -11,6 +11,7 @@ import { NotionRenderer } from 'react-notion-x'
import mediumZoom from 'medium-zoom'
import { useEffect, useRef } from 'react'
import dynamic from 'next/dynamic'
import { useRouter } from 'next/router'
const Code = dynamic(() =>
import('react-notion-x/build/third-party/code').then((m) => m.Code)
@@ -36,6 +37,10 @@ const Modal = dynamic(
}
)
const NotionPage = ({ post }) => {
if (!post || !post.blockMap) {
return <>{post?.summary || ''}</>
}
const zoom = typeof window !== 'undefined' && mediumZoom({
container: '.notion-viewport',
background: 'rgba(0, 0, 0, 0.2)',
@@ -43,6 +48,8 @@ const NotionPage = ({ post }) => {
})
const zoomRef = useRef(zoom ? zoom.clone() : null)
const router = useRouter()
useEffect(() => {
// 将所有container下的所有图片添加medium-zoom
const container = document?.getElementById('container')
@@ -52,18 +59,20 @@ const NotionPage = ({ post }) => {
(zoomRef.current).attach(imgList[i])
}
}
})
}, [router.events])
return <NotionRenderer
recordMap={post.blockMap}
mapPageUrl={mapPageUrl}
components={{
Code,
Collection,
Equation,
Modal,
Pdf
}} />
return <div id='container'>
<NotionRenderer
recordMap={post.blockMap}
mapPageUrl={mapPageUrl}
components={{
Code,
Collection,
Equation,
Modal,
Pdf
}} />
</div>
}
const mapPageUrl = id => {

View File

@@ -3,14 +3,14 @@ import BLOG from 'blog.config'
const cacheTime = BLOG.isProd ? 10 * 60 : 120 * 60 // 120 minutes for dev,10 minutes for prod
export async function getCacheFromMemory (key, options) {
return cache.get(key)
export async function getCacheFromMemory(key, options) {
return await cache.get(key)
}
export async function setCacheToMemory (key, data) {
export async function setCacheToMemory(key, data) {
await cache.put(key, data, cacheTime * 1000)
}
export async function delCacheFromMemory (key) {
export async function delCacheFromMemory(key) {
await cache.del(key)
}

View File

@@ -6,7 +6,6 @@ import { initDarkMode, initTheme, saveThemeToCookies } from '@/lib/theme'
import { ALL_THEME } from '@/themes'
const GlobalContext = createContext()
let hasInit = false
/**
* 全局变量Provider包括语言本地化、样式主题、搜索词
@@ -14,7 +13,7 @@ let hasInit = false
* @returns {JSX.Element}
* @constructor
*/
export function GlobalContextProvider ({ children }) {
export function GlobalContextProvider({ children }) {
const [locale, updateLocale] = useState(generateLocaleDict('en-US'))
const [isDarkMode, updateDarkMode] = useState(false)
const [onLoading, changeLoadingState] = useState(false)
@@ -27,13 +26,13 @@ export function GlobalContextProvider ({ children }) {
changeLoadingState(false)
})
function switchTheme () {
function switchTheme() {
const currentIndex = ALL_THEME.indexOf(theme)
const newIndex = currentIndex < ALL_THEME.length - 1 ? currentIndex + 1 : 0
changeTheme(ALL_THEME[newIndex])
}
function changeTheme (theme) {
function changeTheme(theme) {
Router.query.theme = ''
if (ALL_THEME.indexOf(theme) > -1) {
setTheme(theme)
@@ -44,13 +43,10 @@ export function GlobalContextProvider ({ children }) {
}
useEffect(() => {
if (!hasInit) {
initLocale(locale, updateLocale)
initDarkMode(isDarkMode, updateDarkMode)
initTheme(theme, changeTheme)
hasInit = true
}
})
initLocale(locale, updateLocale)
initDarkMode(isDarkMode, updateDarkMode)
initTheme(theme, changeTheme)
}, [])
return (
<GlobalContext.Provider value={{ onLoading, locale, updateLocale, isDarkMode, updateDarkMode, theme, setTheme, switchTheme, changeTheme }}>

View File

@@ -80,7 +80,7 @@ export async function getNotionPageData({ pageId, from }) {
const cacheKey = 'page_block_' + pageId
const data = await getDataFromCache(cacheKey)
if (data) {
console.log('[请求缓存]:', `from:${from}`, `id:${pageId}`)
console.log('[请求缓存]:', `from:${from}`, `root-page-id:${pageId}`)
return data
}
const pageRecordMap = await getPageRecordMapByNotionAPI({ pageId, from })

View File

@@ -41,7 +41,7 @@ export async function generateRss(posts) {
link: `${BLOG.LINK}/article/${post.slug}`,
description: post.summary,
content: await createFeedContent(post),
date: new Date(post?.date?.start_date || post.createdTime)
date: new Date(post?.date?.start_date || post?.createdTime)
})
}
return feed.atom1()

View File

@@ -42,7 +42,7 @@ export const initTheme = (theme, changeTheme) => {
* 是否优先深色模式, 根据系统深色模式以及当前时间判断
* @returns {*}
*/
export function isPreferDark () {
export function isPreferDark() {
if (BLOG.APPEARANCE === 'dark') {
return true
}

View File

@@ -6,7 +6,7 @@
* @param type js 或 css
* @returns {Promise<unknown>}
*/
export function loadExternalResource (url, type) {
export function loadExternalResource(url, type) {
return new Promise((resolve, reject) => {
let tag
@@ -31,8 +31,8 @@ export function loadExternalResource (url, type) {
* @param {}} variable
* @returns
*/
export function getQueryVariable (variable) {
const query = window.location.search.substring(1)
export function getQueryVariable(variable) {
const query = typeof window !== 'undefined' ? window.location.search.substring(1) : ''
const vars = query.split('&')
for (let i = 0; i < vars.length; i++) {
const pair = vars[i].split('=')
@@ -46,7 +46,7 @@ export function getQueryVariable (variable) {
* @param target
* @param sources
*/
export function mergeDeep (target, ...sources) {
export function mergeDeep(target, ...sources) {
if (!sources.length) return target
const source = sources.shift()
@@ -68,6 +68,6 @@ export function mergeDeep (target, ...sources) {
* @param item
* @returns {boolean}
*/
export function isObject (item) {
export function isObject(item) {
return (item && typeof item === 'object' && !Array.isArray(item))
}

View File

@@ -32,8 +32,8 @@
"lodash.throttle": "^4.1.1",
"memory-cache": "^0.2.0",
"next": "^12.0.5",
"notion-client": "6.5.0",
"notion-utils": "6.5.0",
"notion-client": "6.10.0",
"notion-utils": "6.10.0",
"preact": "^10.5.15",
"qrcode.react": "^1.0.1",
"react": "17.0.2",

View File

@@ -19,9 +19,9 @@ const Slug = props => {
}
// 文章锁🔐
const [lock, setLock] = useState(true)
const [lock, setLock] = useState(post.password && post.password !== '')
useEffect(() => {
if (post && post.password && post.password !== '') {
if (post.password && post.password !== '') {
setLock(true)
} else {
setLock(false)

View File

@@ -24,20 +24,20 @@ const Slug = props => {
const article = document.getElementById('container')
if (!article) {
router.push('/404').then(() => {
console.log('找不到页面', router.asPath)
console.warn('找不到页面', router.asPath)
})
}
}
}, 3000)
})
return <p>Redirecting...</p>
const meta = { title: `${props?.siteInfo?.title} | loading` }
return <ThemeComponents.LayoutSlug {...props} showArticleInfo={true} meta={meta} />
}
// 文章锁🔐
const [lock, setLock] = useState(true)
const [lock, setLock] = useState(post.password && post.password !== '')
useEffect(() => {
if (post && post.password && post.password !== '') {
if (post.password && post.password !== '') {
setLock(true)
} else {
setLock(false)

View File

@@ -2,15 +2,14 @@ import { getGlobalNotionData } from '@/lib/notion/getNotionData'
import { useGlobal } from '@/lib/global'
import { getDataFromCache } from '@/lib/cache/cache_manager'
import * as ThemeMap from '@/themes'
import BLOG from '@/blog.config'
const Index = props => {
const { keyword, siteInfo } = props
const { locale } = useGlobal()
const meta = {
title: `${keyword || ''}${keyword ? ' | ' : ''}${locale.NAV.SEARCH} | ${
siteInfo.title
}`,
description: siteInfo.title,
title: `${keyword || ''}${keyword ? ' | ' : ''}${locale.NAV.SEARCH} | ${siteInfo?.title}`,
description: siteInfo?.title,
slug: 'search/' + (keyword || ''),
type: 'website'
}
@@ -30,7 +29,7 @@ const Index = props => {
* @param {*} param0
* @returns
*/
export async function getServerSideProps({ params: { keyword } }) {
export async function getStaticProps({ params: { keyword } }) {
const props = await getGlobalNotionData({
from: 'search-props',
pageType: ['Post']
@@ -38,7 +37,15 @@ export async function getServerSideProps({ params: { keyword } }) {
props.posts = await filterByMemCache(props.allPosts, keyword)
props.keyword = keyword
return {
props
props,
revalidate: 1
}
}
export async function getStaticPaths() {
return {
paths: [{ params: { keyword: BLOG.TITLE } }],
fallback: true
}
}
@@ -94,9 +101,11 @@ const isIterable = obj =>
*/
async function filterByMemCache(allPosts, keyword) {
const filterPosts = []
if (keyword) {
keyword = keyword.trim()
}
for (const post of allPosts) {
const cacheKey = 'page_block_' + post.id
// const page = await getPostBlocks(post.id, 'search')
const page = await getDataFromCache(cacheKey)
const tagContent = post.tags ? post.tags.join(' ') : ''
const categoryContent = post.category ? post.category.join(' ') : ''
@@ -111,7 +120,7 @@ async function filterByMemCache(allPosts, keyword) {
indexContent = appendText(indexContent, properties, 'caption')
})
}
// console.log('搜索是否命中缓存', page !== null, indexContent)
console.log('全文搜索缓存', cacheKey, page != null)
post.results = []
let hitCount = 0
for (const i in indexContent) {
@@ -119,7 +128,7 @@ async function filterByMemCache(allPosts, keyword) {
if (!c) {
continue
}
const index = c.toLowerCase().indexOf(keyword.toLowerCase()) || -1
const index = c.toLowerCase().indexOf(keyword.toLowerCase())
if (index > -1) {
hit = true
hitCount += 1

View File

@@ -8,13 +8,18 @@ import formatDate from '@/lib/formatDate'
export const LayoutSlug = props => {
const { post, lock, validPassword } = props
if (!post) {
return <LayoutBase {...props} />
}
if (!lock && post?.blockMap?.block) {
post.content = Object.keys(post.blockMap.block)
post.toc = getPageTableOfContents(post, post.blockMap)
}
const { locale } = useGlobal()
const date = formatDate(post?.date?.start_date || post.createdTime, locale.LOCALE)
const date = formatDate(post?.date?.start_date || post?.createdTime, locale.LOCALE)
return (
<LayoutBase {...props}>
@@ -34,7 +39,7 @@ export const LayoutSlug = props => {
</Link>
<span className='mr-2'>|</span>
{post.type[0] !== 'Page' && (<>
{post?.type[0] !== 'Page' && (<>
<Link
href={`/archive#${post?.date?.start_date?.substr(0, 7)}`}
passHref
@@ -60,7 +65,7 @@ export const LayoutSlug = props => {
</section>
{post.blockMap && <NotionPage post={post} />}
{post && <NotionPage post={post} />}
</section>}
</div>

View File

@@ -10,11 +10,15 @@ import ArticleAround from './ArticleAround'
* @param {*} param0
* @returns
*/
export default function ArticleDetail({ post, recommendPosts, prev, next }) {
export default function ArticleDetail(props) {
const { post, prev, next } = props
if (!post) {
return <></>
}
const { locale } = useGlobal()
const date = formatDate(post?.date?.start_date || post.createdTime, locale.LOCALE)
const date = formatDate(post?.date?.start_date || post?.createdTime, locale.LOCALE)
return (<div id="container" className="max-w-5xl overflow-x-auto flex-grow mx-auto w-screen md:w-full ">
{post.type && !post.type.includes('Page') && post?.page_cover && (
{post?.type && !post?.type.includes('Page') && post?.page_cover && (
<div className="w-full relative md:flex-shrink-0 overflow-hidden">
{/* eslint-disable-next-line @next/next/no-img-element */}
<img alt={post.title} src={post?.page_cover} className='object-center w-full' />
@@ -41,7 +45,7 @@ export default function ArticleDetail({ post, recommendPosts, prev, next }) {
</Link>
<span className='mr-2'>|</span>
{post.type[0] !== 'Page' && (<>
{post?.type[0] !== 'Page' && (<>
<Link
href={`/archive#${post?.date?.start_date?.substr(0, 7)}`}
passHref
@@ -69,7 +73,7 @@ export default function ArticleDetail({ post, recommendPosts, prev, next }) {
{/* Notion文章主体 */}
<section id='notion-article' className='px-1'>
{post.blockMap && <NotionPage post={post} />}
{post && <NotionPage post={post} />}
</section>
<section className="px-1 py-2 my-1 text-sm font-light overflow-auto text-gray-600 dark:text-gray-400">

View File

@@ -15,6 +15,15 @@ import ArticleRecommend from './components/ArticleRecommend'
export const LayoutSlug = props => {
const { post, lock, validPassword } = props
if (!post) {
return <LayoutBase
headerSlot={<HeaderArticle {...props} />}
{...props}
showCategory={false}
showTag={false}
></LayoutBase>
}
if (!lock && post?.blockMap?.block) {
post.content = Object.keys(post.blockMap.block)
post.toc = getPageTableOfContents(post, post.blockMap)
@@ -50,7 +59,7 @@ export const LayoutSlug = props => {
<article itemScope itemType="https://schema.org/Movie" className="subpixel-antialiased" >
{/* Notion文章主体 */}
<section id='notion-article' className='px-5'>
{post.blockMap && <NotionPage post={post} />}
{post && <NotionPage post={post} />}
</section>
<section className="px-1 py-2 my-1 text-sm font-light overflow-auto text-gray-600 dark:text-gray-400">

View File

@@ -13,7 +13,7 @@ import BlogPostListEmpty from './BlogPostListEmpty'
*/
const BlogPostListPage = ({ page = 1, posts = [], postCount }) => {
const totalPage = Math.ceil(postCount / BLOG.POSTS_PER_PAGE)
const showPagination = posts.length === BLOG.POSTS_PER_PAGE
const showPagination = postCount >= BLOG.POSTS_PER_PAGE
if (!posts || posts.length === 0) {
return <BlogPostListEmpty />
@@ -26,7 +26,7 @@ const BlogPostListPage = ({ page = 1, posts = [], postCount }) => {
<BlogPostCard key={post.id} post={post} />
))}
</div>
{showPagination && <PaginationNumber page={page} totalPage={totalPage} /> }
{showPagination && <PaginationNumber page={page} totalPage={totalPage} />}
</div>
)
}

View File

@@ -4,12 +4,15 @@ import formatDate from '@/lib/formatDate'
import { useEffect } from 'react'
export default function HeaderArticle({ post, siteInfo }) {
if (!post) {
return <>loading...</>
}
const headerImage = post?.page_cover ? `url("${post.page_cover}")` : `url("${siteInfo?.pageCover}")`
const { isDarkMode } = useGlobal()
const { locale } = useGlobal()
const date = formatDate(
post?.date?.start_date || post.createdTime,
post?.date?.start_date || post?.createdTime,
locale.LOCALE
)
@@ -71,7 +74,7 @@ export default function HeaderArticle({ post, siteInfo }) {
</>}
</div>
<div className='flex justify-center'>
{post.type[0] !== 'Page' && (
{post?.type[0] !== 'Page' && (
<>
<Link
href={`/archive#${post?.date?.start_date?.substr(0, 7)}`}

View File

@@ -9,6 +9,12 @@ import { ArticleLock } from './components/ArticleLock'
export const LayoutSlug = props => {
const { post, lock, validPassword } = props
if (!post) {
return <LayoutBase
{...props}
showInfoCard={true}
/>
}
if (!lock && post?.blockMap?.block) {
post.content = Object.keys(post.blockMap.block)

View File

@@ -15,7 +15,7 @@ export const ArticleDetail = props => {
const { locale } = useGlobal()
const date = formatDate(
post?.date?.start_date || post.createdTime,
post?.date?.start_date || post?.createdTime,
locale.LOCALE
)
return <div id='container'>
@@ -48,7 +48,7 @@ export const ArticleDetail = props => {
</section>
{/* Notion文章主体 */}
<section id="notion-article" className="px-1 max-w-5xl">
{post.blockMap && (<NotionPage post={post} />)}
{post && (<NotionPage post={post} />)}
</section>
<section className="px-1 py-2 my-1 text-sm font-light overflow-auto text-gray-600 dark:text-gray-400">

View File

@@ -10,7 +10,6 @@ const Progress = ({ targetRef, showPercent = true }) => {
const [percent, changePercent] = useState(0)
const scrollListener = () => {
const target = currentRef || document.getElementById('container')
console.log(target)
if (target) {
const clientHeight = target.clientHeight
const scrollY = window.pageYOffset

View File

@@ -11,6 +11,14 @@ import { ArticleLock } from './components/ArticleLock'
export const LayoutSlug = (props) => {
const { post, latestPosts, lock, validPassword } = props
if (!post) {
return <LayoutBase
{...props}
rightAreaSlot={
CONFIG_NEXT.RIGHT_LATEST_POSTS && <Card><LatestPostsGroup posts={latestPosts} /></Card>
}
/>
}
if (!lock && post?.blockMap?.block) {
post.content = Object.keys(post.blockMap.block)

View File

@@ -21,15 +21,15 @@ export default function ArticleDetail(props) {
const { post, recommendPosts, prev, next, showArticleInfo } = props
const url = BLOG.LINK + useRouter().asPath
const { locale } = useGlobal()
const date = formatDate(post?.date?.start_date || post.createdTime, locale.LOCALE)
const date = formatDate(post?.date?.start_date || post?.createdTime, locale.LOCALE)
return (<div id="container" className="shadow md:hover:shadow-2xl overflow-x-auto flex-grow mx-auto w-screen md:w-full ">
<div itemScope itemType="https://schema.org/Movie"
className="subpixel-antialiased py-10 px-5 lg:pt-24 md:px-24 dark:border-gray-700 bg-white dark:bg-gray-800"
className="subpixel-antialiased py-10 px-5 lg:pt-24 md:px-24 dark:border-gray-700 bg-white dark:bg-hexo-black-gray"
>
{showArticleInfo && <header className='animate__slideInDown animate__animated'>
{post.type && !post.type.includes('Page') && post?.page_cover && (
{post?.type && !post?.type.includes('Page') && post?.page_cover && (
<div className="w-full relative md:flex-shrink-0 overflow-hidden">
{/* eslint-disable-next-line @next/next/no-img-element */}
<img alt={post.title} src={post?.page_cover} className='object-center w-full' />
@@ -51,7 +51,7 @@ export default function ArticleDetail(props) {
</Link>
<span className='mr-2'>|</span>
</>}
{post.type[0] !== 'Page' && (<>
{post?.type[0] !== 'Page' && (<>
<Link
href={`/archive#${post?.date?.start_date?.substr(0, 7)}`}
passHref
@@ -84,7 +84,7 @@ export default function ArticleDetail(props) {
{/* Notion内容主体 */}
<article id='notion-article' className='px-1'>
{post.blockMap && (<NotionPage post={post} />)}
{post && (<NotionPage post={post} />)}
</article>
<section className="px-1 py-2 my-1 text-sm font-light overflow-auto text-gray-600 dark:text-gray-400">

View File

@@ -7,17 +7,17 @@ const Footer = ({ title }) => {
const startYear = BLOG.SINCE && BLOG.SINCE !== currentYear && BLOG.SINCE + '-'
return (
<footer
className='dark:bg-gray-900 flex-shrink-0 justify-center text-center m-auto w-full leading-6 text-sm p-6'
className='dark:bg-gray-900 flex-shrink-0 justify-center text-center m-auto w-full leading-6 text-sm p-6 dark:text-gray-400'
>
<i className='fas fa-copyright' /> {`${startYear}${currentYear}`} <span><i className='mx-1 animate-pulse fas fas-heart'/> <a href={BLOG.LINK} className='underline font-bold text-gray-500 dark:text-gray-300 '>{BLOG.AUTHOR}</a>.<br/>
<i className='fas fa-copyright' /> {`${startYear}${currentYear}`} <span><i className='mx-1 animate-pulse fas fas-heart' /> <a href={BLOG.LINK} className='underline font-bold '>{BLOG.AUTHOR}</a>.<br />
{BLOG.BEI_AN && <><i className='fas fa-shield-alt' /> <a href='https://beian.miit.gov.cn/' className='mr-2'>{BLOG.BEI_AN}</a><br/></>}
{BLOG.BEI_AN && <><i className='fas fa-shield-alt' /> <a href='https://beian.miit.gov.cn/' className='mr-2'>{BLOG.BEI_AN}</a><br /></>}
<span className='hidden busuanzi_container_site_pv'>
<i className='fas fa-eye'/><span className='px-1 busuanzi_value_site_pv'> </span> </span>
<span className='pl-2 hidden busuanzi_container_site_uv'>
<i className='fas fa-users'/> <span className='px-1 busuanzi_value_site_uv'> </span> </span>
<br/>
<span className='hidden busuanzi_container_site_pv'>
<i className='fas fa-eye' /><span className='px-1 busuanzi_value_site_pv'> </span> </span>
<span className='pl-2 hidden busuanzi_container_site_uv'>
<i className='fas fa-users' /> <span className='px-1 busuanzi_value_site_uv'> </span> </span>
<br />
<h1>{title}</h1>
<span className='text-xs font-serif text-gray-500 dark:text-gray-300 '>Powered by <a href='https://github.com/tangly1024/NotionNext' className='underline '>NotionNext {BLOG.VERSION}</a>.</span></span>
</footer>

View File

@@ -60,10 +60,10 @@ const Toc = ({ toc }) => {
return <div className='px-3'>
<div className='w-full pb-1'>
<Progress/>
<Progress />
</div>
<div className='overflow-y-auto max-h-96 overscroll-none' ref={tRef}>
<nav className='h-full font-sans text-black'>
<nav className='h-full font-sans text-black dark:text-gray-300'>
{toc.map((tocItem) => {
const id = uuidToId(tocItem.id)
tocIds.push(id)
@@ -74,9 +74,9 @@ const Toc = ({ toc }) => {
className={`notion-table-of-contents-item duration-300 transform font-light
notion-table-of-contents-item-indent-level-${tocItem.indentLevel} `}
>
<span style={{ display: 'inline-block', marginLeft: tocItem.indentLevel * 16 }} className={`${activeSection === id && ' font-bold text-red-400 underline'}`}>
{tocItem.text}
</span>
<span style={{ display: 'inline-block', marginLeft: tocItem.indentLevel * 16 }} className={`${activeSection === id && ' font-bold text-red-400 underline'}`}>
{tocItem.text}
</span>
</a>
)
})}