用twikoo实现列表评论数

This commit is contained in:
tangly1024
2023-05-07 18:30:56 +08:00
parent 74ace70a7b
commit eecac656e1
9 changed files with 156 additions and 31 deletions

View File

@@ -185,7 +185,9 @@ const BLOG = {
// ----> 评论互动 可同时开启多个支持 WALINE VALINE GISCUS CUSDIS UTTERRANCES GITALK // ----> 评论互动 可同时开启多个支持 WALINE VALINE GISCUS CUSDIS UTTERRANCES GITALK
// twikoo // twikoo
COMMENT_TWIKOO_ENV_ID: process.env.NEXT_PUBLIC_COMMENT_ENV_ID || '', // TWIKOO地址 腾讯云环境填 envIdVercel 环境域名地址(https://xxx.vercel.app) COMMENT_TWIKOO_ENV_ID: process.env.NEXT_PUBLIC_COMMENT_ENV_ID || '', // TWIKOO后端地址 腾讯云环境填envIdVercel环境域名,教程:https://tangly1024.com/article/notionnext-twikoo
COMMENT_TWIKOO_COUNT_ENABLE: process.env.NEXT_PUBLIC_COMMENT_TWIKOO_COUNT_ENABLE || false, // 博客列表是否显示评论数
COMMENT_TWIKOO_CDN_URL: process.env.NEXT_PUBLIC_COMMENT_TWIKOO_CDN_URL || 'https://cdn.staticfile.org/twikoo/1.6.16/twikoo.all.min.js', // twikoo客户端cdn
// utterance // utterance
COMMENT_UTTERRANCES_REPO: COMMENT_UTTERRANCES_REPO:

View File

@@ -23,8 +23,10 @@ const PrismMac = () => {
loadExternalResource('/css/prism-mac-style.css', 'css') loadExternalResource('/css/prism-mac-style.css', 'css')
} }
loadExternalResource(BLOG.PRISM_THEME_PATH, 'css') loadExternalResource(BLOG.PRISM_THEME_PATH, 'css')
loadExternalResource(BLOG.PRISM_JS_AUTO_LOADER, 'js').then((e) => { loadExternalResource(BLOG.PRISM_JS_AUTO_LOADER, 'js').then((url) => {
Prism.plugins.autoloader.languages_path = BLOG.PRISM_JS_PATH if (window?.Prism?.plugins?.autoloader) {
window.Prism.plugins.autoloader.languages_path = BLOG.PRISM_JS_PATH
}
renderPrismMac() renderPrismMac()
}) })
} }

View File

@@ -1,6 +1,7 @@
import BLOG from '@/blog.config' import BLOG from '@/blog.config'
import React from 'react' import { loadExternalResource } from '@/lib/utils'
import twikoo from 'twikoo' import { useEffect } from 'react'
// import twikoo from 'twikoo'
/** /**
* Giscus评论 @see https://giscus.app/zh-CN * Giscus评论 @see https://giscus.app/zh-CN
@@ -10,17 +11,48 @@ import twikoo from 'twikoo'
*/ */
const Twikoo = ({ isDarkMode }) => { const Twikoo = ({ isDarkMode }) => {
React.useEffect(() => { const loadTwikoo = async () => {
twikoo({ try {
envId: BLOG.COMMENT_TWIKOO_ENV_ID, // 腾讯云环境填 envIdVercel 环境填地址https://xxx.vercel.app const url = await loadExternalResource(BLOG.COMMENT_TWIKOO_CDN_URL, 'js')
el: '#twikoo', // 容器元素 console.log('twikoo 加载成功', url)
lang: BLOG.LANG // 用于手动设定评论区语言,支持的语言列表 https://github.com/imaegoo/twikoo/blob/main/src/client/utils/i18n/index.js const twikoo = window.twikoo
// region: 'ap-guangzhou', // 环境地域,默认为 ap-shanghai腾讯云环境填 ap-shanghai 或 ap-guangzhouVercel 环境不填 twikoo.init({
// path: location.pathname, // 用于区分不同文章的自定义 js 路径,如果您的文章路径不是 location.pathname需传此参数 envId: BLOG.COMMENT_TWIKOO_ENV_ID, // 腾讯云环境填 envIdVercel 环境填地址https://xxx.vercel.app
}) el: '#twikoo', // 容器元素
}) lang: BLOG.LANG // 用于手动设定评论区语言,支持的语言列表 https://github.com/imaegoo/twikoo/blob/main/src/client/utils/i18n/index.js
// region: 'ap-guangzhou', // 环境地域,默认为 ap-shanghai腾讯云环境填 ap-shanghai 或 ap-guangzhouVercel 环境不填
// path: location.pathname, // 用于区分不同文章的自定义 js 路径,如果您的文章路径不是 location.pathname需传此参数
})
twikoo.getCommentsCount({
envId: BLOG.COMMENT_TWIKOO_ENV_ID, // 环境 ID
// region: 'ap-guangzhou', // 环境地域,默认为 ap-shanghai如果您的环境地域不是上海需传此参数
urls: [ // 不包含协议、域名、参数的文章路径列表,必传参数
'/article/notion-next',
'/article/notion-next-guide'
],
includeReply: false // 评论数是否包括回复默认false
}).then(function (res) {
console.log(res)
// 返回示例: [
// { url: '/2020/10/post-1.html', count: 10 },
// { url: '/2020/11/post-2.html', count: 0 },
// { url: '/2020/12/post-3.html', count: 20 }
// ]
}).catch(function (err) {
// 发生错误
console.error(err)
})
} catch (error) {
console.error('twikoo 加载失败', error)
}
}
useEffect(() => {
loadTwikoo()
}, [])
return ( return (
<div id="twikoo"></div> <div id="twikoo"></div>
) )
} }

