mirror of
https://github.com/d0zingcat/NotionNext.git
synced 2026-05-15 15:09:25 +00:00
Merge branch 'tangly1024:main' into main
This commit is contained in:
@@ -1,2 +1,2 @@
|
||||
# 环境变量 @see https://www.nextjs.cn/docs/basic-features/environment-variables
|
||||
NEXT_PUBLIC_VERSION=4.0.12
|
||||
NEXT_PUBLIC_VERSION=4.0.13
|
||||
39
.github/workflows/baidupush.yml
vendored
Normal file
39
.github/workflows/baidupush.yml
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
## 利用GitHub Actions每天定时给百度推送链接,提高收录率 ##
|
||||
|
||||
name: baidupush
|
||||
|
||||
# 两种触发方式:一、push代码,二、每天国际标准时间23点(北京时间+8即早上7点)运行
|
||||
on:
|
||||
# push:
|
||||
schedule:
|
||||
- cron: '0 23 * * *' # https://help.github.com/en/actions/automating-your-workflow-with-github-actions/events-that-trigger-workflows#scheduled-events-schedule
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
unconditional-invoking:
|
||||
description: 'baidupush unconditionally'
|
||||
type: boolean
|
||||
required: true
|
||||
default: true
|
||||
|
||||
# on:
|
||||
# schedule:
|
||||
# - cron: '*/5 * * * *' # 每5分钟一次,测试用
|
||||
|
||||
jobs:
|
||||
bot:
|
||||
runs-on: ubuntu-latest # 运行环境为最新版的Ubuntu
|
||||
steps:
|
||||
- name: 'Checkout codes' # 步骤一,获取仓库代码
|
||||
uses: actions/checkout@v2
|
||||
# - name: 'Run baiduPush' # 步骤二,执行sh命令文件
|
||||
# run: npm install && npm run baiduPush # 运行目录是仓库根目录
|
||||
- name: Set up Python 3.8
|
||||
uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: 3.8
|
||||
|
||||
- name: install requests
|
||||
run: pip install requests
|
||||
|
||||
- name: baidupush
|
||||
run: npm run baidupush
|
||||
@@ -1,6 +1,6 @@
|
||||
ARG NOTION_PAGE_ID
|
||||
# Install dependencies only when needed
|
||||
FROM node:14-alpine AS deps
|
||||
FROM node:18-alpine3.18 AS deps
|
||||
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
|
||||
RUN apk add --no-cache libc6-compat
|
||||
WORKDIR /app
|
||||
@@ -8,7 +8,7 @@ COPY package.json ./
|
||||
RUN yarn install --frozen-lockfile
|
||||
|
||||
# Rebuild the source code only when needed
|
||||
FROM node:14-alpine AS builder
|
||||
FROM node:18-alpine3.18 AS builder
|
||||
ARG NOTION_PAGE_ID
|
||||
WORKDIR /app
|
||||
COPY --from=deps /app/node_modules ./node_modules
|
||||
|
||||
18
baidupush.py
Normal file
18
baidupush.py
Normal file
@@ -0,0 +1,18 @@
|
||||
import re
|
||||
import ssl
|
||||
import requests
|
||||
import argparse
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
ssl._create_default_https_context = ssl._create_unverified_context
|
||||
parser = argparse.ArgumentParser(description='parse sitemap')
|
||||
parser.add_argument('url', help='The url of your website')
|
||||
args = parser.parse_args()
|
||||
url = f'https://{args.url}/sitemap.xml'
|
||||
result = requests.get(url)
|
||||
big = re.findall('<loc>(.*?)</loc>', result.content.decode('utf-8'), re.S)
|
||||
for i in big:
|
||||
# print(i)
|
||||
op_xml_txt = open('urls.txt', 'a')
|
||||
op_xml_txt.write('%s\n' % i)
|
||||
12
baidupush.sh
Normal file
12
baidupush.sh
Normal file
@@ -0,0 +1,12 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
# 确保脚本抛出遇到的错误
|
||||
set -e
|
||||
|
||||
# 解析sitemap.xml, 记得换成你自己的域名,注意检查是否包含‘www’
|
||||
python baidupush.py 'www.ghlerrix.cn'
|
||||
|
||||
# 百度链接推送,换成自己的token和域名
|
||||
curl -H 'Content-Type:text/plain' --data-binary @urls.txt "http://data.zz.baidu.com/urls?site=https://www.ghlerrix.cn&token=oUldnU4HZvSTlh0e"
|
||||
|
||||
rm -rf urls.txt # 删除文件
|
||||
@@ -107,10 +107,10 @@ const BLOG = {
|
||||
PRISM_JS_AUTO_LOADER: 'https://npm.elemecdn.com/prismjs@1.29.0/plugins/autoloader/prism-autoloader.min.js',
|
||||
|
||||
// 代码主题 @see https://github.com/PrismJS/prism-themes
|
||||
PRISM_THEME_PREFIX_PATH: 'https://cdn.jsdelivr.net/npm/prismjs@1.29.0/themes/prism-okaidia.css', // 代码块默认主题
|
||||
PRISM_THEME_PREFIX_PATH: process.env.NEXT_PUBLIC_PRISM_THEME_PREFIX_PATH || 'https://cdn.jsdelivr.net/npm/prismjs@1.29.0/themes/prism-okaidia.css', // 代码块默认主题
|
||||
PRISM_THEME_SWITCH: process.env.NEXT_PUBLIC_PRISM_THEME_SWITCH || true, // 是否开启浅色/深色模式代码主题切换; 开启后将显示以下两个主题
|
||||
PRISM_THEME_LIGHT_PATH: 'https://cdn.jsdelivr.net/npm/prismjs@1.29.0/themes/prism-solarizedlight.css', // 浅色模式主题
|
||||
PRISM_THEME_DARK_PATH: 'https://cdn.jsdelivr.net/npm/prismjs@1.29.0/themes/prism-okaidia.min.css', // 深色模式主题
|
||||
PRISM_THEME_LIGHT_PATH: process.env.NEXT_PUBLIC_PRISM_THEME_LIGHT_PATH || 'https://cdn.jsdelivr.net/npm/prismjs@1.29.0/themes/prism-solarizedlight.css', // 浅色模式主题
|
||||
PRISM_THEME_DARK_PATH: process.env.NEXT_PUBLIC_PRISM_THEME_DARK_PATH || 'https://cdn.jsdelivr.net/npm/prismjs@1.29.0/themes/prism-okaidia.min.css', // 深色模式主题
|
||||
|
||||
CODE_MAC_BAR: process.env.NEXT_PUBLIC_CODE_MAC_BAR || true, // 代码左上角显示mac的红黄绿图标
|
||||
CODE_LINE_NUMBERS: process.env.NEXT_PUBLIC_CODE_LINE_NUMBERS || false, // 是否显示行号
|
||||
@@ -332,6 +332,8 @@ const BLOG = {
|
||||
|
||||
// <---- 站点统计
|
||||
|
||||
// START---->营收相关
|
||||
|
||||
// 谷歌广告
|
||||
ADSENSE_GOOGLE_ID: process.env.NEXT_PUBLIC_ADSENSE_GOOGLE_ID || '', // 谷歌广告ID e.g ca-pub-xxxxxxxxxxxxxxxx
|
||||
ADSENSE_GOOGLE_TEST: process.env.NEXT_PUBLIC_ADSENSE_GOOGLE_TEST || false, // 谷歌广告ID测试模式,这种模式获取假的测试广告,用于开发 https://www.tangly1024.com/article/local-dev-google-adsense
|
||||
@@ -340,6 +342,12 @@ const BLOG = {
|
||||
ADSENSE_GOOGLE_SLOT_NATIVE: process.env.NEXT_PUBLIC_ADSENSE_GOOGLE_SLOT_NATIVE || '4980048999', // Google AdScene>广告>按单元广告>新建原生广告
|
||||
ADSENSE_GOOGLE_SLOT_AUTO: process.env.NEXT_PUBLIC_ADSENSE_GOOGLE_SLOT_AUTO || '8807314373', // Google AdScene>广告>按单元广告>新建展示广告 (自动广告)
|
||||
|
||||
// 万维广告
|
||||
AD_WWADS_ID: process.env.NEXT_PUBLIC_WWAD_ID || null, // https://wwads.cn/ 创建您的万维广告单元ID
|
||||
AD_WWADS_BLOCK_DETECT: process.env.NEXT_PUBLIC_WWADS_AD_BLOCK_DETECT || false, // 是否开启WWADS广告屏蔽插件检测,开启后会在广告位上以文字提示 @see https://github.com/bytegravity/whitelist-wwads
|
||||
|
||||
// END<----营收相关
|
||||
|
||||
// 自定义配置notion数据库字段名
|
||||
NOTION_PROPERTY_NAME: {
|
||||
password: process.env.NEXT_PUBLIC_NOTION_PROPERTY_PASSWORD || 'password',
|
||||
|
||||
40
components/AdBlockDetect.js
Normal file
40
components/AdBlockDetect.js
Normal file
@@ -0,0 +1,40 @@
|
||||
import { useEffect } from 'react'
|
||||
|
||||
/**
|
||||
* 检测广告插件
|
||||
* @returns
|
||||
*/
|
||||
export default function AdBlockDetect() {
|
||||
useEffect(() => {
|
||||
// 如果检测到广告屏蔽插件
|
||||
function ABDetected() {
|
||||
if (!document) {
|
||||
return
|
||||
}
|
||||
const wwadsCns = document.getElementsByClassName('wwads-cn')
|
||||
if (wwadsCns && wwadsCns.length > 0) {
|
||||
for (const wwadsCn of wwadsCns) {
|
||||
wwadsCn.insertAdjacentHTML('beforeend', "<style>.wwads-horizontal,.wwads-vertical{background-color:#f4f8fa;padding:5px;min-height:120px;margin-top:20px;box-sizing:border-box;border-radius:3px;font-family:sans-serif;display:flex;min-width:150px;position:relative;overflow:hidden;}.wwads-horizontal{flex-wrap:wrap;justify-content:center}.wwads-vertical{flex-direction:column;align-items:center;padding-bottom:32px}.wwads-horizontal a,.wwads-vertical a{text-decoration:none}.wwads-horizontal .wwads-img,.wwads-vertical .wwads-img{margin:5px}.wwads-horizontal .wwads-content,.wwads-vertical .wwads-content{margin:5px}.wwads-horizontal .wwads-content{flex:130px}.wwads-vertical .wwads-content{margin-top:10px}.wwads-horizontal .wwads-text,.wwads-content .wwads-text{font-size:14px;line-height:1.4;color:#0e1011;-webkit-font-smoothing:antialiased}.wwads-horizontal .wwads-poweredby,.wwads-vertical .wwads-poweredby{display:block;font-size:11px;color:#a6b7bf;margin-top:1em}.wwads-vertical .wwads-poweredby{position:absolute;left:10px;bottom:10px}.wwads-horizontal .wwads-poweredby span,.wwads-vertical .wwads-poweredby span{transition:all 0.2s ease-in-out;margin-left:-1em}.wwads-horizontal .wwads-poweredby span:first-child,.wwads-vertical .wwads-poweredby span:first-child{opacity:0}.wwads-horizontal:hover .wwads-poweredby span,.wwads-vertical:hover .wwads-poweredby span{opacity:1;margin-left:0}.wwads-horizontal .wwads-hide,.wwads-vertical .wwads-hide{position:absolute;right:-23px;bottom:-23px;width:46px;height:46px;border-radius:23px;transition:all 0.3s ease-in-out;cursor:pointer;}.wwads-horizontal .wwads-hide:hover,.wwads-vertical .wwads-hide:hover{background:rgb(0 0 0 /0.05)}.wwads-horizontal .wwads-hide svg,.wwads-vertical .wwads-hide svg{position:absolute;left:10px;top:10px;fill:#a6b7bf}.wwads-horizontal .wwads-hide:hover svg,.wwads-vertical .wwads-hide:hover svg{fill:#3E4546}</style><a href='https://wwads.cn/page/whitelist-wwads' class='wwads-img' target='_blank' rel='nofollow'><img src='https://creatives-1301677708.file.myqcloud.com/images/placeholder/wwads-friendly-ads.png' width='130'></a><div class='wwads-content'><a href='https://wwads.cn/page/whitelist-wwads' class='wwads-text' target='_blank' rel='nofollow'>为了本站的长期运营,请将我们的网站加入广告拦截器的白名单,感谢您的支持!</a><a href='https://wwads.cn/page/end-user-privacy' class='wwads-poweredby' title='万维广告 ~ 让广告更优雅,且有用' target='_blank'><span>万维</span><span>广告</span></a></div><a class='wwads-hide' onclick='parentNode.remove()' title='隐藏广告'><svg xmlns='http://www.w3.org/2000/svg' width='6' height='7'><path d='M.879.672L3 2.793 5.121.672a.5.5 0 11.707.707L3.708 3.5l2.12 2.121a.5.5 0 11-.707.707l-2.12-2.12-2.122 2.12a.5.5 0 11-.707-.707l2.121-2.12L.172 1.378A.5.5 0 01.879.672z'></path></svg></a>")
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// check document ready
|
||||
function docReady(t) {
|
||||
document.readyState === 'complete' ||
|
||||
document.readyState === 'interactive'
|
||||
? setTimeout(t, 1)
|
||||
: document.addEventListener('DOMContentLoaded', t)
|
||||
}
|
||||
|
||||
// check if wwads' fire function was blocked after document is ready with 3s timeout (waiting the ad loading)
|
||||
docReady(function () {
|
||||
setTimeout(function () {
|
||||
if (window._AdBlockInit === undefined) {
|
||||
ABDetected()
|
||||
}
|
||||
}, 3000)
|
||||
})
|
||||
}, [])
|
||||
return null
|
||||
}
|
||||
@@ -38,6 +38,8 @@ const CommonScript = () => {
|
||||
/>
|
||||
</>)}
|
||||
|
||||
{BLOG.AD_WWADS_ID && <script type="text/javascript" charSet="UTF-8" src="https://cdn.wwads.cn/js/makemoney.js" async></script>}
|
||||
|
||||
{BLOG.COMMENT_CUSDIS_APP_ID && <script defer src={`https://cusdis.com/js/widget/lang/${BLOG.LANG.toLowerCase()}.js`} />}
|
||||
|
||||
{BLOG.COMMENT_TWIKOO_ENV_ID && <script defer src={BLOG.COMMENT_TWIKOO_CDN_URL}/> }
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useEffect, useState, useRef } from 'react'
|
||||
import { useEffect, useState, useRef, useLayoutEffect } from 'react'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import { saveDarkModeToCookies, THEMES } from '@/themes/theme'
|
||||
import BLOG from '@/blog.config'
|
||||
import useWindowSize from '@/hooks/useWindowSize'
|
||||
|
||||
/**
|
||||
* 自定义右键菜单
|
||||
@@ -15,6 +16,9 @@ export default function CustomContextMenu(props) {
|
||||
const [show, setShow] = useState(false)
|
||||
const { isDarkMode, updateDarkMode, locale } = useGlobal()
|
||||
const menuRef = useRef(null)
|
||||
const windowSize = useWindowSize()
|
||||
const [width, setWidth] = useState(0)
|
||||
const [height, setHeight] = useState(0)
|
||||
|
||||
const { latestPosts } = props
|
||||
const router = useRouter()
|
||||
@@ -27,10 +31,18 @@ export default function CustomContextMenu(props) {
|
||||
router.push(`${BLOG.SUB_PATH}/${randomPost?.slug}`)
|
||||
}
|
||||
|
||||
useLayoutEffect(() => {
|
||||
setWidth(menuRef.current.offsetWidth)
|
||||
setHeight(menuRef.current.offsetHeight)
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
const handleContextMenu = (event) => {
|
||||
event.preventDefault()
|
||||
setPosition({ y: `${event.clientY}px`, x: `${event.clientX}px` })
|
||||
// 计算点击位置加菜单宽高是否超出屏幕,如果超出则贴边弹出
|
||||
const x = (event.clientX < windowSize.width - width) ? event.clientX : windowSize.width - width
|
||||
const y = (event.clientY < windowSize.height - height) ? event.clientY : windowSize.height - height
|
||||
setPosition({ y: `${y}px`, x: `${x}px` })
|
||||
setShow(true)
|
||||
}
|
||||
|
||||
@@ -47,7 +59,7 @@ export default function CustomContextMenu(props) {
|
||||
window.removeEventListener('contextmenu', handleContextMenu)
|
||||
window.removeEventListener('click', handleClick)
|
||||
}
|
||||
}, [])
|
||||
}, [windowSize])
|
||||
|
||||
function handleBack() {
|
||||
window.history.back()
|
||||
|
||||
@@ -32,7 +32,7 @@ const Messenger = dynamic(() => import('@/components/FacebookMessenger'), { ssr:
|
||||
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 })
|
||||
/**
|
||||
* 各种第三方组件
|
||||
* @param {*} props
|
||||
@@ -59,6 +59,7 @@ const ExternalPlugin = (props) => {
|
||||
{JSON.parse(BLOG.CUSTOM_RIGHT_CLICK_CONTEXT_MENU) && <CustomContextMenu {...props} />}
|
||||
{!JSON.parse(BLOG.CAN_COPY) && <DisableCopy/>}
|
||||
{JSON.parse(BLOG.WEB_WHIZ_ENABLED) && <WebWhiz/>}
|
||||
{JSON.parse(BLOG.AD_WWADS_BLOCK_DETECT) && <AdBlockDetect/>}
|
||||
<VConsole/>
|
||||
</>
|
||||
}
|
||||
|
||||
@@ -9,10 +9,6 @@ import { useEffect } from 'react'
|
||||
*/
|
||||
export default function GoogleAdsense() {
|
||||
const initGoogleAdsense = () => {
|
||||
// GoogleAdsense 本地开发请加入 data-adbreak-test="on"
|
||||
// {BLOG.ADSENSE_GOOGLE_ID && <script async src={`https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=${BLOG.ADSENSE_GOOGLE_ID}`}
|
||||
// crossOrigin="anonymous" />}
|
||||
|
||||
loadExternalResource(`https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=${BLOG.ADSENSE_GOOGLE_ID}`, 'js').then(url => {
|
||||
setTimeout(() => {
|
||||
const ads = document.getElementsByClassName('adsbygoogle')
|
||||
@@ -21,7 +17,6 @@ export default function GoogleAdsense() {
|
||||
for (let i = 0; i <= ads.length; i++) {
|
||||
try {
|
||||
adsbygoogle.push(ads[i])
|
||||
// console.log('adsbygoogle', i, ads[i], adsbygoogle)
|
||||
} catch (e) {
|
||||
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ export default function QrCode({ value }) {
|
||||
colorLight: '#ffffff',
|
||||
correctLevel: QRCode.CorrectLevel.H
|
||||
})
|
||||
console.log('二维码', qrcode, value)
|
||||
// console.log('二维码', qrcode, value)
|
||||
})
|
||||
return () => {
|
||||
if (qrcode) {
|
||||
|
||||
@@ -37,7 +37,7 @@ const SideBarDrawer = ({ children, isOpen, onOpen, onClose, className }) => {
|
||||
}
|
||||
}
|
||||
|
||||
return <div id='sidebar-wrapper' className={' block md:hidden top-0 ' + className }>
|
||||
return <div id='sidebar-wrapper' className={' block lg:hidden top-0 ' + className }>
|
||||
<div id="sidebar-drawer" className={`${isOpen ? 'ml-0 w-60 visible' : '-ml-60 max-w-side invisible'} bg-white dark:bg-gray-900 shadow-black shadow-lg flex flex-col duration-300 fixed h-full left-0 overflow-y-scroll scroll-hidden top-0 z-30`}>
|
||||
{children}
|
||||
</div>
|
||||
|
||||
18
components/WWAds.js
Normal file
18
components/WWAds.js
Normal file
@@ -0,0 +1,18 @@
|
||||
import React from 'react'
|
||||
import BLOG from '@/blog.config'
|
||||
|
||||
/**
|
||||
* 万维广告插件
|
||||
* @param {string} orientation - 广告方向,可以是 'vertical' 或 'horizontal'
|
||||
* @param {boolean} sticky - 是否粘性定位
|
||||
* @returns {JSX.Element | null} - 返回渲染的 JSX 元素或 null
|
||||
*/
|
||||
export default function WWAds({ orientation = 'vertical', sticky = false, className }) {
|
||||
if (!JSON.parse(BLOG.AD_WWADS_ID)) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={`wwads-cn ${orientation === 'vertical' ? 'wwads-vertical' : 'wwads-horizontal'} ${sticky ? 'wwads-sticky' : ''} z-30 ${className || ''}`} data-id={BLOG.AD_WWADS_ID}></div>
|
||||
)
|
||||
}
|
||||
30
hooks/useWindowSize.ts
Normal file
30
hooks/useWindowSize.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
|
||||
interface WindowSize {
|
||||
width: number,
|
||||
height: number
|
||||
}
|
||||
|
||||
const useWindowSize = () => {
|
||||
const [size, setSize] = useState<WindowSize>({
|
||||
width: document.documentElement.clientWidth,
|
||||
height: document.documentElement.clientHeight
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
const onResize = () => {
|
||||
setSize({
|
||||
width: document.documentElement.clientWidth,
|
||||
height: document.documentElement.clientHeight
|
||||
})
|
||||
}
|
||||
onResize()
|
||||
window.addEventListener('resize', onResize)
|
||||
return () => {
|
||||
window.removeEventListener('resize', onResize)
|
||||
}
|
||||
}, [])
|
||||
return size
|
||||
}
|
||||
|
||||
export default useWindowSize
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "notion-next",
|
||||
"version": "4.0.12",
|
||||
"version": "4.0.13",
|
||||
"homepage": "https://github.com/tangly1024/NotionNext.git",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
@@ -18,7 +18,8 @@
|
||||
"start": "next start",
|
||||
"post-build": "next-sitemap --config next-sitemap.config.js",
|
||||
"export": "next build && next-sitemap --config next-sitemap.config.js && next export",
|
||||
"bundle-report": "ANALYZE=true yarn build"
|
||||
"bundle-report": "ANALYZE=true yarn build",
|
||||
"baidupush": "bash baidupush.sh"
|
||||
},
|
||||
"dependencies": {
|
||||
"@giscus/react": "^2.2.6",
|
||||
|
||||
@@ -10,7 +10,6 @@ import { isBrowser } from '@/lib/utils'
|
||||
* @constructor
|
||||
*/
|
||||
const Catalog = ({ post }) => {
|
||||
const tocIds = []
|
||||
const toc = post?.toc
|
||||
// 同步选中目录事件
|
||||
const [activeSection, setActiveSection] = useState(null)
|
||||
@@ -28,7 +27,7 @@ const Catalog = ({ post }) => {
|
||||
const actionSectionScrollSpy = useCallback(throttle(() => {
|
||||
const sections = document.getElementsByClassName('notion-h')
|
||||
let prevBBox = null
|
||||
let currentSectionId = activeSection
|
||||
let currentSectionId = null
|
||||
for (let i = 0; i < sections.length; ++i) {
|
||||
const section = sections[i]
|
||||
if (!section || !(section instanceof Element)) continue
|
||||
@@ -48,6 +47,7 @@ const Catalog = ({ post }) => {
|
||||
break
|
||||
}
|
||||
setActiveSection(currentSectionId)
|
||||
const tocIds = post?.toc?.map((t) => uuidToId(t.id)) || []
|
||||
const index = tocIds.indexOf(currentSectionId) || 0
|
||||
if (isBrowser && tocIds?.length > 0) {
|
||||
for (const tocWrapper of document?.getElementsByClassName('toc-wrapper')) {
|
||||
@@ -62,11 +62,10 @@ const Catalog = ({ post }) => {
|
||||
}
|
||||
|
||||
return <>
|
||||
<div id='toc-wrapper' className='toc-wrapper overflow-y-auto max-h-96 overscroll-none scroll-hidden'>
|
||||
<div id='toc-wrapper' className='toc-wrapper overflow-y-auto my-2 max-h-80 overscroll-none scroll-hidden'>
|
||||
<nav className='h-full text-black'>
|
||||
{toc.map((tocItem) => {
|
||||
const id = uuidToId(tocItem.id)
|
||||
tocIds.push(id)
|
||||
return (
|
||||
<a
|
||||
key={id}
|
||||
|
||||
@@ -32,7 +32,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>
|
||||
)
|
||||
|
||||
@@ -47,8 +47,8 @@ export const MenuItemCollapse = (props) => {
|
||||
|
||||
{/* 折叠子菜单 */}
|
||||
{hasSubMenu && <Collapse isOpen={isOpen} onHeightChange={props.onHeightChange}>
|
||||
{link?.subMenus?.map(sLink => {
|
||||
return <div key={sLink.id} className='
|
||||
{link?.subMenus?.map((sLink, index) => {
|
||||
return <div key={index} className='
|
||||
not:last-child:border-b-0 border-b dark:border-gray-800 py-2 px-14 cursor-pointer hover:bg-gray-100 dark:text-gray-200
|
||||
font-extralight dark:bg-black text-left justify-start text-gray-600 bg-gray-50 dark:hover:bg-gray-900 tracking-widest transition-all duration-200'>
|
||||
<Link href={sLink.to} target={link?.to?.indexOf('http') === 0 ? '_blank' : '_self'}>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import NavPostListEmpty from './NavPostListEmpty'
|
||||
import { useRouter } from 'next/router'
|
||||
import NavPostItem from './NavPostItem'
|
||||
import CONFIG from '../config'
|
||||
|
||||
/**
|
||||
* 博客列表滚动分页
|
||||
@@ -15,14 +16,21 @@ const NavPostList = (props) => {
|
||||
let selectedSth = false
|
||||
const groupedArray = filteredNavPages?.reduce((groups, item) => {
|
||||
const categoryName = item?.category ? item?.category : '' // 将category转换为字符串
|
||||
const lastGroup = groups[groups.length - 1] // 获取最后一个分组
|
||||
|
||||
if (!lastGroup || lastGroup?.category !== categoryName) { // 如果当前元素的category与上一个元素不同,则创建新分组
|
||||
groups.push({ category: categoryName, items: [] })
|
||||
let existingGroup = null
|
||||
// 开启自动分组排序
|
||||
if (JSON.parse(CONFIG.AUTO_SORT)) {
|
||||
existingGroup = groups.find(group => group.category === categoryName) // 搜索同名的最后一个分组
|
||||
} else {
|
||||
existingGroup = groups[groups.length - 1] // 获取最后一个分组
|
||||
}
|
||||
|
||||
groups[groups.length - 1].items.push(item) // 将元素加入对应的分组
|
||||
|
||||
// 添加数据
|
||||
if (existingGroup && existingGroup.category === categoryName) {
|
||||
existingGroup.items.push(item)
|
||||
} else {
|
||||
groups.push({ category: categoryName, items: [item] })
|
||||
}
|
||||
return groups
|
||||
}, [])
|
||||
|
||||
|
||||
@@ -64,7 +64,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} />)}
|
||||
<DarkModeButton className='text-sm flex items-center h-full' />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2,6 +2,8 @@ const CONFIG = {
|
||||
|
||||
INDEX_PAGE: 'about', // 文档首页显示的文章,请确此路径包含在您的notion数据库中
|
||||
|
||||
AUTO_SORT: process.env.NEXT_PUBLIC_GITBOOK_AUTO_SORT || true, // 是否自动按分类名 归组排序文章;自动归组可能会打乱您Notion中的文章顺序
|
||||
|
||||
// 菜单
|
||||
MENU_CATEGORY: true, // 显示分类
|
||||
MENU_TAG: true, // 显示标签
|
||||
|
||||
@@ -34,6 +34,8 @@ import CommonHead from '@/components/CommonHead'
|
||||
import BlogArchiveItem from './components/BlogArchiveItem'
|
||||
import BlogPostListPage from './components/BlogPostListPage'
|
||||
import Link from 'next/link'
|
||||
import dynamic from 'next/dynamic'
|
||||
const WWAds = dynamic(() => import('@/components/WWAds'), { ssr: false })
|
||||
|
||||
// 主题全局变量
|
||||
const ThemeGlobalGitbook = createContext()
|
||||
@@ -87,11 +89,11 @@ const LayoutBase = (props) => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id='center-wrapper' className='flex flex-col justify-between w-full relative z-10 pt-12 min-h-screen'>
|
||||
<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'>
|
||||
{slotTop}
|
||||
<AdSlot type='in-article' />
|
||||
<WWAds className='w-full' orientation='horizontal'/>
|
||||
|
||||
<Transition
|
||||
show={!onLoading}
|
||||
@@ -107,7 +109,10 @@ const LayoutBase = (props) => {
|
||||
{children}
|
||||
</Transition>
|
||||
|
||||
{/* Google广告 */}
|
||||
<AdSlot type='in-article' />
|
||||
<WWAds className='w-full' orientation='horizontal'/>
|
||||
|
||||
{/* 回顶按钮 */}
|
||||
<JumpToTopButton />
|
||||
</div>
|
||||
@@ -116,9 +121,6 @@ const LayoutBase = (props) => {
|
||||
<div className='md:hidden'>
|
||||
<Footer {...props} />
|
||||
</div>
|
||||
<div className='text-center'>
|
||||
<AdSlot type='native' />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 右侧侧推拉抽屉 */}
|
||||
@@ -126,7 +128,7 @@ const LayoutBase = (props) => {
|
||||
<div className='py-14 px-6 sticky top-0'>
|
||||
<ArticleInfo post={props?.post ? props?.post : props.notice} />
|
||||
|
||||
<div className='py-6'>
|
||||
<div className='py-4'>
|
||||
<Catalog {...props} />
|
||||
{slotRight}
|
||||
{router.route === '/' && <>
|
||||
@@ -138,6 +140,7 @@ const LayoutBase = (props) => {
|
||||
<Announcement {...props} />
|
||||
</div>
|
||||
|
||||
<AdSlot type='in-article' />
|
||||
<Live2D />
|
||||
|
||||
</div>
|
||||
@@ -236,6 +239,7 @@ const LayoutSlug = (props) => {
|
||||
{post?.type === 'Post' && <ArticleAround prev={prev} next={next} />}
|
||||
|
||||
<AdSlot />
|
||||
<WWAds className='w-full' orientation='horizontal'/>
|
||||
|
||||
<Comment frontMatter={post} />
|
||||
</section>)}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React from 'react'
|
||||
import BLOG from '@/blog.config'
|
||||
import SocialButton from './SocialButton'
|
||||
import { AdSlot } from '@/components/GoogleAdsense'
|
||||
// import DarkModeButton from '@/components/DarkModeButton'
|
||||
|
||||
const Footer = ({ title }) => {
|
||||
@@ -29,7 +28,6 @@ const Footer = ({ title }) => {
|
||||
<SocialButton />
|
||||
</div>
|
||||
|
||||
<AdSlot type='native'/>
|
||||
<br />
|
||||
|
||||
{/* 底部页面信息 */}
|
||||
|
||||
@@ -31,6 +31,8 @@ import LatestPostsGroup from './components/LatestPostsGroup'
|
||||
import FloatTocButton from './components/FloatTocButton'
|
||||
import replaceSearchResult from '@/components/Mark'
|
||||
import LazyImage from '@/components/LazyImage'
|
||||
import WWAds from '@/components/WWAds'
|
||||
import { AdSlot } from '@/components/GoogleAdsense'
|
||||
|
||||
/**
|
||||
* 基础布局 采用上中下布局,移动端使用顶部侧边导航栏
|
||||
@@ -87,6 +89,9 @@ const LayoutIndex = (props) => {
|
||||
{/* 通知横幅 */}
|
||||
<NoticeBar />
|
||||
<Hero {...props} />
|
||||
<div className='max-w-[86rem] mx-auto px-3'>
|
||||
<WWAds className='w-full' orientation='horizontal' />
|
||||
</div>
|
||||
</header>
|
||||
|
||||
// 右侧栏 用户信息+标签列表
|
||||
@@ -226,6 +231,7 @@ const LayoutSlug = props => {
|
||||
return (
|
||||
<LayoutBase {...props} headerSlot={headerSlot} showCategory={false} showTag={false} slotRight={slotRight}>
|
||||
<div className="w-full max-w-5xl 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 && <div id="article-wrapper" className="overflow-x-auto flex-grow mx-auto md:w-full md:px-5 ">
|
||||
@@ -238,7 +244,9 @@ const LayoutSlug = props => {
|
||||
itemScope itemType="https://schema.org/Movie" className="subpixel-antialiased overflow-y-hidden" >
|
||||
{/* Notion文章主体 */}
|
||||
<section className='px-5 justify-center mx-auto'>
|
||||
<WWAds orientation='horizontal' className='w-full'/>
|
||||
{post && <NotionPage post={post} />}
|
||||
<WWAds orientation='horizontal' className='w-full'/>
|
||||
</section>
|
||||
|
||||
{/* 分享 */}
|
||||
@@ -256,12 +264,16 @@ const LayoutSlug = props => {
|
||||
</article>
|
||||
|
||||
<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>
|
||||
<Comment frontMatter={post} className='' />
|
||||
<div className='py-2'>
|
||||
<AdSlot/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useRef } from 'react'
|
||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import throttle from 'lodash.throttle'
|
||||
import { uuidToId } from 'notion-utils'
|
||||
import Progress from './Progress'
|
||||
@@ -13,7 +13,7 @@ import { useGlobal } from '@/lib/global'
|
||||
const Catalog = ({ toc }) => {
|
||||
const { locale } = useGlobal()
|
||||
// 监听滚动事件
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
window.addEventListener('scroll', actionSectionScrollSpy)
|
||||
actionSectionScrollSpy()
|
||||
return () => {
|
||||
@@ -26,10 +26,10 @@ const Catalog = ({ toc }) => {
|
||||
const tocIds = []
|
||||
|
||||
// 同步选中目录事件
|
||||
const [activeSection, setActiveSection] = React.useState(null)
|
||||
const [activeSection, setActiveSection] = useState(null)
|
||||
|
||||
const throttleMs = 200
|
||||
const actionSectionScrollSpy = React.useCallback(throttle(() => {
|
||||
const actionSectionScrollSpy = useCallback(throttle(() => {
|
||||
const sections = document.getElementsByClassName('notion-h')
|
||||
let prevBBox = null
|
||||
let currentSectionId = activeSection
|
||||
|
||||
@@ -37,7 +37,8 @@ const SideBarDrawer = ({ children, isOpen, onOpen, onClose, className }) => {
|
||||
}
|
||||
}
|
||||
|
||||
return <div id='sidebar-wrapper' className={' block md:hidden top-0 ' + className }>
|
||||
return <div id='sidebar-wrapper' className={' block lg:hidden top-0 ' + className }>
|
||||
|
||||
|
||||
<div id="sidebar-drawer" className={`${isOpen ? 'mr-0 w-72 visible' : '-mr-72 max-w-side invisible'} bg-gray-50 right-0 top-0 dark:bg-hexo-black-gray shadow-black shadow-lg flex flex-col duration-300 fixed h-full overflow-y-scroll scroll-hidden z-30`}>
|
||||
{children}
|
||||
|
||||
@@ -13,11 +13,11 @@ export const BlogListPage = props => {
|
||||
const currentPage = +page
|
||||
|
||||
const showPrev = currentPage > 1
|
||||
const showNext = page < totalPage
|
||||
const showNext = currentPage < totalPage && posts?.length > 0
|
||||
const pagePrefix = router.asPath.split('?')[0].replace(/\/page\/[1-9]\d*/, '').replace(/\/$/, '')
|
||||
|
||||
return (
|
||||
<div className="w-full md:pr-12 mb-12">
|
||||
<div className="w-full md:pr-12 my-6">
|
||||
|
||||
<div id="posts-wrapper">
|
||||
{posts?.map(post => (
|
||||
|
||||
@@ -4,7 +4,8 @@ import { useImperativeHandle, useRef, useState } from 'react'
|
||||
|
||||
let lock = false
|
||||
|
||||
const SearchInput = ({ currentTag, currentSearch, cRef }) => {
|
||||
const SearchInput = props => {
|
||||
const { tag, keyword, cRef } = props
|
||||
const { locale } = useGlobal()
|
||||
const router = useRouter()
|
||||
const searchInputRef = useRef(null)
|
||||
@@ -61,14 +62,14 @@ const SearchInput = ({ currentTag, currentSearch, cRef }) => {
|
||||
<input
|
||||
ref={searchInputRef}
|
||||
type='text'
|
||||
placeholder={currentTag ? `${locale.SEARCH.TAGS} #${currentTag}` : `${locale.SEARCH.ARTICLES}`}
|
||||
placeholder={tag ? `${locale.SEARCH.TAGS} #${tag}` : `${locale.SEARCH.ARTICLES}`}
|
||||
className={'outline-none w-full text-sm pl-4 transition focus:shadow-lg font-light leading-10 text-black bg-gray-100 dark:bg-gray-900 dark:text-white'}
|
||||
onKeyUp={handleKeyUp}
|
||||
onCompositionStart={lockSearchInput}
|
||||
onCompositionUpdate={lockSearchInput}
|
||||
onCompositionEnd={unLockSearchInput}
|
||||
onChange={e => updateSearchKey(e.target.value)}
|
||||
defaultValue={currentSearch || ''}
|
||||
defaultValue={keyword || ''}
|
||||
/>
|
||||
|
||||
<div className='-ml-8 cursor-pointer float-right items-center justify-center py-2'
|
||||
|
||||
@@ -45,7 +45,7 @@ const LayoutBase = props => {
|
||||
|
||||
return (
|
||||
<ThemeGlobalNobelium.Provider value={{ searchModal }}>
|
||||
<div id='theme-nobelium' className='nobelium relative dark:text-gray-300 w-full bg-white dark:bg-black min-h-screen'>
|
||||
<div id='theme-nobelium' className='nobelium relative dark:text-gray-300 w-full bg-white dark:bg-black min-h-screen flex flex-col'>
|
||||
{/* SEO相关 */}
|
||||
<CommonHead meta={meta} />
|
||||
{/* SEO相关 */}
|
||||
@@ -144,7 +144,7 @@ const LayoutPostList = props => {
|
||||
* @returns
|
||||
*/
|
||||
const LayoutSearch = props => {
|
||||
const { keyword } = props
|
||||
const { keyword, posts } = props
|
||||
useEffect(() => {
|
||||
if (isBrowser) {
|
||||
replaceSearchResult({
|
||||
@@ -157,7 +157,25 @@ const LayoutSearch = props => {
|
||||
})
|
||||
}
|
||||
}, [])
|
||||
return <LayoutPostList {...props} slotTop={<SearchNavBar {...props} />} />
|
||||
|
||||
// 在列表中进行实时过滤
|
||||
const [filterKey, setFilterKey] = useState('')
|
||||
let filteredBlogPosts = []
|
||||
if (filterKey && posts) {
|
||||
filteredBlogPosts = posts.filter(post => {
|
||||
const tagContent = post?.tags ? post?.tags.join(' ') : ''
|
||||
const searchContent = post.title + post.summary + tagContent
|
||||
return searchContent.toLowerCase().includes(filterKey.toLowerCase())
|
||||
})
|
||||
} else {
|
||||
filteredBlogPosts = deepClone(posts)
|
||||
}
|
||||
console.log('posts', props, posts, filteredBlogPosts)
|
||||
|
||||
return <LayoutBase {...props} topSlot={<BlogListBar {...props} setFilterKey={setFilterKey} />}>
|
||||
<SearchNavBar {...props} />
|
||||
{BLOG.POST_LIST_STYLE === 'page' ? <BlogListPage {...props} posts={filteredBlogPosts} /> : <BlogListScroll {...props} posts={filteredBlogPosts} />}
|
||||
</LayoutBase>
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user