Merge branch 'feat/notion-config' into release/4.1.0

This commit is contained in:
tangly1024.com
2023-11-02 17:08:36 +08:00
18 changed files with 142 additions and 83 deletions

View File

@@ -5,7 +5,7 @@ import { useEffect } from 'react'
import { loadExternalResource } from '@/lib/utils' import { loadExternalResource } from '@/lib/utils'
const CusdisComponent = ({ frontMatter }) => { const CusdisComponent = ({ frontMatter }) => {
const { locale } = useGlobal() const { lang } = useGlobal()
const router = useRouter() const router = useRouter()
const { isDarkMode } = useGlobal() const { isDarkMode } = useGlobal()
@@ -18,7 +18,7 @@ const CusdisComponent = ({ frontMatter }) => {
}, [isDarkMode]) }, [isDarkMode])
return <div id="cusdis_thread" return <div id="cusdis_thread"
lang={locale.LOCALE.toLowerCase()} lang={lang.toLowerCase()}
data-host={BLOG.COMMENT_CUSDIS_HOST} data-host={BLOG.COMMENT_CUSDIS_HOST}
data-app-id={BLOG.COMMENT_CUSDIS_APP_ID} data-app-id={BLOG.COMMENT_CUSDIS_APP_ID}
data-page-id={frontMatter.id} data-page-id={frontMatter.id}

View File