View File

@@ -0,0 +1,22 @@
import BLOG from '@/blog.config'
// import twikoo from 'twikoo'
/**
* 获取博客的评论数,用与在列表中展示
* @returns {JSX.Element}
* @constructor
*/
const TwikooCommentCount = ({ post, className }) => {
if (!JSON.parse(BLOG.COMMENT_TWIKOO_COUNT_ENABLE)) {
return null
}
return <a href={`${post.slug}?target=comment`} className={`mx-1 hidden comment-count-wrapper-${post.id} ${className || ''}`}>
<i className="far fa-comment mr-1"></i>
<span className={`comment-count-text-${post.id}`}>
{/* <i className='fa-solid fa-spinner animate-spin' /> */}
</span>
</a>
}
export default TwikooCommentCount

View File

@@ -0,0 +1,61 @@
import BLOG from '@/blog.config'
import { loadExternalResource } from '@/lib/utils'
import { useRouter } from 'next/router'
import { useEffect } from 'react'
/**
* 获取博客的评论数,用与在列表中展示
* @returns {JSX.Element}
* @constructor
*/
const TwikooCommentCounter = (props) => {
const loadTwikoo = async (posts) => {
posts.forEach(post => {
post.slug = post.slug.startsWith('/') ? post.slug : `/${post.slug}`
})
try {
await loadExternalResource(BLOG.COMMENT_TWIKOO_CDN_URL, 'js')
const twikoo = window.twikoo
twikoo.getCommentsCount({
envId: BLOG.COMMENT_TWIKOO_ENV_ID, // 环境 ID
// region: 'ap-guangzhou', // 环境地域,默认为 ap-shanghai如果您的环境地域不是上海需传此参数
urls: posts.map(post => post.slug), // 不包含协议、域名、参数的文章路径列表,必传参数
includeReply: true // 评论数是否包括回复默认false
}).then(function (res) {
console.log(res)
posts.forEach(post => {
const matchingRes = res.find(r => r.url === post.slug)
if (matchingRes) {
// 修改评论数量div
const textElements = document.querySelectorAll(`.comment-count-text-${post.id}`)
textElements.forEach(element => {
element.innerHTML = matchingRes.count
})
// 取消隐藏
const wrapperElements = document.querySelectorAll(`.comment-count-wrapper-${post.id}`)
wrapperElements.forEach(element => {
element.classList.remove('hidden')
})
}
})
}).catch(function (err) {
// 发生错误
console.error(err)
})
} catch (error) {
console.error('twikoo 加载失败', error)
}
}
const router = useRouter()
useEffect(() => {
if (props?.posts && props?.posts?.length > 0) {
loadTwikoo(props.posts)
}
}, [router.events])
return null
}
export default TwikooCommentCounter

View File

@@ -55,7 +55,6 @@
"react-share": "^4.4.1", "react-share": "^4.4.1",
"react-tweet-embed": "~2.0.0", "react-tweet-embed": "~2.0.0",
"smoothscroll-polyfill": "^0.4.4", "smoothscroll-polyfill": "^0.4.4",
"twikoo": "^1.6.16",
"typed.js": "^2.0.12", "typed.js": "^2.0.12",
"use-ackee": "^3.0.0", "use-ackee": "^3.0.0",
"valine": "^1.4.18" "valine": "^1.4.18"

View File

