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'
const CusdisComponent = ({ frontMatter }) => {
const { locale } = useGlobal()
const { lang } = useGlobal()
const router = useRouter()
const { isDarkMode } = useGlobal()
@@ -18,7 +18,7 @@ const CusdisComponent = ({ frontMatter }) => {
}, [isDarkMode])
return <div id="cusdis_thread"
lang={locale.LOCALE.toLowerCase()}
lang={lang.toLowerCase()}
data-host={BLOG.COMMENT_CUSDIS_HOST}
data-app-id={BLOG.COMMENT_CUSDIS_APP_ID}
data-page-id={frontMatter.id}

View File

@@ -1,5 +1,4 @@
import { useGlobal } from '@/lib/global'
import { saveDarkModeToCookies } from '@/themes/theme'
import { Moon, Sun } from './HeroIcons'
import { useImperativeHandle } from 'react'
@@ -8,7 +7,7 @@ import { useImperativeHandle } from 'react'
*/
const DarkModeButton = (props) => {
const { cRef, className } = props
const { isDarkMode, updateDarkMode } = useGlobal()
const { isDarkMode, toggleDarkMode } = useGlobal()
/**
* 对外暴露方法
@@ -16,22 +15,12 @@ const DarkModeButton = (props) => {
useImperativeHandle(cRef, () => {
return {
handleChangeDarkMode: () => {
handleChangeDarkMode()
toggleDarkMode()
}
}
})
// 用户手动设置主题
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`}>
return <div onClick={toggleDarkMode} 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>
}

View File

@@ -5,19 +5,21 @@ import { THEMES } from '@/themes/theme'
import { useRouter } from 'next/router'
import DarkModeButton from './DarkModeButton'
import { getQueryParam } from '@/lib/utils'
import LANGS from '@/lib/lang'
/**
*
* @returns 主题切换
*/
const ThemeSwitch = () => {
const { theme } = useGlobal()
const { theme, lang, changeLang, locale, isDarkMode, toggleDarkMode } = useGlobal()
const router = useRouter()
const currentTheme = getQueryParam(router.asPath, 'theme') || theme
// const currentLang = getQueryParam(router.asPath, 'lang') || lang
const [isLoading, setIsLoading] = useState(false)
// 修改当前路径url中的 theme 参数
// 例如 http://localhost?theme=hexo 跳转到 http://localhost?theme=newTheme
const onSelectChange = (e) => {
const onThemeSelectChange = (e) => {
setIsLoading(true)
const newTheme = e.target.value
const query = router.query
@@ -27,27 +29,52 @@ const ThemeSwitch = () => {
})
}
const onLangSelectChange = (e) => {
const newLang = e.target.value
changeLang(newLang)
}
return (<>
<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 className="p-3 w-full flex items-center text-sm group duration-200 transition-all">
<DarkModeButton className='mr-2' />
<div className='w-0 group-hover:w-20 transition-all duration-200 overflow-hidden'>
<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 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="text-sm flex items-center group-hover:w-32 transition-all duration-200">
<DarkModeButton />
<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 => {
return <option key={t} value={t}>{t}</option>
})}
</select>
</div>
<i className="fa-solid fa-palette pl-2"/>
</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>
{/* 切换主题加载时的全屏遮罩 */}
<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 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
}
}

View File

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

View File

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

View File

@@ -1,11 +1,10 @@
import { generateLocaleDict, initLocale } from './lang'
import { createContext, useContext, useEffect, useState } from 'react'
import { useRouter } from 'next/router'
import { THEMES, initDarkMode } from '@/themes/theme'
import { THEMES, initDarkMode, saveDarkModeToCookies } from '@/themes/theme'
import BLOG from '@/blog.config'
import NProgress from 'nprogress'
import { isBrowser } from './utils'
const GlobalContext = createContext()
@@ -35,17 +34,34 @@ export function GlobalContextProvider(props) {
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)
checkThemeDOM()
initLocale(lang, locale, updateLang, updateLocale)
}, [])
// 加载默认主题
// useEffect(() => {
// const queryTheme = getQueryVariable('theme') || theme
// setTheme(queryTheme)
// }, [router])
useEffect(() => {
checkThemeDOM()
})
// 加载进度条
useEffect(() => {
@@ -62,6 +78,7 @@ export function GlobalContextProvider(props) {
NProgress.done()
setOnLoading(false)
}
router.events.on('routeChangeStart', handleStart)
router.events.on('routeChangeError', handleStop)
router.events.on('routeChangeComplete', handleStop)
@@ -75,10 +92,11 @@ export function GlobalContextProvider(props) {
return (
<GlobalContext.Provider value={{
NOTION_CONFIG,
toggleDarkMode,
onLoading,
setOnLoading,
lang,
updateLang,
changeLang,
locale,
updateLocale,
isDarkMode,
@@ -99,17 +117,13 @@ export function GlobalContextProvider(props) {
* 切换主题时的特殊处理
*/
const checkThemeDOM = () => {
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)
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])
}
}
}

View File

@@ -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,7 +30,7 @@ export default lang
* @returns 不同语言对应字典
*/
export function generateLocaleDict(langString) {
const supportedLocales = Object.keys(lang)
const supportedLocales = Object.keys(LANGS)
let userLocale
// 将语言字符串拆分为语言和地区代码,例如将 "zh-CN" 拆分为 "zh" 和 "CN"
@@ -39,24 +39,24 @@ export function generateLocaleDict(langString) {
// 优先匹配语言和地区都匹配的情况
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)
}
/**
@@ -67,10 +67,10 @@ export function initLocale(lang, locale, changeLang, changeLocale) {
if (isBrowser) {
const queryLang = getQueryVariable('lang') || loadLangFromCookies()
let currentLang = lang
if (queryLang !== lang) {
if (queryLang && queryLang !== 'undefined' && queryLang !== lang) {
currentLang = queryLang
}
console.log('初始化语言', currentLang)
changeLang(currentLang)
saveLangToCookies(currentLang)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -34,7 +34,7 @@ export async function getConfigMapFromConfigPage(allPages) {
}
const configPageId = configPage.id
console.log('[Notion配置]请求配置数据 ', 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

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-anchor-placement="top-bottom"
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>
{/* 移动端菜单 */}
<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>
</>
)