@@ -1,5 +1,4 @@
import { useGlobal } from '@/lib/global' import { useGlobal } from '@/lib/global'
import { saveDarkModeToCookies } from '@/themes/theme'
import { Moon, Sun } from './HeroIcons' import { Moon, Sun } from './HeroIcons'
import { useImperativeHandle } from 'react' import { useImperativeHandle } from 'react'
@@ -8,7 +7,7 @@ import { useImperativeHandle } from 'react'
*/ */
const DarkModeButton = (props) => { const DarkModeButton = (props) => {
const { cRef, className } = props const { cRef, className } = props
const { isDarkMode, updateDarkMode } = useGlobal() const { isDarkMode, toggleDarkMode } = useGlobal()
/** /**
* 对外暴露方法 * 对外暴露方法
@@ -16,22 +15,12 @@ const DarkModeButton = (props) => {
useImperativeHandle(cRef, () => { useImperativeHandle(cRef, () => {
return { return {
handleChangeDarkMode: () => { handleChangeDarkMode: () => {
handleChangeDarkMode() toggleDarkMode()
} }
} }
}) })
// 用户手动设置主题 return <div onClick={toggleDarkMode} className={`${className || ''} flex justify-center dark:text-gray-200 text-gray-800`}>
const handleChangeDarkMode = () => {
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')
}
return <div onClick={handleChangeDarkMode} className={`${className || ''} flex justify-center dark:text-gray-200 text-gray-800`}>
<div id='darkModeButton' className=' hover:scale-110 cursor-pointer transform duration-200 w-5 h-5'> {isDarkMode ? <Sun /> : <Moon />}</div> <div id='darkModeButton' className=' hover:scale-110 cursor-pointer transform duration-200 w-5 h-5'> {isDarkMode ? <Sun /> : <Moon />}</div>
</div> </div>
} }

View File

@@ -5,19 +5,21 @@ import { THEMES } from '@/themes/theme'
import { useRouter } from 'next/router' import { useRouter } from 'next/router'
import DarkModeButton from './DarkModeButton' import DarkModeButton from './DarkModeButton'
import { getQueryParam } from '@/lib/utils' import { getQueryParam } from '@/lib/utils'
import LANGS from '@/lib/lang'
/** /**
* *
* @returns 主题切换 * @returns 主题切换
*/ */
const ThemeSwitch = () => { const ThemeSwitch = () => {
const { theme } = useGlobal() const { theme, lang, changeLang, locale, isDarkMode, toggleDarkMode } = useGlobal()
const router = useRouter() const router = useRouter()
const currentTheme = getQueryParam(router.asPath, 'theme') || theme const currentTheme = getQueryParam(router.asPath, 'theme') || theme
// const currentLang = getQueryParam(router.asPath, 'lang') || lang
const [isLoading, setIsLoading] = useState(false) const [isLoading, setIsLoading] = useState(false)
// 修改当前路径url中的 theme 参数 // 修改当前路径url中的 theme 参数
// 例如 http://localhost?theme=hexo 跳转到 http://localhost?theme=newTheme // 例如 http://localhost?theme=hexo 跳转到 http://localhost?theme=newTheme
const onSelectChange = (e) => { const onThemeSelectChange = (e) => {
setIsLoading(true) setIsLoading(true)
const newTheme = e.target.value const newTheme = e.target.value
const query = router.query const query = router.query
@@ -27,27 +29,52 @@ const ThemeSwitch = () => {
}) })
} }
const onLangSelectChange = (e) => {
const newLang = e.target.value
changeLang(newLang)
}
return (<> return (<>
<Draggable> <Draggable>
<div id="draggableBox" style={{ left: '10px', top: '80vh' }} className="fixed z-50 dark:text-white bg-gray-50 dark:bg-black rounded-2xl drop-shadow-lg"> <div id="draggableBox" style={{ left: '0px', top: '80vh' }} className="fixed group space-y-2 overflow-hidden z-50 p-3 flex flex-col items-start dark:text-white bg-gray-50 dark:bg-black rounded-xl shadow-lg border dark:border-gray-800">
<div className="p-3 w-full flex items-center text-sm group duration-200 transition-all"> {/* 深色按钮 */}
<DarkModeButton className='mr-2' /> <div className="text-sm flex items-center group-hover:w-32 transition-all duration-200">
<div className='w-0 group-hover:w-20 transition-all duration-200 overflow-hidden'> <DarkModeButton />
<select value={currentTheme} onChange={onSelectChange} name="themes" className='appearance-none outline-none dark:text-white bg-gray-50 dark:bg-black uppercase cursor-pointer'> <div onClick={toggleDarkMode} className='cursor-pointer w-0 group-hover:w-24 transition-all duration-200 overflow-hidden whitespace-nowrap pl-1'>{isDarkMode ? locale.MENU.DARK_MODE : locale.MENU.LIGHT_MODE}</div>
</div>
{/* 翻译按钮 */}
<div className="text-sm flex items-center group-hover:w-32 transition-all duration-200">
<i className="fa-solid fa-language w-5" />
<div className='w-0 group-hover:w-24 transition-all duration-200 overflow-hidden'>
<select value={lang} onChange={onLangSelectChange} name="themes" className='pl-1 bg-gray-50 dark:bg-black appearance-none outline-none dark:text-white uppercase cursor-pointer'>
{Object.keys(LANGS)?.map(t => {
return <option key={t} value={t}>{LANGS[t].LOCALE}</option>
})}
</select>
</div>
</div>
{/* 主题切换按钮 */}
<div className="text-sm flex items-center group-hover:w-32 transition-all duration-200">
<i className="fa-solid fa-palette w-5" />
<div className='w-0 group-hover:w-24 transition-all duration-200 overflow-hidden'>
<select value={currentTheme} onChange={onThemeSelectChange} name="themes" className='pl-1 bg-gray-50 dark:bg-black appearance-none outline-none dark:text-white uppercase cursor-pointer'>
{THEMES?.map(t => { {THEMES?.map(t => {
return <option key={t} value={t}>{t}</option> return <option key={t} value={t}>{t}</option>
})} })}
</select> </select>
</div> </div>
<i className="fa-solid fa-palette pl-2"/>
</div> </div>
</div> </div>
{/* 切换主题加载时的全屏遮罩 */}
<div className={`${isLoading ? 'opacity-50 ' : 'opacity-0'} w-screen h-screen bg-black text-white shadow-text flex justify-center items-center
transition-all fixed top-0 left-0 pointer-events-none duration-1000 z-50 shadow-inner`}>
<i className='text-3xl mr-5 fas fa-spinner animate-spin' />
</div>
</Draggable> </Draggable>
{/* 切换主题加载时的全屏遮罩 */}
<div className={`${isLoading ? 'opacity-50 ' : 'opacity-0'}
w-screen h-screen bg-black text-white shadow-text flex justify-center items-center
transition-all fixed top-0 left-0 pointer-events-none duration-1000 z-50 shadow-inner`}>
<i className='text-3xl mr-5 fas fa-spinner animate-spin' />
</div>
</> </>
) )
} }

View File

