mirror of
https://github.com/d0zingcat/NotionNext.git
synced 2026-05-16 07:26:47 +00:00
Merge branch 'main' into pr/sebastian0619/1657
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
# 环境变量 @see https://www.nextjs.cn/docs/basic-features/environment-variables
|
||||
NEXT_PUBLIC_VERSION=4.1.1
|
||||
NEXT_PUBLIC_VERSION=4.1.4
|
||||
|
||||
|
||||
# 可在此添加环境变量,去掉最左边的(# )注释即可
|
||||
|
||||
@@ -201,6 +201,9 @@
|
||||
- **图标**: [Fontawesome](https://fontawesome.com/v6/icons/)
|
||||
|
||||
|
||||
## 🔗 友情链接
|
||||
- [Elog](https://github.com/LetTTGACO/elog) Markdown 批量导出工具、开放式跨平台博客解决方案,随意组合写作平台(语雀/Notion/FlowUs/飞书)和博客平台(Hexo/Vitepress/Halo/Confluence/WordPress等)
|
||||
|
||||
## License
|
||||
|
||||
The MIT License.
|
||||
|
||||
@@ -90,6 +90,7 @@ const BLOG = {
|
||||
// END ************网站字体*****************
|
||||
CAN_COPY: process.env.NEXT_PUBLIC_CAN_COPY || true, // 是否允许复制页面内容 默认允许,如果设置为false、则全栈禁止复制内容。
|
||||
CUSTOM_RIGHT_CLICK_CONTEXT_MENU: process.env.NEXT_PUBLIC_CUSTOM_RIGHT_CLICK_CONTEXT_MENU || true, // 自定义右键菜单,覆盖系统菜单
|
||||
CUSTOM_RIGHT_CLICK_CONTEXT_MENU_THEME_SWITCH: process.env.NEXT_PUBLIC_CUSTOM_RIGHT_CLICK_CONTEXT_MENU_THEME_SWITCH || true,
|
||||
|
||||
// 自定义外部脚本,外部样式
|
||||
CUSTOM_EXTERNAL_JS: [''], // e.g. ['http://xx.com/script.js','http://xx.com/script.js']
|
||||
@@ -326,6 +327,10 @@ const BLOG = {
|
||||
ANALYTICS_CNZZ_ID: process.env.NEXT_PUBLIC_ANALYTICS_CNZZ_ID || '', // 只需要填写站长统计的id, [cnzz_id] -> https://s9.cnzz.com/z_stat.php?id=[cnzz_id]&web_id=[cnzz_id]
|
||||
ANALYTICS_GOOGLE_ID: process.env.NEXT_PUBLIC_ANALYTICS_GOOGLE_ID || '', // 谷歌Analytics的id e.g: G-XXXXXXXXXX
|
||||
|
||||
// 51la 站点统计 https://www.51.la/
|
||||
ANALYTICS_51LA_ID: process.env.NEXT_PUBLIC_ANALYTICS_51LA_ID || '', // id,在51la后台获取 参阅 https://docs.tangly1024.com/article/notion-next-51-la
|
||||
ANALYTICS_51LA_CK: process.env.NEXT_PUBLIC_ANALYTICS_51LA_CK || '', // ck,在51la后台获取
|
||||
|
||||
// Matomo 网站统计
|
||||
MATOMO_HOST_URL: process.env.NEXT_PUBLIC_MATOMO_HOST_URL || '', // Matomo服务器地址,不带斜杠
|
||||
MATOMO_SITE_ID: process.env.NEXT_PUBLIC_MATOMO_SITE_ID || '', // Matomo网站ID
|
||||
@@ -400,12 +405,14 @@ const BLOG = {
|
||||
IMG_LAZY_LOAD_PLACEHOLDER: process.env.NEXT_PUBLIC_IMG_LAZY_LOAD_PLACEHOLDER || 'data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==', // 懒加载占位图片地址,支持base64或url
|
||||
IMG_URL_TYPE: process.env.NEXT_PUBLIC_IMG_TYPE || 'Notion', // 此配置已失效,请勿使用;AMAZON方案不再支持,仅支持Notion方案。 ['Notion','AMAZON'] 站点图片前缀 默认 Notion:(https://notion.so/images/xx) , AMAZON(https://s3.us-west-2.amazonaws.com/xxx)
|
||||
IMG_SHADOW: process.env.NEXT_PUBLIC_IMG_SHADOW || false, // 文章图片是否自动添加阴影
|
||||
IMG_COMPRESS_WIDTH: process.env.NEXT_PUBLIC_IMG_COMPRESS_WIDTH || 800, // Notion图片压缩宽度
|
||||
|
||||
// 开发相关
|
||||
NOTION_ACCESS_TOKEN: process.env.NOTION_ACCESS_TOKEN || '', // Useful if you prefer not to make your database public
|
||||
DEBUG: process.env.NEXT_PUBLIC_DEBUG || false, // 是否显示调试按钮
|
||||
ENABLE_CACHE: process.env.ENABLE_CACHE || process.env.npm_lifecycle_event === 'build', // 缓存在开发调试和打包过程中选择性开启,正式部署开启此功能意义不大。
|
||||
isProd: process.env.VERCEL_ENV === 'production', // distinguish between development and production environment (ref: https://vercel.com/docs/environment-variables#system-environment-variables) isProd: process.env.VERCEL_ENV === 'production' // distinguish between development and production environment (ref: https://vercel.com/docs/environment-variables#system-environment-variables)
|
||||
BUNDLE_ANALYZER: process.env.ANALYZE === 'true' || false, // 是否展示编译依赖内容与大小
|
||||
VERSION: process.env.NEXT_PUBLIC_VERSION // 版本号
|
||||
}
|
||||
|
||||
|
||||
12
components/AOSAnimation.js
Normal file
12
components/AOSAnimation.js
Normal file
@@ -0,0 +1,12 @@
|
||||
import AOS from 'aos'
|
||||
import { isBrowser } from 'react-notion-x'
|
||||
|
||||
/**
|
||||
* 加载滚动动画
|
||||
* https://michalsnik.github.io/aos/
|
||||
*/
|
||||
export default function AOSAnimation() {
|
||||
if (isBrowser) {
|
||||
AOS.init()
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import dynamic from 'next/dynamic'
|
||||
import Tabs from '@/components/Tabs'
|
||||
import { isBrowser } from '@/lib/utils'
|
||||
import { isBrowser, isSearchEngineBot } from '@/lib/utils'
|
||||
import { useRouter } from 'next/router'
|
||||
import Artalk from './Artalk'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
@@ -73,6 +73,10 @@ const Comment = ({ siteInfo, frontMatter, className }) => {
|
||||
const COMMENT_GITALK_CLIENT_ID = siteConfig('COMMENT_GITALK_CLIENT_ID')
|
||||
const COMMENT_WEBMENTION_ENABLE = siteConfig('COMMENT_WEBMENTION_ENABLE')
|
||||
|
||||
if (isSearchEngineBot()) {
|
||||
return null
|
||||
}
|
||||
|
||||
// 当连接中有特殊参数时跳转到评论区
|
||||
if (isBrowser && ('giscus' in router.query || router.query.target === 'comment')) {
|
||||
setTimeout(() => {
|
||||
|
||||
@@ -162,10 +162,12 @@ export default function CustomContextMenu(props) {
|
||||
{isDarkMode ? <i className="fa-regular fa-sun mr-2" /> : <i className="fa-regular fa-moon mr-2" />}
|
||||
<div className='whitespace-nowrap'> {isDarkMode ? locale.MENU.LIGHT_MODE : locale.MENU.DARK_MODE}</div>
|
||||
</div>
|
||||
{siteConfig('CUSTOM_RIGHT_CLICK_CONTEXT_MENU_THEME_SWITCH') && (
|
||||
<div onClick={handeChangeTheme} title={locale.MENU.THEME_SWITCH} className='w-full px-2 h-10 flex justify-start items-center flex-nowrap cursor-pointer hover:bg-blue-600 hover:text-white rounded-lg duration-200 transition-all'>
|
||||
<i className="fa-solid fa-palette mr-2" />
|
||||
<div className='whitespace-nowrap'>{locale.MENU.THEME_SWITCH}</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -8,8 +8,12 @@ const katexSettings = {
|
||||
strict: false
|
||||
}
|
||||
|
||||
/**
|
||||
* 数学公式
|
||||
* @param {} param0
|
||||
* @returns
|
||||
*/
|
||||
export const Equation = ({ block, math, inline = false, className, ...rest }) => {
|
||||
// const { recordMap } = useNotionContext()
|
||||
math = math || getBlockTitle(block, null)
|
||||
if (!math) return null
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import dynamic from 'next/dynamic'
|
||||
import LA51 from './LA51'
|
||||
import WebWhiz from './Webwhiz'
|
||||
|
||||
const TwikooCommentCounter = dynamic(() => import('@/components/TwikooCommentCounter'), { ssr: false })
|
||||
@@ -11,7 +12,7 @@ const FlutteringRibbon = dynamic(() => import('@/components/FlutteringRibbon'),
|
||||
const Ribbon = dynamic(() => import('@/components/Ribbon'), { ssr: false })
|
||||
const Sakura = dynamic(() => import('@/components/Sakura'), { ssr: false })
|
||||
const StarrySky = dynamic(() => import('@/components/StarrySky'), { ssr: false })
|
||||
const DifyChatbot = dynamic(() => import('./DifyChatbot'), { ssr: false });
|
||||
const DifyChatbot = dynamic(() => import('@/components/DifyChatbot'), { ssr: false });
|
||||
const Analytics = dynamic(() => import('@vercel/analytics/react').then(async (m) => { return m.Analytics }), { ssr: false })
|
||||
const MusicPlayer = dynamic(() => import('@/components/Player'), { ssr: false })
|
||||
const Ackee = dynamic(() => import('@/components/Ackee'), { ssr: false })
|
||||
@@ -23,6 +24,8 @@ const VConsole = dynamic(() => import('@/components/VConsole'), { ssr: false })
|
||||
const CustomContextMenu = dynamic(() => import('@/components/CustomContextMenu'), { ssr: false })
|
||||
const DisableCopy = dynamic(() => import('@/components/DisableCopy'), { ssr: false })
|
||||
const AdBlockDetect = dynamic(() => import('@/components/AdBlockDetect'), { ssr: false })
|
||||
const LoadingProgress = dynamic(() => import('@/components/LoadingProgress'), { ssr: false })
|
||||
const AosAnimation = dynamic(() => import('@/components/AOSAnimation'), { ssr: false })
|
||||
|
||||
/**
|
||||
* 各种插件脚本
|
||||
@@ -30,146 +33,201 @@ const AdBlockDetect = dynamic(() => import('@/components/AdBlockDetect'), { ssr:
|
||||
* @returns
|
||||
*/
|
||||
const ExternalPlugin = (props) => {
|
||||
const DISABLE_PLUGIN = siteConfig('DISABLE_PLUGIN')
|
||||
const THEME_SWITCH = siteConfig('THEME_SWITCH')
|
||||
const DEBUG = siteConfig('DEBUG')
|
||||
const ANALYTICS_ACKEE_TRACKER = siteConfig('ANALYTICS_ACKEE_TRACKER')
|
||||
const ANALYTICS_VERCEL = siteConfig('ANALYTICS_VERCEL')
|
||||
const ANALYTICS_BUSUANZI_ENABLE = siteConfig('ANALYTICS_BUSUANZI_ENABLE')
|
||||
const ADSENSE_GOOGLE_ID = siteConfig('ADSENSE_GOOGLE_ID')
|
||||
const FACEBOOK_APP_ID = siteConfig('FACEBOOK_APP_ID')
|
||||
const FACEBOOK_PAGE_ID = siteConfig('FACEBOOK_PAGE_ID')
|
||||
const FIREWORKS = siteConfig('FIREWORKS')
|
||||
const SAKURA = siteConfig('SAKURA')
|
||||
const STARRY_SKY = siteConfig('STARRY_SKY')
|
||||
const MUSIC_PLAYER = siteConfig('MUSIC_PLAYER')
|
||||
const NEST = siteConfig('NEST')
|
||||
const FLUTTERINGRIBBON = siteConfig('FLUTTERINGRIBBON')
|
||||
const COMMENT_TWIKOO_COUNT_ENABLE = siteConfig('COMMENT_TWIKOO_COUNT_ENABLE')
|
||||
const RIBBON = siteConfig('RIBBON')
|
||||
const CUSTOM_RIGHT_CLICK_CONTEXT_MENU = siteConfig('CUSTOM_RIGHT_CLICK_CONTEXT_MENU')
|
||||
const CAN_COPY = siteConfig('CAN_COPY')
|
||||
const WEB_WHIZ_ENABLED = siteConfig('WEB_WHIZ_ENABLED')
|
||||
const AD_WWADS_BLOCK_DETECT = siteConfig('AD_WWADS_BLOCK_DETECT')
|
||||
const CHATBASE_ID = siteConfig('CHATBASE_ID')
|
||||
const COMMENT_DAO_VOICE_ID = siteConfig('COMMENT_DAO_VOICE_ID')
|
||||
const AD_WWADS_ID = siteConfig('AD_WWADS_ID')
|
||||
const COMMENT_TWIKOO_ENV_ID = siteConfig('COMMENT_TWIKOO_ENV_ID')
|
||||
const COMMENT_TWIKOO_CDN_URL = siteConfig('COMMENT_TWIKOO_CDN_URL')
|
||||
const COMMENT_ARTALK_SERVER = siteConfig('COMMENT_ARTALK_SERVER')
|
||||
const COMMENT_ARTALK_JS = siteConfig('COMMENT_ARTALK_JS')
|
||||
const COMMENT_TIDIO_ID = siteConfig('COMMENT_TIDIO_ID')
|
||||
const COMMENT_GITTER_ROOM = siteConfig('COMMENT_GITTER_ROOM')
|
||||
const ANALYTICS_BAIDU_ID = siteConfig('ANALYTICS_BAIDU_ID')
|
||||
const ANALYTICS_CNZZ_ID = siteConfig('ANALYTICS_CNZZ_ID')
|
||||
const ANALYTICS_GOOGLE_ID = siteConfig('ANALYTICS_GOOGLE_ID')
|
||||
const MATOMO_HOST_URL = siteConfig('MATOMO_HOST_URL')
|
||||
const MATOMO_SITE_ID = siteConfig('MATOMO_SITE_ID')
|
||||
const ANALYTICS_51LA_ID = siteConfig('ANALYTICS_51LA_ID')
|
||||
const ANALYTICS_51LA_CK = siteConfig('ANALYTICS_51LA_CK')
|
||||
const DIFY_CHATBOT_ENABLED = siteConfig('DIFY_CHATBOT_ENABLED')
|
||||
|
||||
if (DISABLE_PLUGIN) {
|
||||
return null
|
||||
}
|
||||
|
||||
return <>
|
||||
{JSON.parse(siteConfig('THEME_SWITCH')) && <ThemeSwitch />}
|
||||
{JSON.parse(siteConfig('DEBUG')) && <DebugPanel />}
|
||||
{siteConfig('ANALYTICS_ACKEE_TRACKER') && <Ackee />}
|
||||
{siteConfig('ANALYTICS_GOOGLE_ID') && <Gtag />}
|
||||
{siteConfig('ANALYTICS_VERCEL') && <Analytics />}
|
||||
{JSON.parse(siteConfig('ANALYTICS_BUSUANZI_ENABLE')) && <Busuanzi />}
|
||||
{siteConfig('ADSENSE_GOOGLE_ID') && <GoogleAdsense />}
|
||||
{siteConfig('FACEBOOK_APP_ID') && siteConfig('FACEBOOK_PAGE_ID') && <Messenger />}
|
||||
{JSON.parse(siteConfig('FIREWORKS')) && <Fireworks />}
|
||||
{JSON.parse(siteConfig('SAKURA')) && <Sakura />}
|
||||
{JSON.parse(siteConfig('STARRY_SKY')) && <StarrySky />}
|
||||
{JSON.parse(siteConfig('DIFY_CHATBOT_ENABLED')) && <DifyChatbot />}
|
||||
{JSON.parse(siteConfig('MUSIC_PLAYER')) && <MusicPlayer />}
|
||||
{JSON.parse(siteConfig('NEST')) && <Nest />}
|
||||
{JSON.parse(siteConfig('FLUTTERINGRIBBON')) && <FlutteringRibbon />}
|
||||
{JSON.parse(siteConfig('COMMENT_TWIKOO_COUNT_ENABLE')) && <TwikooCommentCounter {...props} />}
|
||||
{JSON.parse(siteConfig('RIBBON')) && <Ribbon />}
|
||||
{JSON.parse(siteConfig('CUSTOM_RIGHT_CLICK_CONTEXT_MENU')) && <CustomContextMenu {...props} />}
|
||||
{!JSON.parse(siteConfig('CAN_COPY')) && <DisableCopy />}
|
||||
{JSON.parse(siteConfig('WEB_WHIZ_ENABLED')) && <WebWhiz />}
|
||||
{JSON.parse(siteConfig('AD_WWADS_BLOCK_DETECT')) && <AdBlockDetect />}
|
||||
<VConsole />
|
||||
{THEME_SWITCH && <ThemeSwitch />}
|
||||
{DEBUG && <DebugPanel />}
|
||||
{ANALYTICS_ACKEE_TRACKER && <Ackee />}
|
||||
{ANALYTICS_GOOGLE_ID && <Gtag />}
|
||||
{ANALYTICS_VERCEL && <Analytics />}
|
||||
{ANALYTICS_BUSUANZI_ENABLE && <Busuanzi />}
|
||||
{ADSENSE_GOOGLE_ID && <GoogleAdsense />}
|
||||
{FACEBOOK_APP_ID && FACEBOOK_PAGE_ID && <Messenger />}
|
||||
{FIREWORKS && <Fireworks />}
|
||||
{SAKURA && <Sakura />}
|
||||
{STARRY_SKY && <StarrySky />}
|
||||
{MUSIC_PLAYER && <MusicPlayer />}
|
||||
{NEST && <Nest />}
|
||||
{FLUTTERINGRIBBON && <FlutteringRibbon />}
|
||||
{COMMENT_TWIKOO_COUNT_ENABLE && <TwikooCommentCounter {...props} />}
|
||||
{RIBBON && <Ribbon />}
|
||||
{DIFY_CHATBOT_ENABLED && <DifyChatbot />}
|
||||
{CUSTOM_RIGHT_CLICK_CONTEXT_MENU && <CustomContextMenu {...props} />}
|
||||
{!CAN_COPY && <DisableCopy />}
|
||||
{WEB_WHIZ_ENABLED && <WebWhiz />}
|
||||
{AD_WWADS_BLOCK_DETECT && <AdBlockDetect />}
|
||||
<VConsole />
|
||||
<LoadingProgress />
|
||||
<AosAnimation />
|
||||
{ANALYTICS_51LA_ID && ANALYTICS_51LA_CK && <LA51/>}
|
||||
|
||||
{siteConfig('CHATBASE_ID') && (<>
|
||||
<script id={siteConfig('CHATBASE_ID')} src="https://www.chatbase.co/embed.min.js" defer />
|
||||
<script async dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
window.chatbaseConfig = {
|
||||
chatbotId: "${siteConfig('CHATBASE_ID')}",
|
||||
}
|
||||
`
|
||||
}} />
|
||||
</>)}
|
||||
{ANALYTICS_51LA_ID && ANALYTICS_51LA_CK && (<>
|
||||
<script id="LA_COLLECT" src="//sdk.51.la/js-sdk-pro.min.js" defer/>
|
||||
{/* <script async dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
LA.init({id:"${ANALYTICS_51LA_ID}",ck:"${ANALYTICS_51LA_CK}",hashMode:true,autoTrack:true})
|
||||
`
|
||||
}} /> */}
|
||||
</>)}
|
||||
|
||||
{siteConfig('COMMENT_DAO_VOICE_ID') && (<>
|
||||
{/* DaoVoice 反馈 */}
|
||||
<script async dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
{CHATBASE_ID && (<>
|
||||
<script id={CHATBASE_ID} src="https://www.chatbase.co/embed.min.js" defer />
|
||||
<script async dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
window.chatbaseConfig = {
|
||||
chatbotId: "${CHATBASE_ID}",
|
||||
}
|
||||
`
|
||||
}} />
|
||||
</>)}
|
||||
|
||||
{COMMENT_DAO_VOICE_ID && (<>
|
||||
{/* DaoVoice 反馈 */}
|
||||
<script async dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
(function(i,s,o,g,r,a,m){i["DaoVoiceObject"]=r;i[r]=i[r]||function(){(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;a.charset="utf-8";m.parentNode.insertBefore(a,m)})(window,document,"script",('https:' == document.location.protocol ? 'https:' : 'http:') + "//widget.daovoice.io/widget/daf1a94b.js","daovoice")
|
||||
`
|
||||
}}
|
||||
/>
|
||||
<script async dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
}}
|
||||
/>
|
||||
<script async dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
daovoice('init', {
|
||||
app_id: "${siteConfig('COMMENT_DAO_VOICE_ID')}"
|
||||
app_id: "${COMMENT_DAO_VOICE_ID}"
|
||||
});
|
||||
daovoice('update');
|
||||
`
|
||||
}}
|
||||
/>
|
||||
</>)}
|
||||
}}
|
||||
/>
|
||||
</>)}
|
||||
|
||||
{siteConfig('AD_WWADS_ID') && <script type="text/javascript" charSet="UTF-8" src="https://cdn.wwads.cn/js/makemoney.js" async></script>}
|
||||
{AD_WWADS_ID && <script type="text/javascript" src="https://cdn.wwads.cn/js/makemoney.js" async></script>}
|
||||
|
||||
{siteConfig('COMMENT_TWIKOO_ENV_ID') && <script defer src={siteConfig('COMMENT_TWIKOO_CDN_URL')} />}
|
||||
{COMMENT_TWIKOO_ENV_ID && <script defer src={COMMENT_TWIKOO_CDN_URL} />}
|
||||
|
||||
{siteConfig('COMMENT_ARTALK_SERVER') && <script defer src={siteConfig('COMMENT_ARTALK_JS')} />}
|
||||
{COMMENT_ARTALK_SERVER && <script defer src={COMMENT_ARTALK_JS} />}
|
||||
|
||||
{siteConfig('COMMENT_TIDIO_ID') && <script async src={`//code.tidio.co/${siteConfig('COMMENT_TIDIO_ID')}.js`} />}
|
||||
{COMMENT_TIDIO_ID && <script async src={`//code.tidio.co/${COMMENT_TIDIO_ID}.js`} />}
|
||||
|
||||
{/* gitter聊天室 */}
|
||||
{siteConfig('COMMENT_GITTER_ROOM') && (<>
|
||||
<script src="https://sidecar.gitter.im/dist/sidecar.v1.js" async defer />
|
||||
<script async dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
{/* gitter聊天室 */}
|
||||
{COMMENT_GITTER_ROOM && (<>
|
||||
<script src="https://sidecar.gitter.im/dist/sidecar.v1.js" async defer />
|
||||
<script async dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
((window.gitter = {}).chat = {}).options = {
|
||||
room: '${siteConfig('COMMENT_GITTER_ROOM')}'
|
||||
room: '${COMMENT_GITTER_ROOM}'
|
||||
};
|
||||
`
|
||||
}} />
|
||||
</>)}
|
||||
}} />
|
||||
</>)}
|
||||
|
||||
{/* 百度统计 */}
|
||||
{siteConfig('ANALYTICS_BAIDU_ID') && (
|
||||
<script async
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
{/* 百度统计 */}
|
||||
{ANALYTICS_BAIDU_ID && (
|
||||
<script async
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
var _hmt = _hmt || [];
|
||||
(function() {
|
||||
var hm = document.createElement("script");
|
||||
hm.src = "https://hm.baidu.com/hm.js?${siteConfig('ANALYTICS_BAIDU_ID')}";
|
||||
hm.src = "https://hm.baidu.com/hm.js?${ANALYTICS_BAIDU_ID}";
|
||||
var s = document.getElementsByTagName("script")[0];
|
||||
s.parentNode.insertBefore(hm, s);
|
||||
})();
|
||||
`
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* 站长统计 */}
|
||||
{siteConfig('ANALYTICS_CNZZ_ID') && (
|
||||
<script async
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
document.write(unescape("%3Cspan style='display:none' id='cnzz_stat_icon_${siteConfig('ANALYTICS_CNZZ_ID')}'%3E%3C/span%3E%3Cscript src='https://s9.cnzz.com/z_stat.php%3Fid%3D${siteConfig('ANALYTICS_CNZZ_ID')}' type='text/javascript'%3E%3C/script%3E"));
|
||||
{/* 站长统计 */}
|
||||
{ANALYTICS_CNZZ_ID && (
|
||||
<script async
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
document.write(unescape("%3Cspan style='display:none' id='cnzz_stat_icon_${ANALYTICS_CNZZ_ID}'%3E%3C/span%3E%3Cscript src='https://s9.cnzz.com/z_stat.php%3Fid%3D${ANALYTICS_CNZZ_ID}' type='text/javascript'%3E%3C/script%3E"));
|
||||
`
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* 谷歌统计 */}
|
||||
{siteConfig('ANALYTICS_GOOGLE_ID') && (<>
|
||||
<script async
|
||||
src={`https://www.googletagmanager.com/gtag/js?id=${siteConfig('ANALYTICS_GOOGLE_ID')}`}
|
||||
/>
|
||||
<script async
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
{/* 谷歌统计 */}
|
||||
{ANALYTICS_GOOGLE_ID && (<>
|
||||
<script async
|
||||
src={`https://www.googletagmanager.com/gtag/js?id=${ANALYTICS_GOOGLE_ID}`}
|
||||
/>
|
||||
<script async
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag(){dataLayer.push(arguments);}
|
||||
gtag('js', new Date());
|
||||
gtag('config', '${siteConfig('ANALYTICS_GOOGLE_ID')}', {
|
||||
gtag('config', '${ANALYTICS_GOOGLE_ID}', {
|
||||
page_path: window.location.pathname,
|
||||
});
|
||||
`
|
||||
}}
|
||||
/>
|
||||
</>)}
|
||||
}}
|
||||
/>
|
||||
</>)}
|
||||
|
||||
{/* Matomo 统计 */}
|
||||
{siteConfig('MATOMO_HOST_URL') && siteConfig('MATOMO_SITE_ID') && (
|
||||
<script async dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
{/* Matomo 统计 */}
|
||||
{MATOMO_HOST_URL && MATOMO_SITE_ID && (
|
||||
<script async dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
var _paq = window._paq = window._paq || [];
|
||||
_paq.push(['trackPageView']);
|
||||
_paq.push(['enableLinkTracking']);
|
||||
(function() {
|
||||
var u="//${siteConfig('MATOMO_HOST_URL')}/";
|
||||
var u="//${MATOMO_HOST_URL}/";
|
||||
_paq.push(['setTrackerUrl', u+'matomo.php']);
|
||||
_paq.push(['setSiteId', '${siteConfig('MATOMO_SITE_ID')}']);
|
||||
_paq.push(['setSiteId', '${MATOMO_SITE_ID}']);
|
||||
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
|
||||
g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s);
|
||||
})();
|
||||
`
|
||||
}}/>
|
||||
)}
|
||||
}} />
|
||||
)}
|
||||
|
||||
</>
|
||||
</>
|
||||
}
|
||||
|
||||
export default ExternalPlugin
|
||||
|
||||
@@ -1,14 +1,41 @@
|
||||
import { Component } from 'react'
|
||||
import { Component, useEffect, useState } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
|
||||
export default function Messenger() {
|
||||
return <MessengerCustomerChat
|
||||
pageId={siteConfig('FACEBOOK_PAGE_ID')}
|
||||
appId={siteConfig('FACEBOOK_APP_ID')}
|
||||
language={siteConfig('LANG').replace('-', '_')}
|
||||
shouldShowDialog={true}
|
||||
/>
|
||||
const pageId = siteConfig('FACEBOOK_PAGE_ID')
|
||||
const appId = siteConfig('FACEBOOK_APP_ID')
|
||||
const language = siteConfig('LANG').replace('-', '_')
|
||||
|
||||
// 新增一个状态变量用于追踪是否已经滚动过
|
||||
const [showMessenger, setShowMessenger] = useState(false);
|
||||
|
||||
const showTheComponent = () => {
|
||||
window.removeEventListener('scroll', showTheComponent);
|
||||
if (!showMessenger) {
|
||||
setShowMessenger(true);
|
||||
}
|
||||
};
|
||||
|
||||
// 延时7秒,或页面滚动时加载该组件
|
||||
useEffect(() => {
|
||||
window.addEventListener('scroll', showTheComponent);
|
||||
setTimeout(() => {
|
||||
showTheComponent()
|
||||
}, 7000);
|
||||
return () => {
|
||||
window.removeEventListener('scroll', showTheComponent);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return <>
|
||||
{showMessenger && <MessengerCustomerChat
|
||||
pageId={pageId}
|
||||
appId={appId}
|
||||
language={language}
|
||||
shouldShowDialog={true}
|
||||
/>}
|
||||
</>
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -24,8 +51,8 @@ class MessengerCustomerChat extends Component {
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
*/
|
||||
* 初始化
|
||||
*/
|
||||
componentDidMount() {
|
||||
this.setFbAsyncInit()
|
||||
this.reloadSDKAsynchronously()
|
||||
@@ -34,19 +61,19 @@ class MessengerCustomerChat extends Component {
|
||||
componentDidUpdate(prevProps) {
|
||||
if (
|
||||
prevProps.pageId !== this.props.pageId ||
|
||||
prevProps.appId !== this.props.appId ||
|
||||
prevProps.shouldShowDialog !== this.props.shouldShowDialog ||
|
||||
prevProps.htmlRef !== this.props.htmlRef ||
|
||||
prevProps.minimized !== this.props.minimized ||
|
||||
prevProps.themeColor !== this.props.themeColor ||
|
||||
prevProps.loggedInGreeting !== this.props.loggedInGreeting ||
|
||||
prevProps.loggedOutGreeting !== this.props.loggedOutGreeting ||
|
||||
prevProps.greetingDialogDisplay !== this.props.greetingDialogDisplay ||
|
||||
prevProps.greetingDialogDelay !== this.props.greetingDialogDelay ||
|
||||
prevProps.autoLogAppEvents !== this.props.autoLogAppEvents ||
|
||||
prevProps.xfbml !== this.props.xfbml ||
|
||||
prevProps.version !== this.props.version ||
|
||||
prevProps.language !== this.props.language
|
||||
prevProps.appId !== this.props.appId ||
|
||||
prevProps.shouldShowDialog !== this.props.shouldShowDialog ||
|
||||
prevProps.htmlRef !== this.props.htmlRef ||
|
||||
prevProps.minimized !== this.props.minimized ||
|
||||
prevProps.themeColor !== this.props.themeColor ||
|
||||
prevProps.loggedInGreeting !== this.props.loggedInGreeting ||
|
||||
prevProps.loggedOutGreeting !== this.props.loggedOutGreeting ||
|
||||
prevProps.greetingDialogDisplay !== this.props.greetingDialogDisplay ||
|
||||
prevProps.greetingDialogDelay !== this.props.greetingDialogDelay ||
|
||||
prevProps.autoLogAppEvents !== this.props.autoLogAppEvents ||
|
||||
prevProps.xfbml !== this.props.xfbml ||
|
||||
prevProps.version !== this.props.version ||
|
||||
prevProps.language !== this.props.language
|
||||
) {
|
||||
this.setFbAsyncInit()
|
||||
this.reloadSDKAsynchronously()
|
||||
@@ -60,8 +87,8 @@ class MessengerCustomerChat extends Component {
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
*/
|
||||
* 初始化
|
||||
*/
|
||||
setFbAsyncInit() {
|
||||
const { appId, autoLogAppEvents, xfbml, version } = this.props
|
||||
|
||||
@@ -80,18 +107,18 @@ class MessengerCustomerChat extends Component {
|
||||
loadSDKAsynchronously() {
|
||||
const { language } = this.props;
|
||||
/* eslint-disable */
|
||||
(function (d, s, id) {
|
||||
var js,
|
||||
fjs = d.getElementsByTagName(s)[0];
|
||||
if (d.getElementById(id)) {
|
||||
return;
|
||||
}
|
||||
js = d.createElement(s);
|
||||
js.id = id;
|
||||
js.src = `https://connect.facebook.net/${language}/sdk/xfbml.customerchat.js`;
|
||||
fjs.parentNode.insertBefore(js, fjs);
|
||||
})(document, 'script', 'facebook-jssdk');
|
||||
/* eslint-enable */
|
||||
(function (d, s, id) {
|
||||
var js,
|
||||
fjs = d.getElementsByTagName(s)[0];
|
||||
if (d.getElementById(id)) {
|
||||
return;
|
||||
}
|
||||
js = d.createElement(s);
|
||||
js.id = id;
|
||||
js.src = `https://connect.facebook.net/${language}/sdk/xfbml.customerchat.js`;
|
||||
fjs.parentNode.insertBefore(js, fjs);
|
||||
})(document, 'script', 'facebook-jssdk');
|
||||
/* eslint-enable */
|
||||
}
|
||||
|
||||
removeFacebookSDK() {
|
||||
@@ -147,25 +174,25 @@ class MessengerCustomerChat extends Component {
|
||||
|
||||
const refAttribute = htmlRef !== undefined ? `ref="${htmlRef}"` : ''
|
||||
const minimizedAttribute =
|
||||
minimized !== undefined ? `minimized="${minimized}"` : ''
|
||||
minimized !== undefined ? `minimized="${minimized}"` : ''
|
||||
const themeColorAttribute =
|
||||
themeColor !== undefined ? `theme_color="${themeColor}"` : ''
|
||||
themeColor !== undefined ? `theme_color="${themeColor}"` : ''
|
||||
const loggedInGreetingAttribute =
|
||||
loggedInGreeting !== undefined
|
||||
? `logged_in_greeting="${loggedInGreeting}"`
|
||||
: ''
|
||||
loggedInGreeting !== undefined
|
||||
? `logged_in_greeting="${loggedInGreeting}"`
|
||||
: ''
|
||||
const loggedOutGreetingAttribute =
|
||||
loggedOutGreeting !== undefined
|
||||
? `logged_out_greeting="${loggedOutGreeting}"`
|
||||
: ''
|
||||
loggedOutGreeting !== undefined
|
||||
? `logged_out_greeting="${loggedOutGreeting}"`
|
||||
: ''
|
||||
const greetingDialogDisplayAttribute =
|
||||
greetingDialogDisplay !== undefined
|
||||
? `greeting_dialog_display="${greetingDialogDisplay}"`
|
||||
: ''
|
||||
greetingDialogDisplay !== undefined
|
||||
? `greeting_dialog_display="${greetingDialogDisplay}"`
|
||||
: ''
|
||||
const greetingDialogDelayAttribute =
|
||||
greetingDialogDelay !== undefined
|
||||
? `greeting_dialog_delay="${greetingDialogDelay}"`
|
||||
: ''
|
||||
greetingDialogDelay !== undefined
|
||||
? `greeting_dialog_delay="${greetingDialogDelay}"`
|
||||
: ''
|
||||
|
||||
return {
|
||||
__html: `<div
|
||||
@@ -192,8 +219,8 @@ class MessengerCustomerChat extends Component {
|
||||
const element = event.target
|
||||
if (
|
||||
element.className &&
|
||||
typeof element.className === 'string' &&
|
||||
element.className.includes('fb_dialog')
|
||||
typeof element.className === 'string' &&
|
||||
element.className.includes('fb_dialog')
|
||||
) {
|
||||
this.controlPlugin()
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
18
components/LA51.js
Normal file
18
components/LA51.js
Normal file
@@ -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 <></>
|
||||
}
|
||||
@@ -4,6 +4,10 @@ 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(siteConfig('WIDGET_PET'))
|
||||
|
||||
29
components/LoadingProgress.js
Normal file
29
components/LoadingProgress.js
Normal file
@@ -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])
|
||||
}
|
||||
@@ -1,13 +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 { 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) => {
|
||||
@@ -15,6 +20,7 @@ const Code = dynamic(() =>
|
||||
}), { ssr: false }
|
||||
)
|
||||
|
||||
// 公式
|
||||
const Equation = dynamic(() =>
|
||||
import('@/components/Equation').then(async (m) => {
|
||||
// 化学方程式
|
||||
@@ -36,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 }
|
||||
)
|
||||
|
||||
@@ -1,28 +1,21 @@
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { useRouter } from 'next/router'
|
||||
import React from 'react'
|
||||
import ShareButtons from './ShareButtons'
|
||||
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(siteConfig('POST_SHARE_BAR_ENABLE')) || !post || post?.type !== 'Post') {
|
||||
return <></>
|
||||
}
|
||||
|
||||
const shareUrl = siteConfig('LINK') + router.asPath
|
||||
|
||||
return <div className='m-1 overflow-x-auto'>
|
||||
<div className='flex w-full md:justify-end'>
|
||||
<ShareButtons shareUrl={shareUrl} title={post.title} image={post.pageCover} body={
|
||||
post?.title +
|
||||
' | ' +
|
||||
siteConfig('TITLE') +
|
||||
' ' +
|
||||
shareUrl +
|
||||
' ' +
|
||||
post?.summary
|
||||
} />
|
||||
<ShareButtons post={post} />
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ 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,7 +57,13 @@ const QrCode = dynamic(() => import('@/components/QrCode'), { ssr: false })
|
||||
* @param {*} param0
|
||||
* @returns
|
||||
*/
|
||||
const ShareButtons = ({ shareUrl, title, body, image }) => {
|
||||
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()
|
||||
@@ -336,7 +343,7 @@ const ShareButtons = ({ shareUrl, title, body, image }) => {
|
||||
}
|
||||
if (singleService === 'qq') {
|
||||
return <button key={singleService} className='cursor-pointer bg-blue-600 text-white rounded-full mx-1'>
|
||||
<a target='_blank' rel='noreferrer' href={`http://connect.qq.com/widget/shareqq/index.html?url=${shareUrl}&sharesource=qzone&title=${title}&desc=${body}`} >
|
||||
<a target='_blank' rel='noreferrer' aria-label="Share by QQ" href={`http://connect.qq.com/widget/shareqq/index.html?url=${shareUrl}&sharesource=qzone&title=${title}&desc=${body}`} >
|
||||
<i className='fab fa-qq w-8' />
|
||||
</a>
|
||||
</button>
|
||||
@@ -349,7 +356,7 @@ const ShareButtons = ({ shareUrl, title, body, image }) => {
|
||||
<div className='absolute'>
|
||||
<div id='pop' className={(qrCodeShow ? 'opacity-100 ' : ' invisible opacity-0') + ' z-40 absolute bottom-10 -left-10 bg-white shadow-xl transition-all duration-200 text-center'}>
|
||||
<div className='p-2 mt-1 w-28 h-28'>
|
||||
<QrCode value={shareUrl}/>
|
||||
{ qrCodeShow && <QrCode value={shareUrl}/> }
|
||||
</div>
|
||||
<span className='text-black font-semibold p-1 rounded-t-lg text-sm mx-auto mb-1'>
|
||||
{locale.COMMON.SCAN_QR_CODE}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useState } from 'react'
|
||||
import { useState } from 'react';
|
||||
|
||||
/**
|
||||
* Tabs切换标签
|
||||
@@ -6,59 +6,37 @@ 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 && c !== '')
|
||||
|
||||
let count = 0
|
||||
children.forEach(e => {
|
||||
if (e) {
|
||||
count++
|
||||
}
|
||||
})
|
||||
|
||||
if (count === 0) {
|
||||
return <></>
|
||||
}
|
||||
|
||||
if (count === 1) {
|
||||
return <section className={'duration-200 ' + className}>
|
||||
{children}
|
||||
</section>
|
||||
}
|
||||
|
||||
function tabClickHandle(i) {
|
||||
setCurrentTab(i)
|
||||
}
|
||||
|
||||
return <div className={'mb-5 duration-200 ' + className}>
|
||||
<ul className='flex justify-center space-x-5 pb-4 dark:text-gray-400 text-gray-600 overflow-auto'>
|
||||
{children.map((item, index) => {
|
||||
return <li key={index}
|
||||
className={(currentTab === index ? 'font-black border-b-2 border-red-400 text-red-400 animate__animated animate__jello ' : 'font-extralight cursor-pointer') + ' text-sm font-sans '}
|
||||
onClick={() => {
|
||||
tabClickHandle(index)
|
||||
}}>
|
||||
{item?.key}
|
||||
</li>
|
||||
})}
|
||||
</ul>
|
||||
<div>
|
||||
{children.map((item, index) => {
|
||||
return <section key={index}
|
||||
data-aos="fade-up"
|
||||
data-aos-duration="300"
|
||||
data-aos-once="true"
|
||||
data-aos-anchor-placement="top-bottom">
|
||||
{currentTab === index && item}
|
||||
</section>
|
||||
})}
|
||||
</div>
|
||||
return (
|
||||
<div className={`mb-5 duration-200 ${className}`}>
|
||||
<ul className="flex justify-center space-x-5 pb-4 dark:text-gray-400 text-gray-600 overflow-auto">
|
||||
{validChildren.map((item, index) => (
|
||||
<li key={index}
|
||||
className={`${currentTab === index ? 'font-black border-b-2 border-red-600 text-red-600 animate__animated animate__jello' : 'font-extralight cursor-pointer'} text-sm font-sans`}
|
||||
onClick={() => setCurrentTab(index)}>
|
||||
{item.key}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
{/* 标签切换的时候不销毁 DOM 元素,使用 CSS 样式进行隐藏 */}
|
||||
<div>
|
||||
{validChildren.map((item, index) => (
|
||||
<section
|
||||
key={index}
|
||||
className={`${currentTab === index ? 'opacity-100 static h-auto' : 'opacity-0 absolute h-0'}`}>
|
||||
{item}
|
||||
</section>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
export default Tabs
|
||||
export default Tabs;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { useEffect } from 'react'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
|
||||
/**
|
||||
* 评论插件
|
||||
@@ -9,28 +10,48 @@ import { useEffect } from 'react'
|
||||
* @constructor
|
||||
*/
|
||||
const Utterances = ({ issueTerm, layout }) => {
|
||||
const { isDarkMode } = useGlobal()
|
||||
|
||||
const [isLoading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
const theme =
|
||||
siteConfig('APPEARANCE') === 'auto'
|
||||
? 'preferred-color-scheme'
|
||||
: siteConfig('APPEARANCE') === 'light'
|
||||
? 'github-light'
|
||||
: 'github-dark'
|
||||
const script = document.createElement('script')
|
||||
const anchor = document.getElementById('comments')
|
||||
script.setAttribute('src', 'https://utteranc.es/client.js')
|
||||
script.setAttribute('crossorigin', 'anonymous')
|
||||
script.setAttribute('async', true)
|
||||
script.setAttribute('repo', siteConfig('COMMENT_UTTERRANCES_REPO'))
|
||||
script.setAttribute('issue-term', 'title')
|
||||
script.setAttribute('theme', theme)
|
||||
anchor.appendChild(script)
|
||||
const script = document.createElement('script');
|
||||
const anchor = document.getElementById('comments');
|
||||
script.onload = () => setLoading(false);
|
||||
script.setAttribute('src', 'https://utteranc.es/client.js');
|
||||
script.setAttribute('crossorigin', 'anonymous');
|
||||
script.setAttribute('async', true);
|
||||
script.setAttribute('repo', siteConfig('COMMENT_UTTERRANCES_REPO'));
|
||||
script.setAttribute('issue-term', 'title');
|
||||
// 初始主题
|
||||
script.setAttribute('theme', isDarkMode ? 'github-dark' : 'github-light');
|
||||
anchor.appendChild(script);
|
||||
|
||||
return () => {
|
||||
anchor.innerHTML = ''
|
||||
// anchor.innerHTML = ''
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
// 直接设置 iframe 的类来改变主题,不重新加载脚本
|
||||
const iframe = document.querySelector('iframe.utterances-frame');
|
||||
if (iframe) {
|
||||
iframe.contentWindow.postMessage({
|
||||
type: 'set-theme',
|
||||
theme: isDarkMode ? 'github-dark' : 'github-light'
|
||||
}, 'https://utteranc.es');
|
||||
}
|
||||
})
|
||||
return <div id="comments" className='utterances' >
|
||||
</div>
|
||||
}, [isDarkMode]);
|
||||
|
||||
return (
|
||||
<div id="comments" className='utterances'>
|
||||
{isLoading && (
|
||||
<div className="flex justify-center items-center m-8">
|
||||
<div className="animate-spin rounded-full h-8 w-8 border-2 border-indigo-400 border-t-transparent"></div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Utterances
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
import { generateLocaleDict, initLocale } from './lang'
|
||||
import { generateLocaleDict, initLocale, saveLangToCookies } 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 NProgress from 'nprogress'
|
||||
|
||||
import { APPEARANCE, LANG, THEME } from 'blog.config'
|
||||
const GlobalContext = createContext()
|
||||
|
||||
/**
|
||||
@@ -15,14 +12,17 @@ const GlobalContext = createContext()
|
||||
* @constructor
|
||||
*/
|
||||
export function GlobalContextProvider(props) {
|
||||
const { children, siteInfo, categoryOptions, tagOptions, NOTION_CONFIG } = props
|
||||
const { post, children, siteInfo, categoryOptions, tagOptions, NOTION_CONFIG } = props
|
||||
const router = useRouter()
|
||||
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 [lang, updateLang] = useState(NOTION_CONFIG?.LANG || LANG) // 默认语言
|
||||
const [locale, updateLocale] = useState(generateLocaleDict(NOTION_CONFIG?.LANG || LANG)) // 默认语言
|
||||
const [theme, setTheme] = useState(NOTION_CONFIG?.THEME || THEME) // 默认博客主题
|
||||
const [isDarkMode, updateDarkMode] = useState(NOTION_CONFIG?.APPEARANCE || APPEARANCE === 'dark') // 默认深色模式
|
||||
const [onLoading, setOnLoading] = useState(false) // 抓取文章数据
|
||||
|
||||
// 是否全屏
|
||||
const fullWidth = post?.fullWidth ?? false
|
||||
|
||||
// 切换主题
|
||||
function switchTheme() {
|
||||
const currentIndex = THEMES.indexOf(theme)
|
||||
@@ -49,6 +49,7 @@ export function GlobalContextProvider(props) {
|
||||
*/
|
||||
function changeLang(lang) {
|
||||
if (lang) {
|
||||
saveLangToCookies(lang)
|
||||
updateLang(lang)
|
||||
updateLocale(generateLocaleDict(lang))
|
||||
}
|
||||
@@ -62,7 +63,6 @@ export function GlobalContextProvider(props) {
|
||||
// 加载进度条
|
||||
useEffect(() => {
|
||||
const handleStart = (url) => {
|
||||
NProgress.start()
|
||||
const { theme } = router.query
|
||||
if (theme && !url.includes(`theme=${theme}`)) {
|
||||
const newUrl = `${url}${url.includes('?') ? '&' : '?'}theme=${theme}`
|
||||
@@ -71,7 +71,6 @@ export function GlobalContextProvider(props) {
|
||||
setOnLoading(true)
|
||||
}
|
||||
const handleStop = () => {
|
||||
NProgress.done()
|
||||
setOnLoading(false)
|
||||
}
|
||||
|
||||
@@ -87,6 +86,7 @@ export function GlobalContextProvider(props) {
|
||||
|
||||
return (
|
||||
<GlobalContext.Provider value={{
|
||||
fullWidth,
|
||||
NOTION_CONFIG,
|
||||
toggleDarkMode,
|
||||
onLoading,
|
||||
|
||||
@@ -89,9 +89,9 @@ export const loadLangFromCookies = () => {
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存语言
|
||||
* @param newTheme
|
||||
*/
|
||||
* 保存语言
|
||||
* @param newTheme
|
||||
*/
|
||||
export const saveLangToCookies = (lang) => {
|
||||
cookie.save('lang', lang, { path: '/' })
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ export default {
|
||||
ARTICLE_DETAIL: 'Article Details',
|
||||
PASSWORD_ERROR: 'Password Error!',
|
||||
ARTICLE_LOCK_TIPS: 'Please Enter the password:',
|
||||
NO_RESULTS_FOUND: 'No results found.',
|
||||
SUBMIT: 'Submit',
|
||||
POST_TIME: 'Post on',
|
||||
LAST_EDITED_TIME: 'Last edited',
|
||||
@@ -52,8 +53,8 @@ export default {
|
||||
ANNOUNCEMENT: 'Announcement',
|
||||
START_READING: 'Start Reading',
|
||||
MINUTE: 'min',
|
||||
WORD_COUNT: 'W.C.'
|
||||
|
||||
WORD_COUNT: 'Words',
|
||||
READ_TIME: 'Read Time'
|
||||
},
|
||||
PAGINATION: {
|
||||
PREV: 'Prev',
|
||||
@@ -66,5 +67,10 @@ export default {
|
||||
POST: {
|
||||
BACK: 'Back',
|
||||
TOP: 'Top'
|
||||
},
|
||||
MAILCHIMP: {
|
||||
SUBSCRIBE: 'Subscribe',
|
||||
MSG: 'Get the latest news and articles to your inbox every month.',
|
||||
EMAIL: 'Email'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ export default {
|
||||
VIEWS: '次查看',
|
||||
COPYRIGHT_NOTICE: '本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。',
|
||||
RESULT_OF_SEARCH: '篇搜索到的结果',
|
||||
NO_RESULTS_FOUND: '没有找到文章',
|
||||
ARTICLE_DETAIL: '文章详情',
|
||||
PASSWORD_ERROR: '密码错误!',
|
||||
ARTICLE_LOCK_TIPS: '文章已上锁,请输入访问密码',
|
||||
@@ -54,7 +55,8 @@ export default {
|
||||
ANNOUNCEMENT: '公告',
|
||||
START_READING: '开始阅读',
|
||||
MINUTE: '分钟',
|
||||
WORD_COUNT: '字数'
|
||||
WORD_COUNT: '字数',
|
||||
READ_TIME: '阅读时长'
|
||||
},
|
||||
PAGINATION: {
|
||||
PREV: '上页',
|
||||
@@ -67,5 +69,10 @@ export default {
|
||||
POST: {
|
||||
BACK: '返回上页',
|
||||
TOP: '回到顶部'
|
||||
},
|
||||
MAILCHIMP: {
|
||||
SUBSCRIBE: '邮件订阅',
|
||||
MSG: '订阅以获取每月更新的新闻和文章,直接发送至您的邮箱。',
|
||||
EMAIL: '邮箱'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ export async function getNotion(pageId) {
|
||||
}
|
||||
|
||||
const postInfo = blockMap?.block?.[idToUuid(pageId)].value
|
||||
|
||||
return {
|
||||
id: pageId,
|
||||
type: postInfo,
|
||||
@@ -26,7 +25,7 @@ export async function getNotion(pageId) {
|
||||
status: 'Published',
|
||||
createdTime: formatDate(new Date(postInfo.created_time).toString(), BLOG.LANG),
|
||||
lastEditedDay: formatDate(new Date(postInfo?.last_edited_time).toString(), BLOG.LANG),
|
||||
fullWidth: false,
|
||||
fullWidth: postInfo?.fullWidth,
|
||||
page_cover: getPageCover(postInfo),
|
||||
date: { start_date: formatDate(new Date(postInfo?.last_edited_time).toString(), BLOG.LANG) },
|
||||
blockMap
|
||||
|
||||
@@ -24,7 +24,7 @@ export async function getConfigMapFromConfigPage(allPages) {
|
||||
return null
|
||||
}
|
||||
const configPage = allPages?.find(post => {
|
||||
return post && post?.type && post?.type === 'CONFIG'
|
||||
return post && post?.type && (post?.type === 'CONFIG' || post?.type === 'config' || post?.type === 'Config')
|
||||
})
|
||||
|
||||
if (!configPage) {
|
||||
@@ -32,11 +32,15 @@ export async function getConfigMapFromConfigPage(allPages) {
|
||||
return null
|
||||
}
|
||||
const configPageId = configPage.id
|
||||
|
||||
// console.log('[Notion配置]请求配置数据 ', configPage.id)
|
||||
const pageRecordMap = await getPostBlocks(configPageId, 'config-table')
|
||||
let pageRecordMap = await getPostBlocks(configPageId, 'config-table')
|
||||
// console.log('配置中心Page', configPageId, pageRecordMap)
|
||||
const content = pageRecordMap.block[configPageId].value.content
|
||||
let content = pageRecordMap.block[configPageId].value.content
|
||||
for (const table of ['Config-Table', 'CONFIG-TABLE']) {
|
||||
if (content) break
|
||||
pageRecordMap = await getPostBlocks(configPageId, table)
|
||||
content = pageRecordMap.block[configPageId].value.content
|
||||
}
|
||||
|
||||
if (!content) {
|
||||
console.warn('[Notion配置] 未找到配置表格', pageRecordMap.block[configPageId], pageRecordMap.block[configPageId].value)
|
||||
@@ -119,9 +123,9 @@ export async function getConfigMapFromConfigPage(allPages) {
|
||||
if (properties) {
|
||||
// 将表格中的字段映射成 英文
|
||||
const config = {
|
||||
enable: properties['启用'] === 'Yes',
|
||||
key: properties['配置名'],
|
||||
value: properties['配置值']
|
||||
enable: (properties['启用'] || properties.Enable) === 'Yes',
|
||||
key: properties['配置名'] || properties.Name,
|
||||
value: properties['配置值'] || properties.Value
|
||||
}
|
||||
|
||||
// 只导入生效的配置
|
||||
|
||||
@@ -102,7 +102,10 @@ function filterPostBlocks(id, pageBlock, slice) {
|
||||
}
|
||||
|
||||
// 如果是文件,或嵌入式PDF,需要重新加密签名
|
||||
if ((b?.value?.type === 'file' || b?.value?.type === 'pdf' || b?.value?.type === 'video') && b?.value?.properties?.source?.[0][0]) {
|
||||
if ((b?.value?.type === 'file' || b?.value?.type === 'pdf' || b?.value?.type === 'video' || b?.value?.type === 'audio') &&
|
||||
b?.value?.properties?.source?.[0][0] &&
|
||||
b?.value?.properties?.source?.[0][0].indexOf('amazonaws.com') > 0
|
||||
) {
|
||||
const oldUrl = b?.value?.properties?.source?.[0][0]
|
||||
const newUrl = `https://notion.so/signed/${encodeURIComponent(oldUrl)}?table=block&id=${b?.value?.id}`
|
||||
b.value.properties.source[0][0] = newUrl
|
||||
|
||||
14
lib/utils.js
14
lib/utils.js
@@ -7,6 +7,20 @@ import { memo } from 'react'
|
||||
*/
|
||||
export const isBrowser = typeof window !== 'undefined'
|
||||
|
||||
/**
|
||||
* google机器人
|
||||
* @returns
|
||||
*/
|
||||
export const isSearchEngineBot = () => {
|
||||
if (typeof navigator === 'undefined') {
|
||||
return false
|
||||
}
|
||||
// 获取用户代理字符串
|
||||
const userAgent = navigator.userAgent;
|
||||
// 使用正则表达式检测是否包含搜索引擎爬虫关键字
|
||||
return /Googlebot|bingbot|Baidu/.test(userAgent)
|
||||
}
|
||||
|
||||
/**
|
||||
* 组件持久化
|
||||
*/
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
const withBundleAnalyzer = require('@next/bundle-analyzer')({
|
||||
enabled: process.env.ANALYZE === 'true'
|
||||
})
|
||||
|
||||
const { THEME } = require('./blog.config')
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const BLOG = require('./blog.config')
|
||||
|
||||
const withBundleAnalyzer = require('@next/bundle-analyzer')({
|
||||
enabled: BLOG.BUNDLE_ANALYZER
|
||||
})
|
||||
|
||||
/**
|
||||
* 扫描指定目录下的文件夹名,用于获取当前有几个主题
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "notion-next",
|
||||
"version": "4.1.1",
|
||||
"version": "4.1.4",
|
||||
"homepage": "https://github.com/tangly1024/NotionNext.git",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
|
||||
@@ -6,41 +6,38 @@ import '@/styles/utility-patterns.css'
|
||||
// core styles shared by all of react-notion-x (required)
|
||||
import 'react-notion-x/src/styles.css'
|
||||
import '@/styles/notion.css' // 重写部分样式
|
||||
import 'aos/dist/aos.css' // You can also use <link> for styles
|
||||
|
||||
import { GlobalContextProvider } from '@/lib/global'
|
||||
|
||||
import AOS from 'aos'
|
||||
import 'aos/dist/aos.css' // You can also use <link> for styles
|
||||
import { isBrowser, loadExternalResource } from '@/lib/utils'
|
||||
import BLOG from '@/blog.config'
|
||||
// 各种扩展插件 动画等
|
||||
|
||||
// 各种扩展插件 这个要阻塞引入
|
||||
import ExternalPlugins from '@/components/ExternalPlugins'
|
||||
// const ExternalPlugins = dynamic(() => import('@/components/ExternalPlugins'))
|
||||
import { CUSTOM_EXTERNAL_CSS, CUSTOM_EXTERNAL_JS, IMG_SHADOW } from '@/blog.config'
|
||||
|
||||
const MyApp = ({ Component, pageProps }) => {
|
||||
// 自定义样式css和js引入
|
||||
if (isBrowser) {
|
||||
// 初始化AOS动画
|
||||
AOS.init()
|
||||
// 静态导入本地自定义样式
|
||||
loadExternalResource('/css/custom.css', 'css')
|
||||
loadExternalResource('/js/custom.js', 'js')
|
||||
|
||||
// 自动添加图片阴影
|
||||
if (BLOG.IMG_SHADOW) {
|
||||
if (IMG_SHADOW) {
|
||||
loadExternalResource('/css/img-shadow.css', 'css')
|
||||
}
|
||||
|
||||
// 导入外部自定义脚本
|
||||
if (BLOG.CUSTOM_EXTERNAL_JS && BLOG.CUSTOM_EXTERNAL_JS.length > 0) {
|
||||
for (const url of BLOG.CUSTOM_EXTERNAL_JS) {
|
||||
if (CUSTOM_EXTERNAL_JS && CUSTOM_EXTERNAL_JS.length > 0) {
|
||||
for (const url of CUSTOM_EXTERNAL_JS) {
|
||||
loadExternalResource(url, 'js')
|
||||
}
|
||||
}
|
||||
|
||||
// 导入外部自定义样式
|
||||
if (BLOG.CUSTOM_EXTERNAL_CSS && BLOG.CUSTOM_EXTERNAL_CSS.length > 0) {
|
||||
for (const url of BLOG.CUSTOM_EXTERNAL_CSS) {
|
||||
if (CUSTOM_EXTERNAL_CSS && CUSTOM_EXTERNAL_CSS.length > 0) {
|
||||
for (const url of CUSTOM_EXTERNAL_CSS) {
|
||||
loadExternalResource(url, 'css')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@ const isIterable = obj =>
|
||||
async function filterByMemCache(allPosts, keyword) {
|
||||
const filterPosts = []
|
||||
if (keyword) {
|
||||
keyword = keyword.trim()
|
||||
keyword = keyword.trim().toLowerCase()
|
||||
}
|
||||
for (const post of allPosts) {
|
||||
const cacheKey = 'page_block_' + post.id
|
||||
@@ -131,7 +131,7 @@ async function filterByMemCache(allPosts, keyword) {
|
||||
if (!c) {
|
||||
continue
|
||||
}
|
||||
const index = c.toLowerCase().indexOf(keyword.toLowerCase())
|
||||
const index = c.toLowerCase().indexOf(keyword)
|
||||
if (index > -1) {
|
||||
hit = true
|
||||
hitCount += 1
|
||||
|
||||
@@ -160,6 +160,11 @@ nav {
|
||||
@apply text-blue-700
|
||||
}
|
||||
|
||||
/* twikoo 内置的 element-ui 加载样式 */
|
||||
.el-loading-spinner {
|
||||
@apply flex justify-center items-center;
|
||||
}
|
||||
|
||||
/* Webmention style */
|
||||
.webmention-block {
|
||||
background: rgba(0, 116, 222, .2);
|
||||
|
||||
@@ -1130,9 +1130,10 @@ code[class*='language-'] {
|
||||
transition: background 20ms ease-in 0s;
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
opacity: 0.9;
|
||||
|
||||
padding: 6px 2px;
|
||||
font-size: 14px;
|
||||
font-size: 15px;
|
||||
line-height: 1.2;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -36,8 +36,7 @@ export const Nav = (props) => {
|
||||
<nav className="w-full bg-white md:pt-0 px-6 relative z-20 border-t border-b border-gray-light dark:border-hexo-black-gray dark:bg-black">
|
||||
<div className="container mx-auto max-w-4xl md:flex justify-between items-center text-sm md:text-md md:justify-start">
|
||||
<ul className="w-full text-center md:text-left flex flex-wrap justify-center items-stretch md:justify-start md:items-start">
|
||||
{/* {links.map(link => <NormalMenuItem key={link?.id} link={link}/>)} */}
|
||||
{links.map(link => <MenuItemDrop key={link?.id} link={link} />)}
|
||||
{links.map((link, index) => <MenuItemDrop key={index} link={link} />)}
|
||||
</ul>
|
||||
{/* <div className="w-full md:w-1/3 text-center md:text-right"> */}
|
||||
{/* <!-- extra links --> */}
|
||||
|
||||
@@ -37,7 +37,7 @@ import { siteConfig } from '@/lib/config'
|
||||
*/
|
||||
const LayoutBase = props => {
|
||||
const { children, slotTop, meta } = props
|
||||
const { onLoading } = useGlobal()
|
||||
const { onLoading, fullWidth } = useGlobal()
|
||||
|
||||
// 增加一个状态以触发 Transition 组件的动画
|
||||
// const [showTransition, setShowTransition] = useState(true)
|
||||
@@ -65,12 +65,12 @@ const LayoutBase = props => {
|
||||
<div id='container-inner' className="w-full relative z-10">
|
||||
|
||||
{/* 标题栏 */}
|
||||
<Title {...props} />
|
||||
{fullWidth ? null : <Title {...props} />}
|
||||
|
||||
<div id='container-wrapper' className={(JSON.parse(siteConfig('LAYOUT_SIDEBAR_REVERSE')) ? 'flex-row-reverse' : '') + 'relative container mx-auto justify-center md:flex items-start py-8 px-2'}>
|
||||
|
||||
{/* 内容 */}
|
||||
<div className='w-full max-w-3xl xl:px-14 lg:px-4 '>
|
||||
<div className={`w-full ${fullWidth ? '' : 'max-w-3xl'} xl:px-14 lg:px-4`}>
|
||||
<Transition
|
||||
show={!onLoading}
|
||||
appear={true}
|
||||
@@ -89,7 +89,7 @@ const LayoutBase = props => {
|
||||
</div>
|
||||
|
||||
{/* 侧边栏 */}
|
||||
<SideBar {...props} />
|
||||
{!fullWidth && <SideBar {...props} />}
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
@@ -17,13 +17,13 @@ import WWAds from '@/components/WWAds'
|
||||
*/
|
||||
export default function ArticleDetail(props) {
|
||||
const { post, prev, next } = props
|
||||
const { locale } = useGlobal()
|
||||
const { locale, fullWidth } = useGlobal()
|
||||
|
||||
if (!post) {
|
||||
return <></>
|
||||
}
|
||||
return (
|
||||
<div id="container" className="max-w-5xl overflow-x-auto flex-grow mx-auto w-screen md:w-full ">
|
||||
<div id="container" className={`${fullWidth ? 'px-10' : 'max-w-5xl '} overflow-x-auto flex-grow mx-auto w-screen md:w-full`}>
|
||||
{post?.type && !post?.type !== 'Page' && post?.pageCover && (
|
||||
<div className="w-full relative md:flex-shrink-0 overflow-hidden">
|
||||
<LazyImage alt={post.title} src={post?.pageCover} className='object-center w-full' />
|
||||
|
||||
@@ -13,6 +13,7 @@ import { useFukasawaGlobal } from '..'
|
||||
import CONFIG from '@/themes/fukasawa/config'
|
||||
import { AdSlot } from '@/components/GoogleAdsense'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import MailChimpForm from './MailChimpForm'
|
||||
|
||||
/**
|
||||
* 侧边栏
|
||||
@@ -82,7 +83,11 @@ function AsideLeft(props) {
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<AdSlot type='in-article'/>
|
||||
<MailChimpForm />
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<AdSlot type='in-article' />
|
||||
</section>
|
||||
|
||||
{router.asPath !== '/tag' && <section className='flex flex-col'>
|
||||
|
||||
@@ -11,6 +11,7 @@ const BlogCard = ({ index, post, showSummary, siteInfo }) => {
|
||||
post.pageCoverThumbnail = siteInfo?.pageCover
|
||||
}
|
||||
const showPageCover = siteConfig('FUKASAWA_POST_LIST_COVER', null, CONFIG) && post?.pageCoverThumbnail
|
||||
const SUB_PATH = siteConfig('SUB_PATH', '')
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -25,7 +26,7 @@ const BlogCard = ({ index, post, showSummary, siteInfo }) => {
|
||||
{/* 封面图 */}
|
||||
{showPageCover && (
|
||||
<div className="flex-grow mb-3 w-full duration-200 cursor-pointer transform overflow-hidden">
|
||||
<Link href={`${siteConfig('SUB_PATH', '')}/${post.slug}`} passHref legacyBehavior>
|
||||
<Link href={`${SUB_PATH}/${post.slug}`} passHref legacyBehavior>
|
||||
<LazyImage
|
||||
src={post?.pageCoverThumbnail}
|
||||
alt={post?.title || siteConfig('TITLE')}
|
||||
@@ -37,7 +38,7 @@ const BlogCard = ({ index, post, showSummary, siteInfo }) => {
|
||||
|
||||
{/* 文字部分 */}
|
||||
<div className="flex flex-col w-full">
|
||||
<Link passHref href={`${siteConfig('SUB_PATH', '')}/${post.slug}`}
|
||||
<Link passHref href={`${SUB_PATH}/${post.slug}`}
|
||||
className={`break-words cursor-pointer font-bold hover:underline text-xl ${showPreview ? 'justify-center' : 'justify-start'} leading-tight text-gray-700 dark:text-gray-100 hover:text-blue-500 dark:hover:text-blue-400`}
|
||||
>
|
||||
{post.title}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { useGlobal } from '@/lib/global'
|
||||
|
||||
/**
|
||||
* 空白博客 列表
|
||||
@@ -5,8 +6,9 @@
|
||||
* @constructor
|
||||
*/
|
||||
const BlogListEmpty = ({ currentSearch }) => {
|
||||
const { locale } = useGlobal()
|
||||
return <div className='flex items-center justify-center min-h-screen mx-auto md:-mt-20'>
|
||||
<p className='text-gray-500 dark:text-gray-300'>没有找到文章 {(currentSearch && <div>{currentSearch}</div>)}</p>
|
||||
<p className='text-gray-500 dark:text-gray-300'>{locale.COMMON.NO_RESULTS_FOUND} {(currentSearch && <div>{currentSearch}</div>)}</p>
|
||||
</div>
|
||||
}
|
||||
export default BlogListEmpty
|
||||
|
||||
62
themes/fukasawa/components/MailChimpForm.js
Normal file
62
themes/fukasawa/components/MailChimpForm.js
Normal file
@@ -0,0 +1,62 @@
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { subscribeToNewsletter } from '@/lib/mailchimp'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import CONFIG from '../config'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
|
||||
/**
|
||||
* 邮件订阅表单
|
||||
* @returns
|
||||
*/
|
||||
export default function MailChimpForm() {
|
||||
const formRef = useRef()
|
||||
const [success, setSuccess] = useState(false)
|
||||
const { locale } = useGlobal()
|
||||
|
||||
useEffect(() => {
|
||||
const form = formRef.current
|
||||
const handleSubmit = (e) => {
|
||||
e.preventDefault()
|
||||
const email = document.querySelector('#newsletter').value
|
||||
subscribeToNewsletter(email).then(response => {
|
||||
console.log('Subscription succeeded:', response)
|
||||
// 在此处添加成功订阅后的操作
|
||||
setSuccess(true)
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Subscription failed:', error)
|
||||
// 在此处添加订阅失败后的操作
|
||||
})
|
||||
}
|
||||
form?.addEventListener('submit', handleSubmit)
|
||||
return () => {
|
||||
form?.removeEventListener('submit', handleSubmit)
|
||||
}
|
||||
}, [subscribeToNewsletter])
|
||||
|
||||
return <>
|
||||
{siteConfig('FUKASAWA_MAILCHIMP_FORM', null, CONFIG) && <div className="sm:col-span-6 md:col-span-3 lg:col-span-3">
|
||||
<h6 className="text-gray-800 font-medium mb-2">{locale.MAILCHIMP.SUBSCRIBE}</h6>
|
||||
<p className="text-sm text-gray-600 mb-4">{locale.MAILCHIMP.MSG}</p>
|
||||
<form ref={formRef}>
|
||||
<div className="flex flex-wrap mb-4">
|
||||
<div className="w-full">
|
||||
<label className="block text-sm sr-only" htmlFor="newsletter">{locale.MAILCHIMP.EMAIL}</label>
|
||||
<div className="relative flex items-center max-w-xs">
|
||||
<input disabled={success} id="newsletter" type="email" className="form-input w-full text-gray-800 px-3 py-2 pr-12 text-sm" placeholder={locale.MAILCHIMP.EMAIL} required />
|
||||
<button disabled={success} type="submit" className="absolute inset-0 left-auto" aria-label="Subscribe">
|
||||
<span className="absolute inset-0 right-auto w-px -ml-px my-2 bg-gray-300" aria-hidden="true"></span>
|
||||
<svg className="w-3 h-3 fill-current text-blue-600 mx-3 shrink-0" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.707 5.293L7 .586 5.586 2l3 3H0v2h8.586l-3 3L7 11.414l4.707-4.707a1 1 0 000-1.414z" fillRule="nonzero" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
{/* Success message */}
|
||||
{success && <p className="mt-2 text-green-600 text-sm">Thanks for subscribing!</p>}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
}
|
||||
</>
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
const CONFIG = {
|
||||
FUKASAWA_MAILCHIMP_FORM: false, // 邮件订阅表单
|
||||
|
||||
FUKASAWA_POST_LIST_COVER: true, // 文章列表显示图片封面
|
||||
FUKASAWA_POST_LIST_COVER_FORCE: false, // 即使没有封面也将站点背景图设置为封面
|
||||
|
||||
@@ -46,9 +46,9 @@ export const useFukasawaGlobal = () => useContext(ThemeGlobalFukasawa)
|
||||
const LayoutBase = (props) => {
|
||||
const { children, headerSlot, meta } = props
|
||||
const leftAreaSlot = <Live2D />
|
||||
const { onLoading } = useGlobal()
|
||||
const { onLoading, fullWidth } = useGlobal()
|
||||
|
||||
const FUKASAWA_SIDEBAR_COLLAPSE_SATUS_DEFAULT = siteConfig('FUKASAWA_SIDEBAR_COLLAPSE_SATUS_DEFAULT', null, CONFIG)
|
||||
const FUKASAWA_SIDEBAR_COLLAPSE_SATUS_DEFAULT = fullWidth || siteConfig('FUKASAWA_SIDEBAR_COLLAPSE_SATUS_DEFAULT', null, CONFIG)
|
||||
|
||||
// 侧边栏折叠从 本地存储中获取 open 状态的初始值
|
||||
const [isCollapsed, setIsCollapse] = useState(() => {
|
||||
@@ -80,7 +80,7 @@ const LayoutBase = (props) => {
|
||||
<AsideLeft {...props} slot={leftAreaSlot} />
|
||||
|
||||
<main id='wrapper' className='relative flex w-full py-8 justify-center bg-day dark:bg-night'>
|
||||
<div id='container-inner' className='2xl:max-w-6xl md:max-w-4xl w-full relative z-10'>
|
||||
<div id='container-inner' className={`${fullWidth ? '' : '2xl:max-w-6xl md:max-w-4xl'} w-full relative z-10`}>
|
||||
<Transition
|
||||
show={!onLoading}
|
||||
appear={true}
|
||||
|
||||
@@ -30,9 +30,7 @@ export const MenuBarMobile = (props) => {
|
||||
|
||||
return (
|
||||
<nav id='nav' className=' text-md'>
|
||||
{/* {links.map(link => <NormalMenu key={link?.id} link={link}/>)} */}
|
||||
{links?.map((link, index) => <MenuItemCollapse onHeightChange={props.onHeightChange} key={index} link={link}/>)}
|
||||
|
||||
</nav>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { useGlobal } from '@/lib/global'
|
||||
|
||||
/**
|
||||
* 空白博客 列表
|
||||
@@ -5,8 +6,9 @@
|
||||
* @constructor
|
||||
*/
|
||||
const NavPostListEmpty = ({ currentSearch }) => {
|
||||
const { locale } = useGlobal()
|
||||
return <div className='flex w-full items-center justify-center min-h-screen mx-auto md:-mt-20'>
|
||||
<p className='text-gray-500 dark:text-gray-300'>没有找到文章 {(currentSearch && <div>{currentSearch}</div>)}</p>
|
||||
<p className='text-gray-500 dark:text-gray-300'>{locale.COMMON.NO_RESULTS_FOUND} {(currentSearch && <div>{currentSearch}</div>)}</p>
|
||||
</div>
|
||||
}
|
||||
export default NavPostListEmpty
|
||||
|
||||
@@ -49,7 +49,7 @@ export const useGitBookGlobal = () => useContext(ThemeGlobalGitbook)
|
||||
*/
|
||||
const LayoutBase = (props) => {
|
||||
const { children, post, allNavPages, slotLeft, slotRight, slotTop, meta } = props
|
||||
const { onLoading } = useGlobal()
|
||||
const { onLoading, fullWidth } = useGlobal()
|
||||
const router = useRouter()
|
||||
const [tocVisible, changeTocVisible] = useState(false)
|
||||
const [pageNavVisible, changePageNavVisible] = useState(false)
|
||||
@@ -73,7 +73,9 @@ const LayoutBase = (props) => {
|
||||
<main id='wrapper' className={(JSON.parse(siteConfig('LAYOUT_SIDEBAR_REVERSE')) ? 'flex-row-reverse' : '') + 'relative flex justify-between w-full h-full mx-auto'}>
|
||||
|
||||
{/* 左侧推拉抽屉 */}
|
||||
<div className={'font-sans hidden md:block border-r dark:border-transparent relative z-10 '}>
|
||||
{fullWidth
|
||||
? null
|
||||
: (<div className={'font-sans hidden md:block border-r dark:border-transparent relative z-10 '}>
|
||||
<div className='w-72 py-14 px-6 sticky top-0 overflow-y-scroll h-screen scroll-hidden'>
|
||||
{slotLeft}
|
||||
<SearchInput className='my-3 rounded-md' />
|
||||
@@ -87,11 +89,11 @@ const LayoutBase = (props) => {
|
||||
<div className='w-72 fixed left-0 bottom-0 z-20 bg-white'>
|
||||
<Footer {...props} />
|
||||
</div>
|
||||
</div>
|
||||
</div>) }
|
||||
|
||||
<div id='center-wrapper' className='flex flex-col justify-between w-full relative z-10 pt-14 min-h-screen'>
|
||||
|
||||
<div id='container-inner' className='w-full px-7 max-w-3xl justify-center mx-auto'>
|
||||
<div id='container-inner' className={`w-full px-7 ${fullWidth ? 'px-10' : 'max-w-3xl'} justify-center mx-auto`}>
|
||||
{slotTop}
|
||||
<WWAds className='w-full' orientation='horizontal'/>
|
||||
|
||||
@@ -124,27 +126,29 @@ const LayoutBase = (props) => {
|
||||
</div>
|
||||
|
||||
{/* 右侧侧推拉抽屉 */}
|
||||
<div style={{ width: '32rem' }} className={'hidden xl:block dark:border-transparent relative z-10 '}>
|
||||
<div className='py-14 px-6 sticky top-0'>
|
||||
<ArticleInfo post={props?.post ? props?.post : props.notice} />
|
||||
{fullWidth
|
||||
? null
|
||||
: <div style={{ width: '32rem' }} className={'hidden xl:block dark:border-transparent relative z-10 '}>
|
||||
<div className='py-14 px-6 sticky top-0'>
|
||||
<ArticleInfo post={props?.post ? props?.post : props.notice} />
|
||||
|
||||
<div className='py-4'>
|
||||
<Catalog {...props} />
|
||||
{slotRight}
|
||||
{router.route === '/' && <>
|
||||
<InfoCard {...props} />
|
||||
{siteConfig('GITBOOK_WIDGET_REVOLVER_MAPS', null, CONFIG) === 'true' && <RevolverMaps />}
|
||||
<Live2D />
|
||||
</>}
|
||||
{/* gitbook主题首页只显示公告 */}
|
||||
<Announcement {...props} />
|
||||
</div>
|
||||
<div className='py-4'>
|
||||
<Catalog {...props} />
|
||||
{slotRight}
|
||||
{router.route === '/' && <>
|
||||
<InfoCard {...props} />
|
||||
{siteConfig('GITBOOK_WIDGET_REVOLVER_MAPS', null, CONFIG) === 'true' && <RevolverMaps />}
|
||||
<Live2D />
|
||||
</>}
|
||||
{/* gitbook主题首页只显示公告 */}
|
||||
<Announcement {...props} />
|
||||
</div>
|
||||
|
||||
<AdSlot type='in-article' />
|
||||
<Live2D />
|
||||
<AdSlot type='in-article' />
|
||||
<Live2D />
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>}
|
||||
|
||||
</main>
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@ export function InfoCard(props) {
|
||||
const router = useRouter()
|
||||
// 在文章详情页特殊处理
|
||||
const isSlugPage = router.pathname.indexOf('/[prefix]') === 0
|
||||
console.log('TITLE:', siteConfig('TITLE'))
|
||||
return (
|
||||
<Card className='bg-[#4f65f0] dark:bg-yellow-600 text-white flex flex-col w-72 overflow-hidden relative'>
|
||||
{/* 信息卡牌第一行 */}
|
||||
|
||||
@@ -29,8 +29,7 @@ export const MenuListSide = (props) => {
|
||||
|
||||
return (
|
||||
<nav className='flex-col space-y-2'>
|
||||
{/* {links.map(link => <MenuItemNormal key={link?.id} link={link} />)} */}
|
||||
{links?.map(link => <MenuItemCollapse key={link?.id} link={link} />)}
|
||||
{links?.map((link, index) => <MenuItemCollapse key={index} link={link} />)}
|
||||
</nav>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -57,6 +57,10 @@ const LayoutBase = props => {
|
||||
meta
|
||||
} = props
|
||||
|
||||
// 全屏模式下的最大宽度
|
||||
const { fullWidth } = useGlobal()
|
||||
const maxWidth = fullWidth ? 'max-w-[96rem] mx-auto' : 'max-w-[86rem]' // 普通最大宽度是86rem和顶部菜单栏对齐,留空则与窗口对齐
|
||||
|
||||
return (
|
||||
<div
|
||||
id="theme-heo"
|
||||
@@ -72,7 +76,7 @@ const LayoutBase = props => {
|
||||
{/* 主区块 */}
|
||||
<main
|
||||
id="wrapper-outer"
|
||||
className={'flex-grow w-full max-w-[86rem] mx-auto relative md:px-5'}
|
||||
className={`flex-grow w-full ${maxWidth} mx-auto relative md:px-5`}
|
||||
>
|
||||
<div
|
||||
id="container-inner"
|
||||
@@ -131,10 +135,10 @@ const LayoutIndex = props => {
|
||||
<CategoryBar {...props} />
|
||||
{siteConfig('POST_LIST_STYLE') === 'page'
|
||||
? (
|
||||
<BlogPostListPage {...props} />
|
||||
<BlogPostListPage {...props} />
|
||||
)
|
||||
: (
|
||||
<BlogPostListScroll {...props} />
|
||||
<BlogPostListScroll {...props} />
|
||||
)}
|
||||
</div>
|
||||
</LayoutBase>
|
||||
@@ -165,10 +169,10 @@ const LayoutPostList = props => {
|
||||
<CategoryBar {...props} />
|
||||
{siteConfig('POST_LIST_STYLE') === 'page'
|
||||
? (
|
||||
<BlogPostListPage {...props} />
|
||||
<BlogPostListPage {...props} />
|
||||
)
|
||||
: (
|
||||
<BlogPostListScroll {...props} />
|
||||
<BlogPostListScroll {...props} />
|
||||
)}
|
||||
</div>
|
||||
</LayoutBase>
|
||||
@@ -218,18 +222,18 @@ const LayoutSearch = props => {
|
||||
<div id="post-outer-wrapper" className="px-5 md:px-0">
|
||||
{!currentSearch
|
||||
? (
|
||||
<SearchNav {...props} />
|
||||
<SearchNav {...props} />
|
||||
)
|
||||
: (
|
||||
<div id="posts-wrapper">
|
||||
{siteConfig('POST_LIST_STYLE') === 'page'
|
||||
? (
|
||||
<BlogPostListPage {...props} />
|
||||
)
|
||||
: (
|
||||
<BlogPostListScroll {...props} />
|
||||
)}
|
||||
</div>
|
||||
<div id="posts-wrapper">
|
||||
{siteConfig('POST_LIST_STYLE') === 'page'
|
||||
? (
|
||||
<BlogPostListPage {...props} />
|
||||
)
|
||||
: (
|
||||
<BlogPostListScroll {...props} />
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</LayoutBase>
|
||||
@@ -284,7 +288,7 @@ const LayoutArchive = props => {
|
||||
*/
|
||||
const LayoutSlug = props => {
|
||||
const { post, lock, validPassword } = props
|
||||
const { locale } = useGlobal()
|
||||
const { locale, fullWidth } = useGlobal()
|
||||
|
||||
const [hasCode, setHasCode] = useState(false)
|
||||
|
||||
@@ -294,7 +298,7 @@ const LayoutSlug = props => {
|
||||
}, [])
|
||||
|
||||
// 右侧栏
|
||||
const slotRight = <SideRight {...props} />
|
||||
const slotRight = fullWidth ? null : <SideRight {...props} />
|
||||
const headerSlot = (
|
||||
<header
|
||||
data-aos="fade-up"
|
||||
@@ -307,12 +311,12 @@ const LayoutSlug = props => {
|
||||
<div id="nav-bar-wrapper">
|
||||
<NavBar {...props} />
|
||||
</div>
|
||||
<PostHeader {...props} />
|
||||
{fullWidth ? null : <PostHeader {...props} />}
|
||||
</header>
|
||||
)
|
||||
const commentEnable = siteConfig('COMMENT_TWIKOO_ENV_ID') || siteConfig('COMMENT_WALINE_SERVER_URL') || siteConfig('COMMENT_VALINE_APP_ID') ||
|
||||
siteConfig('COMMENT_GISCUS_REPO') || siteConfig('COMMENT_CUSDIS_APP_ID') || siteConfig('COMMENT_UTTERRANCES_REPO') ||
|
||||
siteConfig('COMMENT_GITALK_CLIENT_ID') || siteConfig('COMMENT_WEBMENTION_ENABLE')
|
||||
siteConfig('COMMENT_GISCUS_REPO') || siteConfig('COMMENT_CUSDIS_APP_ID') || siteConfig('COMMENT_UTTERRANCES_REPO') ||
|
||||
siteConfig('COMMENT_GITALK_CLIENT_ID') || siteConfig('COMMENT_WEBMENTION_ENABLE')
|
||||
|
||||
return (
|
||||
<LayoutBase
|
||||
@@ -322,7 +326,7 @@ const LayoutSlug = props => {
|
||||
showTag={false}
|
||||
slotRight={slotRight}
|
||||
>
|
||||
<div className={`w-full xl:max-w-5xl ${hasCode ? 'xl:w-[73.15vw]' : ''} lg:hover:shadow lg:border rounded-2xl lg:px-2 lg:py-4 bg-white dark:bg-[#18171d] dark:border-gray-600 article`}>
|
||||
<div className={`w-full ${fullWidth ? '' : 'xl:max-w-5xl'} ${hasCode ? 'xl:w-[73.15vw]' : ''} lg:hover:shadow lg:border rounded-2xl lg:px-2 lg:py-4 bg-white dark:bg-[#18171d] dark:border-gray-600 article`}>
|
||||
{lock && <ArticleLock validPassword={validPassword} />}
|
||||
|
||||
{!lock && (
|
||||
@@ -360,21 +364,23 @@ const LayoutSlug = props => {
|
||||
)}
|
||||
</article>
|
||||
|
||||
<div className={`${commentEnable && post ? '' : 'hidden'}`}>
|
||||
<hr className="my-4 border-dashed" />
|
||||
{fullWidth
|
||||
? null
|
||||
: <div className={`${commentEnable && post ? '' : 'hidden'}`}>
|
||||
<hr className="my-4 border-dashed" />
|
||||
|
||||
{/* 评论互动 */}
|
||||
<div className="duration-200 overflow-x-auto px-5">
|
||||
<div className="text-2xl dark:text-white">
|
||||
<i className="fas fa-comment mr-1" />
|
||||
{locale.COMMON.COMMENTS}
|
||||
{/* 评论互动 */}
|
||||
<div className="duration-200 overflow-x-auto px-5">
|
||||
<div className="text-2xl dark:text-white">
|
||||
<i className="fas fa-comment mr-1" />
|
||||
{locale.COMMON.COMMENTS}
|
||||
</div>
|
||||
<Comment frontMatter={post} className="" />
|
||||
<div className="py-2">
|
||||
<AdSlot />
|
||||
</div>
|
||||
</div>
|
||||
<Comment frontMatter={post} className="" />
|
||||
<div className="py-2">
|
||||
<AdSlot />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@@ -390,7 +396,7 @@ const LayoutSlug = props => {
|
||||
*/
|
||||
const Layout404 = props => {
|
||||
const { meta, siteInfo } = props
|
||||
const { onLoading } = useGlobal()
|
||||
const { onLoading, fullWidth } = useGlobal()
|
||||
return (
|
||||
<div
|
||||
id="theme-heo"
|
||||
@@ -411,7 +417,7 @@ const Layout404 = props => {
|
||||
{/* 主区块 */}
|
||||
<main
|
||||
id="wrapper-outer"
|
||||
className={'flex-grow max-w-4xl w-screen mx-auto px-5'}
|
||||
className={`flex-grow ${fullWidth ? '' : 'max-w-4xl'} w-screen mx-auto px-5`}
|
||||
>
|
||||
<div id="error-wrapper" className={'w-full mx-auto justify-center'}>
|
||||
<Transition
|
||||
|
||||
@@ -35,8 +35,7 @@ export const MenuListSide = (props) => {
|
||||
|
||||
return (
|
||||
<nav>
|
||||
{/* {links.map(link => <MenuItemNormal key={link?.id} link={link} />)} */}
|
||||
{links?.map(link => <MenuItemCollapse key={link?.id} link={link} />)}
|
||||
{links?.map((link, index) => <MenuItemCollapse key={index} link={link} />)}
|
||||
</nav>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ export const MenuListTop = (props) => {
|
||||
|
||||
return (<>
|
||||
<nav id='nav-mobile' className='leading-8 justify-center font-light w-full flex'>
|
||||
{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>
|
||||
</>)
|
||||
}
|
||||
|
||||
@@ -7,11 +7,17 @@ import { formatDateFmt } from '@/lib/formatDate'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
|
||||
export default function PostHeader({ post, siteInfo }) {
|
||||
const { locale } = useGlobal()
|
||||
const { locale, fullWidth } = useGlobal()
|
||||
|
||||
if (!post) {
|
||||
return <></>
|
||||
}
|
||||
|
||||
// 文章全屏隐藏标头
|
||||
if (fullWidth) {
|
||||
return <div className='my-8'/>
|
||||
}
|
||||
|
||||
const headerImage = post?.pageCover ? post.pageCover : siteInfo?.pageCover
|
||||
|
||||
return (
|
||||
|
||||
@@ -38,6 +38,12 @@ export default function SideRight(props) {
|
||||
} = props
|
||||
|
||||
const { locale } = useGlobal()
|
||||
|
||||
// 文章全屏处理
|
||||
if (post && post?.fullWidth) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<div id='sideRight' className={'space-y-4 lg:w-80 lg:pt-0 px-2 pt-4'}>
|
||||
<InfoCard {...props} />
|
||||
|
||||
@@ -41,7 +41,7 @@ import { siteConfig } from '@/lib/config'
|
||||
*/
|
||||
const LayoutBase = props => {
|
||||
const { children, headerSlot, floatSlot, slotTop, meta, className } = props
|
||||
const { onLoading } = useGlobal()
|
||||
const { onLoading, fullWidth } = useGlobal()
|
||||
|
||||
return (
|
||||
<div id='theme-hexo'>
|
||||
@@ -70,7 +70,7 @@ const LayoutBase = props => {
|
||||
{/* 主区块 */}
|
||||
<main id="wrapper" className={`${siteConfig('HEXO_HOME_BANNER_ENABLE', null, CONFIG) ? '' : 'pt-16'} bg-hexo-background-gray dark:bg-black w-full py-8 md:px-8 lg:px-24 min-h-screen relative`}>
|
||||
<div id="container-inner" className={(JSON.parse(siteConfig('LAYOUT_SIDEBAR_REVERSE')) ? 'flex-row-reverse' : '') + ' w-full mx-auto lg:flex lg:space-x-4 justify-center relative z-10'} >
|
||||
<div className={`${className || ''} w-full max-w-4xl h-full overflow-hidden`}>
|
||||
<div className={`${className || ''} w-full ${fullWidth ? '' : 'max-w-4xl'} h-full overflow-hidden`}>
|
||||
|
||||
<Transition
|
||||
show={!onLoading}
|
||||
|
||||
@@ -36,12 +36,12 @@ export default function Header() {
|
||||
{/* Desktop sign in links */}
|
||||
<ul className="flex grow justify-end flex-wrap items-center">
|
||||
<li>
|
||||
<Link href={siteConfig('LANDING_HEDEAR_BUTTON_1_URL', null, CONFIG)} target='_blank' className="font-medium hover:font-bold text-gray-600 hover:text-gray-900 px-5 py-3 flex items-center transition duration-150 ease-in-out">
|
||||
<Link href={siteConfig('LANDING_HEADER_BUTTON_1_URL', null, CONFIG)} target='_blank' className="font-medium hover:font-bold text-gray-600 hover:text-gray-900 px-5 py-3 flex items-center transition duration-150 ease-in-out">
|
||||
<div>{siteConfig('LANDING_HEADER_BUTTON_1_TITLE', null, CONFIG)}</div>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link href={siteConfig('LANDING_HEDEAR_BUTTON_2_URL', null, CONFIG)} target='_blank' className="btn-sm text-gray-200 bg-gray-900 hover:bg-gray-800 ml-3">
|
||||
<Link href={siteConfig('LANDING_HEADER_BUTTON_2_URL', null, CONFIG)} target='_blank' className="btn-sm text-gray-200 bg-gray-900 hover:bg-gray-800 ml-3">
|
||||
<span>{siteConfig('LANDING_HEADER_BUTTON_2_TITLE', null, CONFIG)}</span>
|
||||
<svg className="w-3 h-3 fill-current text-gray-400 shrink-0 ml-2 -mr-1" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.707 5.293L7 .586 5.586 2l3 3H0v2h8.586l-3 3L7 11.414l4.707-4.707a1 1 0 000-1.414z" fillRule="nonzero" />
|
||||
|
||||
@@ -67,12 +67,12 @@ export default function MobileMenu() {
|
||||
>
|
||||
<ul className="px-5 py-2">
|
||||
<li>
|
||||
<Link href={siteConfig('LANDING_HEDEAR_BUTTON_1_URL', null, CONFIG)} className="flex font-medium w-full text-gray-600 hover:text-gray-900 py-2 justify-center" onClick={() => setMobileNavOpen(false)}>
|
||||
<Link href={siteConfig('LANDING_HEADER_BUTTON_1_URL', null, CONFIG)} className="flex font-medium w-full text-gray-600 hover:text-gray-900 py-2 justify-center" onClick={() => setMobileNavOpen(false)}>
|
||||
<div>{siteConfig('LANDING_HEADER_BUTTON_1_TITLE', null, CONFIG)}</div>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link href={siteConfig('LANDING_HEDEAR_BUTTON_2_URL', null, CONFIG)} className="btn-sm text-gray-200 bg-gray-900 hover:bg-gray-800 w-full my-2" onClick={() => setMobileNavOpen(false)}>
|
||||
<Link href={siteConfig('LANDING_HEADER_BUTTON_2_URL', null, CONFIG)} className="btn-sm text-gray-200 bg-gray-900 hover:bg-gray-800 w-full my-2" onClick={() => setMobileNavOpen(false)}>
|
||||
<span>{siteConfig('LANDING_HEADER_BUTTON_2_TITLE', null, CONFIG)}</span>
|
||||
<svg className="w-3 h-3 fill-current text-gray-400 shrink-0 ml-2 -mr-1" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.707 5.293L7 .586 5.586 2l3 3H0v2h8.586l-3 3L7 11.414l4.707-4.707a1 1 0 000-1.414z" fill="#999" fillRule="nonzero" />
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
const CONFIG = {
|
||||
|
||||
LANDING_HEADER_BUTTON_1_TITLE: 'Github开源',
|
||||
LANDING_HEDEAR_BUTTON_1_URL: 'https://github.com/tangly1024/NotionNext',
|
||||
LANDING_HEADER_BUTTON_1_URL: 'https://github.com/tangly1024/NotionNext',
|
||||
|
||||
LANDING_HEADER_BUTTON_2_TITLE: '作者博客',
|
||||
LANDING_HEDEAR_BUTTON_2_URL: 'https://blog.tangly1024.com/',
|
||||
LANDING_HEADER_BUTTON_2_URL: 'https://blog.tangly1024.com/',
|
||||
|
||||
// 首页大图英雄板块
|
||||
LANDING_HERO_TITLE_1: 'NotionNext',
|
||||
|
||||
@@ -29,8 +29,7 @@ export const MenuListSide = (props) => {
|
||||
|
||||
return (
|
||||
<nav>
|
||||
{/* {links.map(link => <MenuItemNormal key={link?.id} link={link} />)} */}
|
||||
{links?.map(link => <MenuItemCollapse key={link?.id} link={link} />)}
|
||||
{links?.map((link, index) => <MenuItemCollapse key={index} link={link} />)}
|
||||
</nav>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ export const MenuListTop = (props) => {
|
||||
|
||||
return (
|
||||
<nav id='nav' className='leading-8 flex justify-center font-light w-full'>
|
||||
{links?.map(link => <MenuItemDrop key={link?.id} link={link}/>)}
|
||||
{links?.map((link, index) => <MenuItemDrop key={index} link={link}/>)}
|
||||
</nav>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ import { siteConfig } from '@/lib/config'
|
||||
*/
|
||||
const LayoutBase = props => {
|
||||
const { children, headerSlot, meta, siteInfo, containerSlot, post } = props
|
||||
const { onLoading } = useGlobal()
|
||||
const { onLoading, fullWidth } = useGlobal()
|
||||
|
||||
return (
|
||||
<div id='theme-matery' className="min-h-screen flex flex-col justify-between bg-hexo-background-gray dark:bg-black w-full">
|
||||
@@ -69,11 +69,11 @@ const LayoutBase = props => {
|
||||
|
||||
<main id="wrapper" className={`${siteConfig('MATERY_HOME_BANNER_ENABLE', null, CONFIG) ? '' : 'pt-16'} flex-1 w-full py-8 md:px-8 lg:px-24 relative`}>
|
||||
{/* 嵌入区域 */}
|
||||
<div id="container-slot" className={`w-full max-w-6xl ${post && ' lg:max-w-3xl 2xl:max-w-4xl '} mt-6 px-3 mx-auto lg:flex lg:space-x-4 justify-center relative z-10`}>
|
||||
<div id="container-slot" className={`w-full ${fullWidth ? '' : 'max-w-6xl'} ${post && ' lg:max-w-3xl 2xl:max-w-4xl '} mt-6 px-3 mx-auto lg:flex lg:space-x-4 justify-center relative z-10`}>
|
||||
{containerSlot}
|
||||
</div>
|
||||
|
||||
<div id="container-inner" className="w-full min-h-fit max-w-6xl mx-auto lg:flex lg:space-x-4 justify-center relative z-10">
|
||||
<div id="container-inner" className={`w-full min-h-fit ${fullWidth ? '' : 'max-w-6xl'} mx-auto lg:flex lg:space-x-4 justify-center relative z-10`}>
|
||||
<Transition
|
||||
show={!onLoading}
|
||||
appear={true}
|
||||
@@ -191,13 +191,15 @@ const LayoutArchive = (props) => {
|
||||
*/
|
||||
const LayoutSlug = props => {
|
||||
const { post, lock, validPassword } = props
|
||||
const { fullWidth } = useGlobal()
|
||||
const headerSlot = fullWidth ? null : <PostHeader {...props} />
|
||||
|
||||
return (<LayoutBase {...props} headerSlot={<PostHeader {...props} />} showCategory={false} showTag={false} floatRightBottom={<JumpToCommentButton />}>
|
||||
return (<LayoutBase {...props} headerSlot={headerSlot} showCategory={false} showTag={false} floatRightBottom={<JumpToCommentButton />}>
|
||||
|
||||
<div id='inner-wrapper' className={'w-full lg:max-w-3xl 2xl:max-w-4xl'} >
|
||||
<div id='inner-wrapper' className={`w-full ${fullWidth ? '' : 'lg:max-w-3xl 2xl:max-w-4xl'}`} >
|
||||
|
||||
{/* 文章主体卡片 */}
|
||||
<div className="-mt-32 transition-all duration-300 rounded-md mx-3 lg:border lg:rounded-xl lg:py-4 bg-white dark:bg-hexo-black-gray dark:border-black">
|
||||
<div className={`${fullWidth ? '' : '-mt-32'} transition-all duration-300 rounded-md mx-3 lg:border lg:rounded-xl lg:py-4 bg-white dark:bg-hexo-black-gray dark:border-black`}>
|
||||
|
||||
{lock && <ArticleLock validPassword={validPassword} />}
|
||||
|
||||
@@ -221,7 +223,7 @@ const LayoutSlug = props => {
|
||||
<article itemScope >
|
||||
|
||||
{/* Notion文章主体 */}
|
||||
<section className='justify-center mx-auto max-w-2xl lg:max-w-full'>
|
||||
<section className={`justify-center mx-auto ${fullWidth ? '' : 'max-w-2xl lg:max-w-full'}`}>
|
||||
{post && <NotionPage post={post} />}
|
||||
</section>
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { useGlobal } from '@/lib/global'
|
||||
|
||||
/**
|
||||
* 空白博客 列表
|
||||
@@ -5,8 +6,9 @@
|
||||
* @constructor
|
||||
*/
|
||||
const BlogPostListEmpty = ({ currentSearch }) => {
|
||||
const { locale } = useGlobal()
|
||||
return <div className='flex w-full items-center justify-center min-h-screen mx-auto md:-mt-20'>
|
||||
<p className='text-gray-500 dark:text-gray-300'>没有找到文章 {(currentSearch && <div>{currentSearch}</div>)}</p>
|
||||
<p className='text-gray-500 dark:text-gray-300'>{locale.COMMON.NO_RESULTS_FOUND} {(currentSearch && <div>{currentSearch}</div>)}</p>
|
||||
</div>
|
||||
}
|
||||
export default BlogPostListEmpty
|
||||
|
||||
@@ -30,8 +30,7 @@ export const MenuBarMobile = (props) => {
|
||||
|
||||
return (
|
||||
<nav id='nav' className=' text-md'>
|
||||
{/* {links.map(link => <NormalMenu key={link?.id} link={link}/>)} */}
|
||||
{links?.map(link => <MenuItemCollapse onHeightChange={props.onHeightChange} key={link?.id} link={link}/>)}
|
||||
{links?.map((link, index) => <MenuItemCollapse onHeightChange={props.onHeightChange} key={index} link={link}/>)}
|
||||
|
||||
</nav>
|
||||
)
|
||||
|
||||
@@ -66,7 +66,7 @@ export default function TopNavBar(props) {
|
||||
|
||||
{/* 桌面端顶部菜单 */}
|
||||
<div className='hidden md:flex'>
|
||||
{links && links?.map(link => <MenuItemDrop key={link?.id} link={link}/>)}
|
||||
{links && links?.map((link, index) => <MenuItemDrop key={index} link={link}/>)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -51,14 +51,14 @@ const LayoutBase = props => {
|
||||
const { locale } = useGlobal()
|
||||
const router = useRouter()
|
||||
const [tocVisible, changeTocVisible] = useState(false)
|
||||
const { onLoading } = useGlobal()
|
||||
const { onLoading, fullWidth } = useGlobal()
|
||||
|
||||
return (
|
||||
<ThemeGlobalMedium.Provider value={{ tocVisible, changeTocVisible }}>
|
||||
{/* SEO相关 */}
|
||||
<CommonHead meta={meta}/>
|
||||
<CommonHead meta={meta} />
|
||||
{/* CSS样式 */}
|
||||
<Style/>
|
||||
<Style />
|
||||
|
||||
<div id='theme-medium' className='bg-white dark:bg-hexo-black-gray w-full h-full min-h-screen justify-center dark:text-gray-300'>
|
||||
|
||||
@@ -72,7 +72,7 @@ const LayoutBase = props => {
|
||||
{/* 顶部导航栏 */}
|
||||
<TopNavBar {...props} />
|
||||
|
||||
<div id='container-inner' className='px-7 max-w-5xl justify-center mx-auto min-h-screen'>
|
||||
<div id='container-inner' className={`px-7 ${fullWidth ? '' : 'max-w-5xl'} justify-center mx-auto min-h-screen`}>
|
||||
<Transition
|
||||
show={!onLoading}
|
||||
appear={true}
|
||||
@@ -96,20 +96,23 @@ const LayoutBase = props => {
|
||||
</div>
|
||||
|
||||
{/* 桌面端右侧 */}
|
||||
<div className={`hidden xl:block border-l dark:border-transparent w-96 relative z-10 ${siteConfig('MEDIUM_RIGHT_PANEL_DARK', null, CONFIG) ? 'bg-hexo-black-gray dark' : ''}`}>
|
||||
<div className='py-14 px-6 sticky top-0'>
|
||||
<Tabs>
|
||||
{slotRight}
|
||||
<div key={locale.NAV.ABOUT}>
|
||||
{router.pathname !== '/search' && <SearchInput className='mt-6 mb-12' />}
|
||||
{showInfoCard && <InfoCard {...props} />}
|
||||
{siteConfig('MEDIUM_WIDGET_REVOLVER_MAPS', null, CONFIG) === 'true' && <RevolverMaps />}
|
||||
</div>
|
||||
</Tabs>
|
||||
<Announcement post={notice} />
|
||||
<Live2D />
|
||||
</div>
|
||||
</div>
|
||||
{fullWidth
|
||||
? null
|
||||
: <div className={`hidden xl:block border-l dark:border-transparent w-96 relative z-10 ${siteConfig('MEDIUM_RIGHT_PANEL_DARK', null, CONFIG) ? 'bg-hexo-black-gray dark' : ''}`}>
|
||||
<div className='py-14 px-6 sticky top-0'>
|
||||
<Tabs>
|
||||
{slotRight}
|
||||
<div key={locale.NAV.ABOUT}>
|
||||
{router.pathname !== '/search' && <SearchInput className='mt-6 mb-12' />}
|
||||
{showInfoCard && <InfoCard {...props} />}
|
||||
{siteConfig('MEDIUM_WIDGET_REVOLVER_MAPS', null, CONFIG) === 'true' && <RevolverMaps />}
|
||||
</div>
|
||||
</Tabs>
|
||||
<Announcement post={notice} />
|
||||
<Live2D />
|
||||
</div>
|
||||
</div>}
|
||||
|
||||
</main>
|
||||
|
||||
{/* 移动端底部导航栏 */}
|
||||
@@ -162,7 +165,7 @@ const LayoutSlug = props => {
|
||||
{!lock && <div id='article-wrapper'>
|
||||
|
||||
{/* 文章信息 */}
|
||||
<ArticleInfo {...props}/>
|
||||
<ArticleInfo {...props} />
|
||||
|
||||
{/* Notion文章主体 */}
|
||||
<section className="px-1 max-w-4xl">
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { useGlobal } from '@/lib/global'
|
||||
|
||||
/**
|
||||
* 空白博客 列表
|
||||
@@ -5,8 +6,9 @@
|
||||
* @constructor
|
||||
*/
|
||||
const BlogPostListEmpty = ({ currentSearch }) => {
|
||||
const { locale } = useGlobal()
|
||||
return <div className='flex w-full items-center justify-center min-h-screen mx-auto md:-mt-20'>
|
||||
<p className='text-gray-500 dark:text-gray-300'>没有找到文章 {(currentSearch && <div>{currentSearch}</div>)}</p>
|
||||
<p className='text-gray-500 dark:text-gray-300'>{locale.COMMON.NO_RESULTS_FOUND} {(currentSearch && <div>{currentSearch}</div>)}</p>
|
||||
</div>
|
||||
}
|
||||
export default BlogPostListEmpty
|
||||
|
||||
@@ -35,9 +35,7 @@ export const MenuBarMobile = (props) => {
|
||||
|
||||
return (
|
||||
<nav id='nav' className=' text-md'>
|
||||
{/* {links.map(link => <NormalMenu key={link?.id} link={link}/>)} */}
|
||||
{links?.map((link, index) => <MenuItemCollapse onHeightChange={props.onHeightChange} key={index} link={link}/>)}
|
||||
|
||||
</nav>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ export const MenuItem = ({ link }) => {
|
||||
{/* 菜单 */}
|
||||
<div
|
||||
onClick={toggleOpenSubMenu}
|
||||
className='nav-menu dark:text-neutral-400 text-gray-500 hover:text-black dark:hover:text-white text-sm text-gray w-full items-center duration-300 pt-2 font-light select-none flex justify-between cursor-pointer' key={link?.name}>
|
||||
className='nav-menu dark:text-neutral-400 text-gray-500 hover:text-black dark:hover:text-white text-sm text-gray w-full items-center duration-300 pt-2 font-light select-none flex justify-between cursor-pointer' key={link?.to}>
|
||||
|
||||
{link?.subMenus
|
||||
? (<>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { useGlobal } from '@/lib/global'
|
||||
|
||||
/**
|
||||
* 空白博客 列表
|
||||
@@ -5,8 +6,9 @@
|
||||
* @constructor
|
||||
*/
|
||||
const NavPostListEmpty = ({ currentSearch }) => {
|
||||
const { locale } = useGlobal()
|
||||
return <div className='flex w-full items-center justify-center min-h-screen mx-auto md:-mt-20'>
|
||||
<p className='text-gray-500 dark:text-gray-300'>没有找到文章 {(currentSearch && <div>{currentSearch}</div>)}</p>
|
||||
<p className='text-gray-500 dark:text-gray-300'>{locale.COMMON.NO_RESULTS_FOUND} {(currentSearch && <div>{currentSearch}</div>)}</p>
|
||||
</div>
|
||||
}
|
||||
export default NavPostListEmpty
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { useGlobal } from '@/lib/global'
|
||||
|
||||
/**
|
||||
* 空白博客 列表
|
||||
@@ -5,8 +6,9 @@
|
||||
* @constructor
|
||||
*/
|
||||
const BlogPostListEmpty = ({ currentSearch }) => {
|
||||
const { locale } = useGlobal()
|
||||
return <div className='flex items-center justify-center min-h-screen mx-auto md:-mt-20'>
|
||||
<p className='text-gray-500 dark:text-gray-300'>没有找到文章 {(currentSearch && <div>{currentSearch}</div>)}</p>
|
||||
<p className='text-gray-500 dark:text-gray-300'>{locale.COMMON.NO_RESULTS_FOUND} {(currentSearch && <div>{currentSearch}</div>)}</p>
|
||||
</div>
|
||||
}
|
||||
export default BlogPostListEmpty
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import { useEffect } from 'react'
|
||||
|
||||
/**
|
||||
@@ -9,8 +10,12 @@ export default function WordCount() {
|
||||
countWords()
|
||||
})
|
||||
|
||||
const { locale } = useGlobal()
|
||||
|
||||
return <div id='wordCountWrapper' className='flex justify-center my-auto font-light'>
|
||||
<i className='mr-1 fas fa-file-word my-auto' /> <span className='hidden md:block'>本文字数</span> <strong id='wordCount'>0</strong> | <i className='mr-1 fas fa-clock my-auto' /> <span className='hidden md:block'>阅读时长 ≈</span> <strong id='readTime'>0</strong> 分钟
|
||||
<i className='mr-1 fas fa-file-word my-auto' />
|
||||
<span className='hidden md:block'>{locale.COMMON.WORD_COUNT}≈</span>
|
||||
<strong id='wordCount'>0</strong> | <i className='mr-1 fas fa-clock my-auto' />{locale.COMMON.READ_TIME} <span className='hidden md:block'>≈</span> <strong id='readTime'>0</strong> {locale.COMMON.MINUTE}
|
||||
</div>
|
||||
}
|
||||
|
||||
|
||||
@@ -104,12 +104,12 @@ const NavBar = props => {
|
||||
return (
|
||||
<div className="flex-shrink-0 flex">
|
||||
<ul className="hidden md:flex flex-row">
|
||||
{links?.map(link => <MenuItemDrop key={link?.id} link={link} />)}
|
||||
{links?.map((link, index) => <MenuItemDrop key={index} link={link} />)}
|
||||
</ul>
|
||||
<div className='md:hidden'>
|
||||
<Collapse collapseRef={collapseRef} isOpen={isOpen} type='vertical' className='fixed top-16 right-6'>
|
||||
<div className='dark:border-black bg-white dark:bg-black rounded border p-2 text-sm'>
|
||||
{links?.map(link => <MenuItemCollapse key={link?.id} link={link} onHeightChange={(param) => collapseRef.current?.updateCollapseHeight(param)}/>)}
|
||||
{links?.map((link, index) => <MenuItemCollapse key={index} link={link} onHeightChange={(param) => collapseRef.current?.updateCollapseHeight(param)}/>)}
|
||||
</div>
|
||||
</Collapse>
|
||||
</div>
|
||||
|
||||
@@ -54,7 +54,7 @@ const MenuList = props => {
|
||||
return (
|
||||
<div className="flex-shrink-0">
|
||||
<ul className="hidden md:flex flex-row">
|
||||
{links?.map(link => <MenuItemDrop key={link?.id} link={link} />)}
|
||||
{links?.map((link, index) => <MenuItemDrop key={index} link={link} />)}
|
||||
<li className='my-auto px-2'>
|
||||
<FullScreenButton />
|
||||
</li>
|
||||
|
||||
@@ -70,12 +70,12 @@ const NavBar = props => {
|
||||
return (
|
||||
<div className="flex-shrink-0">
|
||||
<ul className=" hidden md:flex flex-row">
|
||||
{links?.map(link => <MenuItemDrop key={link?.id} link={link} />)}
|
||||
{links?.map((link, index) => <MenuItemDrop key={index} link={link} />)}
|
||||
</ul>
|
||||
<div className='md:hidden'><i onClick={toggleOpen} className='fas fa-bars cursor-pointer px-5 block md:hidden'></i>
|
||||
<Collapse collapseRef={collapseRef} isOpen={isOpen} type='vertical' className='fixed top-16 right-6'>
|
||||
<div className='dark:border-black bg-white dark:bg-black rounded border p-2 text-sm'>
|
||||
{links?.map(link => <MenuItemCollapse key={link?.id} link={link} onHeightChange={(param) => collapseRef.current?.updateCollapseHeight(param)} />)}
|
||||
{links?.map((link, index) => <MenuItemCollapse key={index} link={link} onHeightChange={(param) => collapseRef.current?.updateCollapseHeight(param)} />)}
|
||||
</div>
|
||||
</Collapse>
|
||||
</div>
|
||||
|
||||
@@ -4,29 +4,33 @@ import CONFIG from '../config'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { formatDateFmt } from '@/lib/formatDate'
|
||||
|
||||
export const ArticleInfo = (props) => {
|
||||
/**
|
||||
* 文章描述
|
||||
* @param {*} props
|
||||
* @returns
|
||||
*/
|
||||
export default function ArticleInfo (props) {
|
||||
const { post } = props
|
||||
|
||||
const { locale } = useGlobal()
|
||||
|
||||
return (
|
||||
<section className="flex-wrap flex mt-2 text-gray-400 dark:text-gray-400 font-light leading-8">
|
||||
<div>
|
||||
<h2
|
||||
className="blog-item-title mb-5 font-bold text-black text-xl md:text-2xl no-underline">
|
||||
{post?.title}
|
||||
</h2>
|
||||
<section className="mt-2 text-gray-600 dark:text-gray-400 leading-8">
|
||||
<h2
|
||||
className="blog-item-title mb-5 font-bold text-black text-xl md:text-2xl no-underline">
|
||||
{post?.title}
|
||||
</h2>
|
||||
|
||||
{post?.type !== 'Page' && (<>
|
||||
<div className="mb-4 text-sm text-gray-700 dark:text-gray-300">
|
||||
<div className='flex flex-wrap text-gray-700 dark:text-gray-300'>
|
||||
{post?.type !== 'Page' && (
|
||||
<div className="space-x-3 mr-4">
|
||||
<span> <i className="fa-regular fa-user"></i> <a href={siteConfig('SIMPLE_AUTHOR_LINK', null, CONFIG)}>{siteConfig('AUTHOR')}</a></span>
|
||||
<span> - <i className="fa-regular fa-clock"></i> {post?.publishDay}</span>
|
||||
{post?.category && <span> - <i className="fa-regular fa-folder"></i> <a href={`/category/${post?.category}`} className="hover:text-red-400 transition-all duration-200">{post?.category}</a></span>}
|
||||
<span> <i className="fa-regular fa-clock"></i> {post?.publishDay}</span>
|
||||
{post?.category && <span> <i className="fa-regular fa-folder"></i> <a href={`/category/${post?.category}`} className="hover:text-red-400 transition-all duration-200">{post?.category}</a></span>}
|
||||
{post?.tags && post?.tags?.length > 0 && post?.tags.map(t => <span key={t}> / <Link href={`/tag/${t}`}><span className=' hover:text-red-400 transition-all duration-200'>{t}</span></Link></span>)}
|
||||
</div>
|
||||
</>)}
|
||||
</div>)}
|
||||
|
||||
{post?.type !== 'Page' && (<>
|
||||
{post?.type !== 'Page' && (<div className=''>
|
||||
<span>{locale.COMMON.POST_TIME}:
|
||||
<Link
|
||||
href={`/archive#${formatDateFmt(post?.publishDate, 'yyyy-MM')}`}
|
||||
@@ -36,7 +40,7 @@ export const ArticleInfo = (props) => {
|
||||
</Link>
|
||||
</span>
|
||||
<span className='mr-2'>|</span>
|
||||
<span className='mx-2 text-gray-400 dark:text-gray-500'>
|
||||
<span className='mx-2 dark:text-gray-500'>
|
||||
{locale.COMMON.LAST_EDITED_TIME}: {post?.lastEditedDay}
|
||||
</span>
|
||||
<span className='mr-2'>|</span>
|
||||
@@ -45,10 +49,9 @@ export const ArticleInfo = (props) => {
|
||||
|
||||
<span className="mr-2 busuanzi_value_page_pv" />
|
||||
</span>
|
||||
</>)}
|
||||
</div>)}
|
||||
|
||||
</div>
|
||||
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import { useEffect, useRef } from 'react'
|
||||
* @param validPassword(bool) 回调函数,校验正确回调入参为true
|
||||
* @returns
|
||||
*/
|
||||
export const ArticleLock = props => {
|
||||
export default function ArticleLock (props) {
|
||||
const { validPassword } = props
|
||||
const { locale } = useGlobal()
|
||||
|
||||
|
||||
@@ -3,43 +3,60 @@ import CONFIG from '../config'
|
||||
import TwikooCommentCount from '@/components/TwikooCommentCount'
|
||||
import { formatDateFmt } from '@/lib/formatDate'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import LazyImage from '@/components/LazyImage'
|
||||
|
||||
export const BlogItem = props => {
|
||||
const { post } = props
|
||||
const showPageCover = siteConfig('SIMPLE_POST_COVER_ENABLE', false, CONFIG)
|
||||
|
||||
return <div key={post.id} className="h-42 my-6 pb-12 border-b dark:border-gray-800" >
|
||||
{/* 文章标题 */}
|
||||
|
||||
<h2 className="mb-2">
|
||||
<Link
|
||||
href={`${siteConfig('SUB_PATH', '')}/${post.slug}`}
|
||||
className="blog-item-title font-bold text-black text-2xl menu-link">
|
||||
{post.title}
|
||||
</Link>
|
||||
</h2>
|
||||
<div className='flex'>
|
||||
<div className='article-cover h-full'>
|
||||
{/* 图片封面 */}
|
||||
{showPageCover && (
|
||||
<div className="overflow-hidden mr-2 w-56 h-full">
|
||||
<Link href={`${siteConfig('SUB_PATH', '')}/${post.slug}`} passHref legacyBehavior>
|
||||
<LazyImage src={post?.pageCoverThumbnail} className='w-56 h-full object-cover object-center group-hover:scale-110 duration-500' />
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 文章信息 */}
|
||||
|
||||
<div className="mb-5 text-md text-gray-700 dark:text-gray-300 flex-wrap flex leading-6">
|
||||
<div className='space-x-2'>
|
||||
<span> <a href={siteConfig('SIMPLE_AUTHOR_LINK', null, CONFIG)} className='p-1 hover:text-red-400 transition-all duration-200'><i className="fa-regular fa-user"></i> {siteConfig('AUTHOR')}</a></span>
|
||||
<span>
|
||||
<Link className='p-1 hover:text-red-400 transition-all duration-200' href={`/archive#${formatDateFmt(post?.publishDate, 'yyyy-MM')}`}>
|
||||
<i className="fa-regular fa-clock" /> {post.date?.start_date || post.createdTime}
|
||||
<div className='article-info'>
|
||||
<h2 className="mb-2">
|
||||
<Link
|
||||
href={`${siteConfig('SUB_PATH', '')}/${post.slug}`}
|
||||
className="blog-item-title font-bold text-black text-2xl menu-link">
|
||||
{post.title}
|
||||
</Link>
|
||||
</span>
|
||||
<span><TwikooCommentCount post={post} /></span>
|
||||
</div>
|
||||
</h2>
|
||||
|
||||
<div>
|
||||
{post.category && <Link href={`/category/${post.category}`} className='p-1'> <span className="hover:text-red-400 transition-all duration-200"><i className="fa-regular fa-folder mr-0.5"/>{post.category}</span></Link>}
|
||||
{post?.tags && post?.tags?.length > 0 && post?.tags.map(t => <Link key={t} href={`/tag/${t}`} className=' hover:text-red-400 transition-all duration-200'><span > /{t}</span></Link>)}
|
||||
</div>
|
||||
</div>
|
||||
{/* 文章信息 */}
|
||||
|
||||
<div className="text-gray-700 dark:text-gray-300 leading-normal mb-6">
|
||||
{post.summary}
|
||||
{post.summary && <span>...</span>}
|
||||
<div className="mb-5 text-md text-gray-700 dark:text-gray-300 flex-wrap flex leading-6">
|
||||
<div className='space-x-2'>
|
||||
<span> <a href={siteConfig('SIMPLE_AUTHOR_LINK', null, CONFIG)} className='p-1 hover:text-red-400 transition-all duration-200'><i className="fa-regular fa-user"></i> {siteConfig('AUTHOR')}</a></span>
|
||||
<span>
|
||||
<Link className='p-1 hover:text-red-400 transition-all duration-200' href={`/archive#${formatDateFmt(post?.publishDate, 'yyyy-MM')}`}>
|
||||
<i className="fa-regular fa-clock" /> {post.date?.start_date || post.createdTime}
|
||||
</Link>
|
||||
</span>
|
||||
<span><TwikooCommentCount post={post} /></span>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{post.category && <Link href={`/category/${post.category}`} className='p-1'> <span className="hover:text-red-400 transition-all duration-200"><i className="fa-regular fa-folder mr-0.5" />{post.category}</span></Link>}
|
||||
{post?.tags && post?.tags?.length > 0 && post?.tags.map(t => <Link key={t} href={`/tag/${t}`} className=' hover:text-red-400 transition-all duration-200'><span > /{t}</span></Link>)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="text-gray-700 dark:text-gray-300 leading-normal mb-6">
|
||||
{post.summary}
|
||||
{post.summary && <span>...</span>}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='block'>
|
||||
|
||||
@@ -6,7 +6,12 @@ import { AdSlot } from '@/components/GoogleAdsense'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import CONFIG from '../config'
|
||||
|
||||
export const BlogListPage = props => {
|
||||
/**
|
||||
* 博客列表
|
||||
* @param {*} props
|
||||
* @returns
|
||||
*/
|
||||
export default function BlogListPage(props) {
|
||||
const { page = 1, posts, postCount } = props
|
||||
const router = useRouter()
|
||||
const totalPage = Math.ceil(postCount / parseInt(siteConfig('POSTS_PER_PAGE')))
|
||||
|
||||
@@ -4,7 +4,12 @@ import throttle from 'lodash.throttle'
|
||||
import { BlogItem } from './BlogItem'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
|
||||
export const BlogListScroll = props => {
|
||||
/**
|
||||
* 滚动博客列表
|
||||
* @param {*} props
|
||||
* @returns
|
||||
*/
|
||||
export default function BlogListScroll (props) {
|
||||
const { posts } = props
|
||||
const { locale } = useGlobal()
|
||||
|
||||
|
||||
@@ -57,31 +57,33 @@ const Catalog = ({ post }) => {
|
||||
}
|
||||
|
||||
return <div className='px-3 '>
|
||||
<div className='dark:text-white'><i className='mr-1 fas fa-stream' />{locale.COMMON.TABLE_OF_CONTENTS}</div>
|
||||
<div className='dark:text-white mb-2'>
|
||||
<i className='mr-1 fas fa-stream' />{locale.COMMON.TABLE_OF_CONTENTS}
|
||||
</div>
|
||||
|
||||
<div className='overflow-y-auto overscroll-none max-h-36 lg:max-h-96 scroll-hidden' ref={tRef}>
|
||||
<nav className='h-full text-black'>
|
||||
{post?.toc?.map((tocItem) => {
|
||||
const id = uuidToId(tocItem.id)
|
||||
return (
|
||||
<a
|
||||
key={id}
|
||||
href={`#${id}`}
|
||||
className={`notion-table-of-contents-item duration-300 transform font-light dark:text-gray-200
|
||||
<div className='overflow-y-auto overscroll-none max-h-36 lg:max-h-96 scroll-hidden' ref={tRef}>
|
||||
<nav className='h-full text-black'>
|
||||
{post?.toc?.map((tocItem) => {
|
||||
const id = uuidToId(tocItem.id)
|
||||
return (
|
||||
<a
|
||||
key={id}
|
||||
href={`#${id}`}
|
||||
className={`notion-table-of-contents-item duration-300 transform dark:text-gray-200
|
||||
notion-table-of-contents-item-indent-level-${tocItem.indentLevel} `}
|
||||
>
|
||||
<span style={{ display: 'inline-block', marginLeft: tocItem.indentLevel * 16 }}
|
||||
className={`${activeSection === id && ' font-bold text-red-500 underline overflow-ellipsis truncate'}`}
|
||||
>
|
||||
{tocItem.text}
|
||||
</span>
|
||||
</a>
|
||||
)
|
||||
})}
|
||||
</nav>
|
||||
>
|
||||
<span style={{ display: 'inline-block', marginLeft: tocItem.indentLevel * 16 }}
|
||||
className={`${activeSection === id && ' font-bold text-red-600 underline overflow-ellipsis truncate'}`}
|
||||
>
|
||||
{tocItem.text}
|
||||
</span>
|
||||
</a>
|
||||
)
|
||||
})}
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
export default Catalog
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
import DarkModeButton from '@/components/DarkModeButton'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
|
||||
export const Footer = (props) => {
|
||||
/**
|
||||
* 页脚
|
||||
* @param {*} props
|
||||
* @returns
|
||||
*/
|
||||
export default function Footer (props) {
|
||||
const d = new Date()
|
||||
const currentYear = d.getFullYear()
|
||||
const since = siteConfig('SINCE')
|
||||
|
||||
@@ -3,14 +3,15 @@ import Link from 'next/link'
|
||||
import CONFIG from '../config'
|
||||
import SocialButton from './SocialButton'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { compressImage } from '@/lib/notion/mapImage'
|
||||
|
||||
/**
|
||||
* 网站顶部
|
||||
* @returns
|
||||
*/
|
||||
export const Header = (props) => {
|
||||
export default function Header (props) {
|
||||
const { siteInfo } = props
|
||||
const avatar = siteInfo?.icon || siteConfig('AVATAR')
|
||||
const avatar = compressImage(siteInfo?.icon || siteConfig('AVATAR'), 200)
|
||||
|
||||
return (
|
||||
<header className="text-center justify-between items-center px-6 bg-white h-80 dark:bg-black relative z-10">
|
||||
@@ -18,8 +19,8 @@ export const Header = (props) => {
|
||||
<Link href='/'>
|
||||
{/* 可使用一张单图作为logo */}
|
||||
<div className='flex space-x-6'>
|
||||
<div className='hover:rotate-45 hover:scale-125 transform duration-200 cursor-pointer'>
|
||||
<LazyImage src={avatar} className='rounded-full' width={130} height={130} alt={siteConfig('AUTHOR')} />
|
||||
<div className='hover:rotate-45 hover:scale-125 transform duration-200 cursor-pointer justify-center items-center flex'>
|
||||
<LazyImage priority={true} src={avatar} className='rounded-full' width={100} height={100} alt={siteConfig('AUTHOR')} />
|
||||
</div>
|
||||
|
||||
<div className='flex-col flex justify-center'>
|
||||
|
||||
@@ -51,7 +51,7 @@ export const MenuList = ({ customNav, customMenu }) => {
|
||||
return (<>
|
||||
{/* 大屏模式菜单 */}
|
||||
<div id='nav-menu-pc' className='hidden md:flex my-auto'>
|
||||
{links?.map(link => <MenuItemDrop key={link?.id} link={link} />)}
|
||||
{links?.map((link, index) => <MenuItemDrop key={index} link={link} />)}
|
||||
</div>
|
||||
{/* 移动端小屏菜单 */}
|
||||
<div id='nav-menu-mobile' className='flex md:hidden my-auto justify-start'>
|
||||
@@ -62,7 +62,7 @@ export const MenuList = ({ customNav, customMenu }) => {
|
||||
|
||||
<Collapse collapseRef={collapseRef} className='absolute w-full top-12 left-0' isOpen={isOpen}>
|
||||
<div id='menu-wrap' className='bg-white dark:border-hexo-black-gray border'>
|
||||
{links?.map(link => <MenuItemCollapse key={link?.id} link={link} onHeightChange={(param) => collapseRef.current?.updateCollapseHeight(param)}/>)}
|
||||
{links?.map((link, index) => <MenuItemCollapse key={index} link={link} onHeightChange={(param) => collapseRef.current?.updateCollapseHeight(param)}/>)}
|
||||
</div>
|
||||
</Collapse>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useState } from 'react'
|
||||
import { useSimpleGlobal } from '..'
|
||||
import { MenuList } from './MenuList'
|
||||
|
||||
/**
|
||||
@@ -7,12 +9,18 @@ import { MenuList } from './MenuList'
|
||||
* @param {*} props
|
||||
* @returns
|
||||
*/
|
||||
export const NavBar = (props) => {
|
||||
export default function NavBar (props) {
|
||||
const [showSearchInput, changeShowSearchInput] = useState(false)
|
||||
const router = useRouter()
|
||||
const { searchModal } = useSimpleGlobal()
|
||||
|
||||
// 展示搜索框
|
||||
const toggleShowSearchInput = () => {
|
||||
changeShowSearchInput(!showSearchInput)
|
||||
if (siteConfig('ALGOLIA_APP_ID')) {
|
||||
searchModal.current.openSearch()
|
||||
} else {
|
||||
changeShowSearchInput(!showSearchInput)
|
||||
}
|
||||
}
|
||||
|
||||
const onKeyUp = (e) => {
|
||||
|
||||
@@ -3,7 +3,12 @@ import Live2D from '@/components/Live2D'
|
||||
import Announcement from './Announcement'
|
||||
import Catalog from './Catalog'
|
||||
|
||||
export const SideBar = (props) => {
|
||||
/**
|
||||
* 侧边栏
|
||||
* @param {*} props
|
||||
* @returns
|
||||
*/
|
||||
export default function SideBar (props) {
|
||||
const { notice } = props
|
||||
return (<>
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@ import { siteConfig } from '@/lib/config'
|
||||
* @constructor
|
||||
*/
|
||||
const SocialButton = () => {
|
||||
return <div className='w-52 justify-center flex-wrap flex'>
|
||||
<div className='space-x-3 md:text-xl text-3xl text-gray-600 dark:text-gray-400 text-center'>
|
||||
return <div className='w-52 justify-center flex-wrap flex my-2'>
|
||||
<div className='space-x-5 md:text-xl text-3xl text-gray-600 dark:text-gray-400 text-center'>
|
||||
{siteConfig('CONTACT_GITHUB') && <a target='_blank' rel='noreferrer' title={'github'} href={siteConfig('CONTACT_GITHUB')} >
|
||||
<i className='fab fa-github transform hover:scale-125 duration-150'/>
|
||||
</a>}
|
||||
|
||||
@@ -5,11 +5,13 @@ import { siteConfig } from '@/lib/config'
|
||||
* 网站顶部 提示栏
|
||||
* @returns
|
||||
*/
|
||||
export const TopBar = (props) => {
|
||||
if (siteConfig('SIMPLE_TOP_BAR_CONTENT', null, CONFIG)) {
|
||||
export default function TopBar (props) {
|
||||
const content = siteConfig('SIMPLE_TOP_BAR_CONTENT', null, CONFIG)
|
||||
|
||||
if (content) {
|
||||
return <header className="flex justify-center items-center bg-black dark:bg-hexo-black-gray">
|
||||
<div id='top-bar-inner' className='max-w-9/10 w-full z-20'>
|
||||
<div className='text-xs text-center float-left text-white z-50 leading-5 py-2.5' dangerouslySetInnerHTML={{ __html: siteConfig('SIMPLE_TOP_BAR_CONTENT', null, CONFIG) }}/>
|
||||
<div className='text-xs text-center float-left text-white z-50 leading-5 py-2.5' dangerouslySetInnerHTML={{ __html: content }}/>
|
||||
</div>
|
||||
</header>
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@ const CONFIG = {
|
||||
|
||||
SIMPLE_POST_AD_ENABLE: process.env.NEXT_PUBLIC_SIMPLE_POST_AD_ENABLE || false, // 文章列表是否插入广告
|
||||
|
||||
SIMPLE_POST_COVER_ENABLE: process.env.NEXT_PUBLIC_SIMPLE_POST_COVER_ENABLE || false, // 是否展示博客封面
|
||||
|
||||
// 菜单配置
|
||||
SIMPLE_MENU_CATEGORY: true, // 显示分类
|
||||
SIMPLE_MENU_TAG: true, // 显示标签
|
||||
|
||||
@@ -1,31 +1,39 @@
|
||||
import CONFIG from './config'
|
||||
import { BlogListPage } from './components/BlogListPage'
|
||||
import { BlogListScroll } from './components/BlogListScroll'
|
||||
import { useEffect } from 'react'
|
||||
import { isBrowser, loadExternalResource } from '@/lib/utils'
|
||||
import BlogArchiveItem from './components/BlogArchiveItem'
|
||||
import { ArticleLock } from './components/ArticleLock'
|
||||
import NotionPage from '@/components/NotionPage'
|
||||
import { ArticleInfo } from './components/ArticleInfo'
|
||||
import Comment from '@/components/Comment'
|
||||
import ArticleAround from './components/ArticleAround'
|
||||
import ShareBar from '@/components/ShareBar'
|
||||
import { AdSlot } from '@/components/GoogleAdsense'
|
||||
import Link from 'next/link'
|
||||
import { TopBar } from './components/TopBar'
|
||||
import { Header } from './components/Header'
|
||||
import { NavBar } from './components/NavBar'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { SideBar } from './components/SideBar'
|
||||
import JumpToTopButton from './components/JumpToTopButton'
|
||||
import { Footer } from './components/Footer'
|
||||
import { createContext, useContext, useEffect, useRef } from 'react'
|
||||
import { isBrowser } from '@/lib/utils'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import SearchInput from './components/SearchInput'
|
||||
import { AdSlot } from '@/components/GoogleAdsense'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { Transition } from '@headlessui/react'
|
||||
import Link from 'next/link'
|
||||
import { Style } from './style'
|
||||
import replaceSearchResult from '@/components/Mark'
|
||||
import CommonHead from '@/components/CommonHead'
|
||||
import WWAds from '@/components/WWAds'
|
||||
import dynamic from 'next/dynamic'
|
||||
import NotionPage from '@/components/NotionPage'
|
||||
import AlgoliaSearchModal from '@/components/AlgoliaSearchModal'
|
||||
|
||||
// 主题组件
|
||||
const BlogListScroll = dynamic(() => import('./components/BlogListScroll'), { ssr: false });
|
||||
const BlogArchiveItem = dynamic(() => import('./components/BlogArchiveItem'), { ssr: false });
|
||||
const ArticleLock = dynamic(() => import('./components/ArticleLock'), { ssr: false });
|
||||
const ArticleInfo = dynamic(() => import('./components/ArticleInfo'), { ssr: false });
|
||||
const Comment = dynamic(() => import('@/components/Comment'), { ssr: false });
|
||||
const ArticleAround = dynamic(() => import('./components/ArticleAround'), { ssr: false });
|
||||
const ShareBar = dynamic(() => import('@/components/ShareBar'), { ssr: false });
|
||||
const TopBar = dynamic(() => import('./components/TopBar'), { ssr: false });
|
||||
const Header = dynamic(() => import('./components/Header'), { ssr: false });
|
||||
const NavBar = dynamic(() => import('./components/NavBar'), { ssr: false });
|
||||
const SideBar = dynamic(() => import('./components/SideBar'), { ssr: false });
|
||||
const JumpToTopButton = dynamic(() => import('./components/JumpToTopButton'), { ssr: false });
|
||||
const Footer = dynamic(() => import('./components/Footer'), { ssr: false });
|
||||
const SearchInput = dynamic(() => import('./components/SearchInput'), { ssr: false });
|
||||
const CommonHead = dynamic(() => import('@/components/CommonHead'), { ssr: false });
|
||||
const WWAds = dynamic(() => import('@/components/WWAds'), { ssr: false });
|
||||
const BlogListPage = dynamic(() => import('./components/BlogListPage'), { ssr: false })
|
||||
|
||||
// 主题全局状态
|
||||
const ThemeGlobalSimple = createContext()
|
||||
export const useSimpleGlobal = () => useContext(ThemeGlobalSimple)
|
||||
|
||||
/**
|
||||
* 基础布局
|
||||
@@ -35,12 +43,11 @@ import WWAds from '@/components/WWAds'
|
||||
*/
|
||||
const LayoutBase = props => {
|
||||
const { children, slotTop, meta } = props
|
||||
const { onLoading } = useGlobal()
|
||||
const { onLoading, fullWidth } = useGlobal()
|
||||
const searchModal = useRef(null)
|
||||
|
||||
if (isBrowser) {
|
||||
loadExternalResource('/css/theme-simple.css', 'css')
|
||||
}
|
||||
return (
|
||||
<ThemeGlobalSimple.Provider value={{ searchModal }}>
|
||||
<div id='theme-simple' className='min-h-screen flex flex-col dark:text-gray-300 bg-white dark:bg-black'>
|
||||
{/* SEO相关 */}
|
||||
<CommonHead meta={meta}/>
|
||||
@@ -75,9 +82,11 @@ const LayoutBase = props => {
|
||||
<AdSlot type='native' />
|
||||
</div>
|
||||
|
||||
<div id='right-sidebar' className="hidden xl:block flex-none sticky top-8 w-96 border-l dark:border-gray-800 pl-12 border-gray-100">
|
||||
<SideBar {...props} />
|
||||
</div>
|
||||
{fullWidth
|
||||
? null
|
||||
: <div id='right-sidebar' className="hidden xl:block flex-none sticky top-8 w-96 border-l dark:border-gray-800 pl-12 border-gray-100">
|
||||
<SideBar {...props} />
|
||||
</div>}
|
||||
|
||||
</div>
|
||||
|
||||
@@ -85,9 +94,13 @@ const LayoutBase = props => {
|
||||
<JumpToTopButton />
|
||||
</div>
|
||||
|
||||
{/* 搜索框 */}
|
||||
<AlgoliaSearchModal cRef={searchModal} {...props}/>
|
||||
|
||||
<Footer {...props} />
|
||||
|
||||
</div>
|
||||
</ThemeGlobalSimple.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -135,7 +148,9 @@ const LayoutSearch = props => {
|
||||
}
|
||||
}, [])
|
||||
|
||||
return <LayoutPostList {...props} slotTop={<SearchInput {...props} />} />
|
||||
const slotTop = siteConfig('ALGOLIA_APP_ID') ? null : <SearchInput {...props} />
|
||||
|
||||
return <LayoutPostList {...props} slotTop={slotTop} />
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -161,13 +176,14 @@ const LayoutArchive = props => {
|
||||
*/
|
||||
const LayoutSlug = props => {
|
||||
const { post, lock, validPassword, prev, next } = props
|
||||
const { fullWidth } = useGlobal()
|
||||
|
||||
return (
|
||||
<LayoutBase {...props}>
|
||||
|
||||
{lock && <ArticleLock validPassword={validPassword} />}
|
||||
|
||||
<div id="article-wrapper" className="px-2 xl:max-w-4xl 2xl:max-w-6xl ">
|
||||
<div id="article-wrapper" className={`px-2 ${fullWidth ? '' : 'xl:max-w-4xl 2xl:max-w-6xl'}`}>
|
||||
|
||||
{/* 文章信息 */}
|
||||
<ArticleInfo post={post} />
|
||||
|
||||
Reference in New Issue
Block a user