@@ -26,6 +26,7 @@ import smoothscroll from 'smoothscroll-polyfill'
import AOS from 'aos' import AOS from 'aos'
import 'aos/dist/aos.css' // You can also use <link> for styles import 'aos/dist/aos.css' // You can also use <link> for styles
import { isMobile } from '@/lib/utils' import { isMobile } from '@/lib/utils'
import TwikooCommentCounter from '@/components/TwikooCommentCounter'
const Ackee = dynamic(() => import('@/components/Ackee'), { ssr: false }) const Ackee = dynamic(() => import('@/components/Ackee'), { ssr: false })
const Gtag = dynamic(() => import('@/components/Gtag'), { ssr: false }) const Gtag = dynamic(() => import('@/components/Gtag'), { ssr: false })
@@ -53,6 +54,7 @@ const MyApp = ({ Component, pageProps }) => {
{JSON.parse(BLOG.MUSIC_PLAYER) && <MusicPlayer />} {JSON.parse(BLOG.MUSIC_PLAYER) && <MusicPlayer />}
{JSON.parse(BLOG.NEST) && <Nest />} {JSON.parse(BLOG.NEST) && <Nest />}
{JSON.parse(BLOG.FLUTTERINGRIBBON) && <FlutteringRibbon />} {JSON.parse(BLOG.FLUTTERINGRIBBON) && <FlutteringRibbon />}
{JSON.parse(BLOG.COMMENT_TWIKOO_COUNT_ENABLE) && <TwikooCommentCounter {...pageProps}/>}
{JSON.parse(BLOG.RIBBON) && <Ribbon />} {JSON.parse(BLOG.RIBBON) && <Ribbon />}
<ExternalScript/> <ExternalScript/>
</> </>

View File

@@ -2,6 +2,7 @@ import BLOG from '@/blog.config'
import NotionPage from '@/components/NotionPage' import NotionPage from '@/components/NotionPage'
import Link from 'next/link' import Link from 'next/link'
import TagItemMini from './TagItemMini' import TagItemMini from './TagItemMini'
import TwikooCommentCount from '@/components/TwikooCommenCount'
/** /**
* 博客列表的文字内容 * 博客列表的文字内容
@@ -26,17 +27,19 @@ export const BlogPostCardInfo = ({ post, showPreview, showPageCover, showSummary
{/* 日期 */} {/* 日期 */}
<div <div
className={`flex mt-2 items-center ${showPreview ? 'justify-center' : 'justify-start' className={`flex mt-2 items-center ${showPreview ? 'justify-center' : 'justify-start'
} flex-wrap dark:text-gray-500 text-gray-400 hover:text-indigo-700 dark:hover:text-indigo-400`} } flex-wrap dark:text-gray-500 text-gray-400 `}
> >
<Link <Link
href={`/archive#${post?.date?.start_date?.substr(0, 7)}`} href={`/archive#${post?.date?.start_date?.substr(0, 7)}`}
passHref passHref
className="font-light hover:underline cursor-pointer text-sm leading-4 mr-3"> className="font-light cursor-pointer text-sm leading-4 mr-3 hover:text-indigo-700 dark:hover:text-indigo-400">
<i className="far fa-calendar-alt mr-1" /> <i className="far fa-calendar-alt mr-1" />
{post.date?.start_date || post.lastEditedTime} {post.date?.start_date || post.lastEditedTime}
</Link> </Link>
<TwikooCommentCount className='text-sm hover:text-indigo-700 dark:hover:text-indigo-400' post={post}/>
</div> </div>
{/* 摘要 */} {/* 摘要 */}

View File

@@ -25,21 +25,23 @@ const HexoRecentComments = (props) => {
}, []) }, [])
return ( return (
<Card > <Card >
<div className=" mb-2 px-1 justify-between"> <div className=" mb-2 px-1 justify-between">
<i className="mr-2 fas fas fa-comment" /> <i className="mr-2 fas fas fa-comment" />
{locale.COMMON.RECENT_COMMENTS} {locale.COMMON.RECENT_COMMENTS}
</div> </div>
{onLoading && <div>Loading...<i className='ml-2 fas fa-spinner animate-spin' /></div>} {onLoading && <div>Loading...<i className='ml-2 fas fa-spinner animate-spin' /></div>}
{!onLoading && comments && comments.length === 0 && <div>No Comments</div>} {!onLoading && comments && comments.length === 0 && <div>No Comments</div>}
{!onLoading && comments && comments.length > 0 && comments.map((comment) => <div key={comment.objectId} className='pb-2 pl-1'> {!onLoading && comments && comments.length > 0 && comments.map((comment) => <div key={comment.objectId} className='pb-2 pl-1'>
<div className='dark:text-gray-200 text-sm waline-recent-content wl-content' dangerouslySetInnerHTML={{ __html: comment.comment }} /> <div className='dark:text-gray-200 text-sm waline-recent-content wl-content' dangerouslySetInnerHTML={{ __html: comment.comment }} />
<div className='dark:text-gray-400 text-gray-400 text-sm text-right cursor-pointer hover:text-red-500 hover:underline pt-1 pr-2'><Link href={{ pathname: comment.url, hash: comment.objectId, query: { target: 'comment' } }}>--{comment.nick}</Link></div> <div className='dark:text-gray-400 text-gray-400 text-sm text-right cursor-pointer hover:text-red-500 hover:underline pt-1 pr-2'>
</div>)} <Link href={{ pathname: comment.url, hash: comment.objectId, query: { target: 'comment' } }}>--{comment.nick}</Link>
</div>
</div>)}
</Card> </Card>
); )
} }
export default HexoRecentComments export default HexoRecentComments