@@ -3,15 +3,6 @@ import FileCache from './local_file_cache'
import MongoCache from './mongo_db_cache' import MongoCache from './mongo_db_cache'
import BLOG from '@/blog.config' 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数据将被缓存 * 为减少频繁接口请求notion数据将被缓存
* @param {*} key * @param {*} key
@@ -19,11 +10,11 @@ if (process.env.MONGO_DB_URL && process.env.MONGO_DB_NAME) {
*/ */
export async function getDataFromCache(key, force) { export async function getDataFromCache(key, force) {
if (BLOG.ENABLE_CACHE || force) { if (BLOG.ENABLE_CACHE || force) {
const dataFromCache = await api.getCache(key) const dataFromCache = await getApi().getCache(key)
if (JSON.stringify(dataFromCache) === '[]') { if (JSON.stringify(dataFromCache) === '[]') {
return null return null
} }
return api.getCache(key) return getApi().getCache(key)
} else { } else {
return null return null
} }
@@ -33,12 +24,26 @@ export async function setDataToCache(key, data) {
if (!data) { if (!data) {
return return
} }
await api.setCache(key, data) await getApi().setCache(key, data)
} }
export async function delCacheData(key) { export async function delCacheData(key) {
if (!BLOG.ENABLE_CACHE) { if (!BLOG.ENABLE_CACHE) {
return 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
}
} }

View File

@@ -41,7 +41,7 @@ export async function setCache (key, data) {
fs.writeFileSync(jsonFile, JSON.stringify(json)) fs.writeFileSync(jsonFile, JSON.stringify(json))
} }
export async function delCache (key, data) { export async function delCache (key) {
const exist = await fs.existsSync(jsonFile) const exist = await fs.existsSync(jsonFile)
const json = exist ? JSON.parse(await fs.readFileSync(jsonFile)) : {} const json = exist ? JSON.parse(await fs.readFileSync(jsonFile)) : {}
delete json.key delete json.key
@@ -49,4 +49,12 @@ export async function delCache (key, data) {
fs.writeFileSync(jsonFile, JSON.stringify(json)) fs.writeFileSync(jsonFile, JSON.stringify(json))
} }
/**
* 清理缓存
*/
export async function cleanCache() {
const json = {}
fs.writeFileSync(jsonFile, JSON.stringify(json))
}
export default { getCache, setCache, delCache } export default { getCache, setCache, delCache }

View File

@@ -64,7 +64,7 @@ export const siteConfigMap = () => {
const val = deepClone(BLOG) const val = deepClone(BLOG)
for (const key in val) { for (const key in val) {
val[key] = siteConfig(key) val[key] = siteConfig(key)
console.log('site', key, val[key], siteConfig(key)) // console.log('site', key, val[key], siteConfig(key))
} }
return val return val
} }

View File

@@ -1,11 +1,10 @@
import { generateLocaleDict, initLocale } from './lang' import { generateLocaleDict, initLocale } from './lang'
import { createContext, useContext, useEffect, useState } from 'react' import { createContext, useContext, useEffect, useState } from 'react'
import { useRouter } from 'next/router' import { useRouter } from 'next/router'
import { THEMES, initDarkMode } from '@/themes/theme' import { THEMES, initDarkMode, saveDarkModeToCookies } from '@/themes/theme'
import BLOG from '@/blog.config' import BLOG from '@/blog.config'
import NProgress from 'nprogress' import NProgress from 'nprogress'
import { isBrowser } from './utils'
const GlobalContext = createContext() const GlobalContext = createContext()
@@ -35,17 +34,34 @@ export function GlobalContextProvider(props) {
return newTheme 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(() => { useEffect(() => {
initLocale(lang, locale, updateLang, updateLocale)
initDarkMode(updateDarkMode) initDarkMode(updateDarkMode)
checkThemeDOM() initLocale(lang, locale, updateLang, updateLocale)
}, []) }, [])
// 加载默认主题 useEffect(() => {
// useEffect(() => { checkThemeDOM()
// const queryTheme = getQueryVariable('theme') || theme })
// setTheme(queryTheme)
// }, [router])
// 加载进度条 // 加载进度条
useEffect(() => { useEffect(() => {
@@ -62,6 +78,7 @@ export function GlobalContextProvider(props) {
NProgress.done() NProgress.done()
setOnLoading(false) setOnLoading(false)
} }
router.events.on('routeChangeStart', handleStart) router.events.on('routeChangeStart', handleStart)
router.events.on('routeChangeError', handleStop) router.events.on('routeChangeError', handleStop)
router.events.on('routeChangeComplete', handleStop) router.events.on('routeChangeComplete', handleStop)
@@ -75,10 +92,11 @@ export function GlobalContextProvider(props) {
return ( return (
<GlobalContext.Provider value={{ <GlobalContext.Provider value={{
NOTION_CONFIG, NOTION_CONFIG,
toggleDarkMode,
onLoading, onLoading,
setOnLoading, setOnLoading,
lang, lang,
updateLang, changeLang,
locale, locale,
updateLocale, updateLocale,
isDarkMode, isDarkMode,
@@ -99,17 +117,13 @@ export function GlobalContextProvider(props) {
* 切换主题时的特殊处理 * 切换主题时的特殊处理
*/ */
const checkThemeDOM = () => { const checkThemeDOM = () => {
if (isBrowser) { const elements = document.querySelectorAll('[id^="theme-"]')
setTimeout(() => { if (elements?.length > 1) {
const elements = document.querySelectorAll('[id^="theme-"]') elements[elements.length - 1].scrollIntoView()
if (elements?.length > 1) { // 删除前面的元素,只保留最后一个元素
elements[elements.length - 1].scrollIntoView() for (let i = 0; i < elements.length - 1; i++) {
// 删除前面的元素,只保留最后一个元素 elements[i].parentNode.removeChild(elements[i])
for (let i = 0; i < elements.length - 1; i++) { }
elements[i].parentNode.removeChild(elements[i])
}
}
}, 500)
} }
} }

View File

@@ -12,7 +12,7 @@ import { getQueryVariable, isBrowser, mergeDeep } from './utils'
* 在这里配置所有支持的语言 * 在这里配置所有支持的语言
* 国家-地区 * 国家-地区
*/ */
const lang = { const LANGS = {
'en-US': enUS, 'en-US': enUS,
'zh-CN': zhCN, 'zh-CN': zhCN,
'zh-HK': zhHK, 'zh-HK': zhHK,
@@ -22,7 +22,7 @@ const lang = {
'ja-JP': jaJP 'ja-JP': jaJP
} }
export default lang export default LANGS
/** /**
* 获取当前语言字典 * 获取当前语言字典
@@ -30,7 +30,7 @@ export default lang
* @returns 不同语言对应字典 * @returns 不同语言对应字典
*/ */
export function generateLocaleDict(langString) { export function generateLocaleDict(langString) {
const supportedLocales = Object.keys(lang) const supportedLocales = Object.keys(LANGS)
let userLocale let userLocale
// 将语言字符串拆分为语言和地区代码,例如将 "zh-CN" 拆分为 "zh" 和 "CN" // 将语言字符串拆分为语言和地区代码,例如将 "zh-CN" 拆分为 "zh" 和 "CN"
@@ -39,24 +39,24 @@ export function generateLocaleDict(langString) {
// 优先匹配语言和地区都匹配的情况 // 优先匹配语言和地区都匹配的情况
const specificLocale = `${language}-${region}` const specificLocale = `${language}-${region}`
if (supportedLocales.includes(specificLocale)) { if (supportedLocales.includes(specificLocale)) {
userLocale = lang[specificLocale] userLocale = LANGS[specificLocale]
} }
// 然后尝试匹配只有语言匹配的情况 // 然后尝试匹配只有语言匹配的情况
if (!userLocale) { if (!userLocale) {
const languageOnlyLocales = supportedLocales.filter(locale => locale.startsWith(language)) const languageOnlyLocales = supportedLocales.filter(locale => locale.startsWith(language))
if (languageOnlyLocales.length > 0) { if (languageOnlyLocales.length > 0) {
userLocale = lang[languageOnlyLocales[0]] userLocale = LANGS[languageOnlyLocales[0]]
} }
} }
// 如果还没匹配到,则返回最接近的语言包 // 如果还没匹配到,则返回最接近的语言包
if (!userLocale) { if (!userLocale) {
const fallbackLocale = supportedLocales.find(locale => locale.startsWith('en')) 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)
} }
/** /**
@@ -67,10 +67,10 @@ export function initLocale(lang, locale, changeLang, changeLocale) {
if (isBrowser) { if (isBrowser) {
const queryLang = getQueryVariable('lang') || loadLangFromCookies() const queryLang = getQueryVariable('lang') || loadLangFromCookies()
let currentLang = lang let currentLang = lang
if (queryLang !== lang) { if (queryLang && queryLang !== 'undefined' && queryLang !== lang) {
currentLang = queryLang currentLang = queryLang
} }
console.log('初始化语言', currentLang)
changeLang(currentLang) changeLang(currentLang)
saveLangToCookies(currentLang) saveLangToCookies(currentLang)

View File

@@ -1,5 +1,5 @@
export default { export default {
LOCALE: 'en-US', LOCALE: 'English',
MENU: { MENU: {
WALK_AROUND: 'Walk Around', WALK_AROUND: 'Walk Around',
CATEGORY: 'Category', CATEGORY: 'Category',

View File

@@ -1,5 +1,5 @@
export default { export default {
LOCALE: 'fr-FR', LOCALE: 'français',
NAV: { NAV: {
INDEX: 'Accueil', INDEX: 'Accueil',
RSS: 'RSS', RSS: 'RSS',

View File

@@ -1,5 +1,5 @@
export default { export default {
LOCALE: 'ja-JP', LOCALE: '日本語',
NAV: { NAV: {
INDEX: 'ホーム', INDEX: 'ホーム',
RSS: '購読', RSS: '購読',

View File

@@ -1,5 +1,5 @@
export default { export default {
LOCALE: 'tr-TR', LOCALE: 'Türkçe',
NAV: { NAV: {
INDEX: 'Blog', INDEX: 'Blog',
RSS: 'RSS', RSS: 'RSS',

View File

@@ -1,5 +1,5 @@
export default { export default {
LOCALE: 'zh-CN', LOCALE: '中文(简体)',
MENU: { MENU: {
WALK_AROUND: '随便逛逛', WALK_AROUND: '随便逛逛',
CATEGORY: '博客分类', CATEGORY: '博客分类',

View File

@@ -1,4 +1,5 @@
export default { export default {
LOCALE: '中文(繁体香港)',
NAV: { NAV: {
INDEX: '網誌', INDEX: '網誌',
RSS: '訂閱', RSS: '訂閱',

View File

@@ -1,5 +1,5 @@
export default { export default {
LOCALE: 'zh-TW', LOCALE: '中文(繁体台湾)',
NAV: { NAV: {
INDEX: '部落格', INDEX: '部落格',
RSS: '訂閱', RSS: '訂閱',

View File

@@ -34,7 +34,7 @@ export async function getConfigMapFromConfigPage(allPages) {
} }
const configPageId = configPage.id const configPageId = configPage.id
console.log('[Notion配置]请求配置数据 ', configPage.id) // console.log('[Notion配置]请求配置数据 ', configPage.id)
const pageRecordMap = await getPostBlocks(configPageId, 'config-table') const pageRecordMap = await getPostBlocks(configPageId, 'config-table')
// console.log('配置中心Page', configPageId, pageRecordMap) // console.log('配置中心Page', configPageId, pageRecordMap)
const content = pageRecordMap.block[configPageId].value.content const content = pageRecordMap.block[configPageId].value.content

15
pages/api/cache.js Normal file
View File

@@ -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 })
}
}

View File

@@ -40,12 +40,12 @@ export const MenuList = (props) => {
data-aos-once="true" data-aos-once="true"
data-aos-anchor-placement="top-bottom" data-aos-anchor-placement="top-bottom"
className='hidden md:block leading-8 text-gray-500 dark:text-gray-400 font-sans'> className='hidden md:block leading-8 text-gray-500 dark:text-gray-400 font-sans'>
{links.map(link => link && link.show && <MenuItemDrop key={link?.id} link={link} />)} {links.map((link, index) => link && link.show && <MenuItemDrop key={index} link={link} />)}
</nav> </nav>
{/* 移动端菜单 */} {/* 移动端菜单 */}
<div id='nav-menu-mobile' className='block md:hidden my-auto justify-start bg-white'> <div id='nav-menu-mobile' className='block md:hidden my-auto justify-start bg-white'>
{links?.map(link => link && link.show && <MenuItemCollapse onHeightChange={props.onHeightChange} key={link?.id} link={link} />)} {links?.map((link, index) => link && link.show && <MenuItemCollapse onHeightChange={props.onHeightChange} key={index} link={link} />)}
</div> </div>
</> </>
) )