Skip to content

Commit eecac65

Browse files
committed
用twikoo实现列表评论数
1 parent 74ace70 commit eecac65

9 files changed

+156
-31
lines changed

blog.config.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,9 @@ const BLOG = {
185185
// ----> 评论互动 可同时开启多个支持 WALINE VALINE GISCUS CUSDIS UTTERRANCES GITALK
186186

187187
// twikoo
188-
COMMENT_TWIKOO_ENV_ID: process.env.NEXT_PUBLIC_COMMENT_ENV_ID || '', // TWIKOO地址 腾讯云环境填 envId;Vercel 环境域名地址(https://xxx.vercel.app)
188+
COMMENT_TWIKOO_ENV_ID: process.env.NEXT_PUBLIC_COMMENT_ENV_ID || '', // TWIKOO后端地址 腾讯云环境填envId;Vercel环境填域名,教程:https://tangly1024.com/article/notionnext-twikoo
189+
COMMENT_TWIKOO_COUNT_ENABLE: process.env.NEXT_PUBLIC_COMMENT_TWIKOO_COUNT_ENABLE || false, // 博客列表是否显示评论数
190+
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
189191

190192
// utterance
191193
COMMENT_UTTERRANCES_REPO:

components/PrismMac.js

+4-2
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,10 @@ const PrismMac = () => {
2323
loadExternalResource('/css/prism-mac-style.css', 'css')
2424
}
2525
loadExternalResource(BLOG.PRISM_THEME_PATH, 'css')
26-
loadExternalResource(BLOG.PRISM_JS_AUTO_LOADER, 'js').then((e) => {
27-
Prism.plugins.autoloader.languages_path = BLOG.PRISM_JS_PATH
26+
loadExternalResource(BLOG.PRISM_JS_AUTO_LOADER, 'js').then((url) => {
27+
if (window?.Prism?.plugins?.autoloader) {
28+
window.Prism.plugins.autoloader.languages_path = BLOG.PRISM_JS_PATH
29+
}
2830
renderPrismMac()
2931
})
3032
}

components/Twikoo.js

+44-12
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import BLOG from '@/blog.config'
2-
import React from 'react'
3-
import twikoo from 'twikoo'
2+
import { loadExternalResource } from '@/lib/utils'
3+
import { useEffect } from 'react'
4+
// import twikoo from 'twikoo'
45

56
/**
67
* Giscus评论 @see https://giscus.app/zh-CN
@@ -10,17 +11,48 @@ import twikoo from 'twikoo'
1011
*/
1112

1213
const Twikoo = ({ isDarkMode }) => {
13-
React.useEffect(() => {
14-
twikoo({
15-
envId: BLOG.COMMENT_TWIKOO_ENV_ID, // 腾讯云环境填 envId;Vercel 环境填地址(https://xxx.vercel.app)
16-
el: '#twikoo', // 容器元素
17-
lang: BLOG.LANG // 用于手动设定评论区语言,支持的语言列表 https://github.com/imaegoo/twikoo/blob/main/src/client/utils/i18n/index.js
18-
// region: 'ap-guangzhou', // 环境地域,默认为 ap-shanghai,腾讯云环境填 ap-shanghai 或 ap-guangzhou;Vercel 环境不填
19-
// path: location.pathname, // 用于区分不同文章的自定义 js 路径,如果您的文章路径不是 location.pathname,需传此参数
20-
})
21-
})
14+
const loadTwikoo = async () => {
15+
try {
16+
const url = await loadExternalResource(BLOG.COMMENT_TWIKOO_CDN_URL, 'js')
17+
console.log('twikoo 加载成功', url)
18+
const twikoo = window.twikoo
19+
twikoo.init({
20+
envId: BLOG.COMMENT_TWIKOO_ENV_ID, // 腾讯云环境填 envId;Vercel 环境填地址(https://xxx.vercel.app)
21+
el: '#twikoo', // 容器元素
22+
lang: BLOG.LANG // 用于手动设定评论区语言,支持的语言列表 https://github.com/imaegoo/twikoo/blob/main/src/client/utils/i18n/index.js
23+
// region: 'ap-guangzhou', // 环境地域,默认为 ap-shanghai,腾讯云环境填 ap-shanghai 或 ap-guangzhou;Vercel 环境不填
24+
// path: location.pathname, // 用于区分不同文章的自定义 js 路径,如果您的文章路径不是 location.pathname,需传此参数
25+
})
26+
27+
twikoo.getCommentsCount({
28+
envId: BLOG.COMMENT_TWIKOO_ENV_ID, // 环境 ID
29+
// region: 'ap-guangzhou', // 环境地域,默认为 ap-shanghai,如果您的环境地域不是上海,需传此参数
30+
urls: [ // 不包含协议、域名、参数的文章路径列表,必传参数
31+
'/article/notion-next',
32+
'/article/notion-next-guide'
33+
],
34+
includeReply: false // 评论数是否包括回复,默认:false
35+
}).then(function (res) {
36+
console.log(res)
37+
// 返回示例: [
38+
// { url: '/2020/10/post-1.html', count: 10 },
39+
// { url: '/2020/11/post-2.html', count: 0 },
40+
// { url: '/2020/12/post-3.html', count: 20 }
41+
// ]
42+
}).catch(function (err) {
43+
// 发生错误
44+
console.error(err)
45+
})
46+
} catch (error) {
47+
console.error('twikoo 加载失败', error)
48+
}
49+
}
50+
51+
useEffect(() => {
52+
loadTwikoo()
53+
}, [])
2254
return (
23-
<div id="twikoo"></div>
55+
<div id="twikoo"></div>
2456
)
2557
}
2658

components/TwikooCommenCount.js

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import BLOG from '@/blog.config'
2+
// import twikoo from 'twikoo'
3+
4+
/**
5+
* 获取博客的评论数,用与在列表中展示
6+
* @returns {JSX.Element}
7+
* @constructor
8+
*/
9+
10+
const TwikooCommentCount = ({ post, className }) => {
11+
if (!JSON.parse(BLOG.COMMENT_TWIKOO_COUNT_ENABLE)) {
12+
return null
13+
}
14+
return <a href={`${post.slug}?target=comment`} className={`mx-1 hidden comment-count-wrapper-${post.id} ${className || ''}`}>
15+
<i className="far fa-comment mr-1"></i>
16+
<span className={`comment-count-text-${post.id}`}>
17+
{/* <i className='fa-solid fa-spinner animate-spin' /> */}
18+
</span>
19+
</a>
20+
}
21+
22+
export default TwikooCommentCount

components/TwikooCommentCounter.js

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import BLOG from '@/blog.config'
2+
import { loadExternalResource } from '@/lib/utils'
3+
import { useRouter } from 'next/router'
4+
import { useEffect } from 'react'
5+
6+
/**
7+
* 获取博客的评论数,用与在列表中展示
8+
* @returns {JSX.Element}
9+
* @constructor
10+
*/
11+
12+
const TwikooCommentCounter = (props) => {
13+
const loadTwikoo = async (posts) => {
14+
posts.forEach(post => {
15+
post.slug = post.slug.startsWith('/') ? post.slug : `/${post.slug}`
16+
})
17+
try {
18+
await loadExternalResource(BLOG.COMMENT_TWIKOO_CDN_URL, 'js')
19+
const twikoo = window.twikoo
20+
twikoo.getCommentsCount({
21+
envId: BLOG.COMMENT_TWIKOO_ENV_ID, // 环境 ID
22+
// region: 'ap-guangzhou', // 环境地域,默认为 ap-shanghai,如果您的环境地域不是上海,需传此参数
23+
urls: posts.map(post => post.slug), // 不包含协议、域名、参数的文章路径列表,必传参数
24+
includeReply: true // 评论数是否包括回复,默认:false
25+
}).then(function (res) {
26+
console.log(res)
27+
posts.forEach(post => {
28+
const matchingRes = res.find(r => r.url === post.slug)
29+
if (matchingRes) {
30+
// 修改评论数量div
31+
const textElements = document.querySelectorAll(`.comment-count-text-${post.id}`)
32+
textElements.forEach(element => {
33+
element.innerHTML = matchingRes.count
34+
})
35+
// 取消隐藏
36+
const wrapperElements = document.querySelectorAll(`.comment-count-wrapper-${post.id}`)
37+
wrapperElements.forEach(element => {
38+
element.classList.remove('hidden')
39+
})
40+
}
41+
})
42+
}).catch(function (err) {
43+
// 发生错误
44+
console.error(err)
45+
})
46+
} catch (error) {
47+
console.error('twikoo 加载失败', error)
48+
}
49+
}
50+
51+
const router = useRouter()
52+
53+
useEffect(() => {
54+
if (props?.posts && props?.posts?.length > 0) {
55+
loadTwikoo(props.posts)
56+
}
57+
}, [router.events])
58+
return null
59+
}
60+
61+
export default TwikooCommentCounter

package.json

-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@
5555
"react-share": "^4.4.1",
5656
"react-tweet-embed": "~2.0.0",
5757
"smoothscroll-polyfill": "^0.4.4",
58-
"twikoo": "^1.6.16",
5958
"typed.js": "^2.0.12",
6059
"use-ackee": "^3.0.0",
6160
"valine": "^1.4.18"

pages/_app.js

+2
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import smoothscroll from 'smoothscroll-polyfill'
2626
import AOS from 'aos'
2727
import 'aos/dist/aos.css' // You can also use <link> for styles
2828
import { isMobile } from '@/lib/utils'
29+
import TwikooCommentCounter from '@/components/TwikooCommentCounter'
2930

3031
const Ackee = dynamic(() => import('@/components/Ackee'), { ssr: false })
3132
const Gtag = dynamic(() => import('@/components/Gtag'), { ssr: false })
@@ -53,6 +54,7 @@ const MyApp = ({ Component, pageProps }) => {
5354
{JSON.parse(BLOG.MUSIC_PLAYER) && <MusicPlayer />}
5455
{JSON.parse(BLOG.NEST) && <Nest />}
5556
{JSON.parse(BLOG.FLUTTERINGRIBBON) && <FlutteringRibbon />}
57+
{JSON.parse(BLOG.COMMENT_TWIKOO_COUNT_ENABLE) && <TwikooCommentCounter {...pageProps}/>}
5658
{JSON.parse(BLOG.RIBBON) && <Ribbon />}
5759
<ExternalScript/>
5860
</>

themes/hexo/components/BlogPostCardInfo.js

+5-2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import BLOG from '@/blog.config'
22
import NotionPage from '@/components/NotionPage'
33
import Link from 'next/link'
44
import TagItemMini from './TagItemMini'
5+
import TwikooCommentCount from '@/components/TwikooCommenCount'
56

67
/**
78
* 博客列表的文字内容
@@ -26,17 +27,19 @@ export const BlogPostCardInfo = ({ post, showPreview, showPageCover, showSummary
2627
{/* 日期 */}
2728
<div
2829
className={`flex mt-2 items-center ${showPreview ? 'justify-center' : 'justify-start'
29-
} flex-wrap dark:text-gray-500 text-gray-400 hover:text-indigo-700 dark:hover:text-indigo-400`}
30+
} flex-wrap dark:text-gray-500 text-gray-400 `}
3031
>
3132
<Link
3233
href={`/archive#${post?.date?.start_date?.substr(0, 7)}`}
3334
passHref
34-
className="font-light hover:underline cursor-pointer text-sm leading-4 mr-3">
35+
className="font-light cursor-pointer text-sm leading-4 mr-3 hover:text-indigo-700 dark:hover:text-indigo-400">
3536

3637
<i className="far fa-calendar-alt mr-1" />
3738
{post.date?.start_date || post.lastEditedTime}
3839

3940
</Link>
41+
42+
<TwikooCommentCount className='text-sm hover:text-indigo-700 dark:hover:text-indigo-400' post={post}/>
4043
</div>
4144

4245
{/* 摘要 */}

themes/hexo/components/HexoRecentComments.js

+15-13
Original file line numberDiff line numberDiff line change
@@ -25,21 +25,23 @@ const HexoRecentComments = (props) => {
2525
}, [])
2626

