diff --git a/components/TwikooCommentCounter.js b/components/TwikooCommentCounter.js
index 87c16aaf..7e2a15df 100644
--- a/components/TwikooCommentCounter.js
+++ b/components/TwikooCommentCounter.js
@@ -1,4 +1,4 @@
-import BLOG from '@/blog.config'
+import { siteConfig } from '@/lib/config'
import { useGlobal } from '@/lib/global'
import { loadExternalResource } from '@/lib/utils'
import { useRouter } from 'next/router'
@@ -13,16 +13,37 @@ import { useEffect } from 'react'
const TwikooCommentCounter = (props) => {
let commentsData = []
const { theme } = useGlobal()
+ const router = useRouter()
+ useEffect(() => {
+ // console.log('路由触发评论计数')
+ if (props?.posts && props?.posts?.length > 0) {
+ fetchTwikooData(props.posts)
+ }
+ }, [router.events])
+
+ // 监控主题变化时的的评论数
+ useEffect(() => {
+ // console.log('主题触发评论计数', commentsData)
+ updateCommentCount()
+ }, [theme])
+
+ const twikooCDNURL = siteConfig('COMMENT_TWIKOO_CDN_URL')
+ const twikooENVID = siteConfig('COMMENT_TWIKOO_ENV_ID')
+
+ /**
+ * 加载外部twikoojs
+ * @param {*} posts
+ */
const fetchTwikooData = async (posts) => {
posts.forEach(post => {
post.slug = post.slug.startsWith('/') ? post.slug : `/${post.slug}`
})
try {
- await loadExternalResource(BLOG.COMMENT_TWIKOO_CDN_URL, 'js')
+ await loadExternalResource(twikooCDNURL, 'js')
const twikoo = window.twikoo
twikoo.getCommentsCount({
- envId: BLOG.COMMENT_TWIKOO_ENV_ID, // 环境 ID
+ envId: twikooENVID, // 环境 ID
// region: 'ap-guangzhou', // 环境地域,默认为 ap-shanghai,如果您的环境地域不是上海,需传此参数
urls: posts?.map(post => post.slug), // 不包含协议、域名、参数的文章路径列表,必传参数
includeReply: true // 评论数是否包括回复,默认:false
@@ -58,20 +79,7 @@ const TwikooCommentCounter = (props) => {
}
})
}
- const router = useRouter()
- useEffect(() => {
- // console.log('路由触发评论计数')
- if (props?.posts && props?.posts?.length > 0) {
- fetchTwikooData(props.posts)
- }
- }, [router.events])
-
- // 监控主题变化时的的评论数
- useEffect(() => {
- // console.log('主题触发评论计数', commentsData)
- updateCommentCount()
- }, [theme])
return null
}
diff --git a/components/Utterances.js b/components/Utterances.js
index e0298daf..3524c646 100644
--- a/components/Utterances.js
+++ b/components/Utterances.js
@@ -1,4 +1,4 @@
-import BLOG from '@/blog.config'
+import { siteConfig } from '@/lib/config'
import { useEffect } from 'react'
/**
@@ -11,9 +11,9 @@ import { useEffect } from 'react'
const Utterances = ({ issueTerm, layout }) => {
useEffect(() => {
const theme =
- BLOG.APPEARANCE === 'auto'
+ siteConfig('APPEARANCE') === 'auto'
? 'preferred-color-scheme'
- : BLOG.APPEARANCE === 'light'
+ : siteConfig('APPEARANCE') === 'light'
? 'github-light'
: 'github-dark'
const script = document.createElement('script')
@@ -21,7 +21,7 @@ const Utterances = ({ issueTerm, layout }) => {
script.setAttribute('src', 'https://utteranc.es/client.js')
script.setAttribute('crossorigin', 'anonymous')
script.setAttribute('async', true)
- script.setAttribute('repo', BLOG.COMMENT_UTTERRANCES_REPO)
+ script.setAttribute('repo', siteConfig('COMMENT_UTTERRANCES_REPO'))
script.setAttribute('issue-term', 'title')
script.setAttribute('theme', theme)
anchor.appendChild(script)
diff --git a/components/ValineComponent.js b/components/ValineComponent.js
index 0a7ab6cb..b26fb34d 100644
--- a/components/ValineComponent.js
+++ b/components/ValineComponent.js
@@ -1,23 +1,23 @@
-import BLOG from '@/blog.config'
+import { siteConfig } from '@/lib/config'
import { loadExternalResource } from '@/lib/utils'
import { useEffect } from 'react'
const ValineComponent = ({ path }) => {
const loadValine = async () => {
try {
- await loadExternalResource(BLOG.COMMENT_VALINE_CDN, 'js')
+ await loadExternalResource(siteConfig('COMMENT_VALINE_CDN'), 'js')
const Valine = window.Valine
// eslint-disable-next-line no-unused-vars
const valine = new Valine({
el: '#valine', // 容器元素
- lang: BLOG.LANG, // 用于手动设定评论区语言,支持的语言列表 https://github.com/imaegoo/twikoo/blob/main/src/client/utils/i18n/index.js
- appId: BLOG.COMMENT_VALINE_APP_ID,
- appKey: BLOG.COMMENT_VALINE_APP_KEY,
+ lang: siteConfig('LANG'), // 用于手动设定评论区语言,支持的语言列表 https://github.com/imaegoo/twikoo/blob/main/src/client/utils/i18n/index.js
+ appId: siteConfig('COMMENT_VALINE_APP_ID'),
+ appKey: siteConfig('COMMENT_VALINE_APP_KEY'),
avatar: '',
path,
recordIP: true,
- placeholder: BLOG.COMMENT_VALINE_PLACEHOLDER,
- serverURLs: BLOG.COMMENT_VALINE_SERVER_URLS,
+ placeholder: siteConfig('COMMENT_VALINE_PLACEHOLDER'),
+ serverURLs: siteConfig('COMMENT_VALINE_SERVER_URLS'),
visitor: true
})
} catch (error) {
diff --git a/components/WWAds.js b/components/WWAds.js
index 87901f39..d1f786a4 100644
--- a/components/WWAds.js
+++ b/components/WWAds.js
@@ -1,5 +1,4 @@
-import React from 'react'
-import BLOG from '@/blog.config'
+import { siteConfig } from '@/lib/config'
/**
* 万维广告插件
@@ -8,11 +7,13 @@ import BLOG from '@/blog.config'
* @returns {JSX.Element | null} - 返回渲染的 JSX 元素或 null
*/
export default function WWAds({ orientation = 'vertical', sticky = false, className }) {
- if (!JSON.parse(BLOG.AD_WWADS_ID)) {
+ const adWWADSId = siteConfig('AD_WWADS_ID')
+
+ if (!adWWADSId) {
return null
}
- return (
-
- )
+ return
}
diff --git a/components/WalineComponent.js b/components/WalineComponent.js
index 0b033922..37247d48 100644
--- a/components/WalineComponent.js
+++ b/components/WalineComponent.js
@@ -1,8 +1,8 @@
-import React from 'react'
+import React, { createRef } from 'react'
import { init } from '@waline/client'
-import BLOG from '@/blog.config'
import { useRouter } from 'next/router'
import '@waline/client/dist/waline.css'
+import { siteConfig } from '@/lib/config'
const path = ''
let waline = null
@@ -12,7 +12,7 @@ let waline = null
* @returns
*/
const WalineComponent = (props) => {
- const containerRef = React.createRef()
+ const containerRef = createRef()
const router = useRouter()
const updateWaline = url => {
@@ -26,8 +26,8 @@ const WalineComponent = (props) => {
waline = init({
...props,
el: containerRef.current,
- serverURL: BLOG.COMMENT_WALINE_SERVER_URL,
- lang: BLOG.lang,
+ serverURL: siteConfig('COMMENT_WALINE_SERVER_URL'),
+ lang: siteConfig('LANG'),
reaction: true,
dark: 'html.dark',
emoji: [
diff --git a/components/WebMention.js b/components/WebMention.js
index 375a0141..61e11c69 100644
--- a/components/WebMention.js
+++ b/components/WebMention.js
@@ -1,7 +1,7 @@
-import BLOG from '@/blog.config'
import { useEffect, useState } from 'react'
import { useRouter } from 'next/router'
import Image from 'next/image'
+import { siteConfig } from '@/lib/config'
/**
* 评论插件
@@ -78,7 +78,7 @@ const WebmentionReplies = ({ target }) => {
const [mentions, setMentions] = useState([])
const fetchMentions = async (target) =>
fetch(
- `https://webmention.io/api/mentions.jf2?per-page=500&target=${encodeURIComponent(target)}&token=${BLOG.COMMENT_WEBMENTION.TOKEN}`
+ `https://webmention.io/api/mentions.jf2?per-page=500&target=${encodeURIComponent(target)}&token=${siteConfig('COMMENT_WEBMENTION_TOKEN')}`
).then((response) => (response.json ? response.json() : response))
useEffect(() => {
async function getMentions() {
@@ -137,8 +137,8 @@ const WebmentionReplies = ({ target }) => {
const WebMentionBlock = ({ frontMatter }) => {
const router = useRouter()
- const url = `https://${BLOG.COMMENT_WEBMENTION.HOSTNAME}${router.asPath}`
- const tweet = `${frontMatter.title} by @${BLOG.COMMENT_WEBMENTION.TWITTER_USERNAME} ${url}`
+ const url = `https://${siteConfig('COMMENT_WEBMENTION_HOSTNAME')}${router.asPath}`
+ const tweet = `${frontMatter.title} by @${siteConfig('COMMENT_WEBMENTION_TWITTER_USERNAME')} ${url}`
return (
diff --git a/components/Webwhiz.js b/components/Webwhiz.js
index 690333cb..4a736647 100644
--- a/components/Webwhiz.js
+++ b/components/Webwhiz.js
@@ -1,4 +1,4 @@
-import BLOG from '@/blog.config'
+import { siteConfig } from '@/lib/config'
import ExternalScript from './ExternalScript'
/**
@@ -10,8 +10,8 @@ export default function WebWhiz() {
const props = {
id: '__webwhizSdk__',
src: 'https://www.unpkg.com/webwhiz@1.0.0/dist/sdk.js',
- baseUrl: BLOG.WEB_WHIZ_BASE_URL,
- chatbotId: BLOG.WEB_WHIZ_CHAT_BOT_ID
+ baseUrl: siteConfig('WEB_WHIZ_BASE_URL'),
+ chatbotId: siteConfig('WEB_WHIZ_CHAT_BOT_ID')
}
return
}
diff --git a/lib/cache/cache_manager.js b/lib/cache/cache_manager.js
index 8796d3f4..4928bca9 100644
--- a/lib/cache/cache_manager.js
+++ b/lib/cache/cache_manager.js
@@ -3,15 +3,6 @@ import FileCache from './local_file_cache'
import MongoCache from './mongo_db_cache'
import BLOG from '@/blog.config'
-let api
-if (process.env.MONGO_DB_URL && process.env.MONGO_DB_NAME) {
- api = MongoCache
-} else if (process.env.ENABLE_FILE_CACHE) {
- api = FileCache
-} else {
- api = MemoryCache
-}
-
/**
* 为减少频繁接口请求,notion数据将被缓存
* @param {*} key
@@ -19,11 +10,11 @@ if (process.env.MONGO_DB_URL && process.env.MONGO_DB_NAME) {
*/
export async function getDataFromCache(key, force) {
if (BLOG.ENABLE_CACHE || force) {
- const dataFromCache = await api.getCache(key)
+ const dataFromCache = await getApi().getCache(key)
if (JSON.stringify(dataFromCache) === '[]') {
return null
}
- return api.getCache(key)
+ return getApi().getCache(key)
} else {
return null
}
@@ -33,12 +24,26 @@ export async function setDataToCache(key, data) {
if (!data) {
return
}
- await api.setCache(key, data)
+ await getApi().setCache(key, data)
}
export async function delCacheData(key) {
if (!BLOG.ENABLE_CACHE) {
return
}
- await api.delCache(key)
+ await getApi().delCache(key)
+}
+
+/**
+ * 缓存实现类
+ * @returns
+ */
+function getApi() {
+ if (process.env.MONGO_DB_URL && process.env.MONGO_DB_NAME) {
+ return MongoCache
+ } else if (process.env.ENABLE_FILE_CACHE) {
+ return FileCache
+ } else {
+ return MemoryCache
+ }
}
diff --git a/lib/cache/local_file_cache.js b/lib/cache/local_file_cache.js
index cda7f35a..c411d6e5 100644
--- a/lib/cache/local_file_cache.js
+++ b/lib/cache/local_file_cache.js
@@ -41,7 +41,7 @@ export async function setCache (key, data) {
fs.writeFileSync(jsonFile, JSON.stringify(json))
}
-export async function delCache (key, data) {
+export async function delCache (key) {
const exist = await fs.existsSync(jsonFile)
const json = exist ? JSON.parse(await fs.readFileSync(jsonFile)) : {}
delete json.key
@@ -49,4 +49,12 @@ export async function delCache (key, data) {
fs.writeFileSync(jsonFile, JSON.stringify(json))
}
+/**
+ * 清理缓存
+ */
+export async function cleanCache() {
+ const json = {}
+ fs.writeFileSync(jsonFile, JSON.stringify(json))
+}
+
export default { getCache, setCache, delCache }
diff --git a/lib/config.js b/lib/config.js
new file mode 100644
index 00000000..e2630825
--- /dev/null
+++ b/lib/config.js
@@ -0,0 +1,93 @@
+'use client'
+
+import BLOG from '@/blog.config'
+import { useGlobal } from './global'
+import { deepClone } from './utils'
+
+/**
+ * 读取配置顺序
+ * 1. 优先读取NotionConfig表
+ * 2. 其次读取环境变量
+ * 3. 再读取blog.config.js / 或各个主题的CONFIG文件
+ * @param {*} key ; 参数名
+ * @param {*} defaultVal ; 参数不存在默认返回值
+ * @param {*} extendConfig ; 参考配置对象{key:val},如果notion中找不到优先尝试在这里面查找
+ * @returns
+ */
+export const siteConfig = (key, defaultVal = null, extendConfig = null) => {
+ let global = null
+ try {
+ // eslint-disable-next-line react-hooks/rules-of-hooks
+ global = useGlobal()
+ } catch (error) {}
+
+ // 首先 配置最优先读取NOTION中的表格配置
+ let val = null
+ let siteInfo = null
+
+ if (global) {
+ val = global.NOTION_CONFIG?.[key]
+ siteInfo = global.siteInfo
+ // console.log('当前变量', key, val)
+ }
+
+ if (!val) {
+ // 这里针对部分key做一些兼容处理
+ switch (key) {
+ case 'HOME_BANNER_IMAGE':
+ val = siteInfo?.pageCover // 封面图取Notion的封面
+ break
+ case 'AVATAR':
+ val = siteInfo?.icon // 封面图取Notion的头像
+ break
+ case 'TITLE':
+ val = siteInfo?.title // 标题取Notion中的标题
+ break
+ case 'DESCRIPTION':
+ val = siteInfo?.description // 标题取Notion中的标题
+ break
+ }
+ }
+
+ // 其次 有传入的配置参考,则尝试读取
+ if (!val && extendConfig) {
+ val = extendConfig[key]
+ }
+
+ // 其次 NOTION没有找到配置,则会读取blog.config.js文件
+ if (!val) {
+ val = BLOG[key]
+ }
+
+ if (!val) {
+ return defaultVal
+ } else {
+ if (typeof val === 'string') {
+ return val;
+ } else {
+ try {
+ return JSON.parse(val);
+ } catch (error) {
+ // 如果值是一个字符串但不是有效的 JSON 格式,直接返回字符串
+ return val;
+ }
+ }
+ }
+}
+
+/**
+ * 读取所有配置
+ * 1. 优先读取NotionConfig表
+ * 2. 其次读取环境变量
+ * 3. 再读取blog.config.js文件
+ * @param {*} key
+ * @returns
+ */
+export const siteConfigMap = () => {
+ const val = deepClone(BLOG)
+ for (const key in val) {
+ val[key] = siteConfig(key)
+ // console.log('site', key, val[key], siteConfig(key))
+ }
+ return val
+}
diff --git a/lib/global.js b/lib/global.js
index c290a9e0..df119ac3 100644
--- a/lib/global.js
+++ b/lib/global.js
@@ -1,34 +1,65 @@
import { generateLocaleDict, initLocale } from './lang'
import { createContext, useContext, useEffect, useState } from 'react'
import { useRouter } from 'next/router'
+import { THEMES, initDarkMode, saveDarkModeToCookies } from '@/themes/theme'
+
import BLOG from '@/blog.config'
-import { THEMES, initDarkMode } from '@/themes/theme'
import NProgress from 'nprogress'
-import { getQueryVariable, isBrowser } from './utils'
const GlobalContext = createContext()
/**
- * 全局变量Provider,包括语言本地化、样式主题、搜索词
+ * 定义全局变量,包括语言、主题、深色模式、加载状态
* @param children
* @returns {JSX.Element}
* @constructor
*/
export function GlobalContextProvider(props) {
- const { children, siteInfo, categoryOptions, tagOptions } = props
+ const { children, siteInfo, categoryOptions, tagOptions, NOTION_CONFIG } = props
const router = useRouter()
- const [lang, updateLang] = useState(BLOG.LANG) // 默认语言
- const [locale, updateLocale] = useState(generateLocaleDict(BLOG.LANG)) // 默认语言
- const [theme, setTheme] = useState(BLOG.THEME) // 默认博客主题
- const [isDarkMode, updateDarkMode] = useState(BLOG.APPEARANCE === 'dark') // 默认深色模式
+ const [lang, updateLang] = useState(NOTION_CONFIG?.LANG || BLOG.LANG) // 默认语言
+ const [locale, updateLocale] = useState(generateLocaleDict(NOTION_CONFIG?.LANG || BLOG.LANG)) // 默认语言
+ const [theme, setTheme] = useState(NOTION_CONFIG?.THEME || BLOG.THEME) // 默认博客主题
+ const [isDarkMode, updateDarkMode] = useState(NOTION_CONFIG?.APPEARANCE || BLOG.APPEARANCE === 'dark') // 默认深色模式
const [onLoading, setOnLoading] = useState(false) // 抓取文章数据
+ // 切换主题
+ function switchTheme() {
+ const currentIndex = THEMES.indexOf(theme)
+ const newIndex = currentIndex < THEMES.length - 1 ? currentIndex + 1 : 0
+ const newTheme = THEMES[newIndex]
+ const query = router.query
+ query.theme = newTheme
+ router.push({ pathname: router.pathname, query })
+ return newTheme
+ }
+
+ // 切换深色模式
+ const toggleDarkMode = () => {
+ const newStatus = !isDarkMode
+ saveDarkModeToCookies(newStatus)
+ updateDarkMode(newStatus)
+ const htmlElement = document.getElementsByTagName('html')[0]
+ htmlElement.classList?.remove(newStatus ? 'light' : 'dark')
+ htmlElement.classList?.add(newStatus ? 'dark' : 'light')
+ }
+
+ /**
+ * 更新语言
+ */
+ function changeLang(lang) {
+ if (lang) {
+ updateLang(lang)
+ updateLocale(generateLocaleDict(lang))
+ }
+ }
+
useEffect(() => {
- initLocale(lang, locale, updateLang, updateLocale)
initDarkMode(updateDarkMode)
- initTheme()
+ initLocale(lang, locale, updateLang, updateLocale)
}, [])
+ // 加载进度条
useEffect(() => {
const handleStart = (url) => {
NProgress.start()
@@ -43,8 +74,7 @@ export function GlobalContextProvider(props) {
NProgress.done()
setOnLoading(false)
}
- const queryTheme = getQueryVariable('theme') || BLOG.THEME
- setTheme(queryTheme)
+
router.events.on('routeChangeStart', handleStart)
router.events.on('routeChangeError', handleStop)
router.events.on('routeChangeComplete', handleStop)
@@ -55,21 +85,14 @@ export function GlobalContextProvider(props) {
}
}, [router])
- // 切换主题
- function switchTheme() {
- const currentIndex = THEMES.indexOf(theme)
- const newIndex = currentIndex < THEMES.length - 1 ? currentIndex + 1 : 0
- const newTheme = THEMES[newIndex]
- const query = router.query
- query.theme = newTheme
- router.push({ pathname: router.pathname, query })
- return newTheme
- }
-
return (
{
- if (isBrowser) {
- setTimeout(() => {
- const elements = document.querySelectorAll('[id^="theme-"]')
- if (elements?.length > 1) {
- elements[elements.length - 1].scrollIntoView()
- // 删除前面的元素,只保留最后一个元素
- for (let i = 0; i < elements.length - 1; i++) {
- elements[i].parentNode.removeChild(elements[i])
- }
- }
- }, 500)
- }
-}
-
export const useGlobal = () => useContext(GlobalContext)
diff --git a/lib/lang.js b/lib/lang.js
index abc51354..3e2307f7 100644
--- a/lib/lang.js
+++ b/lib/lang.js
@@ -12,7 +12,7 @@ import { getQueryVariable, isBrowser, mergeDeep } from './utils'
* 在这里配置所有支持的语言
* 国家-地区
*/
-const lang = {
+const LANGS = {
'en-US': enUS,
'zh-CN': zhCN,
'zh-HK': zhHK,
@@ -22,7 +22,7 @@ const lang = {
'ja-JP': jaJP
}
-export default lang
+export default LANGS
/**
* 获取当前语言字典
@@ -30,33 +30,33 @@ export default lang
* @returns 不同语言对应字典
*/
export function generateLocaleDict(langString) {
- const supportedLocales = Object.keys(lang)
+ const supportedLocales = Object.keys(LANGS)
let userLocale
// 将语言字符串拆分为语言和地区代码,例如将 "zh-CN" 拆分为 "zh" 和 "CN"
- const [language, region] = langString.split(/[-_]/)
+ const [language, region] = langString?.split(/[-_]/)
// 优先匹配语言和地区都匹配的情况
const specificLocale = `${language}-${region}`
if (supportedLocales.includes(specificLocale)) {
- userLocale = lang[specificLocale]
+ userLocale = LANGS[specificLocale]
}
// 然后尝试匹配只有语言匹配的情况
if (!userLocale) {
const languageOnlyLocales = supportedLocales.filter(locale => locale.startsWith(language))
if (languageOnlyLocales.length > 0) {
- userLocale = lang[languageOnlyLocales[0]]
+ userLocale = LANGS[languageOnlyLocales[0]]
}
}
// 如果还没匹配到,则返回最接近的语言包
if (!userLocale) {
const fallbackLocale = supportedLocales.find(locale => locale.startsWith('en'))
- userLocale = lang[fallbackLocale]
+ userLocale = LANGS[fallbackLocale]
}
- return mergeDeep({}, lang['en-US'], userLocale)
+ return mergeDeep({}, LANGS['en-US'], userLocale)
}
/**
@@ -65,11 +65,12 @@ export function generateLocaleDict(langString) {
*/
export function initLocale(lang, locale, changeLang, changeLocale) {
if (isBrowser) {
- const queryLang = getQueryVariable('lang') || loadLangFromCookies() || window.navigator.language
+ const queryLang = getQueryVariable('lang') || loadLangFromCookies()
let currentLang = lang
- if (queryLang !== lang) {
+ if (queryLang && queryLang !== 'undefined' && queryLang !== lang) {
currentLang = queryLang
}
+
changeLang(currentLang)
saveLangToCookies(currentLang)
diff --git a/lib/lang/en-US.js b/lib/lang/en-US.js
index 51e38115..594ddfc0 100644
--- a/lib/lang/en-US.js
+++ b/lib/lang/en-US.js
@@ -1,5 +1,5 @@
export default {
- LOCALE: 'en-US',
+ LOCALE: 'English',
MENU: {
WALK_AROUND: 'Walk Around',
CATEGORY: 'Category',
diff --git a/lib/lang/fr-FR.js b/lib/lang/fr-FR.js
index 30089a70..2bc40057 100644
--- a/lib/lang/fr-FR.js
+++ b/lib/lang/fr-FR.js
@@ -1,5 +1,5 @@
export default {
- LOCALE: 'fr-FR',
+ LOCALE: 'français',
NAV: {
INDEX: 'Accueil',
RSS: 'RSS',
diff --git a/lib/lang/ja-JP.js b/lib/lang/ja-JP.js
index ab0e81f8..c27eef0b 100644
--- a/lib/lang/ja-JP.js
+++ b/lib/lang/ja-JP.js
@@ -1,5 +1,5 @@
export default {
- LOCALE: 'ja-JP',
+ LOCALE: '日本語',
NAV: {
INDEX: 'ホーム',
RSS: '購読',
diff --git a/lib/lang/tr-TR.js b/lib/lang/tr-TR.js
index 227b3fba..5be7a520 100644
--- a/lib/lang/tr-TR.js
+++ b/lib/lang/tr-TR.js
@@ -1,5 +1,5 @@
export default {
- LOCALE: 'tr-TR',
+ LOCALE: 'Türkçe',
NAV: {
INDEX: 'Blog',
RSS: 'RSS',
diff --git a/lib/lang/zh-CN.js b/lib/lang/zh-CN.js
index 91a4600a..e29af3d5 100644
--- a/lib/lang/zh-CN.js
+++ b/lib/lang/zh-CN.js
@@ -1,5 +1,5 @@
export default {
- LOCALE: 'zh-CN',
+ LOCALE: '中文(简体)',
MENU: {
WALK_AROUND: '随便逛逛',
CATEGORY: '博客分类',
diff --git a/lib/lang/zh-HK.js b/lib/lang/zh-HK.js
index 6a05df55..f434fe6e 100644
--- a/lib/lang/zh-HK.js
+++ b/lib/lang/zh-HK.js
@@ -1,4 +1,5 @@
export default {
+ LOCALE: '中文(繁体香港)',
NAV: {
INDEX: '網誌',
RSS: '訂閱',
diff --git a/lib/lang/zh-TW.js b/lib/lang/zh-TW.js
index 673fe109..de746c08 100644
--- a/lib/lang/zh-TW.js
+++ b/lib/lang/zh-TW.js
@@ -1,5 +1,5 @@
export default {
- LOCALE: 'zh-TW',
+ LOCALE: '中文(繁体台湾)',
NAV: {
INDEX: '部落格',
RSS: '訂閱',
diff --git a/lib/notion/getNotionConfig.js b/lib/notion/getNotionConfig.js
new file mode 100644
index 00000000..7d026fbd
--- /dev/null
+++ b/lib/notion/getNotionConfig.js
@@ -0,0 +1,136 @@
+/**
+ * 从Notion中读取站点配置;
+ * 在Notion模板中创建一个类型为CONFIG的页面,再添加一个数据库表格,即可用于填写配置
+ * Notion数据库配置优先级最高,将覆盖vercel环境变量以及blog.config.js中的配置
+ * --注意--
+ * 数据库请从模板复制 https://www.notion.so/tanghh/287869a92e3d4d598cf366bd6994755e
+ *
+ */
+import { getDateValue, getTextContent } from 'notion-utils'
+import { getPostBlocks } from './getPostBlocks'
+import getAllPageIds from './getAllPageIds'
+
+/**
+ * 从Notion中读取Config配置表
+ * @param {*} allPages
+ * @returns
+ */
+export async function getConfigMapFromConfigPage(allPages) {
+ // 默认返回配置文件
+ const notionConfig = {}
+
+ if (!allPages || !Array.isArray(allPages) || allPages.length === 0) {
+ console.warn('[Notion配置] 忽略的配置')
+ return null
+ }
+ const configPage = allPages?.find(post => {
+ return post && post?.type && post?.type === 'CONFIG'
+ })
+
+ if (!configPage) {
+ console.warn('[Notion配置] 未找到配置页面')
+ return null
+ }
+ const configPageId = configPage.id
+
+ // console.log('[Notion配置]请求配置数据 ', configPage.id)
+ const pageRecordMap = await getPostBlocks(configPageId, 'config-table')
+ // console.log('配置中心Page', configPageId, pageRecordMap)
+ const content = pageRecordMap.block[configPageId].value.content
+
+ if (!content) {
+ console.warn('[Notion配置] 未找到配置表格', pageRecordMap.block[configPageId], pageRecordMap.block[configPageId].value)
+ return null
+ }
+
+ // 找到配置文件中的database
+ // for (const contentId of content) {
+ // console.log('内容', contentId, configPageRecordMap.block[contentId].value.type === 'collection_view')
+ // }
+ const configTableId = content?.find(contentId => {
+ return pageRecordMap.block[contentId].value.type === 'collection_view'
+ })
+
+ // eslint-disable-next-line no-constant-condition, no-self-compare
+ if (!configTableId) {
+ console.warn('[Notion配置]未找到配置表格数据', pageRecordMap.block[configPageId], pageRecordMap.block[configPageId].value)
+ return null
+ }
+
+ // 页面查找
+ const databaseRecordMap = pageRecordMap.block[configTableId]
+ const block = pageRecordMap.block || {}
+ const rawMetadata = databaseRecordMap.value
+ // Check Type Page-Database和Inline-Database
+ if (
+ rawMetadata?.type !== 'collection_view_page' && rawMetadata?.type !== 'collection_view'
+ ) {
+ console.error(`pageId "${configTableId}" is not a database`)
+ return null
+ }
+ // console.log('表格', databaseRecordMap, block, rawMetadata)
+ const collectionId = rawMetadata?.collection_id
+ const collection = pageRecordMap.collection[collectionId].value
+ const collectionQuery = pageRecordMap.collection_query
+ const collectionView = pageRecordMap.collection_view
+ const schema = collection?.schema
+ const viewIds = rawMetadata?.view_ids
+ const pageIds = getAllPageIds(collectionQuery, collectionId, collectionView, viewIds)
+ if (pageIds?.length === 0) {
+ console.error('[Notion配置]获取到的文章列表为空,请检查notion模板', collectionQuery, collection, collectionView, viewIds, databaseRecordMap)
+ }
+ // 遍历用户的表格
+ for (let i = 0; i < pageIds.length; i++) {
+ const id = pageIds[i]
+ const value = block[id]?.value
+ if (!value) {
+ continue
+ }
+ const rawProperties = Object.entries(block?.[id]?.value?.properties || [])
+ const excludeProperties = ['date', 'select', 'multi_select', 'person']
+ const properties = {}
+ for (let i = 0; i < rawProperties.length; i++) {
+ const [key, val] = rawProperties[i]
+ properties.id = id
+ if (schema[key]?.type && !excludeProperties.includes(schema[key].type)) {
+ properties[schema[key].name] = getTextContent(val)
+ } else {
+ switch (schema[key]?.type) {
+ case 'date': {
+ const dateProperty = getDateValue(val)
+ delete dateProperty.type
+ properties[schema[key].name] = dateProperty
+ break
+ }
+ case 'select':
+ case 'multi_select': {
+ const selects = getTextContent(val)
+ if (selects[0]?.length) {
+ properties[schema[key].name] = selects.split(',')
+ }
+ break
+ }
+ default:
+ break
+ }
+ }
+ }
+
+ if (properties) {
+ // 将表格中的字段映射成 英文
+ const config = {
+ enable: properties['启用'] === 'Yes',
+ key: properties['配置名'],
+ value: properties['配置值']
+ }
+
+ // 只导入生效的配置
+ if (config.enable) {
+ // console.log('[Notion配置]', config.key, config.value)
+ notionConfig[config.key] = config.value
+ }
+ }
+ }
+
+ return notionConfig
+}
diff --git a/lib/notion/getNotionData.js b/lib/notion/getNotionData.js
index f885f22b..5b77e716 100755
--- a/lib/notion/getNotionData.js
+++ b/lib/notion/getNotionData.js
@@ -8,6 +8,7 @@ import getAllPageIds from './getAllPageIds'
import { getAllTags } from './getAllTags'
import getPageProperties from './getPageProperties'
import { mapImgUrl, compressImage } from './mapImage'
+import { getConfigMapFromConfigPage } from './getNotionConfig'
/**
* 获取博客数据
@@ -272,6 +273,7 @@ async function getDataBaseInfoByNotionAPI({ pageId, from }) {
// 文章计数
let postCount = 0
+
// 查找所有的Post和Page
const allPages = collectionData.filter(post => {
if (post?.type === 'Post' && post.status === 'Published') {
@@ -282,6 +284,9 @@ async function getDataBaseInfoByNotionAPI({ pageId, from }) {
(post?.status === 'Invisible' || post?.status === 'Published')
})
+ // 站点配置优先读取配置表格,否则读取blog.config.js 文件
+ const NOTION_CONFIG = await getConfigMapFromConfigPage(collectionData) || {}
+
// Sort by date
if (BLOG.POSTS_SORT_BY === 'date') {
allPages.sort((a, b) => {
@@ -300,6 +305,7 @@ async function getDataBaseInfoByNotionAPI({ pageId, from }) {
const allNavPages = getNavPages({ allPages })
return {
+ NOTION_CONFIG,
notice,
siteInfo,
allPages,
diff --git a/lib/notion/getPageProperties.js b/lib/notion/getPageProperties.js
index ddf47f3e..3ea1ce28 100644
--- a/lib/notion/getPageProperties.js
+++ b/lib/notion/getPageProperties.js
@@ -6,6 +6,15 @@ import formatDate from '../formatDate'
import md5 from 'js-md5'
import { mapImgUrl } from './mapImage'
+/**
+ * 获取页面元素成员属性
+ * @param {*} id
+ * @param {*} block
+ * @param {*} schema
+ * @param {*} authToken
+ * @param {*} tagOptions
+ * @returns
+ */
export default async function getPageProperties(id, block, schema, authToken, tagOptions) {
const rawProperties = Object.entries(block?.[id]?.value?.properties || [])
const excludeProperties = ['date', 'select', 'multi_select', 'person']
@@ -108,6 +117,7 @@ export default async function getPageProperties(id, block, schema, authToken, ta
properties.slug += '.html'
}
}
+ // 密码字段md5
properties.password = properties.password ? md5(properties.slug + properties.password) : ''
return properties
}
diff --git a/lib/utils.js b/lib/utils.js
index 75278521..30e0e038 100644
--- a/lib/utils.js
+++ b/lib/utils.js
@@ -75,7 +75,6 @@ export function getQueryVariable(key) {
}
return (false)
}
-
/**
* 获取 URL 中指定参数的值
* @param {string} url
@@ -83,8 +82,10 @@ export function getQueryVariable(key) {
* @returns {string|null}
*/
export function getQueryParam(url, param) {
- const searchParams = new URLSearchParams(url.split('?')[1])
- return searchParams.get(param)
+ // 移除哈希部分
+ const urlWithoutHash = url.split('#')[0];
+ const searchParams = new URLSearchParams(urlWithoutHash.split('?')[1]);
+ return searchParams.get(param);
}
/**
@@ -202,3 +203,46 @@ export const isMobile = () => {
return isMobile
}
+
+/**
+ * 扫描页面上的所有文本节点,将url格式的文本转为可点击链接
+ * @param {*} node
+ */
+export const scanAndConvertToLinks = (node) => {
+ if (node.nodeType === Node.TEXT_NODE) {
+ const text = node.textContent;
+ const urlRegex = /https?:\/\/[^\s]+/g;
+ let lastIndex = 0;
+ let match;
+
+ const newNode = document.createElement('span');
+
+ while ((match = urlRegex.exec(text)) !== null) {
+ const beforeText = text.substring(lastIndex, match.index);
+ const url = match[0];
+
+ if (beforeText) {
+ newNode.appendChild(document.createTextNode(beforeText));
+ }
+
+ const link = document.createElement('a');
+ link.href = url;
+ link.target = '_blank'
+ link.textContent = url;
+
+ newNode.appendChild(link);
+
+ lastIndex = urlRegex.lastIndex;
+ }
+
+ if (lastIndex < text.length) {
+ newNode.appendChild(document.createTextNode(text.substring(lastIndex)));
+ }
+
+ node.parentNode.replaceChild(newNode, node);
+ } else if (node.nodeType === Node.ELEMENT_NODE) {
+ for (const childNode of node.childNodes) {
+ scanAndConvertToLinks(childNode);
+ }
+ }
+}
diff --git a/next-sitemap.config.js b/next-sitemap.config.js
index a094e809..29e1ca67 100644
--- a/next-sitemap.config.js
+++ b/next-sitemap.config.js
@@ -1,5 +1,8 @@
const BLOG = require('./blog.config')
+/**
+ * 通常没啥用,sitemap交给 /pages/sitemap.xml.js 动态生成
+ */
module.exports = {
siteUrl: BLOG.LINK,
changefreq: 'daily',
diff --git a/next.config.js b/next.config.js
index 0a8bd36d..aff79920 100644
--- a/next.config.js
+++ b/next.config.js
@@ -91,6 +91,7 @@ module.exports = withBundleAnalyzer({
// })
// }
// 动态主题:添加 resolve.alias 配置,将动态路径映射到实际路径
+ console.log('加载默认主题', path.resolve(__dirname, 'themes', THEME))
config.resolve.alias['@theme-components'] = path.resolve(__dirname, 'themes', THEME)
return config
},
diff --git a/package.json b/package.json
index 6164cbb4..ca67780c 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "notion-next",
- "version": "4.0.18",
+ "version": "4.1.0",
"homepage": "https://github.com/tangly1024/NotionNext.git",
"license": "MIT",
"repository": {
diff --git a/pages/404.js b/pages/404.js
index c770385f..ae8eea37 100644
--- a/pages/404.js
+++ b/pages/404.js
@@ -1,7 +1,7 @@
import { getGlobalData } from '@/lib/notion/getNotionData'
-import { useGlobal } from '@/lib/global'
import { useRouter } from 'next/router'
import { getLayoutByTheme } from '@/themes/theme'
+import { siteConfig } from '@/lib/config'
/**
* 404
@@ -9,13 +9,12 @@ import { getLayoutByTheme } from '@/themes/theme'
* @returns
*/
const NoFound = props => {
- const { siteInfo } = useGlobal()
- const meta = { title: `${props?.siteInfo?.title} | 页面找不到啦`, image: siteInfo?.pageCover }
+ const meta = { title: `${siteConfig('TITLE')} | 页面找不到啦`, image: siteConfig('HOME_BANNER_IMAGE') }
props = { ...props, meta }
// 根据页面路径加载不同Layout文件
- const Layout = getLayoutByTheme(useRouter())
+ const Layout = getLayoutByTheme({ theme: siteConfig('THEME'), router: useRouter() })
return
}
diff --git a/pages/[prefix]/index.js b/pages/[prefix]/index.js
index 6fa5d47b..dfc90fcc 100644
--- a/pages/[prefix]/index.js
+++ b/pages/[prefix]/index.js
@@ -10,6 +10,7 @@ import { getLayoutByTheme } from '@/themes/theme'
import md5 from 'js-md5'
import { isBrowser } from '@/lib/utils'
import { uploadDataToAlgolia } from '@/lib/algolia'
+import { siteConfig } from '@/lib/config'
/**
* 根据notion的slug访问页面
@@ -66,7 +67,7 @@ const Slug = props => {
}, [post])
const meta = {
- title: post ? `${post?.title} | ${siteInfo?.title}` : `${props?.siteInfo?.title || BLOG.TITLE} | loading`,
+ title: post ? `${post?.title} | ${siteConfig('TITLE')}` : `${siteConfig('TITLE')} | loading`,
description: post?.summary,
type: post?.type,
slug: post?.slug,
@@ -76,7 +77,7 @@ const Slug = props => {
}
props = { ...props, lock, meta, setLock, validPassword }
// 根据页面路径加载不同Layout文件
- const Layout = getLayoutByTheme(useRouter())
+ const Layout = getLayoutByTheme({ theme: siteConfig('THEME'), router: useRouter() })
return
}
diff --git a/pages/_app.js b/pages/_app.js
index a16513b9..a19933cb 100644
--- a/pages/_app.js
+++ b/pages/_app.js
@@ -11,12 +11,11 @@ import { GlobalContextProvider } from '@/lib/global'
import AOS from 'aos'
import 'aos/dist/aos.css' // You can also use for styles
-import dynamic from 'next/dynamic'
import { isBrowser, loadExternalResource } from '@/lib/utils'
import BLOG from '@/blog.config'
-
// 各种扩展插件 动画等
-const ExternalPlugins = dynamic(() => import('@/components/ExternalPlugins'))
+import ExternalPlugins from '@/components/ExternalPlugins'
+// const ExternalPlugins = dynamic(() => import('@/components/ExternalPlugins'))
const MyApp = ({ Component, pageProps }) => {
// 自定义样式css和js引入
diff --git a/pages/_document.js b/pages/_document.js
index 9c7bd4c8..976a45fb 100644
--- a/pages/_document.js
+++ b/pages/_document.js
@@ -1,7 +1,6 @@
// eslint-disable-next-line @next/next/no-document-import-in-page
import Document, { Html, Head, Main, NextScript } from 'next/document'
import BLOG from '@/blog.config'
-import CommonScript from '@/components/CommonScript'
class MyDocument extends Document {
static async getInitialProps(ctx) {
@@ -14,7 +13,6 @@ class MyDocument extends Document {
-
{/* 预加载字体 */}
{BLOG.FONT_AWESOME && <>
diff --git a/pages/api/cache.js b/pages/api/cache.js
new file mode 100644
index 00000000..97738651
--- /dev/null
+++ b/pages/api/cache.js
@@ -0,0 +1,15 @@
+import { cleanCache } from '@/lib/cache/local_file_cache'
+
+/**
+ * 清理缓存
+ * @param {*} req
+ * @param {*} res
+ */
+export default async function handler(req, res) {
+ try {
+ await cleanCache()
+ res.status(200).json({ status: 'success', message: 'Clean cache successful!' })
+ } catch (error) {
+ res.status(400).json({ status: 'error', message: 'Clean cache failed!', error })
+ }
+}
diff --git a/pages/archive/index.js b/pages/archive/index.js
index 106ad7d2..a0a57f28 100644
--- a/pages/archive/index.js
+++ b/pages/archive/index.js
@@ -6,13 +6,14 @@ import { useRouter } from 'next/router'
import { getLayoutByTheme } from '@/themes/theme'
import { isBrowser } from '@/lib/utils'
import { formatDateFmt } from '@/lib/formatDate'
+import { siteConfig } from '@/lib/config'
const ArchiveIndex = props => {
const { siteInfo } = props
const { locale } = useGlobal()
// 根据页面路径加载不同Layout文件
- const Layout = getLayoutByTheme(useRouter())
+ const Layout = getLayoutByTheme({ theme: siteConfig('THEME'), router: useRouter() })
useEffect(() => {
if (isBrowser) {
@@ -29,8 +30,8 @@ const ArchiveIndex = props => {
}, [])
const meta = {
- title: `${locale.NAV.ARCHIVE} | ${siteInfo?.title}`,
- description: siteInfo?.description,
+ title: `${locale.NAV.ARCHIVE} | ${siteConfig('TITLE')}`,
+ description: siteConfig('DESCRIPTION'),
image: siteInfo?.pageCover,
slug: 'archive',
type: 'website'
diff --git a/pages/category/[category]/index.js b/pages/category/[category]/index.js
index 03cd4fbd..a623cc6b 100644
--- a/pages/category/[category]/index.js
+++ b/pages/category/[category]/index.js
@@ -4,6 +4,7 @@ import { useGlobal } from '@/lib/global'
import BLOG from '@/blog.config'
import { useRouter } from 'next/router'
import { getLayoutByTheme } from '@/themes/theme'
+import { siteConfig } from '@/lib/config'
/**
* 分类页
@@ -15,13 +16,13 @@ export default function Category(props) {
const { locale } = useGlobal()
// 根据页面路径加载不同Layout文件
- const Layout = getLayoutByTheme(useRouter())
+ const Layout = getLayoutByTheme({ theme: siteConfig('THEME'), router: useRouter() })
const meta = {
title: `${props.category} | ${locale.COMMON.CATEGORY} | ${
- siteInfo?.title || ''
+ siteConfig('TITLE') || ''
}`,
- description: siteInfo?.description,
+ description: siteConfig('DESCRIPTION'),
slug: 'category/' + props.category,
image: siteInfo?.pageCover,
type: 'website'
diff --git a/pages/category/[category]/page/[page].js b/pages/category/[category]/page/[page].js
index 9174e117..ae5124b1 100644
--- a/pages/category/[category]/page/[page].js
+++ b/pages/category/[category]/page/[page].js
@@ -4,6 +4,7 @@ import { useGlobal } from '@/lib/global'
import BLOG from '@/blog.config'
import { useRouter } from 'next/router'
import { getLayoutByTheme } from '@/themes/theme'
+import { siteConfig } from '@/lib/config'
/**
* 分类页
@@ -15,13 +16,13 @@ export default function Category(props) {
const { siteInfo } = props
const { locale } = useGlobal()
// 根据页面路径加载不同Layout文件
- const Layout = getLayoutByTheme(useRouter())
+ const Layout = getLayoutByTheme({ theme: siteConfig('THEME'), router: useRouter() })
const meta = {
title: `${props.category} | ${locale.COMMON.CATEGORY} | ${
- siteInfo?.title || ''
+ siteConfig('TITLE') || ''
}`,
- description: siteInfo?.description,
+ description: siteConfig('DESCRIPTION'),
slug: 'category/' + props.category,
image: siteInfo?.pageCover,
type: 'website'
diff --git a/pages/category/index.js b/pages/category/index.js
index b4872000..85121910 100644
--- a/pages/category/index.js
+++ b/pages/category/index.js
@@ -4,6 +4,7 @@ import { useGlobal } from '@/lib/global'
import BLOG from '@/blog.config'
import { useRouter } from 'next/router'
import { getLayoutByTheme } from '@/themes/theme'
+import { siteConfig } from '@/lib/config'
/**
* 分类首页
@@ -15,11 +16,11 @@ export default function Category(props) {
const { siteInfo } = props
// 根据页面路径加载不同Layout文件
- const Layout = getLayoutByTheme(useRouter())
+ const Layout = getLayoutByTheme({ theme: siteConfig('THEME'), router: useRouter() })
const meta = {
- title: `${locale.COMMON.CATEGORY} | ${siteInfo?.title}`,
- description: siteInfo?.description,
+ title: `${locale.COMMON.CATEGORY} | ${siteConfig('TITLE')}`,
+ description: siteConfig('DESCRIPTION'),
image: siteInfo?.pageCover,
slug: 'category',
type: 'website'
diff --git a/pages/index.js b/pages/index.js
index 328ece1e..18f7ec47 100644
--- a/pages/index.js
+++ b/pages/index.js
@@ -3,8 +3,9 @@ import { getPostBlocks } from '@/lib/notion'
import { getGlobalData } from '@/lib/notion/getNotionData'
import { generateRss } from '@/lib/rss'
import { generateRobotsTxt } from '@/lib/robots.txt'
-import { useRouter } from 'next/router'
import { getLayoutByTheme } from '@/themes/theme'
+import { siteConfig } from '@/lib/config'
+import { useRouter } from 'next/router'
/**
* 首页布局
@@ -13,8 +14,16 @@ import { getLayoutByTheme } from '@/themes/theme'
*/
const Index = props => {
// 根据页面路径加载不同Layout文件
- const Layout = getLayoutByTheme(useRouter())
- return
+ const Layout = getLayoutByTheme({ theme: siteConfig('THEME'), router: useRouter() })
+
+ const meta = {
+ title: `${siteConfig('TITLE')} | ${siteConfig('DESCRIPTION')}`,
+ description: siteConfig('DESCRIPTION'),
+ image: siteConfig('HOME_BANNER_IMAGE'),
+ slug: '',
+ type: 'website'
+ }
+ return
}
/**
@@ -25,16 +34,8 @@ export async function getStaticProps() {
const from = 'index'
const props = await getGlobalData({ from })
- const { siteInfo } = props
props.posts = props.allPages?.filter(page => page.type === 'Post' && page.status === 'Published')
- const meta = {
- title: `${siteInfo?.title} | ${siteInfo?.description}`,
- description: siteInfo?.description,
- image: siteInfo?.pageCover,
- slug: '',
- type: 'website'
- }
// 处理分页
if (BLOG.POST_LIST_STYLE === 'scroll') {
// 滚动列表默认给前端返回所有数据
@@ -65,10 +66,7 @@ export async function getStaticProps() {
delete props.allPages
return {
- props: {
- meta,
- ...props
- },
+ props,
revalidate: parseInt(BLOG.NEXT_REVALIDATE_SECOND)
}
}
diff --git a/pages/page/[page].js b/pages/page/[page].js
index 02b847a8..c34aef1d 100644
--- a/pages/page/[page].js
+++ b/pages/page/[page].js
@@ -3,6 +3,7 @@ import { getPostBlocks } from '@/lib/notion'
import { getGlobalData } from '@/lib/notion/getNotionData'
import { useRouter } from 'next/router'
import { getLayoutByTheme } from '@/themes/theme'
+import { siteConfig } from '@/lib/config'
/**
* 文章列表分页
@@ -13,11 +14,11 @@ const Page = props => {
const { siteInfo } = props
// 根据页面路径加载不同Layout文件
- const Layout = getLayoutByTheme(useRouter())
+ const Layout = getLayoutByTheme({ theme: siteConfig('THEME'), router: useRouter() })
const meta = {
- title: `${props?.page} | Page | ${siteInfo?.title}`,
- description: siteInfo?.description,
+ title: `${props?.page} | Page | ${siteConfig('TITLE')}`,
+ description: siteConfig('DESCRIPTION'),
image: siteInfo?.pageCover,
slug: 'page/' + props.page,
type: 'website'
diff --git a/pages/search/[keyword]/index.js b/pages/search/[keyword]/index.js
index 042bbf85..cd6166ed 100644
--- a/pages/search/[keyword]/index.js
+++ b/pages/search/[keyword]/index.js
@@ -4,17 +4,18 @@ import { getDataFromCache } from '@/lib/cache/cache_manager'
import BLOG from '@/blog.config'
import { useRouter } from 'next/router'
import { getLayoutByTheme } from '@/themes/theme'
+import { siteConfig } from '@/lib/config'
const Index = props => {
const { keyword, siteInfo } = props
const { locale } = useGlobal()
// 根据页面路径加载不同Layout文件
- const Layout = getLayoutByTheme(useRouter())
+ const Layout = getLayoutByTheme({ theme: siteConfig('THEME'), router: useRouter() })
const meta = {
- title: `${keyword || ''}${keyword ? ' | ' : ''}${locale.NAV.SEARCH} | ${siteInfo?.title}`,
- description: siteInfo?.title,
+ title: `${keyword || ''}${keyword ? ' | ' : ''}${locale.NAV.SEARCH} | ${siteConfig('TITLE')}`,
+ description: siteConfig('TITLE'),
image: siteInfo?.pageCover,
slug: 'search/' + (keyword || ''),
type: 'website'
diff --git a/pages/search/[keyword]/page/[page].js b/pages/search/[keyword]/page/[page].js
index 20240f19..9de43cc1 100644
--- a/pages/search/[keyword]/page/[page].js
+++ b/pages/search/[keyword]/page/[page].js
@@ -4,17 +4,18 @@ import { getDataFromCache } from '@/lib/cache/cache_manager'
import BLOG from '@/blog.config'
import { useRouter } from 'next/router'
import { getLayoutByTheme } from '@/themes/theme'
+import { siteConfig } from '@/lib/config'
const Index = props => {
const { keyword, siteInfo } = props
const { locale } = useGlobal()
// 根据页面路径加载不同Layout文件
- const Layout = getLayoutByTheme(useRouter())
+ const Layout = getLayoutByTheme({ theme: siteConfig('THEME'), router: useRouter() })
const meta = {
- title: `${keyword || ''}${keyword ? ' | ' : ''}${locale.NAV.SEARCH} | ${siteInfo?.title}`,
- description: siteInfo?.title,
+ title: `${keyword || ''}${keyword ? ' | ' : ''}${locale.NAV.SEARCH} | ${siteConfig('TITLE')}`,
+ description: siteConfig('TITLE'),
image: siteInfo?.pageCover,
slug: 'search/' + (keyword || ''),
type: 'website'
diff --git a/pages/search/index.js b/pages/search/index.js
index 22061cbd..fa45d2d5 100644
--- a/pages/search/index.js
+++ b/pages/search/index.js
@@ -3,6 +3,7 @@ import { useGlobal } from '@/lib/global'
import { useRouter } from 'next/router'
import BLOG from '@/blog.config'
import { getLayoutByTheme } from '@/themes/theme'
+import { siteConfig } from '@/lib/config'
/**
* 搜索路由
@@ -14,7 +15,7 @@ const Search = props => {
const { locale } = useGlobal()
// 根据页面路径加载不同Layout文件
- const Layout = getLayoutByTheme(useRouter())
+ const Layout = getLayoutByTheme({ theme: siteConfig('THEME'), router: useRouter() })
const router = useRouter()
const keyword = getSearchKey(router)
@@ -34,8 +35,8 @@ const Search = props => {
}
const meta = {
- title: `${keyword || ''}${keyword ? ' | ' : ''}${locale.NAV.SEARCH} | ${siteInfo?.title}`,
- description: siteInfo?.description,
+ title: `${keyword || ''}${keyword ? ' | ' : ''}${locale.NAV.SEARCH} | ${siteConfig('TITLE')}`,
+ description: siteConfig('DESCRIPTION'),
image: siteInfo?.pageCover,
slug: 'search',
type: 'website'
diff --git a/pages/tag/[tag]/index.js b/pages/tag/[tag]/index.js
index bd9f7529..1d6c94d2 100644
--- a/pages/tag/[tag]/index.js
+++ b/pages/tag/[tag]/index.js
@@ -3,6 +3,7 @@ import { getGlobalData } from '@/lib/notion/getNotionData'
import BLOG from '@/blog.config'
import { useRouter } from 'next/router'
import { getLayoutByTheme } from '@/themes/theme'
+import { siteConfig } from '@/lib/config'
/**
* 标签下的文章列表
@@ -14,11 +15,11 @@ const Tag = props => {
const { tag, siteInfo } = props
// 根据页面路径加载不同Layout文件
- const Layout = getLayoutByTheme(useRouter())
+ const Layout = getLayoutByTheme({ theme: siteConfig('THEME'), router: useRouter() })
const meta = {
- title: `${tag} | ${locale.COMMON.TAGS} | ${siteInfo?.title}`,
- description: siteInfo?.description,
+ title: `${tag} | ${locale.COMMON.TAGS} | ${siteConfig('TITLE')}`,
+ description: siteConfig('DESCRIPTION'),
image: siteInfo?.pageCover,
slug: 'tag/' + tag,
type: 'website'
diff --git a/pages/tag/[tag]/page/[page].js b/pages/tag/[tag]/page/[page].js
index 2eaef518..ab28fdbc 100644
--- a/pages/tag/[tag]/page/[page].js
+++ b/pages/tag/[tag]/page/[page].js
@@ -3,17 +3,18 @@ import { getGlobalData } from '@/lib/notion/getNotionData'
import BLOG from '@/blog.config'
import { useRouter } from 'next/router'
import { getLayoutByTheme } from '@/themes/theme'
+import { siteConfig } from '@/lib/config'
const Tag = props => {
const { locale } = useGlobal()
const { tag, siteInfo } = props
// 根据页面路径加载不同Layout文件
- const Layout = getLayoutByTheme(useRouter())
+ const Layout = getLayoutByTheme({ theme: siteConfig('THEME'), router: useRouter() })
const meta = {
- title: `${tag} | ${locale.COMMON.TAGS} | ${siteInfo?.title}`,
- description: siteInfo?.description,
+ title: `${tag} | ${locale.COMMON.TAGS} | ${siteConfig('TITLE')}`,
+ description: siteConfig('DESCRIPTION'),
image: siteInfo?.pageCover,
slug: 'tag/' + tag,
type: 'website'
diff --git a/pages/tag/index.js b/pages/tag/index.js
index 54dab9a4..bb618414 100644
--- a/pages/tag/index.js
+++ b/pages/tag/index.js
@@ -3,6 +3,7 @@ import { useGlobal } from '@/lib/global'
import BLOG from '@/blog.config'
import { useRouter } from 'next/router'
import { getLayoutByTheme } from '@/themes/theme'
+import { siteConfig } from '@/lib/config'
/**
* 标签首页
@@ -14,11 +15,11 @@ const TagIndex = props => {
const { siteInfo } = props
// 根据页面路径加载不同Layout文件
- const Layout = getLayoutByTheme(useRouter())
+ const Layout = getLayoutByTheme({ theme: siteConfig('THEME'), router: useRouter() })
const meta = {
- title: `${locale.COMMON.TAGS} | ${siteInfo?.title}`,
- description: siteInfo?.description,
+ title: `${locale.COMMON.TAGS} | ${siteConfig('TITLE')}`,
+ description: siteConfig('DESCRIPTION'),
image: siteInfo?.pageCover,
slug: 'tag',
type: 'website'
diff --git a/styles/notion.css b/styles/notion.css
index c7d939be..50ce45f3 100644
--- a/styles/notion.css
+++ b/styles/notion.css
@@ -724,7 +724,6 @@ svg.notion-page-icon {
.notion-text {
width: 100%;
white-space: pre-wrap;
- line-height: 1.5rem !important;
word-break: break-word;
padding: 3px 2px !important;
margin: 1px 0 !important;
diff --git a/themes/example/components/BlogListGroupByDate.js b/themes/example/components/BlogListGroupByDate.js
index ac5f9cbd..29598f0b 100644
--- a/themes/example/components/BlogListGroupByDate.js
+++ b/themes/example/components/BlogListGroupByDate.js
@@ -1,4 +1,4 @@
-import BLOG from '@/blog.config'
+import { siteConfig } from '@/lib/config'
import Link from 'next/link'
/**
@@ -24,7 +24,7 @@ export default function BlogListGroupByDate({ archiveTitle, archivePosts }) {
{post?.publishDay}
{' '}
-
+
{post.title}
diff --git a/themes/example/components/BlogListPage.js b/themes/example/components/BlogListPage.js
index 429ee30e..5ae2cf5c 100644
--- a/themes/example/components/BlogListPage.js
+++ b/themes/example/components/BlogListPage.js
@@ -1,5 +1,5 @@
-import BLOG from '@/blog.config'
+import { siteConfig } from '@/lib/config'
import { useGlobal } from '@/lib/global'
import { useRouter } from 'next/router'
import Link from 'next/link'
@@ -10,14 +10,14 @@ export const BlogListPage = props => {
const { page = 1, posts, postCount } = props
const { locale } = useGlobal()
const router = useRouter()
- const totalPage = Math.ceil(postCount / BLOG.POSTS_PER_PAGE)
+ const totalPage = Math.ceil(postCount / parseInt(siteConfig('POSTS_PER_PAGE')))
const currentPage = +page
const showPrev = currentPage > 1
const showNext = page < totalPage
const pagePrefix = router.asPath.split('?')[0].replace(/\/page\/[1-9]\d*/, '').replace(/\/$/, '')
- const showPageCover = CONFIG.POST_LIST_COVER
+ const showPageCover = siteConfig('EXAMPLE_POST_LIST_COVER', null, CONFIG)
return (
diff --git a/themes/example/components/BlogListScroll.js b/themes/example/components/BlogListScroll.js
index ad6df10e..e876f312 100644
--- a/themes/example/components/BlogListScroll.js
+++ b/themes/example/components/BlogListScroll.js
@@ -1,6 +1,6 @@
-import BLOG from '@/blog.config'
+import { siteConfig } from '@/lib/config'
import { useGlobal } from '@/lib/global'
-import React, { useEffect } from 'react'
+import { useCallback, useEffect, useRef, useState } from 'react'
import throttle from 'lodash.throttle'
import BlogPostCard from './BlogPostCard'
import CONFIG from '../config'
@@ -9,33 +9,33 @@ export const BlogListScroll = props => {
const { posts } = props
const { locale } = useGlobal()
- const [page, updatePage] = React.useState(1)
+ const [page, updatePage] = useState(1)
let hasMore = false
const postsToShow = posts
- ? Object.assign(posts).slice(0, BLOG.POSTS_PER_PAGE * page)
+ ? Object.assign(posts).slice(0, parseInt(siteConfig('POSTS_PER_PAGE')) * page)
: []
if (posts) {
const totalCount = posts.length
- hasMore = page * BLOG.POSTS_PER_PAGE < totalCount
+ hasMore = page * parseInt(siteConfig('POSTS_PER_PAGE')) < totalCount
}
const handleGetMore = () => {
if (!hasMore) return
updatePage(page + 1)
}
- const targetRef = React.useRef(null)
+ const targetRef = useRef(null)
// 监听滚动自动分页加载
- const scrollTrigger = React.useCallback(throttle(() => {
+ const scrollTrigger = useCallback(throttle(() => {
const scrollS = window.scrollY + window.outerHeight
const clientHeight = targetRef ? (targetRef.current ? (targetRef.current.clientHeight) : 0) : 0
if (scrollS > clientHeight + 100) {
handleGetMore()
}
}, 500))
- const showPageCover = CONFIG.POST_LIST_COVER
+ const showPageCover = siteConfig('EXAMPLE_POST_LIST_COVER', null, CONFIG)
useEffect(() => {
window.addEventListener('scroll', scrollTrigger)
diff --git a/themes/example/components/BlogPostCard.js b/themes/example/components/BlogPostCard.js
index faf1753d..5b690743 100644
--- a/themes/example/components/BlogPostCard.js
+++ b/themes/example/components/BlogPostCard.js
@@ -1,11 +1,11 @@
-import BLOG from '@/blog.config'
+import { siteConfig } from '@/lib/config'
import CONFIG from '../config'
import Link from 'next/link'
import TwikooCommentCount from '@/components/TwikooCommentCount'
import LazyImage from '@/components/LazyImage'
const BlogPostCard = ({ post }) => {
- const showPageCover = CONFIG.POST_LIST_COVER && post?.pageCoverThumbnail
+ const showPageCover = siteConfig('EXAMPLE_POST_LIST_COVER', null, CONFIG) && post?.pageCoverThumbnail
return
@@ -18,7 +18,7 @@ const BlogPostCard = ({ post }) => {
{/* 图片封面 */}
{showPageCover && (
-
-
-
+
+
+
)}
diff --git a/themes/example/components/ExampleRecentComments.js b/themes/example/components/ExampleRecentComments.js
index b1555c1d..e5097740 100644
--- a/themes/example/components/ExampleRecentComments.js
+++ b/themes/example/components/ExampleRecentComments.js
@@ -1,5 +1,5 @@
-import React from 'react'
-import BLOG from '@/blog.config'
+import { useEffect, useState } from 'react'
+import { siteConfig } from '@/lib/config'
import Link from 'next/link'
import { RecentComments } from '@waline/client'
@@ -9,11 +9,11 @@ import { RecentComments } from '@waline/client'
* @returns
*/
const ExampleRecentComments = (props) => {
- const [comments, updateComments] = React.useState([])
- const [onLoading, changeLoading] = React.useState(true)
- React.useEffect(() => {
+ const [comments, updateComments] = useState([])
+ const [onLoading, changeLoading] = useState(true)
+ useEffect(() => {
RecentComments({
- serverURL: BLOG.COMMENT_WALINE_SERVER_URL,
+ serverURL: siteConfig('COMMENT_WALINE_SERVER_URL'),
count: 5
}).then(({ comments }) => {
changeLoading(false)
diff --git a/themes/example/components/Footer.js b/themes/example/components/Footer.js
index 09af18fe..77c8bad6 100644
--- a/themes/example/components/Footer.js
+++ b/themes/example/components/Footer.js
@@ -1,28 +1,24 @@
-import BLOG from '@/blog.config'
import DarkModeButton from '@/components/DarkModeButton'
+import { siteConfig } from '@/lib/config'
export const Footer = (props) => {
const d = new Date()
const currentYear = d.getFullYear()
- const copyrightDate = (function() {
- if (Number.isInteger(BLOG.SINCE) && BLOG.SINCE < currentYear) {
- return BLOG.SINCE + '-' + currentYear
- }
- return currentYear
- })()
+ const since = siteConfig('SINCE')
+ const copyrightDate = parseInt(since) < currentYear ? since + '-' + currentYear : currentYear
return