mirror of
https://github.com/d0zingcat/NotionNext.git
synced 2026-05-14 15:09:22 +00:00
@@ -1,2 +1,2 @@
|
||||
# 环境变量 @see https://www.nextjs.cn/docs/basic-features/environment-variables
|
||||
NEXT_PUBLIC_VERSION=4.0.8
|
||||
NEXT_PUBLIC_VERSION=4.0.9
|
||||
@@ -119,6 +119,8 @@ const BLOG = {
|
||||
|
||||
// Mermaid 图表CDN
|
||||
MERMAID_CDN: process.env.NEXT_PUBLIC_MERMAID_CDN || 'https://cdnjs.cloudflare.com/ajax/libs/mermaid/10.2.4/mermaid.min.js', // CDN
|
||||
// QRCodeCDN
|
||||
QR_CODE_CDN: process.env.NEXT_PUBLIC_QR_CODE_CDN || 'https://cdnjs.cloudflare.com/ajax/libs/qrcodejs/1.0.0/qrcode.min.js',
|
||||
|
||||
BACKGROUND_LIGHT: '#eeeeee', // use hex value, don't forget '#' e.g #fffefc
|
||||
BACKGROUND_DARK: '#000000', // use hex value, don't forget '#'
|
||||
@@ -307,12 +309,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
|
||||
|
||||
ANALYTICS_ACKEE_TRACKER:
|
||||
process.env.NEXT_PUBLIC_ANALYTICS_ACKEE_TRACKER || '', // e.g 'https://ackee.tangly1024.net/tracker.js'
|
||||
ANALYTICS_ACKEE_DATA_SERVER:
|
||||
process.env.NEXT_PUBLIC_ANALYTICS_ACKEE_DATA_SERVER || '', // e.g https://ackee.tangly1024.net , don't end with a slash
|
||||
ANALYTICS_ACKEE_DOMAIN_ID:
|
||||
process.env.NEXT_PUBLIC_ANALYTICS_ACKEE_DOMAIN_ID || '', // e.g '0e2257a8-54d4-4847-91a1-0311ea48cc7b'
|
||||
// ACKEE网站访客统计工具
|
||||
ANALYTICS_ACKEE_TRACKER: process.env.NEXT_PUBLIC_ANALYTICS_ACKEE_TRACKER || '', // e.g 'https://ackee.tangly1024.com/tracker.js'
|
||||
ANALYTICS_ACKEE_DATA_SERVER: process.env.NEXT_PUBLIC_ANALYTICS_ACKEE_DATA_SERVER || '', // e.g https://ackee.tangly1024.com , don't end with a slash
|
||||
ANALYTICS_ACKEE_DOMAIN_ID: process.env.NEXT_PUBLIC_ANALYTICS_ACKEE_DOMAIN_ID || '', // e.g '82e51db6-dec2-423a-b7c9-b4ff7ebb3302'
|
||||
|
||||
SEO_GOOGLE_SITE_VERIFICATION:
|
||||
process.env.NEXT_PUBLIC_SEO_GOOGLE_SITE_VERIFICATION || '', // Remove the value or replace it with your own google site verification code
|
||||
|
||||
@@ -1,21 +1,83 @@
|
||||
import { useRouter } from 'next/router'
|
||||
import useAckee from 'use-ackee'
|
||||
import BLOG from '@/blog.config'
|
||||
'use strict'
|
||||
|
||||
import { useEffect } from 'react'
|
||||
import BLOG from '@/blog.config'
|
||||
import { loadExternalResource } from '@/lib/utils'
|
||||
import { useRouter } from 'next/router'
|
||||
const Ackee = () => {
|
||||
const router = useRouter()
|
||||
useAckee(
|
||||
router.asPath,
|
||||
{
|
||||
server: BLOG.ANALYTICS_ACKEE_DATA_SERVER,
|
||||
domainId: BLOG.ANALYTICS_ACKEE_DOMAIN_ID
|
||||
},
|
||||
{
|
||||
detailed: false,
|
||||
ignoreLocalhost: true
|
||||
}
|
||||
)
|
||||
|
||||
// handleAckee 函数
|
||||
const handleAckeeCallback = () => {
|
||||
handleAckee(
|
||||
router.asPath,
|
||||
{
|
||||
server: BLOG.ANALYTICS_ACKEE_DATA_SERVER,
|
||||
domainId: BLOG.ANALYTICS_ACKEE_DOMAIN_ID
|
||||
},
|
||||
{
|
||||
/*
|
||||
* Enable or disable tracking of personal data.
|
||||
* We recommend to ask the user for permission before turning this option on.
|
||||
*/
|
||||
detailed: true,
|
||||
/*
|
||||
* Enable or disable tracking when on localhost.
|
||||
*/
|
||||
ignoreLocalhost: false,
|
||||
/*
|
||||
* Enable or disable the tracking of your own visits.
|
||||
* This is enabled by default, but should be turned off when using a wildcard Access-Control-Allow-Origin header.
|
||||
* Some browsers strictly block third-party cookies. The option won't have an impact when this is the case.
|
||||
*/
|
||||
ignoreOwnVisits: false
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// 或者使用其他依赖数组,根据需要执行 handleAckee
|
||||
useEffect(() => {
|
||||
handleAckeeCallback()
|
||||
}, [router])
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
export default Ackee
|
||||
|
||||
/**
|
||||
* Function to use Ackee.
|
||||
* Creates an instance once and a new record every time the pathname changes.
|
||||
* Safely no-ops during server-side rendering.
|
||||
* @param {?String} pathname - Current path.
|
||||
* @param {Object} environment - Object containing the URL of the Ackee server and the domain id.
|
||||
* @param {?Object} options - Ackee options.
|
||||
*/
|
||||
const handleAckee = async function(pathname, environment, options = {}) {
|
||||
await loadExternalResource(BLOG.ANALYTICS_ACKEE_TRACKER, 'js')
|
||||
const ackeeTracker = window.ackeeTracker
|
||||
|
||||
const instance = ackeeTracker.create(environment.server, options)
|
||||
|
||||
if (instance == null) {
|
||||
console.warn('Skipped record creation because useAckee has been called in a non-browser environment')
|
||||
return
|
||||
}
|
||||
|
||||
const hasPathname = (
|
||||
pathname != null && pathname !== ''
|
||||
)
|
||||
|
||||
if (hasPathname === false) {
|
||||
console.warn('Skipped record creation because useAckee has been called without pathname')
|
||||
return
|
||||
}
|
||||
|
||||
const attributes = ackeeTracker.attributes(options.detailed)
|
||||
const url = new URL(pathname, location)
|
||||
|
||||
return instance.record(environment.domainId, {
|
||||
...attributes,
|
||||
siteLocation: url.href
|
||||
}).stop
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ const ValineComponent = dynamic(() => import('@/components/ValineComponent'), {
|
||||
const Comment = ({ frontMatter, className }) => {
|
||||
const router = useRouter()
|
||||
|
||||
if (isBrowser() && ('giscus' in router.query || router.query.target === 'comment')) {
|
||||
if (isBrowser && ('giscus' in router.query || router.query.target === 'comment')) {
|
||||
setTimeout(() => {
|
||||
const url = router.asPath.replace('?target=comment', '')
|
||||
history.replaceState({}, '', url)
|
||||
|
||||
@@ -56,12 +56,12 @@ const CommonScript = () => {
|
||||
|
||||
{/* 代码统计 */}
|
||||
{/* ackee统计脚本 */}
|
||||
{BLOG.ANALYTICS_ACKEE_TRACKER && (
|
||||
{/* {BLOG.ANALYTICS_ACKEE_TRACKER && (
|
||||
<script async src={BLOG.ANALYTICS_ACKEE_TRACKER}
|
||||
data-ackee-server={BLOG.ANALYTICS_ACKEE_DATA_SERVER}
|
||||
data-ackee-domain-id={BLOG.ANALYTICS_ACKEE_DOMAIN_ID}
|
||||
/>
|
||||
)}
|
||||
)} */}
|
||||
|
||||
{/* 百度统计 */}
|
||||
{BLOG.ANALYTICS_BAIDU_ID && (
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import { ReactCusdis } from 'react-cusdis'
|
||||
import BLOG from '@/blog.config'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useEffect } from 'react'
|
||||
import { loadExternalResource } from '@/lib/utils'
|
||||
|
||||
const CusdisComponent = ({ frontMatter }) => {
|
||||
const { locale } = useGlobal()
|
||||
@@ -11,33 +11,21 @@ const CusdisComponent = ({ frontMatter }) => {
|
||||
|
||||
// 处理cusdis主题
|
||||
useEffect(() => {
|
||||
const cusdisThread = document?.getElementById('cusdis_thread')
|
||||
const cusdisIframe = cusdisThread?.getElementsByTagName('iframe')
|
||||
if (cusdisIframe) {
|
||||
const cusdisWrapper = cusdisIframe[0]?.contentDocument?.getElementById('root')
|
||||
if (isDarkMode) {
|
||||
cusdisWrapper?.classList?.remove('light')
|
||||
cusdisWrapper?.classList?.add('dark')
|
||||
} else {
|
||||
cusdisWrapper?.classList?.remove('dark')
|
||||
cusdisWrapper?.classList?.add('light')
|
||||
}
|
||||
if (!cusdisWrapper?.firstElementChild?.classList?.contains('dark:text-gray-100')) {
|
||||
cusdisWrapper?.firstElementChild?.classList?.add('dark:text-gray-100')
|
||||
}
|
||||
}
|
||||
})
|
||||
loadExternalResource(BLOG.COMMENT_CUSDIS_SCRIPT_SRC, 'js').then(url => {
|
||||
const CUSDIS = window.CUSDIS
|
||||
CUSDIS.initial()
|
||||
})
|
||||
}, [isDarkMode])
|
||||
|
||||
return <ReactCusdis
|
||||
lang={locale.LOCALE.toLowerCase()}
|
||||
attrs={{
|
||||
host: BLOG.COMMENT_CUSDIS_HOST,
|
||||
appId: BLOG.COMMENT_CUSDIS_APP_ID,
|
||||
pageId: frontMatter.id,
|
||||
pageTitle: frontMatter.title,
|
||||
pageUrl: BLOG.LINK + router.asPath
|
||||
}}
|
||||
/>
|
||||
return <div id="cusdis_thread"
|
||||
lang={locale.LOCALE.toLowerCase()}
|
||||
data-host={BLOG.COMMENT_CUSDIS_HOST}
|
||||
data-app-id={BLOG.COMMENT_CUSDIS_APP_ID}
|
||||
data-page-id={frontMatter.id}
|
||||
data-page-url={BLOG.LINK + router.asPath}
|
||||
data-page-title={frontMatter.title}
|
||||
data-theme={isDarkMode ? 'dark' : 'light'}
|
||||
></div>
|
||||
}
|
||||
|
||||
export default CusdisComponent
|
||||
|
||||
@@ -8,7 +8,7 @@ import { isBrowser, loadExternalResource } from '@/lib/utils'
|
||||
* @returns
|
||||
*/
|
||||
const ExternalScript = () => {
|
||||
if (isBrowser()) {
|
||||
if (isBrowser) {
|
||||
// 静态导入本地自定义样式
|
||||
loadExternalResource('/css/custom.css', 'css')
|
||||
loadExternalResource('/js/custom.js', 'js')
|
||||
|
||||
@@ -1,11 +1,255 @@
|
||||
import BLOG from '@/blog.config'
|
||||
import MessengerCustomerChat from 'react-messenger-customer-chat'
|
||||
import { Component } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
const Messenger = () => (
|
||||
<MessengerCustomerChat
|
||||
export default function Messenger() {
|
||||
console.log('facebook', BLOG.FACEBOOK_PAGE_ID, BLOG.FACEBOOK_APP_ID)
|
||||
return <MessengerCustomerChat
|
||||
pageId={BLOG.FACEBOOK_PAGE_ID}
|
||||
appId={BLOG.FACEBOOK_APP_ID}
|
||||
language={BLOG.LANG.replace('-', '_')}
|
||||
/>
|
||||
)
|
||||
export default Messenger
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://github.com/Yoctol/react-messenger-customer-chat
|
||||
*/
|
||||
class MessengerCustomerChat extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
fbLoaded: false,
|
||||
shouldShowDialog: undefined
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
*/
|
||||
componentDidMount() {
|
||||
this.setFbAsyncInit()
|
||||
this.reloadSDKAsynchronously()
|
||||
}
|
||||
|
||||
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
|
||||
) {
|
||||
this.setFbAsyncInit()
|
||||
this.reloadSDKAsynchronously()
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (window.FB !== undefined) {
|
||||
window.FB.CustomerChat.hide()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
*/
|
||||
setFbAsyncInit() {
|
||||
const { appId, autoLogAppEvents, xfbml, version } = this.props
|
||||
|
||||
window.fbAsyncInit = () => {
|
||||
window.FB.init({
|
||||
appId,
|
||||
autoLogAppEvents,
|
||||
xfbml,
|
||||
version: `v${version}`
|
||||
})
|
||||
|
||||
this.setState({ fbLoaded: true })
|
||||
}
|
||||
}
|
||||
|
||||
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 */
|
||||
}
|
||||
|
||||
removeFacebookSDK() {
|
||||
removeElementByIds(['facebook-jssdk', 'fb-root'])
|
||||
|
||||
delete window.FB
|
||||
}
|
||||
|
||||
reloadSDKAsynchronously() {
|
||||
this.removeFacebookSDK()
|
||||
this.loadSDKAsynchronously()
|
||||
}
|
||||
|
||||
controlPlugin() {
|
||||
const { shouldShowDialog } = this.props
|
||||
|
||||
if (shouldShowDialog) {
|
||||
window.FB.CustomerChat.showDialog()
|
||||
} else {
|
||||
window.FB.CustomerChat.hideDialog()
|
||||
}
|
||||
}
|
||||
|
||||
subscribeEvents() {
|
||||
const { onCustomerChatDialogShow, onCustomerChatDialogHide } = this.props
|
||||
|
||||
if (onCustomerChatDialogShow) {
|
||||
window.FB.Event.subscribe(
|
||||
'customerchat.dialogShow',
|
||||
onCustomerChatDialogShow
|
||||
)
|
||||
}
|
||||
|
||||
if (onCustomerChatDialogHide) {
|
||||
window.FB.Event.subscribe(
|
||||
'customerchat.dialogHide',
|
||||
onCustomerChatDialogHide
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
createMarkup() {
|
||||
const {
|
||||
pageId,
|
||||
htmlRef,
|
||||
minimized,
|
||||
themeColor,
|
||||
loggedInGreeting,
|
||||
loggedOutGreeting,
|
||||
greetingDialogDisplay,
|
||||
greetingDialogDelay
|
||||
} = this.props
|
||||
|
||||
const refAttribute = htmlRef !== undefined ? `ref="${htmlRef}"` : ''
|
||||
const minimizedAttribute =
|
||||
minimized !== undefined ? `minimized="${minimized}"` : ''
|
||||
const themeColorAttribute =
|
||||
themeColor !== undefined ? `theme_color="${themeColor}"` : ''
|
||||
const loggedInGreetingAttribute =
|
||||
loggedInGreeting !== undefined
|
||||
? `logged_in_greeting="${loggedInGreeting}"`
|
||||
: ''
|
||||
const loggedOutGreetingAttribute =
|
||||
loggedOutGreeting !== undefined
|
||||
? `logged_out_greeting="${loggedOutGreeting}"`
|
||||
: ''
|
||||
const greetingDialogDisplayAttribute =
|
||||
greetingDialogDisplay !== undefined
|
||||
? `greeting_dialog_display="${greetingDialogDisplay}"`
|
||||
: ''
|
||||
const greetingDialogDelayAttribute =
|
||||
greetingDialogDelay !== undefined
|
||||
? `greeting_dialog_delay="${greetingDialogDelay}"`
|
||||
: ''
|
||||
|
||||
return {
|
||||
__html: `<div
|
||||
class="fb-customerchat"
|
||||
page_id="${pageId}"
|
||||
${refAttribute}
|
||||
${minimizedAttribute}
|
||||
${themeColorAttribute}
|
||||
${loggedInGreetingAttribute}
|
||||
${loggedOutGreetingAttribute}
|
||||
${greetingDialogDisplayAttribute}
|
||||
${greetingDialogDelayAttribute}
|
||||
></div>`
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { fbLoaded, shouldShowDialog } = this.state
|
||||
|
||||
if (fbLoaded && shouldShowDialog !== this.props.shouldShowDialog) {
|
||||
document.addEventListener(
|
||||
'DOMNodeInserted',
|
||||
(event) => {
|
||||
const element = event.target
|
||||
if (
|
||||
element.className &&
|
||||
typeof element.className === 'string' &&
|
||||
element.className.includes('fb_dialog')
|
||||
) {
|
||||
this.controlPlugin()
|
||||
}
|
||||
},
|
||||
false
|
||||
)
|
||||
this.subscribeEvents()
|
||||
}
|
||||
// Add a random key to rerender. Reference:
|
||||
// https://stackoverflow.com/questions/30242530/dangerouslysetinnerhtml-doesnt-update-during-render
|
||||
return <div key={Date()} dangerouslySetInnerHTML={this.createMarkup()} />
|
||||
}
|
||||
}
|
||||
|
||||
const removeElementByIds = (ids) => {
|
||||
ids.forEach((id) => {
|
||||
const element = document.getElementById(id)
|
||||
if (element && element.parentNode) {
|
||||
element.parentNode.removeChild(element)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
MessengerCustomerChat.propTypes = {
|
||||
pageId: PropTypes.string.isRequired,
|
||||
appId: PropTypes.string,
|
||||
shouldShowDialog: PropTypes.bool,
|
||||
htmlRef: PropTypes.string,
|
||||
minimized: PropTypes.bool,
|
||||
themeColor: PropTypes.string,
|
||||
loggedInGreeting: PropTypes.string,
|
||||
loggedOutGreeting: PropTypes.string,
|
||||
greetingDialogDisplay: PropTypes.oneOf(['show', 'hide', 'fade']),
|
||||
greetingDialogDelay: PropTypes.number,
|
||||
autoLogAppEvents: PropTypes.bool,
|
||||
xfbml: PropTypes.bool,
|
||||
version: PropTypes.string,
|
||||
language: PropTypes.string,
|
||||
onCustomerChatDialogShow: PropTypes.func,
|
||||
onCustomerChatDialogHide: PropTypes.func
|
||||
}
|
||||
|
||||
MessengerCustomerChat.defaultProps = {
|
||||
appId: null,
|
||||
shouldShowDialog: false,
|
||||
htmlRef: undefined,
|
||||
minimized: undefined,
|
||||
themeColor: undefined,
|
||||
loggedInGreeting: undefined,
|
||||
loggedOutGreeting: undefined,
|
||||
greetingDialogDisplay: undefined,
|
||||
greetingDialogDelay: undefined,
|
||||
autoLogAppEvents: true,
|
||||
xfbml: true,
|
||||
version: '11.0',
|
||||
language: 'en_US',
|
||||
onCustomerChatDialogShow: undefined,
|
||||
onCustomerChatDialogHide: undefined
|
||||
}
|
||||
|
||||
@@ -2,13 +2,17 @@ import BLOG from '@/blog.config'
|
||||
import { FacebookProvider, Page } from 'react-facebook'
|
||||
import { FacebookIcon } from 'react-share'
|
||||
|
||||
/**
|
||||
* facebook个人主页
|
||||
* @returns
|
||||
*/
|
||||
const FacebookPage = () => {
|
||||
if (!BLOG.FACEBOOK_APP_ID && !BLOG.FACEBOOK_PAGE) {
|
||||
if (!BLOG.FACEBOOK_APP_ID || !BLOG.FACEBOOK_PAGE) {
|
||||
return <></>
|
||||
}
|
||||
return <div className="shadow-md hover:shadow-xl dark:text-gray-300 border dark:border-black rounded-xl px-2 py-4 bg-white dark:bg-hexo-black-gray lg:duration-100 justify-center">
|
||||
{BLOG.FACEBOOK_PAGE && (
|
||||
<div className="flex items-center pb-2">
|
||||
<div className="flex items-center pb-2">
|
||||
<a
|
||||
href={BLOG.FACEBOOK_PAGE}
|
||||
target="_blank"
|
||||
|
||||
@@ -9,7 +9,7 @@ const FullScreenButton = () => {
|
||||
const [isFullScreen, setIsFullScreen] = useState(false)
|
||||
|
||||
const handleFullScreenClick = () => {
|
||||
if (!isBrowser()) {
|
||||
if (!isBrowser) {
|
||||
return
|
||||
}
|
||||
const element = document.documentElement
|
||||
|
||||
@@ -21,7 +21,7 @@ export default function GoogleAdsense() {
|
||||
for (let i = 0; i <= ads.length; i++) {
|
||||
try {
|
||||
adsbygoogle.push(ads[i])
|
||||
console.log('adsbygoogle', i, ads[i], adsbygoogle)
|
||||
// console.log('adsbygoogle', i, ads[i], adsbygoogle)
|
||||
} catch (e) {
|
||||
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ const NotionPage = ({ post, className }) => {
|
||||
// 将相册gallery下的图片加入放大功能
|
||||
if (JSON.parse(BLOG.POST_DISABLE_GALLERY_CLICK)) {
|
||||
setTimeout(() => {
|
||||
if (isBrowser()) {
|
||||
if (isBrowser) {
|
||||
const imgList = document?.querySelectorAll('.notion-collection-card-cover img')
|
||||
if (imgList && zoomRef.current) {
|
||||
for (let i = 0; i < imgList.length; i++) {
|
||||
@@ -80,6 +80,25 @@ const NotionPage = ({ post, className }) => {
|
||||
}
|
||||
}, 800)
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理页面内连接跳转
|
||||
* 如果链接就是当前网站,则不打开新窗口访问
|
||||
*/
|
||||
if (isBrowser) {
|
||||
const currentURL = window.location.origin + window.location.pathname
|
||||
const allAnchorTags = document.getElementsByTagName('a') // 或者使用 document.querySelectorAll('a') 获取 NodeList
|
||||
for (const anchorTag of allAnchorTags) {
|
||||
if (anchorTag?.target === '_blank') {
|
||||
const hrefWithoutQueryHash = anchorTag.href.split('?')[0].split('#')[0]
|
||||
const hrefWithRelativeHash = currentURL.split('#')[0] + anchorTag.href.split('#')[1]
|
||||
|
||||
if (currentURL === hrefWithoutQueryHash || currentURL === hrefWithRelativeHash) {
|
||||
anchorTag.target = '_self'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [])
|
||||
|
||||
if (!post || !post.blockMap) {
|
||||
|
||||
@@ -141,7 +141,6 @@ const renderMermaid = async() => {
|
||||
if (needLoad) {
|
||||
loadExternalResource(BLOG.MERMAID_CDN, 'js').then(url => {
|
||||
const mermaid = window.mermaid
|
||||
console.log('mermaid加载成功', url, mermaid)
|
||||
mermaid.contentLoaded()
|
||||
})
|
||||
}
|
||||
|
||||
34
components/QrCode.js
Normal file
34
components/QrCode.js
Normal file
@@ -0,0 +1,34 @@
|
||||
import BLOG from '@/blog.config'
|
||||
import { loadExternalResource } from '@/lib/utils'
|
||||
import { useEffect } from 'react'
|
||||
|
||||
/**
|
||||
* 二维码生成
|
||||
*/
|
||||
export default function QrCode({ value }) {
|
||||
useEffect(() => {
|
||||
let qrcode
|
||||
if (!value) {
|
||||
return
|
||||
}
|
||||
loadExternalResource(BLOG.QR_CODE_CDN, 'js').then(url => {
|
||||
const QRCode = window.QRCode
|
||||
qrcode = new QRCode(document.getElementById('qrcode'), {
|
||||
text: value,
|
||||
width: 256,
|
||||
height: 256,
|
||||
colorDark: '#000000',
|
||||
colorLight: '#ffffff',
|
||||
correctLevel: QRCode.CorrectLevel.H
|
||||
})
|
||||
console.log('二维码', qrcode, value)
|
||||
})
|
||||
return () => {
|
||||
if (qrcode) {
|
||||
qrcode.clear() // clear the code.
|
||||
}
|
||||
}
|
||||
}, [])
|
||||
|
||||
return <div id="qrcode"></div>
|
||||
}
|
||||
@@ -49,12 +49,7 @@ import {
|
||||
HatenaIcon
|
||||
} from 'react-share'
|
||||
|
||||
const QRCode = dynamic(
|
||||
() => {
|
||||
return import('qrcode.react')
|
||||
},
|
||||
{ ssr: false }
|
||||
)
|
||||
const QrCode = dynamic(() => import('@/components/QrCode'), { ssr: false })
|
||||
|
||||
/**
|
||||
* @author https://github.com/txs
|
||||
@@ -353,8 +348,8 @@ const ShareButtons = ({ shareUrl, title, body, image }) => {
|
||||
</div>
|
||||
<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'>
|
||||
<QRCode value={shareUrl} fgColor='#000000' />
|
||||
<div className='p-2 mt-1 w-28 h-28'>
|
||||
<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}
|
||||
|
||||
@@ -13,8 +13,7 @@ import { useEffect } from 'react'
|
||||
const Twikoo = ({ isDarkMode }) => {
|
||||
const loadTwikoo = async () => {
|
||||
try {
|
||||
const url = await loadExternalResource(BLOG.COMMENT_TWIKOO_CDN_URL, 'js')
|
||||
console.log('twikoo 加载成功', url)
|
||||
await loadExternalResource(BLOG.COMMENT_TWIKOO_CDN_URL, 'js')
|
||||
const twikoo = window.twikoo
|
||||
twikoo.init({
|
||||
envId: BLOG.COMMENT_TWIKOO_ENV_ID, // 腾讯云环境填 envId;Vercel 环境填地址(https://xxx.vercel.app)
|
||||
|
||||
@@ -6,7 +6,6 @@ const ValineComponent = ({ path }) => {
|
||||
const loadValine = async () => {
|
||||
try {
|
||||
await loadExternalResource(BLOG.COMMENT_VALINE_CDN, 'js')
|
||||
// console.log('valine 加载成功', url)
|
||||
const Valine = window.Valine
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const valine = new Valine({
|
||||
|
||||
@@ -91,7 +91,7 @@ export function GlobalContextProvider(props) {
|
||||
* @param {*} setTheme
|
||||
*/
|
||||
const initTheme = () => {
|
||||
if (isBrowser()) {
|
||||
if (isBrowser) {
|
||||
setTimeout(() => {
|
||||
const elements = document.querySelectorAll('[id^="theme-"]')
|
||||
if (elements?.length > 1) {
|
||||
|
||||
@@ -64,7 +64,7 @@ export function generateLocaleDict(langString) {
|
||||
* 根据用户当前浏览器语言进行切换
|
||||
*/
|
||||
export function initLocale(lang, locale, changeLang, changeLocale) {
|
||||
if (isBrowser()) {
|
||||
if (isBrowser) {
|
||||
const queryLang = getQueryVariable('lang') || loadLangFromCookies() || window.navigator.language
|
||||
let currentLang = lang
|
||||
if (queryLang !== lang) {
|
||||
|
||||
26
lib/utils.js
26
lib/utils.js
@@ -1,6 +1,12 @@
|
||||
// 封装异步加载资源的方法
|
||||
import { memo } from 'react'
|
||||
|
||||
/**
|
||||
* 判断是否客户端
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export const isBrowser = typeof window !== 'undefined'
|
||||
|
||||
/**
|
||||
* 组件持久化
|
||||
*/
|
||||
@@ -39,8 +45,14 @@ export function loadExternalResource(url, type) {
|
||||
tag.src = url
|
||||
}
|
||||
if (tag) {
|
||||
tag.onload = () => resolve(url)
|
||||
tag.onerror = () => reject(url)
|
||||
tag.onload = () => {
|
||||
console.log('Load Success', url)
|
||||
resolve(url)
|
||||
}
|
||||
tag.onerror = () => {
|
||||
console.log('Load Error', url)
|
||||
reject(url)
|
||||
}
|
||||
document.head.appendChild(tag)
|
||||
}
|
||||
})
|
||||
@@ -52,7 +64,7 @@ export function loadExternalResource(url, type) {
|
||||
* @returns
|
||||
*/
|
||||
export function getQueryVariable(key) {
|
||||
const query = isBrowser() ? window.location.search.substring(1) : ''
|
||||
const query = isBrowser ? window.location.search.substring(1) : ''
|
||||
const vars = query.split('&')
|
||||
for (let i = 0; i < vars.length; i++) {
|
||||
const pair = vars[i].split('=')
|
||||
@@ -145,12 +157,6 @@ export function deepClone(obj) {
|
||||
*/
|
||||
export const delay = ms => new Promise(resolve => setTimeout(resolve, ms))
|
||||
|
||||
/**
|
||||
* 判断是否客户端
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export const isBrowser = () => typeof window !== 'undefined'
|
||||
|
||||
/**
|
||||
* 获取从第1页到指定页码的文章
|
||||
* @param pageIndex 第几页
|
||||
@@ -170,7 +176,7 @@ export const getListByPage = function (list, pageIndex, pageSize) {
|
||||
*/
|
||||
export const isMobile = () => {
|
||||
let isMobile = false
|
||||
if (!isBrowser()) {
|
||||
if (!isBrowser) {
|
||||
return isMobile
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "notion-next",
|
||||
"version": "4.0.8",
|
||||
"version": "4.0.9",
|
||||
"homepage": "https://github.com/tangly1024/NotionNext.git",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
@@ -43,18 +43,14 @@
|
||||
"nprogress": "^0.2.0",
|
||||
"preact": "^10.5.15",
|
||||
"prism-themes": "1.9.0",
|
||||
"qrcode.react": "^1.0.1",
|
||||
"react": "^18.2.0",
|
||||
"react-cookies": "^0.1.1",
|
||||
"react-cusdis": "^2.1.3",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-facebook": "^8.1.4",
|
||||
"react-messenger-customer-chat": "^0.8.0",
|
||||
"react-notion-x": "6.16.0",
|
||||
"react-share": "^4.4.1",
|
||||
"react-tweet-embed": "~2.0.0",
|
||||
"typed.js": "^2.0.12",
|
||||
"use-ackee": "^3.0.0"
|
||||
"typed.js": "^2.0.12"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@waline/client": "^2.5.1",
|
||||
|
||||
@@ -41,7 +41,7 @@ const Slug = props => {
|
||||
// 404
|
||||
if (!post) {
|
||||
setTimeout(() => {
|
||||
if (isBrowser()) {
|
||||
if (isBrowser) {
|
||||
const article = document.getElementById('notion-article')
|
||||
if (!article) {
|
||||
router.push('/404').then(() => {
|
||||
|
||||
@@ -15,7 +15,7 @@ const ArchiveIndex = props => {
|
||||
const Layout = getLayoutByTheme(useRouter())
|
||||
|
||||
useEffect(() => {
|
||||
if (isBrowser()) {
|
||||
if (isBrowser) {
|
||||
const anchor = window.location.hash
|
||||
if (anchor) {
|
||||
setTimeout(() => {
|
||||
|
||||
@@ -177,7 +177,7 @@ const LayoutSearch = props => {
|
||||
const slotTop = <div className='pb-12'><SearchInput {...props} /></div>
|
||||
const router = useRouter()
|
||||
useEffect(() => {
|
||||
if (isBrowser()) {
|
||||
if (isBrowser) {
|
||||
// 高亮搜索到的结果
|
||||
const container = document.getElementById('posts-wrapper')
|
||||
if (keyword && container) {
|
||||
|
||||
@@ -77,7 +77,7 @@ const BlogListPage = ({ page = 1, posts = [], postCount, siteInfo }) => {
|
||||
* @returns
|
||||
*/
|
||||
const calculateColumns = () => {
|
||||
if (!isBrowser()) {
|
||||
if (!isBrowser) {
|
||||
return 3
|
||||
} else {
|
||||
if (window.innerWidth >= 1024) {
|
||||
|
||||
@@ -57,7 +57,7 @@ const LayoutBase = (props) => {
|
||||
|
||||
// 在组件卸载时保存 open 状态到本地存储中
|
||||
useEffect(() => {
|
||||
if (isBrowser()) {
|
||||
if (isBrowser) {
|
||||
localStorage.setItem('fukasawa-sidebar-collapse', isCollapsed)
|
||||
}
|
||||
}, [isCollapsed])
|
||||
@@ -149,7 +149,7 @@ const LayoutSearch = props => {
|
||||
const { keyword } = props
|
||||
const router = useRouter()
|
||||
useEffect(() => {
|
||||
if (isBrowser()) {
|
||||
if (isBrowser) {
|
||||
replaceSearchResult({
|
||||
doms: document.getElementById('posts-wrapper'),
|
||||
search: keyword,
|
||||
|
||||
@@ -49,7 +49,7 @@ const Catalog = ({ post }) => {
|
||||
}
|
||||
setActiveSection(currentSectionId)
|
||||
const index = tocIds.indexOf(currentSectionId) || 0
|
||||
if (isBrowser() && tocIds?.length > 0) {
|
||||
if (isBrowser && tocIds?.length > 0) {
|
||||
for (const tocWrapper of document?.getElementsByClassName('toc-wrapper')) {
|
||||
tocWrapper?.scrollTo({ top: 28 * index, behavior: 'smooth' })
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ const Progress = ({ targetRef, showPercent = true }) => {
|
||||
const currentRef = targetRef?.current || targetRef
|
||||
const [percent, changePercent] = useState(0)
|
||||
const scrollListener = () => {
|
||||
const target = currentRef || (isBrowser() && document.getElementById('posts-wrapper'))
|
||||
const target = currentRef || (isBrowser && document.getElementById('posts-wrapper'))
|
||||
if (target) {
|
||||
const clientHeight = target.clientHeight
|
||||
const scrollY = window.pageYOffset
|
||||
|
||||
@@ -56,7 +56,6 @@ const LayoutBase = (props) => {
|
||||
const showTocButton = post?.toc?.length > 1
|
||||
|
||||
useEffect(() => {
|
||||
console.log('更新导航', allNavPages)
|
||||
setFilteredNavPages(allNavPages)
|
||||
}, [post])
|
||||
|
||||
@@ -174,7 +173,7 @@ const LayoutIndex = (props) => {
|
||||
router.push(CONFIG.INDEX_PAGE).then(() => {
|
||||
// console.log('跳转到指定首页', CONFIG.INDEX_PAGE)
|
||||
setTimeout(() => {
|
||||
if (isBrowser()) {
|
||||
if (isBrowser) {
|
||||
const article = document.getElementById('notion-article')
|
||||
if (!article) {
|
||||
console.log('请检查您的Notion数据库中是否包含此slug页面: ', CONFIG.INDEX_PAGE)
|
||||
|
||||
@@ -84,12 +84,12 @@ const NavBar = props => {
|
||||
}
|
||||
}
|
||||
|
||||
if (isBrowser()) {
|
||||
if (isBrowser) {
|
||||
window.addEventListener('scroll', handleScroll)
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (isBrowser()) {
|
||||
if (isBrowser) {
|
||||
window.removeEventListener('scroll', handleScroll)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ export function Swipe({ items }) {
|
||||
const [activeIndex, setActiveIndex] = useState(0)
|
||||
|
||||
const handleClick = (item) => {
|
||||
if (isBrowser()) {
|
||||
if (isBrowser) {
|
||||
window.open(item?.url)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ const Progress = ({ targetRef, showPercent = true }) => {
|
||||
const currentRef = targetRef?.current || targetRef
|
||||
const [percent, changePercent] = useState(0)
|
||||
const scrollListener = () => {
|
||||
const target = currentRef || (isBrowser() && document.getElementById('article-wrapper'))
|
||||
const target = currentRef || (isBrowser && document.getElementById('article-wrapper'))
|
||||
if (target) {
|
||||
const clientHeight = target.clientHeight
|
||||
const scrollY = window.pageYOffset
|
||||
|
||||
@@ -191,7 +191,7 @@ const LayoutSlug = props => {
|
||||
const { post, lock, validPassword } = props
|
||||
const drawerRight = useRef(null)
|
||||
|
||||
const targetRef = isBrowser() ? document.getElementById('article-wrapper') : null
|
||||
const targetRef = isBrowser ? document.getElementById('article-wrapper') : null
|
||||
|
||||
const floatSlot = <>
|
||||
{post?.toc?.length > 1 && <div className="block lg:hidden">
|
||||
@@ -254,7 +254,7 @@ const Layout404 = props => {
|
||||
useEffect(() => {
|
||||
// 延时3秒如果加载失败就返回首页
|
||||
setTimeout(() => {
|
||||
if (isBrowser()) {
|
||||
if (isBrowser) {
|
||||
const article = document.getElementById('notion-article')
|
||||
if (!article) {
|
||||
router.push('/').then(() => {
|
||||
|
||||
@@ -11,7 +11,7 @@ const Progress = ({ targetRef, showPercent = true }) => {
|
||||
const [percent, changePercent] = useState(0)
|
||||
const scrollListener = () => {
|
||||
requestAnimationFrame(() => {
|
||||
const target = currentRef || (isBrowser() && document.getElementById('article-wrapper'))
|
||||
const target = currentRef || (isBrowser && document.getElementById('article-wrapper'))
|
||||
if (target) {
|
||||
const clientHeight = target.clientHeight
|
||||
const scrollY = window.pageYOffset
|
||||
|
||||
@@ -10,7 +10,7 @@ const Progress = ({ targetRef, showPercent = true }) => {
|
||||
const currentRef = targetRef?.current || targetRef
|
||||
const [percent, changePercent] = useState(0)
|
||||
const scrollListener = () => {
|
||||
const target = currentRef || (isBrowser() && document.getElementById('article-wrapper'))
|
||||
const target = currentRef || (isBrowser && document.getElementById('article-wrapper'))
|
||||
if (target) {
|
||||
const clientHeight = target.clientHeight
|
||||
const scrollY = window.pageYOffset
|
||||
|
||||
@@ -206,7 +206,7 @@ const LayoutSearch = (props) => {
|
||||
const currentSearch = keyword || router?.query?.s
|
||||
|
||||
useEffect(() => {
|
||||
if (isBrowser()) {
|
||||
if (isBrowser) {
|
||||
replaceSearchResult({
|
||||
doms: document.getElementById('posts-wrapper'),
|
||||
search: keyword,
|
||||
|
||||
@@ -10,7 +10,7 @@ const Progress = ({ targetRef, showPercent = true }) => {
|
||||
const currentRef = targetRef?.current || targetRef
|
||||
const [percent, changePercent] = useState(0)
|
||||
const scrollListener = () => {
|
||||
const target = currentRef || (isBrowser() && document.getElementById('article-wrapper'))
|
||||
const target = currentRef || (isBrowser && document.getElementById('article-wrapper'))
|
||||
if (target) {
|
||||
const clientHeight = target.clientHeight
|
||||
const scrollY = window.pageYOffset
|
||||
|
||||
@@ -161,7 +161,7 @@ const LayoutSearch = (props) => {
|
||||
const { posts, keyword } = props
|
||||
|
||||
useEffect(() => {
|
||||
if (isBrowser()) {
|
||||
if (isBrowser) {
|
||||
replaceSearchResult({
|
||||
doms: document.getElementById('posts-wrapper'),
|
||||
search: keyword,
|
||||
@@ -201,7 +201,7 @@ const Layout404 = props => {
|
||||
useEffect(() => {
|
||||
// 延时3秒如果加载失败就返回首页
|
||||
setTimeout(() => {
|
||||
const article = isBrowser() && document.getElementById('article-wrapper')
|
||||
const article = isBrowser && document.getElementById('article-wrapper')
|
||||
if (!article) {
|
||||
router.push('/').then(() => {
|
||||
// console.log('找不到页面', router.asPath)
|
||||
@@ -253,7 +253,7 @@ const LayoutArchive = (props) => {
|
||||
const LayoutSlug = (props) => {
|
||||
const { post, lock, validPassword } = props
|
||||
const drawerRight = useRef(null)
|
||||
const targetRef = isBrowser() ? document.getElementById('article-wrapper') : null
|
||||
const targetRef = isBrowser ? document.getElementById('article-wrapper') : null
|
||||
const floatSlot = <div className='block lg:hidden'>
|
||||
<TocDrawerButton onClick={() => {
|
||||
drawerRight?.current?.handleSwitchVisible()
|
||||
|
||||
@@ -134,7 +134,7 @@ const LayoutPostList = props => {
|
||||
const LayoutSearch = props => {
|
||||
const { keyword } = props
|
||||
useEffect(() => {
|
||||
if (isBrowser()) {
|
||||
if (isBrowser) {
|
||||
replaceSearchResult({
|
||||
doms: document.getElementById('posts-wrapper'),
|
||||
search: keyword,
|
||||
|
||||
@@ -132,7 +132,7 @@ const LayoutSearch = props => {
|
||||
const { keyword } = props
|
||||
|
||||
useEffect(() => {
|
||||
if (isBrowser()) {
|
||||
if (isBrowser) {
|
||||
replaceSearchResult({
|
||||
doms: document.getElementById('posts-wrapper'),
|
||||
search: keyword,
|
||||
|
||||
@@ -36,7 +36,7 @@ const LayoutBase = props => {
|
||||
const { children, slotTop, meta } = props
|
||||
const { onLoading } = useGlobal()
|
||||
|
||||
if (isBrowser()) {
|
||||
if (isBrowser) {
|
||||
loadExternalResource('/css/theme-simple.css', 'css')
|
||||
}
|
||||
return (
|
||||
@@ -122,7 +122,7 @@ const LayoutSearch = props => {
|
||||
const { keyword } = props
|
||||
|
||||
useEffect(() => {
|
||||
if (isBrowser()) {
|
||||
if (isBrowser) {
|
||||
replaceSearchResult({
|
||||
doms: document.getElementById('posts-wrapper'),
|
||||
search: keyword,
|
||||
|
||||
Reference in New Issue
Block a user