2727
return (
28-
<Card >
29-
<div className=" mb-2 px-1 justify-between">
30-
<i className="mr-2 fas fas fa-comment" />
31-
{locale.COMMON.RECENT_COMMENTS}
32-
</div>
28+
<Card >
29+
<div className=" mb-2 px-1 justify-between">
30+
<i className="mr-2 fas fas fa-comment" />
31+
{locale.COMMON.RECENT_COMMENTS}
32+
</div>
3333

34-
{onLoading && <div>Loading...<i className='ml-2 fas fa-spinner animate-spin' /></div>}
35-
{!onLoading && comments && comments.length === 0 && <div>No Comments</div>}
36-
{!onLoading && comments && comments.length > 0 && comments.map((comment) => <div key={comment.objectId} className='pb-2 pl-1'>
37-
<div className='dark:text-gray-200 text-sm waline-recent-content wl-content' dangerouslySetInnerHTML={{ __html: comment.comment }} />
38-
<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>
39-
</div>)}
34+
{onLoading && <div>Loading...<i className='ml-2 fas fa-spinner animate-spin' /></div>}
35+
{!onLoading && comments && comments.length === 0 && <div>No Comments</div>}
36+
{!onLoading && comments && comments.length > 0 && comments.map((comment) => <div key={comment.objectId} className='pb-2 pl-1'>
37+
<div className='dark:text-gray-200 text-sm waline-recent-content wl-content' dangerouslySetInnerHTML={{ __html: comment.comment }} />
38+
<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'>
39+
<Link href={{ pathname: comment.url, hash: comment.objectId, query: { target: 'comment' } }}>--{comment.nick}</Link>
40+
</div>
41+
</div>)}
4042

41-
</Card>
42-
);
43+
</Card>
44+
)
4345
}
4446

4547
export default HexoRecentComments

0 commit comments

Comments
 (0)