mirror of
https://github.com/d0zingcat/NotionNext.git
synced 2026-05-14 07:26:52 +00:00
Merge branch 'main' into feat/new-theme-landing
This commit is contained in:
@@ -1,2 +1,2 @@
|
||||
# 环境变量 @see https://www.nextjs.cn/docs/basic-features/environment-variables
|
||||
NEXT_PUBLIC_VERSION=3.16.2
|
||||
NEXT_PUBLIC_VERSION=3.16.3
|
||||
@@ -5,7 +5,7 @@ const BLOG = {
|
||||
process.env.NOTION_PAGE_ID || '02ab3b8678004aa69e9e415905ef32a5',
|
||||
PSEUDO_STATIC: false, // 伪静态路径,开启后所有文章URL都以 .html 结尾。
|
||||
NEXT_REVALIDATE_SECOND: process.env.NEXT_PUBLIC_REVALIDATE_SECOND || 5, // 更新内容缓存间隔 单位(秒);即每个页面有5秒的纯静态期、此期间无论多少次访问都不会抓取notion数据;调大该值有助于节省Vercel资源、同时提升访问速率,但也会使文章更新有延迟。
|
||||
THEME: process.env.NEXT_PUBLIC_THEME || 'fukasawa', // 主题, 支持 ['next','hexo',"fukasawa','medium','example','matery','gitbook','simple'] @see https://preview.tangly1024.com
|
||||
THEME: process.env.NEXT_PUBLIC_THEME || 'hexo', // 主题, 支持 ['next','hexo',"fukasawa','medium','example','matery','gitbook','simple'] @see https://preview.tangly1024.com
|
||||
THEME_SWITCH: process.env.NEXT_PUBLIC_THEME_SWITCH || false, // 是否显示切换主题按钮
|
||||
LANG: process.env.NEXT_PUBLIC_LANG || 'zh-CN', // e.g 'zh-CN','en-US' see /lib/lang.js for more.
|
||||
SINCE: 2021, // e.g if leave this empty, current year will be used.
|
||||
@@ -154,7 +154,7 @@ const BLOG = {
|
||||
WIDGET_PET_LINK:
|
||||
process.env.NEXT_PUBLIC_WIDGET_PET_LINK ||
|
||||
'https://cdn.jsdelivr.net/npm/live2d-widget-model-wanko@1.0.5/assets/wanko.model.json', // 挂件模型地址 @see https://github.com/xiazeyu/live2d-widget-models
|
||||
WIDGET_PET_SWITCH_THEME: true, // 点击宠物挂件切换博客主题
|
||||
WIDGET_PET_SWITCH_THEME: process.env.NEXT_PUBLIC_WIDGET_PET_SWITCH_THEME || true, // 点击宠物挂件切换博客主题
|
||||
|
||||
// 音乐播放插件
|
||||
MUSIC_PLAYER: process.env.NEXT_PUBLIC_MUSIC_PLAYER || false, // 是否使用音乐播放插件
|
||||
|
||||
@@ -56,7 +56,7 @@ const CommonHead = ({ meta, children }) => {
|
||||
<>
|
||||
<meta
|
||||
property="article:published_time"
|
||||
content={meta.date || meta.createdTime}
|
||||
content={meta.publishTime}
|
||||
/>
|
||||
<meta property="article:author" content={BLOG.AUTHOR} />
|
||||
<meta property="article:section" content={category} />
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useRef, useEffect } from 'react'
|
||||
import { useRef, useEffect, useState } from 'react'
|
||||
/**
|
||||
* 可拖拽组件
|
||||
*/
|
||||
@@ -7,6 +7,7 @@ export const Draggable = (props) => {
|
||||
const { children } = props
|
||||
const draggableRef = useRef(null)
|
||||
const rafRef = useRef(null)
|
||||
const [moving, setMoving] = useState(false)
|
||||
let currentObj, offsetX, offsetY
|
||||
|
||||
useEffect(() => {
|
||||
@@ -54,6 +55,8 @@ export const Draggable = (props) => {
|
||||
event.preventDefault() // 阻止默认的滚动行为
|
||||
document.documentElement.style.overflow = 'hidden' // 防止页面一起滚动
|
||||
}
|
||||
|
||||
setMoving(true)
|
||||
offsetX = event.mx - currentObj.offsetLeft
|
||||
offsetY = event.my - currentObj.offsetTop
|
||||
|
||||
@@ -73,6 +76,7 @@ export const Draggable = (props) => {
|
||||
event = e(event)
|
||||
document.documentElement.style.overflow = 'auto' // 恢复默认的滚动行为
|
||||
cancelAnimationFrame(rafRef.current)
|
||||
setMoving(false)
|
||||
currentObj = document.ontouchmove = document.ontouchend = document.onmousemove = document.onmouseup = null
|
||||
}
|
||||
|
||||
@@ -138,7 +142,7 @@ export const Draggable = (props) => {
|
||||
}
|
||||
}, [])
|
||||
|
||||
return <div className='draggable cursor-move select-none' ref={draggableRef}>
|
||||
return <div className={`draggable ${moving ? 'cursor-grabbing' : 'cursor-grab'} select-none`} ref={draggableRef}>
|
||||
{children}
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ export default function Live2D() {
|
||||
}, [theme])
|
||||
|
||||
function handleClick() {
|
||||
if (BLOG.WIDGET_PET_SWITCH_THEME) {
|
||||
if (JSON.parse(BLOG.WIDGET_PET_SWITCH_THEME)) {
|
||||
switchTheme()
|
||||
}
|
||||
}
|
||||
@@ -35,5 +35,9 @@ export default function Live2D() {
|
||||
return <></>
|
||||
}
|
||||
|
||||
return <canvas id="live2d" className='cursor-pointer' width="280" height="250" onClick={handleClick} alt='切换主题' title='切换主题' />
|
||||
return <canvas id="live2d" width="280" height="250" onClick={handleClick}
|
||||
className="cursor-grab"
|
||||
onMouseDown={(e) => e.target.classList.add('cursor-grabbing')}
|
||||
onMouseUp={(e) => e.target.classList.remove('cursor-grabbing')}
|
||||
/>
|
||||
}
|
||||
|
||||
@@ -23,14 +23,13 @@ const ThemeSwitch = () => {
|
||||
return (<>
|
||||
<Draggable>
|
||||
<div id="draggableBox" style={{ left: '10px', top: '85vh' }} className="fixed text-white bg-black z-50 rounded-lg shadow-card">
|
||||
<div className="py-2 flex items-center text-sm">
|
||||
<i className='fas fa-arrows cursor-move px-2' />
|
||||
{/* <div className='uppercase font-sans whitespace-nowrap cursor-pointer ' onClick={switchTheme}> {theme}</div> */}
|
||||
<div className="py-2 flex items-center text-sm px-2">
|
||||
<select value={theme} onChange={onSelectChange} name="cars" className='text-white bg-black uppercase cursor-pointer'>
|
||||
{THEMES.map(t => {
|
||||
return <option key={t} value={t}>{t}</option>
|
||||
})}
|
||||
</select>
|
||||
<i className='fas fa-palette pl-1' />
|
||||
</div>
|
||||
</div>
|
||||
</Draggable>
|
||||
|
||||
@@ -13,7 +13,6 @@ export default function formatDate (date, local) {
|
||||
? res.replace('年', '-').replace('月', '-').replace('日', '')
|
||||
: res
|
||||
}
|
||||
|
||||
export function formatDateFmt (timestamp, fmt) {
|
||||
const date = new Date(timestamp)
|
||||
const o = {
|
||||
|
||||
@@ -45,7 +45,7 @@ export async function getAllPosts({ notionPageData, from, pageType }) {
|
||||
// Sort by date
|
||||
if (BLOG.POSTS_SORT_BY === 'date') {
|
||||
posts.sort((a, b) => {
|
||||
return b?.sortDate - a?.sortDate
|
||||
return b?.publishDate - a?.publishDate
|
||||
})
|
||||
}
|
||||
return posts
|
||||
|
||||
@@ -20,23 +20,23 @@ import { mapImgUrl, compressImage } from './mapImage'
|
||||
* @returns
|
||||
*
|
||||
*/
|
||||
export async function getGlobalNotionData({
|
||||
export async function getGlobalData({
|
||||
pageId = BLOG.NOTION_PAGE_ID,
|
||||
from
|
||||
}) {
|
||||
// 获取Notion数据
|
||||
const notionPageData = deepClone(await getNotionPageData({ pageId, from }))
|
||||
notionPageData.allNavPages = getNavPages({ allPages: notionPageData.allPages })
|
||||
delete notionPageData.block
|
||||
delete notionPageData.schema
|
||||
delete notionPageData.rawMetadata
|
||||
delete notionPageData.pageIds
|
||||
delete notionPageData.viewIds
|
||||
delete notionPageData.collection
|
||||
delete notionPageData.collectionQuery
|
||||
delete notionPageData.collectionId
|
||||
delete notionPageData.collectionView
|
||||
return notionPageData
|
||||
// 从notion获取
|
||||
const db = deepClone(await getNotionPageData({ pageId, from }))
|
||||
// 不返回的敏感数据
|
||||
delete db.block
|
||||
delete db.schema
|
||||
delete db.rawMetadata
|
||||
delete db.pageIds
|
||||
delete db.viewIds
|
||||
delete db.collection
|
||||
delete db.collectionQuery
|
||||
delete db.collectionId
|
||||
delete db.collectionView
|
||||
return db
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -48,8 +48,8 @@ function getLatestPosts({ allPages, from, latestPostCount }) {
|
||||
const allPosts = allPages?.filter(page => page.type === 'Post' && page.status === 'Published')
|
||||
|
||||
const latestPosts = Object.create(allPosts).sort((a, b) => {
|
||||
const dateA = new Date(a?.lastEditedTime || a?.sortDate)
|
||||
const dateB = new Date(b?.lastEditedTime || b?.sortDate)
|
||||
const dateA = new Date(a?.lastEditedTime || a?.publishDate)
|
||||
const dateB = new Date(b?.lastEditedTime || b?.publishDate)
|
||||
return dateB - dateA
|
||||
})
|
||||
return latestPosts.slice(0, latestPostCount)
|
||||
@@ -69,12 +69,12 @@ export async function getNotionPageData({ pageId, from }) {
|
||||
console.log('[缓存]:', `from:${from}`, `root-page-id:${pageId}`)
|
||||
return data
|
||||
}
|
||||
const pageRecordMap = await getDataBaseInfoByNotionAPI({ pageId, from })
|
||||
const db = await getDataBaseInfoByNotionAPI({ pageId, from })
|
||||
// 存入缓存
|
||||
if (pageRecordMap) {
|
||||
await setDataToCache(cacheKey, pageRecordMap)
|
||||
if (db) {
|
||||
await setDataToCache(cacheKey, db)
|
||||
}
|
||||
return pageRecordMap
|
||||
return db
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -298,7 +298,7 @@ async function getDataBaseInfoByNotionAPI({ pageId, from }) {
|
||||
// Sort by date
|
||||
if (BLOG.POSTS_SORT_BY === 'date') {
|
||||
allPages.sort((a, b) => {
|
||||
return b?.sortDate - a?.sortDate
|
||||
return b?.publishDate - a?.publishDate
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -92,11 +92,9 @@ export default async function getPageProperties(id, block, schema, authToken, ta
|
||||
properties.slug += '.html'
|
||||
}
|
||||
}
|
||||
|
||||
properties.sortDate = value?.date?.start_date || value.created_time
|
||||
properties.createdTime = formatDate(new Date(value.created_time).toString(), BLOG.LANG)
|
||||
properties.publishTime = value?.date?.start_date ? formatDate(new Date(value?.date?.start_date).toString, BLOG.LANG) : properties.createdTime
|
||||
properties.lastEditedTime = formatDate(new Date(value?.last_edited_time).toString(), BLOG.LANG)
|
||||
properties.publishDate = new Date(properties?.date?.start_date || value.created_time).getTime()
|
||||
properties.publishTime = formatDate(properties.publishDate, BLOG.LANG)
|
||||
properties.lastEditedTime = formatDate(new Date(value?.last_edited_time), BLOG.LANG)
|
||||
properties.fullWidth = value.format?.page_full_width ?? false
|
||||
properties.pageIcon = mapImgUrl(block[id].value?.format?.page_icon, block[id].value) ?? ''
|
||||
properties.pageCover = mapImgUrl(block[id].value?.format?.page_cover, block[id].value) ?? ''
|
||||
|
||||
@@ -46,7 +46,7 @@ export async function generateRss(posts) {
|
||||
link: `${BLOG.LINK}/${post.slug}`,
|
||||
description: post.summary,
|
||||
content: await createFeedContent(post),
|
||||
date: new Date(post?.publishTime || post?.createdTime)
|
||||
date: new Date(post?.publishTime)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ export async function generateSitemapXml({ allPages }) {
|
||||
allPages?.forEach(post => {
|
||||
urls.push({
|
||||
loc: `${BLOG.LINK}/${post.slug}`,
|
||||
lastmod: new Date(post?.publishTime || post?.createdTime).toISOString().split('T')[0],
|
||||
lastmod: new Date(post?.publishTime).toISOString().split('T')[0],
|
||||
changefreq: 'daily'
|
||||
})
|
||||
})
|
||||
|
||||
@@ -94,6 +94,9 @@ module.exports = withBundleAnalyzer({
|
||||
config.resolve.alias['@theme-components'] = path.resolve(__dirname, 'themes', THEME)
|
||||
return config
|
||||
},
|
||||
experimental: {
|
||||
scrollRestoration: true
|
||||
},
|
||||
publicRuntimeConfig: { // 这里的配置既可以服务端获取到,也可以在浏览器端获取到
|
||||
NODE_ENV_API: process.env.NODE_ENV_API || 'prod',
|
||||
THEMES: themes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "notion-next",
|
||||
"version": "3.16.2",
|
||||
"version": "3.16.3",
|
||||
"homepage": "https://github.com/tangly1024/NotionNext.git",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { getGlobalNotionData } from '@/lib/notion/getNotionData'
|
||||
import { getGlobalData } from '@/lib/notion/getNotionData'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import { useRouter } from 'next/router'
|
||||
import { getLayoutByTheme } from '@/themes/theme'
|
||||
@@ -21,7 +21,7 @@ const NoFound = props => {
|
||||
}
|
||||
|
||||
export async function getStaticProps () {
|
||||
const props = (await getGlobalNotionData({ from: '404' })) || {}
|
||||
const props = (await getGlobalData({ from: '404' })) || {}
|
||||
return { props }
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import BLOG from '@/blog.config'
|
||||
import { getPostBlocks } from '@/lib/notion'
|
||||
import { getGlobalNotionData } from '@/lib/notion/getNotionData'
|
||||
import { getGlobalData } from '@/lib/notion/getNotionData'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { idToUuid } from 'notion-utils'
|
||||
import { useRouter } from 'next/router'
|
||||
@@ -61,9 +61,6 @@ const Slug = props => {
|
||||
post.toc = getPageTableOfContents(post, post.blockMap)
|
||||
}
|
||||
}
|
||||
router.events.on('routeChangeComplete', () => {
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' })
|
||||
})
|
||||
}, [post])
|
||||
|
||||
const meta = {
|
||||
@@ -90,7 +87,7 @@ export async function getStaticPaths() {
|
||||
}
|
||||
|
||||
const from = 'slug-paths'
|
||||
const { allPages } = await getGlobalNotionData({ from })
|
||||
const { allPages } = await getGlobalData({ from })
|
||||
return {
|
||||
paths: allPages?.map(row => ({ params: { slug: [row.slug] } })),
|
||||
fallback: true
|
||||
@@ -105,7 +102,7 @@ export async function getStaticProps({ params: { slug } }) {
|
||||
}
|
||||
}
|
||||
const from = `slug-props-${fullSlug}`
|
||||
const props = await getGlobalNotionData({ from })
|
||||
const props = await getGlobalData({ from })
|
||||
// 在列表内查找文章
|
||||
props.post = props?.allPages?.find((p) => {
|
||||
return p.slug === fullSlug || p.id === idToUuid(fullSlug)
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { getGlobalNotionData } from '@/lib/notion/getNotionData'
|
||||
import React, { useEffect } from 'react'
|
||||
import { getGlobalData } from '@/lib/notion/getNotionData'
|
||||
import { useEffect } from 'react'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import BLOG from '@/blog.config'
|
||||
import { useRouter } from 'next/router'
|
||||
import { getLayoutByTheme } from '@/themes/theme'
|
||||
import { isBrowser } from '@/lib/utils'
|
||||
import { formatDateFmt } from '@/lib/formatDate'
|
||||
|
||||
const ArchiveIndex = props => {
|
||||
const { siteInfo } = props
|
||||
@@ -41,7 +42,7 @@ const ArchiveIndex = props => {
|
||||
}
|
||||
|
||||
export async function getStaticProps() {
|
||||
const props = await getGlobalNotionData({ from: 'archive-index' })
|
||||
const props = await getGlobalData({ from: 'archive-index' })
|
||||
// 处理分页
|
||||
props.posts = props.allPages.filter(page => page.type === 'Post' && page.status === 'Published')
|
||||
delete props.allPages
|
||||
@@ -49,15 +50,13 @@ export async function getStaticProps() {
|
||||
const postsSortByDate = Object.create(props.posts)
|
||||
|
||||
postsSortByDate.sort((a, b) => {
|
||||
const dateA = new Date(a?.publishTime || a.createdTime)
|
||||
const dateB = new Date(b?.publishTime || b.createdTime)
|
||||
return dateB - dateA
|
||||
return b?.publishDate - a?.publishDate
|
||||
})
|
||||
|
||||
const archivePosts = {}
|
||||
|
||||
postsSortByDate.forEach(post => {
|
||||
const date = post.date?.start_date?.slice(0, 7) || post.createdTime
|
||||
const date = formatDateFmt(post.publishDate, 'yyyy-MM')
|
||||
if (archivePosts[date]) {
|
||||
archivePosts[date].push(post)
|
||||
} else {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { getGlobalNotionData } from '@/lib/notion/getNotionData'
|
||||
import { getGlobalData } from '@/lib/notion/getNotionData'
|
||||
import React from 'react'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import BLOG from '@/blog.config'
|
||||
@@ -34,7 +34,7 @@ export default function Category(props) {
|
||||
|
||||
export async function getStaticProps({ params: { category } }) {
|
||||
const from = 'category-props'
|
||||
let props = await getGlobalNotionData({ from })
|
||||
let props = await getGlobalData({ from })
|
||||
|
||||
// 过滤状态
|
||||
props.posts = props.allPages.filter(page => page.type === 'Post' && page.status === 'Published')
|
||||
@@ -61,7 +61,7 @@ export async function getStaticProps({ params: { category } }) {
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const from = 'category-paths'
|
||||
const { categoryOptions } = await getGlobalNotionData({ from })
|
||||
const { categoryOptions } = await getGlobalData({ from })
|
||||
return {
|
||||
paths: Object.keys(categoryOptions).map(category => ({
|
||||
params: { category: categoryOptions[category]?.name }
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { getGlobalNotionData } from '@/lib/notion/getNotionData'
|
||||
import { getGlobalData } from '@/lib/notion/getNotionData'
|
||||
import React from 'react'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import BLOG from '@/blog.config'
|
||||
@@ -34,7 +34,7 @@ export default function Category(props) {
|
||||
|
||||
export async function getStaticProps({ params: { category, page } }) {
|
||||
const from = 'category-page-props'
|
||||
let props = await getGlobalNotionData({ from })
|
||||
let props = await getGlobalData({ from })
|
||||
|
||||
// 过滤状态类型
|
||||
props.posts = props.allPages.filter(page => page.type === 'Post' && page.status === 'Published').filter(post => post && post.category && post.category.includes(category))
|
||||
@@ -56,7 +56,7 @@ export async function getStaticProps({ params: { category, page } }) {
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const from = 'category-paths'
|
||||
const { categoryOptions, allPages } = await getGlobalNotionData({ from })
|
||||
const { categoryOptions, allPages } = await getGlobalData({ from })
|
||||
const paths = []
|
||||
|
||||
categoryOptions?.forEach(category => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { getGlobalNotionData } from '@/lib/notion/getNotionData'
|
||||
import { getGlobalData } from '@/lib/notion/getNotionData'
|
||||
import React from 'react'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import BLOG from '@/blog.config'
|
||||
@@ -30,7 +30,7 @@ export default function Category(props) {
|
||||
}
|
||||
|
||||
export async function getStaticProps() {
|
||||
const props = await getGlobalNotionData({ from: 'category-index-props' })
|
||||
const props = await getGlobalData({ from: 'category-index-props' })
|
||||
delete props.allPages
|
||||
return {
|
||||
props,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import BLOG from '@/blog.config'
|
||||
import { getPostBlocks } from '@/lib/notion'
|
||||
import { getGlobalNotionData } from '@/lib/notion/getNotionData'
|
||||
import { getGlobalData } from '@/lib/notion/getNotionData'
|
||||
import { generateRss } from '@/lib/rss'
|
||||
import { generateRobotsTxt } from '@/lib/robots.txt'
|
||||
|
||||
@@ -24,7 +24,7 @@ const Index = props => {
|
||||
*/
|
||||
export async function getStaticProps() {
|
||||
const from = 'index'
|
||||
const props = await getGlobalNotionData({ from })
|
||||
const props = await getGlobalData({ from })
|
||||
|
||||
const { siteInfo } = props
|
||||
props.posts = props.allPages.filter(page => page.type === 'Post' && page.status === 'Published')
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import BLOG from '@/blog.config'
|
||||
import { getPostBlocks } from '@/lib/notion'
|
||||
import { getGlobalNotionData } from '@/lib/notion/getNotionData'
|
||||
import { getGlobalData } from '@/lib/notion/getNotionData'
|
||||
import { useRouter } from 'next/router'
|
||||
import { getLayoutByTheme } from '@/themes/theme'
|
||||
|
||||
@@ -30,7 +30,7 @@ const Page = props => {
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const from = 'page-paths'
|
||||
const { postCount } = await getGlobalNotionData({ from })
|
||||
const { postCount } = await getGlobalData({ from })
|
||||
const totalPages = Math.ceil(postCount / BLOG.POSTS_PER_PAGE)
|
||||
return {
|
||||
// remove first page, we 're not gonna handle that.
|
||||
@@ -43,7 +43,7 @@ export async function getStaticPaths() {
|
||||
|
||||
export async function getStaticProps({ params: { page } }) {
|
||||
const from = `page-${page}`
|
||||
const props = await getGlobalNotionData({ from })
|
||||
const props = await getGlobalData({ from })
|
||||
const { allPages } = props
|
||||
const allPosts = allPages.filter(page => page.type === 'Post' && page.status === 'Published')
|
||||
// 处理分页
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { getGlobalNotionData } from '@/lib/notion/getNotionData'
|
||||
import { getGlobalData } from '@/lib/notion/getNotionData'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import { getDataFromCache } from '@/lib/cache/cache_manager'
|
||||
import BLOG from '@/blog.config'
|
||||
@@ -31,7 +31,7 @@ const Index = props => {
|
||||
* @returns
|
||||
*/
|
||||
export async function getStaticProps({ params: { keyword } }) {
|
||||
const props = await getGlobalNotionData({
|
||||
const props = await getGlobalData({
|
||||
from: 'search-props',
|
||||
pageType: ['Post']
|
||||
})
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { getGlobalNotionData } from '@/lib/notion/getNotionData'
|
||||
import { getGlobalData } from '@/lib/notion/getNotionData'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import { getDataFromCache } from '@/lib/cache/cache_manager'
|
||||
import BLOG from '@/blog.config'
|
||||
@@ -31,7 +31,7 @@ const Index = props => {
|
||||
* @returns
|
||||
*/
|
||||
export async function getStaticProps({ params: { keyword, page } }) {
|
||||
const props = await getGlobalNotionData({
|
||||
const props = await getGlobalData({
|
||||
from: 'search-props',
|
||||
pageType: ['Post']
|
||||
})
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { getGlobalNotionData } from '@/lib/notion/getNotionData'
|
||||
import { getGlobalData } from '@/lib/notion/getNotionData'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import { useRouter } from 'next/router'
|
||||
import BLOG from '@/blog.config'
|
||||
@@ -45,7 +45,7 @@ const Search = props => {
|
||||
* 浏览器前端搜索
|
||||
*/
|
||||
export async function getStaticProps() {
|
||||
const props = await getGlobalNotionData({
|
||||
const props = await getGlobalData({
|
||||
from: 'search-props',
|
||||
pageType: ['Post']
|
||||
})
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
// pages/sitemap.xml.js
|
||||
import { getServerSideSitemap } from 'next-sitemap'
|
||||
import { getGlobalNotionData } from '@/lib/notion/getNotionData'
|
||||
import { getGlobalData } from '@/lib/notion/getNotionData'
|
||||
import BLOG from '@/blog.config'
|
||||
|
||||
export const getServerSideProps = async (ctx) => {
|
||||
const { allPages } = await getGlobalNotionData({ from: 'rss' })
|
||||
const { allPages } = await getGlobalData({ from: 'rss' })
|
||||
const defaultFields = [
|
||||
{
|
||||
loc: `${BLOG.LINK}`,
|
||||
@@ -41,7 +41,7 @@ export const getServerSideProps = async (ctx) => {
|
||||
const postFields = allPages?.filter(p => p.status === BLOG.NOTION_PROPERTY_NAME.status_publish)?.map(post => {
|
||||
return {
|
||||
loc: `${BLOG.LINK}/${post.slug}`,
|
||||
lastmod: new Date(post?.publishTime || post?.createdTime).toISOString().split('T')[0],
|
||||
lastmod: new Date(post?.publishTime).toISOString().split('T')[0],
|
||||
changefreq: 'daily',
|
||||
priority: '0.7'
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import { getGlobalNotionData } from '@/lib/notion/getNotionData'
|
||||
import { getGlobalData } from '@/lib/notion/getNotionData'
|
||||
import BLOG from '@/blog.config'
|
||||
import { useRouter } from 'next/router'
|
||||
import { getLayoutByTheme } from '@/themes/theme'
|
||||
@@ -30,7 +30,7 @@ const Tag = props => {
|
||||
|
||||
export async function getStaticProps({ params: { tag } }) {
|
||||
const from = 'tag-props'
|
||||
const props = await getGlobalNotionData({ from })
|
||||
const props = await getGlobalData({ from })
|
||||
|
||||
// 过滤状态
|
||||
props.posts = props.allPages.filter(page => page.type === 'Post' && page.status === 'Published').filter(post => post && post.tags && post.tags.includes(tag))
|
||||
@@ -68,7 +68,7 @@ function getTagNames(tags) {
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const from = 'tag-static-path'
|
||||
const { tagOptions } = await getGlobalNotionData({ from })
|
||||
const { tagOptions } = await getGlobalData({ from })
|
||||
const tagNames = getTagNames(tagOptions)
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import { getGlobalNotionData } from '@/lib/notion/getNotionData'
|
||||
import { getGlobalData } from '@/lib/notion/getNotionData'
|
||||
import BLOG from '@/blog.config'
|
||||
import { useRouter } from 'next/router'
|
||||
import { getLayoutByTheme } from '@/themes/theme'
|
||||
@@ -25,7 +25,7 @@ const Tag = props => {
|
||||
|
||||
export async function getStaticProps({ params: { tag, page } }) {
|
||||
const from = 'tag-page-props'
|
||||
const props = await getGlobalNotionData({ from })
|
||||
const props = await getGlobalData({ from })
|
||||
// 过滤状态、标签
|
||||
props.posts = props.allPages.filter(page => page.type === 'Post' && page.status === 'Published').filter(post => post && post.tags && post.tags.includes(tag))
|
||||
// 处理文章数
|
||||
@@ -44,7 +44,7 @@ export async function getStaticProps({ params: { tag, page } }) {
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const from = 'tag-page-static-path'
|
||||
const { tagOptions, allPages } = await getGlobalNotionData({ from })
|
||||
const { tagOptions, allPages } = await getGlobalData({ from })
|
||||
const paths = []
|
||||
tagOptions?.forEach(tag => {
|
||||
// 过滤状态类型
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { getGlobalNotionData } from '@/lib/notion/getNotionData'
|
||||
import { getGlobalData } from '@/lib/notion/getNotionData'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import BLOG from '@/blog.config'
|
||||
import { useRouter } from 'next/router'
|
||||
@@ -30,7 +30,7 @@ const TagIndex = props => {
|
||||
|
||||
export async function getStaticProps() {
|
||||
const from = 'tag-index-props'
|
||||
const props = await getGlobalNotionData({ from })
|
||||
const props = await getGlobalData({ from })
|
||||
delete props.allPages
|
||||
return {
|
||||
props,
|
||||
|
||||
@@ -19,7 +19,7 @@ export default function ArticleDetail(props) {
|
||||
if (!post) {
|
||||
return <></>
|
||||
}
|
||||
const date = formatDate(post?.publishTime || post?.createdTime, locale.LOCALE)
|
||||
const date = formatDate(post?.publishTime, 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 !== 'Page' && post?.pageCover && (
|
||||
|
||||
138
themes/gitbook/LayoutBase.js
Normal file
138
themes/gitbook/LayoutBase.js
Normal file
@@ -0,0 +1,138 @@
|
||||
import CommonHead from '@/components/CommonHead'
|
||||
import { useState, createContext, useContext, useEffect } from 'react'
|
||||
import Footer from './components/Footer'
|
||||
import InfoCard from './components/InfoCard'
|
||||
import RevolverMaps from './components/RevolverMaps'
|
||||
import CONFIG_GITBOOK from './config_gitbook'
|
||||
import TopNavBar from './components/TopNavBar'
|
||||
import SearchInput from './components/SearchInput'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import Live2D from '@/components/Live2D'
|
||||
import BLOG from '@/blog.config'
|
||||
import NavPostList from './components/NavPostList'
|
||||
import ArticleInfo from './components/ArticleInfo'
|
||||
import Catalog from './components/Catalog'
|
||||
import { useRouter } from 'next/router'
|
||||
import Announcement from './components/Announcement'
|
||||
import PageNavDrawer from './components/PageNavDrawer'
|
||||
import FloatTocButton from './components/FloatTocButton'
|
||||
import { AdSlot } from '@/components/GoogleAdsense'
|
||||
import JumpToTopButton from './components/JumpToTopButton'
|
||||
const ThemeGlobalMedium = createContext()
|
||||
|
||||
/**
|
||||
* 基础布局 采用左右两侧布局,移动端使用顶部导航栏
|
||||
|
||||
* @returns {JSX.Element}
|
||||
* @constructor
|
||||
*/
|
||||
const LayoutBase = (props) => {
|
||||
const { children, meta, post, allNavPages, slotLeft, slotRight, slotTop } = props
|
||||
const [tocVisible, changeTocVisible] = useState(false)
|
||||
const [pageNavVisible, changePageNavVisible] = useState(false)
|
||||
const [filterPosts, setFilterPosts] = useState(allNavPages)
|
||||
const { onLoading } = useGlobal()
|
||||
const router = useRouter()
|
||||
|
||||
const showTocButton = post?.toc?.length > 1
|
||||
|
||||
useEffect(() => {
|
||||
setFilterPosts(allNavPages)
|
||||
}, [post])
|
||||
|
||||
const LoadingCover = <div id='cover-loading' className={`${onLoading ? 'z-50 opacity-50' : '-z-10 opacity-0'} pointer-events-none transition-all duration-300`}>
|
||||
<div className='w-full h-screen flex justify-center items-center'>
|
||||
<i className="fa-solid fa-spinner text-2xl text-black dark:text-white animate-spin"> </i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
return (
|
||||
<ThemeGlobalMedium.Provider value={{ tocVisible, changeTocVisible, filterPosts, setFilterPosts, allNavPages, pageNavVisible, changePageNavVisible }}>
|
||||
<CommonHead meta={meta} />
|
||||
|
||||
<div id='theme-medium' className='bg-white dark:bg-hexo-black-gray w-full h-full min-h-screen justify-center dark:text-gray-300'>
|
||||
{/* 顶部导航栏 */}
|
||||
<TopNavBar {...props} />
|
||||
|
||||
<main id='wrapper' className={(BLOG.LAYOUT_SIDEBAR_REVERSE ? 'flex-row-reverse' : '') + 'relative flex justify-between w-full h-full mx-auto'}>
|
||||
|
||||
{/* 左侧推拉抽屉 */}
|
||||
<div style={{ width: '32rem' }} className={'font-sans hidden md:block border-r dark:border-transparent relative z-10 '}>
|
||||
<div className='pt-14 pb-4 px-6 sticky top-0 overflow-y-scroll h-screen flex flex-col justify-between'>
|
||||
{slotLeft}
|
||||
|
||||
<SearchInput className='my-3 rounded-md' />
|
||||
|
||||
{/* 所有文章列表 */}
|
||||
<NavPostList posts={filterPosts} />
|
||||
|
||||
<div className='mt-2'>
|
||||
<Footer {...props} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div id='center-wrapper' className='flex flex-col justify-between w-full relative z-10 pt-12 min-h-screen'>
|
||||
|
||||
<div id='container-inner' className='w-full px-7 max-w-3xl justify-center mx-auto'>
|
||||
{slotTop}
|
||||
|
||||
<AdSlot type='in-article' />
|
||||
|
||||
{onLoading ? LoadingCover : children}
|
||||
|
||||
<AdSlot type='in-article' />
|
||||
|
||||
{/* 回顶按钮 */}
|
||||
<JumpToTopButton />
|
||||
</div>
|
||||
|
||||
{/* 底部 */}
|
||||
<div className='md:hidden'>
|
||||
<Footer {...props}/>
|
||||
</div>
|
||||
<div className='text-center'>
|
||||
<AdSlot type='native' />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 右侧侧推拉抽屉 */}
|
||||
<div style={{ width: '32rem' }} className={'hidden xl:block dark:border-transparent relative z-10 '}>
|
||||
<div className='py-14 px-6 sticky top-0'>
|
||||
<ArticleInfo post={props?.post ? props?.post : props.notice} />
|
||||
|
||||
<div className='py-6'>
|
||||
<Catalog {...props} />
|
||||
{slotRight}
|
||||
{router.route === '/' && <>
|
||||
<InfoCard {...props} />
|
||||
{CONFIG_GITBOOK.WIDGET_REVOLVER_MAPS === 'true' && <RevolverMaps />}
|
||||
<Live2D />
|
||||
</>}
|
||||
{/* gitbook主题首页只显示公告 */}
|
||||
<Announcement {...props} />
|
||||
</div>
|
||||
|
||||
<Live2D />
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</main>
|
||||
|
||||
{showTocButton && !tocVisible && <div className='md:hidden fixed right-0 bottom-52 z-30 bg-white border-l border-t border-b dark:border-gray-800 rounded'>
|
||||
<FloatTocButton {...props} />
|
||||
</div>}
|
||||
|
||||
<PageNavDrawer {...props} />
|
||||
|
||||
{/* 移动端底部导航栏 */}
|
||||
{/* <BottomMenuBar {...props} className='block md:hidden' /> */}
|
||||
</div>
|
||||
</ThemeGlobalMedium.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export default LayoutBase
|
||||
export const useGitBookGlobal = () => useContext(ThemeGlobalMedium)
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react'
|
||||
import BLOG from '@/blog.config'
|
||||
|
||||
const Footer = ({ title }) => {
|
||||
const Footer = ({ siteInfo }) => {
|
||||
const d = new Date()
|
||||
const currentYear = d.getFullYear()
|
||||
const copyrightDate = (function () {
|
||||
@@ -13,23 +13,24 @@ const Footer = ({ title }) => {
|
||||
|
||||
return (
|
||||
<footer
|
||||
className='max-w-3xl z-10 dark:bg-hexo-black-gray flex-shrink-0 justify-center text-center mx-auto w-full leading-6 text-sm px-6 pb-6 relative'
|
||||
className='z-10 bg:white dark:bg-hexo-black-gray justify-center text-center w-full text-sm relative'
|
||||
>
|
||||
<hr />
|
||||
<hr className='py-2' />
|
||||
|
||||
<div className='flex justify-between'>
|
||||
<div className='flex'>© {`${copyrightDate}`} <div><i className='mx-1 animate-pulse fas fa-heart' /> <a href={BLOG.LINK} className='underline font-bold text-gray-500 dark:text-gray-300 '>{BLOG.AUTHOR}</a>.<br /></div></div>
|
||||
<div className='text-xs font-serif'><a href='https://github.com/tangly1024/NotionNext' className='underline text-gray-500 dark:text-gray-300'>NotionNext {BLOG.VERSION}</a></div>
|
||||
<div className='flex justify-center'>
|
||||
<div><i className='mx-1 animate-pulse fas fa-heart' /> <a href={BLOG.LINK} className='underline font-bold text-gray-500 dark:text-gray-300 '>{BLOG.AUTHOR}</a>.<br /></div>
|
||||
© {`${copyrightDate}`}
|
||||
</div>
|
||||
|
||||
<div className='text-xs font-serif'>Powered By <a href='https://github.com/tangly1024/NotionNext' className='underline text-gray-500 dark:text-gray-300'>NotionNext</a></div>
|
||||
|
||||
{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 />
|
||||
<h1>{title}</h1>
|
||||
<h1 className='pt-1'>{siteInfo?.title}</h1>
|
||||
|
||||
</footer>
|
||||
)
|
||||
|
||||
@@ -36,7 +36,7 @@ const NavPostList = (props) => {
|
||||
if (!filteredPosts || filteredPosts.length === 0) {
|
||||
return <NavPostListEmpty currentSearch={currentSearch} />
|
||||
} else {
|
||||
return <div id='posts-wrapper' className='w-full'>
|
||||
return <div id='posts-wrapper' className='w-full flex-grow'>
|
||||
{/* 文章列表 */}
|
||||
{filteredPosts?.map((group, index) => <NavPostItem key={index} group={group} onHeightChange={props.onHeightChange}/>)}
|
||||
</div>
|
||||
|
||||
@@ -10,7 +10,7 @@ import BLOG from '@/blog.config'
|
||||
* @returns
|
||||
*/
|
||||
export const BlogPostCardInfo = ({ post, showPreview, showPageCover, showSummary }) => {
|
||||
return <div className={`flex flex-col justify-between lg:p-6 p-4 ${showPageCover && !showPreview ? 'md:w-7/12 w-full h-56 md:max-h-60 ' : 'w-full '}`}>
|
||||
return <div className={`flex flex-col justify-between lg:p-6 p-4 ${showPageCover && !showPreview ? 'md:w-7/12 w-full md:max-h-60' : 'w-full'}`}>
|
||||
<div>
|
||||
{/* 标题 */}
|
||||
<Link
|
||||
|
||||
@@ -14,7 +14,7 @@ export default function HeaderArticle({ post, siteInfo }) {
|
||||
const headerImage = post?.pageCover ? `url("${post.pageCover}")` : `url("${siteInfo?.pageCover}")`
|
||||
|
||||
const date = formatDate(
|
||||
post?.publishTime || post?.createdTime,
|
||||
post?.publishTime,
|
||||
locale.LOCALE
|
||||
)
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ export const ArticleInfo = (props) => {
|
||||
const { post } = props
|
||||
|
||||
const { locale } = useGlobal()
|
||||
const date = formatDate(post?.publishTime || post?.createdTime, locale.LOCALE)
|
||||
const date = formatDate(post?.publishTime, locale.LOCALE)
|
||||
|
||||
return (
|
||||
<section className='mb-3 dark:text-gray-200'>
|
||||
|
||||
96
themes/medium/LayoutSlug.js
Normal file
96
themes/medium/LayoutSlug.js
Normal file
@@ -0,0 +1,96 @@
|
||||
import LayoutBase from './LayoutBase'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import React from 'react'
|
||||
import Catalog from './components/Catalog'
|
||||
import { ArticleLock } from './components/ArticleLock'
|
||||
import formatDate from '@/lib/formatDate'
|
||||
import BLOG from '@/blog.config'
|
||||
import Link from 'next/link'
|
||||
import NotionPage from '@/components/NotionPage'
|
||||
import CONFIG_MEDIUM from './config_medium'
|
||||
import Comment from '@/components/Comment'
|
||||
import ArticleAround from './components/ArticleAround'
|
||||
import TocDrawer from './components/TocDrawer'
|
||||
import CategoryItem from './components/CategoryItem'
|
||||
import TagItemMini from './components/TagItemMini'
|
||||
import ShareBar from '@/components/ShareBar'
|
||||
|
||||
export const LayoutSlug = props => {
|
||||
const { post, prev, next, siteInfo, lock, validPassword } = props
|
||||
const { locale } = useGlobal()
|
||||
|
||||
const date = formatDate(
|
||||
post?.publishTime || post?.createdTime,
|
||||
locale.LOCALE
|
||||
)
|
||||
if (!post) {
|
||||
return <LayoutBase {...props} showInfoCard={true} />
|
||||
}
|
||||
|
||||
const slotRight = post?.toc && post?.toc?.length >= 3 && (
|
||||
<div key={locale.COMMON.TABLE_OF_CONTENTS} >
|
||||
<Catalog toc={post.toc} />
|
||||
{/* <JumpToTopButton className='text-gray-400 hover:text-green-500 hover:bg-gray-100 py-1 duration-200' /> */}
|
||||
</div>
|
||||
)
|
||||
|
||||
return (
|
||||
<LayoutBase showInfoCard={true} slotRight={slotRight} {...props} >
|
||||
{/* 文章锁 */}
|
||||
{lock && <ArticleLock validPassword={validPassword} />}
|
||||
|
||||
{!lock && <div id='container'>
|
||||
|
||||
{/* title */}
|
||||
<h1 className="text-3xl pt-12 dark:text-gray-300">{post?.title}</h1>
|
||||
|
||||
{/* meta */}
|
||||
<section className="py-2 items-center text-sm px-1">
|
||||
<div className='flex flex-wrap text-gray-500 py-1 dark:text-gray-600'>
|
||||
<span className='whitespace-nowrap'> <i className='far fa-calendar mr-2' />{date}</span>
|
||||
<span className='mx-1'>|</span>
|
||||
<span className='whitespace-nowrap mr-2'><i className='far fa-calendar-check mr-2' />{post.lastEditedTime}</span>
|
||||
<div className="hidden busuanzi_container_page_pv font-light mr-2 whitespace-nowrap">
|
||||
<i className="mr-1 fas fa-eye" /><span className="busuanzi_value_page_pv" />
|
||||
</div>
|
||||
</div>
|
||||
<Link href="/about" passHref legacyBehavior>
|
||||
<div className='flex pt-2'>
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img src={siteInfo?.icon} className='rounded-full cursor-pointer' width={22} alt={BLOG.AUTHOR} />
|
||||
|
||||
<div className="mr-3 ml-2 my-auto text-green-500 cursor-pointer">
|
||||
{BLOG.AUTHOR}
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
</section>
|
||||
|
||||
{/* Notion文章主体 */}
|
||||
<section id="notion-article" className="px-1 max-w-4xl">
|
||||
{post && (<NotionPage post={post} />)}
|
||||
</section>
|
||||
|
||||
<section>
|
||||
|
||||
{/* 分享 */}
|
||||
<ShareBar post={post} />
|
||||
{/* 文章分类和标签信息 */}
|
||||
<div className='flex justify-between'>
|
||||
{CONFIG_MEDIUM.POST_DETAIL_CATEGORY && post.category && <CategoryItem category={post.category} />}
|
||||
<div>
|
||||
{CONFIG_MEDIUM.POST_DETAIL_TAG && post?.tagItems?.map(tag => <TagItemMini key={tag.name} tag={tag} />)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{post.type === 'Post' && <ArticleAround prev={prev} next={next} />}
|
||||
<Comment frontMatter={post} />
|
||||
</section>
|
||||
|
||||
<TocDrawer {...props} />
|
||||
</div>}
|
||||
</LayoutBase>
|
||||
)
|
||||
}
|
||||
|
||||
export default LayoutSlug
|
||||
Reference in New Issue
Block a user