diff --git a/.env.local b/.env.local index fbd52c87..cfc0b2cd 100644 --- a/.env.local +++ b/.env.local @@ -1,2 +1,174 @@ # 环境变量 @see https://www.nextjs.cn/docs/basic-features/environment-variables -NEXT_PUBLIC_VERSION=4.0.13 \ No newline at end of file +NEXT_PUBLIC_VERSION=4.2.0 + + +# 可在此添加环境变量,去掉最左边的(# )注释即可 +# Notion页面ID,必须 +# NOTION_PAGE_ID= + +# 非必须 +# NEXT_PUBLIC_PSEUDO_STATIC= +# NEXT_PUBLIC_REVALIDATE_SECOND= +# NEXT_PUBLIC_THEME= +# NEXT_PUBLIC_THEME_SWITCH= +# NEXT_PUBLIC_LANG= +# NEXT_PUBLIC_APPEARANCE= +# NEXT_PUBLIC_APPEARANCE_DARK_TIME= +# NEXT_PUBLIC_GREETING_WORDS= +# NEXT_PUBLIC_CUSTOM_MENU= +# NEXT_PUBLIC_AUTHOR= +# NEXT_PUBLIC_BIO= +# NEXT_PUBLIC_LINK= +# NEXT_PUBLIC_KEYWORD= +# NEXT_PUBLIC_CONTACT_EMAIL= +# NEXT_PUBLIC_CONTACT_WEIBO= +# NEXT_PUBLIC_CONTACT_TWITTER= +# NEXT_PUBLIC_CONTACT_GITHUB= +# NEXT_PUBLIC_CONTACT_TELEGRAM= +# NEXT_PUBLIC_CONTACT_LINKEDIN= +# NEXT_PUBLIC_CONTACT_INSTAGRAM= +# NEXT_PUBLIC_CONTACT_BILIBILI= +# NEXT_PUBLIC_CONTACT_YOUTUBE= +# NEXT_PUBLIC_FAVICON= +# NEXT_PUBLIC_FONT_STYLE= +# NEXT_PUBLIC_FONT_URL= +# NEXT_PUBLIC_FONT_SANS= +# NEXT_PUBLIC_FONT_SERIF= +# NEXT_PUBLIC_FONT_AWESOME_PATH= +# NEXT_PUBLIC_PRISM_THEME_PREFIX_PATH= +# NEXT_PUBLIC_PRISM_THEME_SWITCH= +# NEXT_PUBLIC_PRISM_THEME_LIGHT_PATH= +# NEXT_PUBLIC_PRISM_THEME_DARK_PATH= +# NEXT_PUBLIC_CODE_MAC_BAR= +# NEXT_PUBLIC_CODE_LINE_NUMBERS= +# NEXT_PUBLIC_CODE_COLLAPSE= +# NEXT_PUBLIC_CODE_COLLAPSE_EXPAND_DEFAULT= +# NEXT_PUBLIC_MERMAID_CDN= +# NEXT_PUBLIC_QR_CODE_CDN= +# NEXT_PUBLIC_BACKGROUND_LIGHT= +# NEXT_PUBLIC_BACKGROUND_DARK= +# NEXT_PUBLIC_SUB_PATH= +# NEXT_PUBLIC_POST_SHARE_BAR= +# NEXT_PUBLIC_POST_SHARE_SERVICES= +# NEXT_PUBLIC_POST_URL_PREFIX= +# NEXT_PUBLIC_POST_LIST_STYLE= +# NEXT_PUBLIC_POST_PREVIEW= +# NEXT_PUBLIC_POST_RECOMMEND_COUNT= +# NEXT_PUBLIC_POSTS_PER_PAGE= +# NEXT_PUBLIC_POST_SORT_BY= +# NEXT_PUBLIC_ALGOLIA_APP_ID= +# ALGOLIA_ADMIN_APP_KEY= +# NEXT_PUBLIC_ALGOLIA_SEARCH_ONLY_APP_KEY= +# NEXT_PUBLIC_ALGOLIA_INDEX= +# NEXT_PUBLIC_PREVIEW_CATEGORY_COUNT= +# NEXT_PUBLIC_PREVIEW_TAG_COUNT= +# NEXT_PUBLIC_POST_DISABLE_GALLERY_CLICK= +# NEXT_PUBLIC_FIREWORKS= +# NEXT_PUBLIC_FIREWORKS_COLOR= +# NEXT_PUBLIC_SAKURA= +# NEXT_PUBLIC_NEST= +# NEXT_PUBLIC_FLUTTERINGRIBBON= +# NEXT_PUBLIC_RIBBON= +# NEXT_PUBLIC_STARRY_SKY= +# NEXT_PUBLIC_CHATBASE_ID= +# NEXT_PUBLIC_WEB_WHIZ_ENABLED= +# NEXT_PUBLIC_WEB_WHIZ_BASE_URL= +# NEXT_PUBLIC_WEB_WHIZ_CHAT_BOT_ID= +# NEXT_PUBLIC_WIDGET_PET= +# NEXT_PUBLIC_WIDGET_PET_LINK= +# NEXT_PUBLIC_WIDGET_PET_SWITCH_THEME= +# NEXT_PUBLIC_MUSIC_PLAYER= +# NEXT_PUBLIC_MUSIC_PLAYER_VISIBLE= +# NEXT_PUBLIC_MUSIC_PLAYER_AUTO_PLAY= +# NEXT_PUBLIC_MUSIC_PLAYER_LRC_TYPE= +# NEXT_PUBLIC_MUSIC_PLAYER_CDN_URL= +# NEXT_PUBLIC_MUSIC_PLAYER_ORDER= +# NEXT_PUBLIC_MUSIC_PLAYER_AUDIO_LIST= +# NEXT_PUBLIC_MUSIC_PLAYER_METING= +# NEXT_PUBLIC_MUSIC_PLAYER_METING_SERVER= +# NEXT_PUBLIC_MUSIC_PLAYER_METING_ID= +# NEXT_PUBLIC_MUSIC_PLAYER_METING_LRC_TYPE= +# NEXT_PUBLIC_COMMENT_ARTALK_SERVER= +# NEXT_PUBLIC_COMMENT_ARTALK_JS= +# NEXT_PUBLIC_COMMENT_ARTALK_CSS= +# NEXT_PUBLIC_COMMENT_ENV_ID= +# NEXT_PUBLIC_COMMENT_TWIKOO_COUNT_ENABLE= +# NEXT_PUBLIC_COMMENT_TWIKOO_CDN_URL= +# NEXT_PUBLIC_COMMENT_UTTERRANCES_REPO= +# NEXT_PUBLIC_COMMENT_GISCUS_REPO= +# NEXT_PUBLIC_COMMENT_GISCUS_REPO_ID= +# NEXT_PUBLIC_COMMENT_GISCUS_CATEGORY_ID= +# NEXT_PUBLIC_COMMENT_GISCUS_MAPPING= +# NEXT_PUBLIC_COMMENT_GISCUS_REACTIONS_ENABLED= +# NEXT_PUBLIC_COMMENT_GISCUS_EMIT_METADATA= +# NEXT_PUBLIC_COMMENT_GISCUS_INPUT_POSITION= +# NEXT_PUBLIC_COMMENT_GISCUS_LANG= +# NEXT_PUBLIC_COMMENT_GISCUS_LOADING= +# NEXT_PUBLIC_COMMENT_GISCUS_CROSSORIGIN= +# NEXT_PUBLIC_COMMENT_CUSDIS_APP_ID= +# NEXT_PUBLIC_COMMENT_CUSDIS_HOST= +# NEXT_PUBLIC_COMMENT_CUSDIS_SCRIPT_SRC= +# NEXT_PUBLIC_COMMENT_GITALK_REPO= +# NEXT_PUBLIC_COMMENT_GITALK_OWNER= +# NEXT_PUBLIC_COMMENT_GITALK_ADMIN= +# NEXT_PUBLIC_COMMENT_GITALK_CLIENT_ID= +# NEXT_PUBLIC_COMMENT_GITALK_CLIENT_SECRET= +# NEXT_PUBLIC_COMMENT_GITALK_JS_CDN_URL= +# NEXT_PUBLIC_COMMENT_GITALK_CSS_CDN_URL= +# NEXT_PUBLIC_COMMENT_GITTER_ROOM= +# NEXT_PUBLIC_COMMENT_DAO_VOICE_ID= +# NEXT_PUBLIC_COMMENT_TIDIO_ID= +# NEXT_PUBLIC_VALINE_CDN= +# NEXT_PUBLIC_VALINE_ID= +# NEXT_PUBLIC_VALINE_KEY= +# NEXT_PUBLIC_VALINE_SERVER_URLS= +# NEXT_PUBLIC_VALINE_PLACEHOLDER= +# NEXT_PUBLIC_WALINE_SERVER_URL= +# NEXT_PUBLIC_WALINE_RECENT= +# NEXT_PUBLIC_WEBMENTION_ENABLE= +# NEXT_PUBLIC_WEBMENTION_AUTH= +# NEXT_PUBLIC_WEBMENTION_HOSTNAME= +# NEXT_PUBLIC_TWITTER_USERNAME= +# NEXT_PUBLIC_WEBMENTION_TOKEN= +# NEXT_PUBLIC_ANALYTICS_VERCEL= +# NEXT_PUBLIC_ANALYTICS_BUSUANZI_ENABLE= +# NEXT_PUBLIC_ANALYTICS_BAIDU_ID= +# NEXT_PUBLIC_ANALYTICS_CNZZ_ID= +# NEXT_PUBLIC_ANALYTICS_GOOGLE_ID= +# NEXT_PUBLIC_ANALYTICS_ACKEE_TRACKER= +# NEXT_PUBLIC_ANALYTICS_ACKEE_DATA_SERVER= +# NEXT_PUBLIC_ANALYTICS_ACKEE_DOMAIN_ID= +# NEXT_PUBLIC_SEO_GOOGLE_SITE_VERIFICATION= +# NEXT_PUBLIC_SEO_BAIDU_SITE_VERIFICATION= +# NEXT_PUBLIC_ADSENSE_GOOGLE_ID= +# NEXT_PUBLIC_ADSENSE_GOOGLE_TEST= +# NEXT_PUBLIC_ADSENSE_GOOGLE_SLOT_IN_ARTICLE= +# NEXT_PUBLIC_ADSENSE_GOOGLE_SLOT_FLOW= +# NEXT_PUBLIC_ADSENSE_GOOGLE_SLOT_NATIVE= +# NEXT_PUBLIC_ADSENSE_GOOGLE_SLOT_AUTO= +# NEXT_PUBLIC_WWAD_ID= +# NEXT_PUBLIC_WWADS_AD_BLOCK_DETECT= +# NEXT_PUBLIC_NOTION_PROPERTY_PASSWORD= +# NEXT_PUBLIC_NOTION_PROPERTY_TYPE= +# NEXT_PUBLIC_NOTION_PROPERTY_TYPE_POST= +# NEXT_PUBLIC_NOTION_PROPERTY_TYPE_PAGE= +# NEXT_PUBLIC_NOTION_PROPERTY_TYPE_NOTICE= +# NEXT_PUBLIC_NOTION_PROPERTY_TYPE_MENU= +# NEXT_PUBLIC_NOTION_PROPERTY_TYPE_SUB_MENU= +# NEXT_PUBLIC_NOTION_PROPERTY_TITLE= +# NEXT_PUBLIC_NOTION_PROPERTY_STATUS= +# NEXT_PUBLIC_NOTION_PROPERTY_STATUS_PUBLISH= +# NEXT_PUBLIC_NOTION_PROPERTY_STATUS_INVISIBLE= +# NEXT_PUBLIC_NOTION_PROPERTY_SUMMARY= +# NEXT_PUBLIC_NOTION_PROPERTY_SLUG= +# NEXT_PUBLIC_NOTION_PROPERTY_CATEGORY= +# NEXT_PUBLIC_NOTION_PROPERTY_DATE= +# NEXT_PUBLIC_NOTION_PROPERTY_TAGS= +# NEXT_PUBLIC_NOTION_PROPERTY_ICON= +# NEXT_PUBLIC_ENABLE_RSS= +# MAILCHIMP_LIST_ID= +# MAILCHIMP_API_KEY= +# NEXT_PUBLIC_DEBUG= +# ENABLE_CACHE= +# VERCEL_ENV= +# NEXT_PUBLIC_VERSION= diff --git a/.eslintrc.js b/.eslintrc.js index bab5ea27..9836da77 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -26,6 +26,7 @@ module.exports = { } }, rules: { + semi: 0, 'react/no-unknown-property': 'off', // ) +} + +export { GlobalStyle } diff --git a/components/GoogleAdsense.js b/components/GoogleAdsense.js index 2163f791..61606150 100644 --- a/components/GoogleAdsense.js +++ b/components/GoogleAdsense.js @@ -1,4 +1,4 @@ -import BLOG from '@/blog.config' +import { siteConfig } from '@/lib/config' import { loadExternalResource } from '@/lib/utils' import { useRouter } from 'next/router' import { useEffect } from 'react' @@ -9,7 +9,7 @@ import { useEffect } from 'react' */ export default function GoogleAdsense() { const initGoogleAdsense = () => { - loadExternalResource(`https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=${BLOG.ADSENSE_GOOGLE_ID}`, 'js').then(url => { + loadExternalResource(`https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=${siteConfig('ADSENSE_GOOGLE_ID')}`, 'js').then(url => { setTimeout(() => { const ads = document.getElementsByClassName('adsbygoogle') const adsbygoogle = window.adsbygoogle @@ -44,7 +44,7 @@ export default function GoogleAdsense() { * 添加 可以在本地调试 */ const AdSlot = ({ type = 'show' }) => { - if (!BLOG.ADSENSE_GOOGLE_ID) { + if (!siteConfig('ADSENSE_GOOGLE_ID')) { return null } // 文章内嵌广告 @@ -53,9 +53,9 @@ const AdSlot = ({ type = 'show' }) => { style={{ display: 'block', textAlign: 'center' }} data-ad-layout="in-article" data-ad-format="fluid" - data-adtest={BLOG.ADSENSE_GOOGLE_TEST ? 'on' : 'off'} - data-ad-client={BLOG.ADSENSE_GOOGLE_ID} - data-ad-slot={BLOG.ADSENSE_GOOGLE_SLOT_IN_ARTICLE}> + data-adtest={siteConfig('ADSENSE_GOOGLE_TEST') ? 'on' : 'off'} + data-ad-client={siteConfig('ADSENSE_GOOGLE_ID')} + data-ad-slot={siteConfig('ADSENSE_GOOGLE_SLOT_IN_ARTICLE')}> } // 信息流广告 @@ -64,9 +64,9 @@ const AdSlot = ({ type = 'show' }) => { data-ad-format="fluid" data-ad-layout-key="-5j+cz+30-f7+bf" style={{ display: 'block' }} - data-adtest={BLOG.ADSENSE_GOOGLE_TEST ? 'on' : 'off'} - data-ad-client={BLOG.ADSENSE_GOOGLE_ID} - data-ad-slot={BLOG.ADSENSE_GOOGLE_SLOT_FLOW}> + data-adtest={siteConfig('ADSENSE_GOOGLE_TEST') ? 'on' : 'off'} + data-ad-client={siteConfig('ADSENSE_GOOGLE_ID')} + data-ad-slot={siteConfig('ADSENSE_GOOGLE_SLOT_FLOW')}> } // 原生广告 @@ -74,17 +74,17 @@ const AdSlot = ({ type = 'show' }) => { return + data-adtest={siteConfig('ADSENSE_GOOGLE_TEST') ? 'on' : 'off'} + data-ad-client={siteConfig('ADSENSE_GOOGLE_ID')} + data-ad-slot={siteConfig('ADSENSE_GOOGLE_SLOT_NATIVE')}> } // 展示广告 return } diff --git a/components/KatexReact.js b/components/KatexReact.js index 5a585e13..9adc8870 100644 --- a/components/KatexReact.js +++ b/components/KatexReact.js @@ -1,6 +1,11 @@ import KaTeX from 'katex' -import React from 'react' +import { memo, useEffect, useState } from 'react' +/** + * 数学公式 + * @param {*} param0 + * @returns + */ const TeX = ({ children, math, @@ -13,9 +18,9 @@ const TeX = ({ }) => { const Component = asComponent || (block ? 'div' : 'span') const content = (children ?? math) - const [state, setState] = React.useState({ innerHtml: '' }) + const [state, setState] = useState({ innerHtml: '' }) - React.useEffect(() => { + useEffect(() => { try { const innerHtml = KaTeX.renderToString(content, { displayMode: true, @@ -50,4 +55,4 @@ const TeX = ({ ) } -export default React.memo(TeX) +export default memo(TeX) diff --git a/components/LA51.js b/components/LA51.js new file mode 100644 index 00000000..df506666 --- /dev/null +++ b/components/LA51.js @@ -0,0 +1,18 @@ +import { siteConfig } from '@/lib/config' +import { useEffect } from 'react' + +/** + * 51LA统计 + */ +export default function LA51() { + const ANALYTICS_51LA_ID = siteConfig('ANALYTICS_51LA_ID') + const ANALYTICS_51LA_CK = siteConfig('ANALYTICS_51LA_CK') + useEffect(() => { + const LA = window.LA + if (LA) { + LA.init({ id: `${ANALYTICS_51LA_ID}`, ck: `${ANALYTICS_51LA_CK}`, hashMode: true, autoTrack: true }) + } + }, []) + + return <> +} diff --git a/components/LazyImage.js b/components/LazyImage.js index 4556bd0d..3e44eb4c 100644 --- a/components/LazyImage.js +++ b/components/LazyImage.js @@ -1,4 +1,4 @@ -import BLOG from '@/blog.config' +import { siteConfig } from '@/lib/config' import Head from 'next/head' import React, { useEffect, useRef, useState } from 'react' @@ -12,7 +12,7 @@ export default function LazyImage({ id, src, alt, - placeholderSrc = BLOG.IMG_LAZY_LOAD_PLACEHOLDER, + placeholderSrc, className, width, height, @@ -22,6 +22,9 @@ export default function LazyImage({ }) { const imageRef = useRef(null) const [imageLoaded, setImageLoaded] = useState(false) + if (!placeholderSrc) { + placeholderSrc = siteConfig('IMG_LAZY_LOAD_PLACEHOLDER') + } const handleImageLoad = () => { setImageLoaded(true) diff --git a/components/Live2D.js b/components/Live2D.js index 6a2fc624..4dbbe31a 100644 --- a/components/Live2D.js +++ b/components/Live2D.js @@ -1,12 +1,17 @@ /* eslint-disable no-undef */ -import BLOG from '@/blog.config' +import { siteConfig } from '@/lib/config' import { useGlobal } from '@/lib/global' import { loadExternalResource } from '@/lib/utils' import { useEffect } from 'react' +/** + * 网页动画 + * @returns + */ export default function Live2D() { const { theme, switchTheme } = useGlobal() - const showPet = JSON.parse(BLOG.WIDGET_PET) + const showPet = JSON.parse(siteConfig('WIDGET_PET')) + const petLink = siteConfig('WIDGET_PET_LINK') useEffect(() => { if (showPet) { @@ -16,7 +21,7 @@ export default function Live2D() { if (typeof window?.loadlive2d !== 'undefined') { // https://github.com/xiazeyu/live2d-widget-models try { - loadlive2d('live2d', BLOG.WIDGET_PET_LINK) + loadlive2d('live2d', petLink) } catch (error) { console.error('读取PET模型', error) } @@ -26,7 +31,7 @@ export default function Live2D() { }, [theme]) function handleClick() { - if (JSON.parse(BLOG.WIDGET_PET_SWITCH_THEME)) { + if (JSON.parse(siteConfig('WIDGET_PET_SWITCH_THEME'))) { switchTheme() } } diff --git a/components/LoadingProgress.js b/components/LoadingProgress.js new file mode 100644 index 00000000..d7f7b5f7 --- /dev/null +++ b/components/LoadingProgress.js @@ -0,0 +1,29 @@ +import { useRouter } from 'next/router' +import NProgress from 'nprogress' +import { useEffect } from 'react' + +/** + * 出现页面加载进度条 + */ +export default function LoadingProgress() { + const router = useRouter() + // 加载进度条 + useEffect(() => { + const handleStart = (url) => { + NProgress.start() + } + + const handleStop = () => { + NProgress.done() + } + + router.events.on('routeChangeStart', handleStart) + router.events.on('routeChangeError', handleStop) + router.events.on('routeChangeComplete', handleStop) + return () => { + router.events.off('routeChangeStart', handleStart) + router.events.off('routeChangeComplete', handleStop) + router.events.off('routeChangeError', handleStop) + } + }, [router]) +} diff --git a/components/NotionPage.js b/components/NotionPage.js index 16e8716e..fb68691a 100644 --- a/components/NotionPage.js +++ b/components/NotionPage.js @@ -1,14 +1,18 @@ -import { NotionRenderer } from 'react-notion-x' import dynamic from 'next/dynamic' import mediumZoom from '@fisch0920/medium-zoom' -import React, { useEffect, useRef } from 'react' -// import { Code } from 'react-notion-x/build/third-party/code' -import TweetEmbed from 'react-tweet-embed' - -import BLOG from '@/blog.config' +import { useEffect, useRef } from 'react' import 'katex/dist/katex.min.css' import { mapImgUrl } from '@/lib/notion/mapImage' import { isBrowser } from '@/lib/utils' +import { siteConfig } from '@/lib/config' +import { NotionRenderer } from 'react-notion-x' + +// Notion渲染 +// const NotionRenderer = dynamic(() => import('react-notion-x').then(async (m) => { +// return m.NotionRenderer +// }), { +// ssr: false +// }) const Code = dynamic(() => import('react-notion-x/build/third-party/code').then(async (m) => { @@ -16,6 +20,7 @@ const Code = dynamic(() => }), { ssr: false } ) +// 公式 const Equation = dynamic(() => import('@/components/Equation').then(async (m) => { // 化学方程式 @@ -37,6 +42,13 @@ const PrismMac = dynamic(() => import('@/components/PrismMac'), { ssr: false }) +/** + * tweet嵌入 + */ +const TweetEmbed = dynamic(() => import('react-tweet-embed'), { + ssr: false +}) + const Collection = dynamic(() => import('react-notion-x/build/third-party/collection').then((m) => m.Collection), { ssr: true } ) @@ -63,7 +75,7 @@ const NotionPage = ({ post, className }) => { useEffect(() => { // 将相册gallery下的图片加入放大功能 - if (JSON.parse(BLOG.POST_DISABLE_GALLERY_CLICK)) { + if (siteConfig('POST_DISABLE_GALLERY_CLICK')) { setTimeout(() => { if (isBrowser) { const imgList = document?.querySelectorAll('.notion-collection-card-cover img') diff --git a/components/Player.js b/components/Player.js index 82295e69..8c252615 100644 --- a/components/Player.js +++ b/components/Player.js @@ -1,27 +1,54 @@ -import BLOG from '@/blog.config' +import { siteConfig } from '@/lib/config' +import { loadExternalResource } from '@/lib/utils' import { useEffect, useRef, useState } from 'react' +/** + * 音乐播放器 + * @returns + */ const Player = () => { const [player, setPlayer] = useState() const ref = useRef(null) + const lrcType = JSON.parse(siteConfig('MUSIC_PLAYER_LRC_TYPE')) + const playerVisible = JSON.parse(siteConfig('MUSIC_PLAYER_VISIBLE')) + const autoPlay = JSON.parse(siteConfig('MUSIC_PLAYER_AUTO_PLAY')) + const meting = JSON.parse(siteConfig('MUSIC_PLAYER_METING')) + const order = siteConfig('MUSIC_PLAYER_ORDER') + const audio = siteConfig('MUSIC_PLAYER_AUDIO_LIST') - const lrcType = JSON.parse(BLOG.MUSIC_PLAYER_LRC_TYPE) - const playerVisible = JSON.parse(BLOG.MUSIC_PLAYER_VISIBLE) - const autoPlay = JSON.parse(BLOG.MUSIC_PLAYER_AUTO_PLAY) + const musicPlayerEnable = siteConfig('MUSIC_PLAYER') + const musicPlayerCDN = siteConfig('MUSIC_PLAYER_CDN_URL') + const musicMetingEnable = siteConfig('MUSIC_PLAYER_METING') + const musicMetingCDNUrl = siteConfig('MUSIC_PLAYER_METING_CDN_URL', 'https://cdnjs.cloudflare.com/ajax/libs/meting/2.0.1/Meting.min.js') - const meting = JSON.parse(BLOG.MUSIC_PLAYER_METING) + const initMusicPlayer = async () => { + if (!musicPlayerEnable) { + return + } + try { + await loadExternalResource(musicPlayerCDN, 'js') + } catch (error) { + console.error('音乐组件异常', error) + } + + if (musicMetingEnable) { + await loadExternalResource(musicMetingCDNUrl, 'js') + } - useEffect(() => { if (!meting && window.APlayer) { setPlayer(new window.APlayer({ container: ref.current, fixed: true, lrcType: lrcType, autoplay: autoPlay, - order: BLOG.MUSIC_PLAYER_ORDER, - audio: BLOG.MUSIC_PLAYER_AUDIO_LIST + order: order, + audio: audio })) } + } + + useEffect(() => { + initMusicPlayer() return () => { setPlayer(undefined) } @@ -39,11 +66,11 @@ const Player = () => { fixed="true" type="playlist" preload="auto" - lrc-type={BLOG.MUSIC_PLAYER_METING_LRC_TYPE} + lrc-type={siteConfig('MUSIC_PLAYER_METING_LRC_TYPE')} autoplay={autoPlay} - order={BLOG.MUSIC_PLAYER_ORDER} - server={BLOG.MUSIC_PLAYER_METING_SERVER} - id={BLOG.MUSIC_PLAYER_METING_ID} + order={siteConfig('MUSIC_PLAYER_ORDER')} + server={siteConfig('MUSIC_PLAYER_METING_SERVER')} + id={siteConfig('MUSIC_PLAYER_METING_ID')} /> :
} diff --git a/components/PrismMac.js b/components/PrismMac.js index 0a69e55e..c92d594d 100644 --- a/components/PrismMac.js +++ b/components/PrismMac.js @@ -10,10 +10,10 @@ import 'prismjs/plugins/line-numbers/prism-line-numbers' import 'prismjs/plugins/line-numbers/prism-line-numbers.css' // mermaid图 -import BLOG from '@/blog.config' import { loadExternalResource } from '@/lib/utils' import { useRouter } from 'next/navigation' import { useGlobal } from '@/lib/global' +import { siteConfig } from '@/lib/config' /** * 代码美化相关 @@ -23,22 +23,36 @@ import { useGlobal } from '@/lib/global' const PrismMac = () => { const router = useRouter() const { isDarkMode } = useGlobal() + const codeMacBar = siteConfig('CODE_MAC_BAR') + const prismjsAutoLoader = siteConfig('PRISM_JS_AUTO_LOADER') + const prismjsPath = siteConfig('PRISM_JS_PATH') + + const prismThemeSwitch = siteConfig('PRISM_THEME_SWITCH') + const prismThemeDarkPath = siteConfig('PRISM_THEME_DARK_PATH') + const prismThemeLightPath = siteConfig('PRISM_THEME_LIGHT_PATH') + const prismThemePrefixPath = siteConfig('PRISM_THEME_PREFIX_PATH') + + const mermaidCDN = siteConfig('MERMAID_CDN') + const codeLineNumbers = siteConfig('CODE_LINE_NUMBERS') + + const codeCollapse = siteConfig('CODE_COLLAPSE') + const codeCollapseExpandDefault = siteConfig('CODE_COLLAPSE_EXPAND_DEFAULT') useEffect(() => { - if (JSON.parse(BLOG.CODE_MAC_BAR)) { + if (codeMacBar) { loadExternalResource('/css/prism-mac-style.css', 'css') } // 加载prism样式 - loadPrismThemeCSS(isDarkMode) + loadPrismThemeCSS(isDarkMode, prismThemeSwitch, prismThemeDarkPath, prismThemeLightPath, prismThemePrefixPath) // 折叠代码 - loadExternalResource(BLOG.PRISM_JS_AUTO_LOADER, 'js').then((url) => { + loadExternalResource(prismjsAutoLoader, 'js').then((url) => { if (window?.Prism?.plugins?.autoloader) { - window.Prism.plugins.autoloader.languages_path = BLOG.PRISM_JS_PATH + window.Prism.plugins.autoloader.languages_path = prismjsPath } - renderPrismMac() - renderMermaid() - renderCollapseCode() + renderPrismMac(codeLineNumbers) + renderMermaid(mermaidCDN) + renderCollapseCode(codeCollapse, codeCollapseExpandDefault) }) }, [router, isDarkMode]) @@ -46,18 +60,18 @@ const PrismMac = () => { } /** - * 加载样式 + * 加载Prism主题样式 */ -const loadPrismThemeCSS = (isDarkMode) => { +const loadPrismThemeCSS = (isDarkMode, prismThemeSwitch, prismThemeDarkPath, prismThemeLightPath, prismThemePrefixPath) => { let PRISM_THEME let PRISM_PREVIOUS - if (JSON.parse(BLOG.PRISM_THEME_SWITCH)) { + if (prismThemeSwitch) { if (isDarkMode) { - PRISM_THEME = BLOG.PRISM_THEME_DARK_PATH - PRISM_PREVIOUS = BLOG.PRISM_THEME_LIGHT_PATH + PRISM_THEME = prismThemeDarkPath + PRISM_PREVIOUS = prismThemeLightPath } else { - PRISM_THEME = BLOG.PRISM_THEME_LIGHT_PATH - PRISM_PREVIOUS = BLOG.PRISM_THEME_DARK_PATH + PRISM_THEME = prismThemeLightPath + PRISM_PREVIOUS = prismThemeDarkPath } const previousTheme = document.querySelector(`link[href="${PRISM_PREVIOUS}"]`) if (previousTheme) { @@ -65,15 +79,15 @@ const loadPrismThemeCSS = (isDarkMode) => { } loadExternalResource(PRISM_THEME, 'css') } else { - loadExternalResource(BLOG.PRISM_THEME_PREFIX_PATH, 'css') + loadExternalResource(prismThemePrefixPath, 'css') } } /* * 将代码块转为可折叠对象 */ -const renderCollapseCode = () => { - if (!JSON.parse(BLOG.CODE_COLLAPSE)) { +const renderCollapseCode = (codeCollapse, codeCollapseExpandDefault) => { + if (!codeCollapse) { return } const codeBlocks = document.querySelectorAll('.code-toolbar') @@ -105,20 +119,27 @@ const renderCollapseCode = () => { codeBlock.parentNode.insertBefore(collapseWrapper, codeBlock) panel.appendChild(codeBlock) - header.addEventListener('click', () => { + function collapseCode() { panel.classList.toggle('invisible') panel.classList.toggle('h-0') panel.classList.toggle('h-auto') header.querySelector('svg').classList.toggle('rotate-180') panelWrapper.classList.toggle('border-gray-300') - }) + } + + // 点击后折叠展开代码 + header.addEventListener('click', collapseCode) + // 是否自动展开 + if (codeCollapseExpandDefault) { + header.click() + } } } /** * 将mermaid语言 渲染成图片 */ -const renderMermaid = async() => { +const renderMermaid = async(mermaidCDN) => { const observer = new MutationObserver(async mutationsList => { for (const m of mutationsList) { if (m.target.className === 'notion-code language-mermaid') { @@ -139,7 +160,7 @@ const renderMermaid = async() => { } } if (needLoad) { - loadExternalResource(BLOG.MERMAID_CDN, 'js').then(url => { + loadExternalResource(mermaidCDN, 'js').then(url => { setTimeout(() => { const mermaid = window.mermaid mermaid?.contentLoaded() @@ -155,11 +176,11 @@ const renderMermaid = async() => { } } -function renderPrismMac() { +function renderPrismMac(codeLineNumbers) { const container = document?.getElementById('notion-article') // Add line numbers - if (JSON.parse(BLOG.CODE_LINE_NUMBERS)) { + if (codeLineNumbers) { const codeBlocks = container?.getElementsByTagName('pre') if (codeBlocks) { Array.from(codeBlocks).forEach(item => { @@ -193,7 +214,7 @@ function renderPrismMac() { } // 折叠代码行号bug - if (JSON.parse(BLOG.CODE_LINE_NUMBERS)) { + if (codeLineNumbers) { fixCodeLineStyle() } } diff --git a/components/QrCode.js b/components/QrCode.js index 081a695f..09a203a7 100644 --- a/components/QrCode.js +++ b/components/QrCode.js @@ -1,4 +1,4 @@ -import BLOG from '@/blog.config' +import { siteConfig } from '@/lib/config' import { loadExternalResource } from '@/lib/utils' import { useEffect } from 'react' @@ -6,22 +6,26 @@ import { useEffect } from 'react' * 二维码生成 */ export default function QrCode({ value }) { + const qrCodeCDN = siteConfig('QR_CODE_CDN') + useEffect(() => { let qrcode if (!value) { return } - loadExternalResource(BLOG.QR_CODE_CDN, 'js').then(url => { - const QRCode = window.QRCode - qrcode = new QRCode(document.getElementById('qrcode'), { - text: value, - width: 256, - height: 256, - colorDark: '#000000', - colorLight: '#ffffff', - correctLevel: QRCode.CorrectLevel.H - }) - // console.log('二维码', qrcode, value) + loadExternalResource(qrCodeCDN, 'js').then(url => { + const QRCode = window?.QRCode + if (typeof QRCode !== 'undefined') { + qrcode = new QRCode(document.getElementById('qrcode'), { + text: value, + width: 256, + height: 256, + colorDark: '#000000', + colorLight: '#ffffff', + correctLevel: QRCode.CorrectLevel.H + }) + // console.log('二维码', qrcode, value) + } }) return () => { if (qrcode) { diff --git a/components/ShareBar.js b/components/ShareBar.js index caf03666..334a5f12 100644 --- a/components/ShareBar.js +++ b/components/ShareBar.js @@ -1,28 +1,21 @@ -import BLOG from '@/blog.config' -import { useRouter } from 'next/router' -import React from 'react' -import ShareButtons from './ShareButtons' +import { siteConfig } from '@/lib/config' +import dynamic from 'next/dynamic' +const ShareButtons = dynamic(() => import('@/components/ShareButtons'), { ssr: false }) + +/** + * 分享栏 + * @param {} param0 + * @returns + */ const ShareBar = ({ post }) => { - const router = useRouter() - - if (!JSON.parse(BLOG.POST_SHARE_BAR_ENABLE) || !post || post?.type !== 'Post') { + if (!JSON.parse(siteConfig('POST_SHARE_BAR_ENABLE')) || !post || post?.type !== 'Post') { return <> } - const shareUrl = BLOG.LINK + router.asPath - return
- +
} diff --git a/components/ShareButtons.js b/components/ShareButtons.js index efd3ab87..e3714b79 100644 --- a/components/ShareButtons.js +++ b/components/ShareButtons.js @@ -1,7 +1,8 @@ -import BLOG from '@/blog.config' +import { siteConfig } from '@/lib/config' import { useGlobal } from '@/lib/global' import copy from 'copy-to-clipboard' import dynamic from 'next/dynamic' +import { useRouter } from 'next/router' import { useState } from 'react' import { @@ -56,9 +57,15 @@ const QrCode = dynamic(() => import('@/components/QrCode'), { ssr: false }) * @param {*} param0 * @returns */ -const ShareButtons = ({ shareUrl, title, body, image }) => { - const services = BLOG.POSTS_SHARE_SERVICES.split(',') - const titleWithSiteInfo = title + ' | ' + BLOG.TITLE +const ShareButtons = ({ post }) => { + const router = useRouter() + const shareUrl = siteConfig('LINK') + router.asPath + const title = post.title || siteConfig('TITLE') + const image = post.pageCover + const body = post?.title + ' | ' + title + ' ' + shareUrl + ' ' + post?.summary + + const services = siteConfig('POSTS_SHARE_SERVICES').split(',') + const titleWithSiteInfo = title + ' | ' + siteConfig('TITLE') const { locale } = useGlobal() const [qrCodeShow, setQrCodeShow] = useState(false) @@ -93,7 +100,7 @@ const ShareButtons = ({ shareUrl, title, body, image }) => { @@ -336,7 +343,7 @@ const ShareButtons = ({ shareUrl, title, body, image }) => { } if (singleService === 'qq') { return @@ -349,7 +356,7 @@ const ShareButtons = ({ shareUrl, title, body, image }) => {
- + { qrCodeShow && }
{locale.COMMON.SCAN_QR_CODE} diff --git a/components/StarrySky.js b/components/StarrySky.js index 5479df93..e0fd3ac4 100644 --- a/components/StarrySky.js +++ b/components/StarrySky.js @@ -1,23 +1,24 @@ /* eslint-disable */ -import React from 'react' +import { useEffect } from 'react' const StarrySky = () => { - React.useEffect(() => { - dark() + useEffect(() => { + renderStarrySky() }, []) return (
- +
) } export default StarrySky + /** * 创建星空雨 * @param config */ -function dark() { +function renderStarrySky() { window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || @@ -124,7 +125,7 @@ function dark() { u() })(), (function t() { - document.getElementsByTagName('html')[0].className == 'dark' && u(), + document.getElementsByTagName('html')[0].className.indexOf('dark')>=0 && u(), window.requestAnimationFrame(t) })() } diff --git a/components/Tabs.js b/components/Tabs.js index b354e533..ef951228 100644 --- a/components/Tabs.js +++ b/components/Tabs.js @@ -1,4 +1,5 @@ -import React, { useState } from 'react' +import { useState } from 'react'; +import { siteConfig } from '@/lib/config' /** * Tabs切换标签 @@ -6,59 +7,39 @@ import React, { useState } from 'react' * @returns */ const Tabs = ({ className, children }) => { - const [currentTab, setCurrentTab] = useState(0) + const [currentTab, setCurrentTab] = useState(0); - if (!children) { - return <> + const validChildren = children.filter(c => c); + + if (validChildren.length === 0) { + return <>; } - children = children.filter(c => c !== '') - - let count = 0 - children.forEach(e => { - if (e) { - count++ - } - }) - - if (count === 0) { - return <> - } - - if (count === 1) { - return
- {children} -
- } - - function tabClickHandle(i) { - setCurrentTab(i) - } - - return
-