mirror of
https://github.com/d0zingcat/NotionNext.git
synced 2026-05-14 07:26:52 +00:00
Merge pull request #1038 from tangly1024/feat/twikoo-comment-count
Feat/twikoo comment count
This commit is contained in:
@@ -1,2 +1,2 @@
|
||||
# 环境变量 @see https://www.nextjs.cn/docs/basic-features/environment-variables
|
||||
NEXT_PUBLIC_VERSION=3.13.5
|
||||
NEXT_PUBLIC_VERSION=3.13.6
|
||||
@@ -185,7 +185,9 @@ const BLOG = {
|
||||
// ----> 评论互动 可同时开启多个支持 WALINE VALINE GISCUS CUSDIS UTTERRANCES GITALK
|
||||
|
||||
// twikoo
|
||||
COMMENT_TWIKOO_ENV_ID: process.env.NEXT_PUBLIC_COMMENT_ENV_ID || '', // TWIKOO地址 腾讯云环境填 envId;Vercel 环境域名地址(https://xxx.vercel.app)
|
||||
COMMENT_TWIKOO_ENV_ID: process.env.NEXT_PUBLIC_COMMENT_ENV_ID || '', // TWIKOO后端地址 腾讯云环境填envId;Vercel环境填域名,教程: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
|
||||
COMMENT_UTTERRANCES_REPO:
|
||||
|
||||
@@ -23,8 +23,10 @@ const PrismMac = () => {
|
||||
loadExternalResource('/css/prism-mac-style.css', 'css')
|
||||
}
|
||||
loadExternalResource(BLOG.PRISM_THEME_PATH, 'css')
|
||||
loadExternalResource(BLOG.PRISM_JS_AUTO_LOADER, 'js').then((e) => {
|
||||
Prism.plugins.autoloader.languages_path = BLOG.PRISM_JS_PATH
|
||||
loadExternalResource(BLOG.PRISM_JS_AUTO_LOADER, 'js').then((url) => {
|
||||
if (window?.Prism?.plugins?.autoloader) {
|
||||
window.Prism.plugins.autoloader.languages_path = BLOG.PRISM_JS_PATH
|
||||
}
|
||||
renderPrismMac()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import BLOG from '@/blog.config'
|
||||
import React from 'react'
|
||||
import twikoo from 'twikoo'
|
||||
import { loadExternalResource } from '@/lib/utils'
|
||||
import { useEffect } from 'react'
|
||||
// import twikoo from 'twikoo'
|
||||
|
||||
/**
|
||||
* Giscus评论 @see https://giscus.app/zh-CN
|
||||
@@ -10,17 +11,48 @@ import twikoo from 'twikoo'
|
||||
*/
|
||||
|
||||
const Twikoo = ({ isDarkMode }) => {
|
||||
React.useEffect(() => {
|
||||
twikoo({
|
||||
envId: BLOG.COMMENT_TWIKOO_ENV_ID, // 腾讯云环境填 envId;Vercel 环境填地址(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-guangzhou;Vercel 环境不填
|
||||
// path: location.pathname, // 用于区分不同文章的自定义 js 路径,如果您的文章路径不是 location.pathname,需传此参数
|
||||
})
|
||||
})
|
||||
const loadTwikoo = async () => {
|
||||
try {
|
||||
const url = await loadExternalResource(BLOG.COMMENT_TWIKOO_CDN_URL, 'js')
|
||||
console.log('twikoo 加载成功', url)
|
||||
const twikoo = window.twikoo
|
||||
twikoo.init({
|
||||
envId: BLOG.COMMENT_TWIKOO_ENV_ID, // 腾讯云环境填 envId;Vercel 环境填地址(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-guangzhou;Vercel 环境不填
|
||||
// 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 (
|
||||
<div id="twikoo"></div>
|
||||
<div id="twikoo"></div>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
22
components/TwikooCommentCount.js
Normal file
22
components/TwikooCommentCount.js
Normal 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
|
||||
79
components/TwikooCommentCounter.js
Normal file
79
components/TwikooCommentCounter.js
Normal file
@@ -0,0 +1,79 @@
|
||||
import BLOG from '@/blog.config'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import { loadExternalResource } from '@/lib/utils'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useEffect } from 'react'
|
||||
|
||||
/**
|
||||
* 获取博客的评论数,用与在列表中展示
|
||||
* @returns {JSX.Element}
|
||||
* @constructor
|
||||
*/
|
||||
|
||||
const TwikooCommentCounter = (props) => {
|
||||
let commentsData = []
|
||||
const { theme } = useGlobal()
|
||||
|
||||
const fetchTwikooData = 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)
|
||||
commentsData = res
|
||||
updateCommentCount()
|
||||
}).catch(function (err) {
|
||||
// 发生错误
|
||||
console.error(err)
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('twikoo 加载失败', error)
|
||||
}
|
||||
}
|
||||
|
||||
const updateCommentCount = () => {
|
||||
if (commentsData.length === 0) {
|
||||
return
|
||||
}
|
||||
props.posts.forEach(post => {
|
||||
const matchingRes = commentsData.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')
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
const router = useRouter()
|
||||
|
||||
useEffect(() => {
|
||||
console.log('路由触发评论计数')
|
||||
if (props?.posts && props?.posts?.length > 0) {
|
||||
fetchTwikooData(props.posts)
|
||||
}
|
||||
}, [router.events])
|
||||
|
||||
// 监控主题变化时的的评论数
|
||||
useEffect(() => {
|
||||
console.log('主题触发评论计数', commentsData)
|
||||
updateCommentCount()
|
||||
}, [theme])
|
||||
return null
|
||||
}
|
||||
|
||||
export default TwikooCommentCounter
|
||||
12
components/TwikooRecentComments.js
Normal file
12
components/TwikooRecentComments.js
Normal file
@@ -0,0 +1,12 @@
|
||||
|
||||
/**
|
||||
* 显示最近评论 TODO
|
||||
* @returns {JSX.Element}
|
||||
* @constructor
|
||||
*/
|
||||
|
||||
const TwikooRecentComments = (props) => {
|
||||
return null
|
||||
}
|
||||
|
||||
export default TwikooRecentComments
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "notion-next",
|
||||
"version": "3.13.5",
|
||||
"version": "3.13.6",
|
||||
"homepage": "https://github.com/tangly1024/NotionNext.git",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
@@ -55,7 +55,6 @@
|
||||
"react-share": "^4.4.1",
|
||||
"react-tweet-embed": "~2.0.0",
|
||||
"smoothscroll-polyfill": "^0.4.4",
|
||||
"twikoo": "^1.6.16",
|
||||
"typed.js": "^2.0.12",
|
||||
"use-ackee": "^3.0.0",
|
||||
"valine": "^1.4.18"
|
||||
|
||||
@@ -26,6 +26,7 @@ import smoothscroll from 'smoothscroll-polyfill'
|
||||
import AOS from 'aos'
|
||||
import 'aos/dist/aos.css' // You can also use <link> for styles
|
||||
import { isMobile } from '@/lib/utils'
|
||||
import TwikooCommentCounter from '@/components/TwikooCommentCounter'
|
||||
|
||||
const Ackee = dynamic(() => import('@/components/Ackee'), { 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.NEST) && <Nest />}
|
||||
{JSON.parse(BLOG.FLUTTERINGRIBBON) && <FlutteringRibbon />}
|
||||
{JSON.parse(BLOG.COMMENT_TWIKOO_COUNT_ENABLE) && <TwikooCommentCounter {...pageProps}/>}
|
||||
{JSON.parse(BLOG.RIBBON) && <Ribbon />}
|
||||
<ExternalScript/>
|
||||
</>
|
||||
|
||||
@@ -5,12 +5,22 @@ import * as ThemeMap from '@/themes'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import { generateRss } from '@/lib/rss'
|
||||
import { generateRobotsTxt } from '@/lib/robots.txt'
|
||||
|
||||
/**
|
||||
* 首页布局
|
||||
* @param {*} props
|
||||
* @returns
|
||||
*/
|
||||
const Index = props => {
|
||||
const { theme } = useGlobal()
|
||||
const ThemeComponents = ThemeMap[theme]
|
||||
return <ThemeComponents.LayoutIndex {...props} />
|
||||
}
|
||||
|
||||
/**
|
||||
* SSG 获取数据
|
||||
* @returns
|
||||
*/
|
||||
export async function getStaticProps() {
|
||||
const from = 'index'
|
||||
const props = await getGlobalNotionData({ from })
|
||||
|
||||
@@ -4,6 +4,7 @@ import { useGlobal } from '@/lib/global'
|
||||
import { useRouter } from 'next/router'
|
||||
import Link from 'next/link'
|
||||
import CONFIG_EXAMPLE from '../config_example'
|
||||
import BlogPostCard from './BlogPostCard'
|
||||
|
||||
export const BlogListPage = props => {
|
||||
const { page = 1, posts, postCount } = props
|
||||
@@ -22,46 +23,8 @@ export const BlogListPage = props => {
|
||||
<div className={`w-full ${showPageCover ? 'md:pr-2' : 'md:pr-12'}} mb-12`}>
|
||||
|
||||
<div id="container">
|
||||
{posts?.map(p => (
|
||||
<article key={p.id} className={`mb-12 ${showPageCover ? 'flex md:flex-row flex-col-reverse' : ''}`}>
|
||||
<div className={`${showPageCover ? 'md:w-7/12' : ''}`}>
|
||||
<h2 className="mb-4">
|
||||
<Link
|
||||
href={`/${p.slug}`}
|
||||
className="text-black dark:text-gray-100 text-xl md:text-2xl no-underline hover:underline">
|
||||
{p.title}
|
||||
</Link>
|
||||
</h2>
|
||||
|
||||
<div className="mb-4 text-sm text-gray-700 dark:text-gray-300">
|
||||
by <a href="#" className="text-gray-700 dark:text-gray-300">{BLOG.AUTHOR}</a> on {p.date?.start_date || p.createdTime}
|
||||
<span className="font-bold mx-1"> | </span>
|
||||
<a href={`/category${p.category}`} className="text-gray-700 dark:text-gray-300 hover:underline">{p.category}</a>
|
||||
{/* <span className="font-bold mx-1"> | </span> */}
|
||||
{/* <a href="#" className="text-gray-700">2 Comments</a> */}
|
||||
</div>
|
||||
|
||||
<p className="text-gray-700 dark:text-gray-400 leading-normal p-3-lines">
|
||||
{p.summary}
|
||||
</p>
|
||||
{/* 搜索结果 */}
|
||||
{p.results && (
|
||||
<p className="p-4-lines mt-4 text-gray-700 dark:text-gray-300 text-sm font-light leading-7">
|
||||
{p.results.map(r => (
|
||||
<span key={r}>{r}</span>
|
||||
))}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
{/* 图片封面 */}
|
||||
{showPageCover && (
|
||||
<div className="md:w-5/12 w-full overflow-hidden p-1">
|
||||
<Link href={`${BLOG.SUB_PATH}/${p.slug}`} passHref legacyBehavior>
|
||||
<div className='h-44 bg-center bg-cover hover:scale-110 duration-200' style={{ backgroundImage: `url('${p?.page_cover}')` }} />
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
</article>
|
||||
{posts?.map(post => (
|
||||
<BlogPostCard key={post.id} post = {post}/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import BLOG from '@/blog.config'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import Link from 'next/link'
|
||||
import React from 'react'
|
||||
import React, { useEffect } from 'react'
|
||||
import throttle from 'lodash.throttle'
|
||||
import BlogPostCard from './BlogPostCard'
|
||||
import CONFIG_EXAMPLE from '../config_example'
|
||||
|
||||
export const BlogListScroll = props => {
|
||||
@@ -35,8 +35,9 @@ export const BlogListScroll = props => {
|
||||
handleGetMore()
|
||||
}
|
||||
}, 500))
|
||||
const showPageCover = CONFIG_EXAMPLE.POST_LIST_COVER
|
||||
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
window.addEventListener('scroll', scrollTrigger)
|
||||
|
||||
return () => {
|
||||
@@ -44,60 +45,22 @@ export const BlogListScroll = props => {
|
||||
}
|
||||
})
|
||||
|
||||
const showPageCover = CONFIG_EXAMPLE.POST_LIST_COVER
|
||||
|
||||
return (
|
||||
<div id="container" className="w-full md:pr-12 mb-12" ref={targetRef}>
|
||||
{postsToShow.map(p => (
|
||||
<article key={p.id} className={`mb-12 ${showPageCover ? 'flex md:flex-row flex-col-reverse' : ''}`}>
|
||||
<div className={`${showPageCover ? 'md:w-7/12' : ''}`}>
|
||||
<h2 className="mb-4">
|
||||
<Link
|
||||
href={`/${p.slug}`}
|
||||
className="text-black dark:text-gray-100 text-xl md:text-2xl no-underline hover:underline">
|
||||
{p.title}
|
||||
</Link>
|
||||
</h2>
|
||||
|
||||
<div className="mb-4 text-sm text-gray-700 dark:text-gray-300">
|
||||
by <a href="#" className="text-gray-700 dark:text-gray-300">{BLOG.AUTHOR}</a> on {p.date?.start_date || p.createdTime}
|
||||
<span className="font-bold mx-1"> | </span>
|
||||
<a href={`/category${p.category}`} className="text-gray-700 dark:text-gray-300 hover:underline">{p.category}</a>
|
||||
{/* <span className="font-bold mx-1"> | </span> */}
|
||||
{/* <a href="#" className="text-gray-700">2 Comments</a> */}
|
||||
</div>
|
||||
<div className={`w-full ${showPageCover ? 'md:pr-2' : 'md:pr-12'}} mb-12`} ref={targetRef}>
|
||||
|
||||
<p className="text-gray-700 dark:text-gray-400 leading-normal p-3-lines">
|
||||
{p.summary}
|
||||
</p>
|
||||
{/* 搜索结果 */}
|
||||
{p.results && (
|
||||
<p className="p-4-lines mt-4 text-gray-700 dark:text-gray-300 text-sm font-light leading-7">
|
||||
{p.results.map(r => (
|
||||
<span key={r}>{r}</span>
|
||||
))}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
{/* 图片封面 */}
|
||||
{showPageCover && (
|
||||
<div className="md:w-5/12 w-full overflow-hidden p-1">
|
||||
<Link href={`${BLOG.SUB_PATH}/${p.slug}`} passHref legacyBehavior>
|
||||
<div className='h-44 bg-center bg-cover hover:scale-110 duration-200' style={{ backgroundImage: `url('${p?.page_cover}')` }} />
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
</article>
|
||||
))}
|
||||
{postsToShow?.map(post => (
|
||||
<BlogPostCard key={post.id} post={post} />
|
||||
))}
|
||||
|
||||
<div
|
||||
onClick={handleGetMore}
|
||||
className="w-full my-4 py-4 text-center cursor-pointer "
|
||||
>
|
||||
{' '}
|
||||
{hasMore ? locale.COMMON.MORE : `${locale.COMMON.NO_MORE} 😰`}{' '}
|
||||
</div>
|
||||
<div
|
||||
onClick={handleGetMore}
|
||||
className="w-full my-4 py-4 text-center cursor-pointer "
|
||||
>
|
||||
{' '}
|
||||
{hasMore ? locale.COMMON.MORE : `${locale.COMMON.NO_MORE} 😰`}{' '}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
51
themes/example/components/BlogPostCard.js
Normal file
51
themes/example/components/BlogPostCard.js
Normal file
@@ -0,0 +1,51 @@
|
||||
import BLOG from '@/blog.config'
|
||||
import CONFIG_EXAMPLE from '../config_example'
|
||||
import Link from 'next/link'
|
||||
import TwikooCommentCount from '@/components/TwikooCommentCount'
|
||||
|
||||
const BlogPostCard = ({ post }) => {
|
||||
const showPageCover = CONFIG_EXAMPLE.POST_LIST_COVER
|
||||
|
||||
return <article className={`mb-12 ${showPageCover ? 'flex md:flex-row flex-col-reverse' : ''}`}>
|
||||
<div className={`${showPageCover ? 'md:w-7/12' : ''}`}>
|
||||
<h2 className="mb-4">
|
||||
<Link
|
||||
href={`/${post.slug}`}
|
||||
className="text-black dark:text-gray-100 text-xl md:text-2xl no-underline hover:underline">
|
||||
{post.title}
|
||||
</Link>
|
||||
</h2>
|
||||
|
||||
<div className="mb-4 text-sm text-gray-700 dark:text-gray-300">
|
||||
by <a href="#" className="text-gray-700 dark:text-gray-300">{BLOG.AUTHOR}</a> on {post.date?.start_date || post.createdTime}
|
||||
<TwikooCommentCount post={post} className='pl-1'/>
|
||||
<span className="font-bold mx-1"> | </span>
|
||||
<a href={`/category${post.category}`} className="text-gray-700 dark:text-gray-300 hover:underline">{post.category}</a>
|
||||
{/* <span className="font-bold mx-1"> | </span> */}
|
||||
{/* <a href="#" className="text-gray-700">2 Comments</a> */}
|
||||
</div>
|
||||
|
||||
<p className="text-gray-700 dark:text-gray-400 leading-normal p-3-lines">
|
||||
{post.summary}
|
||||
</p>
|
||||
{/* 搜索结果 */}
|
||||
{post.results && (
|
||||
<p className="p-4-lines mt-4 text-gray-700 dark:text-gray-300 text-sm font-light leading-7">
|
||||
{post.results.map(r => (
|
||||
<span key={r}>{r}</span>
|
||||
))}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
{/* 图片封面 */}
|
||||
{showPageCover && (
|
||||
<div className="md:w-5/12 w-full overflow-hidden p-1">
|
||||
<Link href={`${BLOG.SUB_PATH}/${post.slug}`} passHref legacyBehavior>
|
||||
<div className='h-44 bg-center bg-cover hover:scale-110 duration-200' style={{ backgroundImage: `url('${post?.page_cover}')` }} />
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
</article>
|
||||
}
|
||||
|
||||
export default BlogPostCard
|
||||
@@ -2,6 +2,7 @@ import BLOG from '@/blog.config'
|
||||
import NotionPage from '@/components/NotionPage'
|
||||
import Link from 'next/link'
|
||||
import TagItemMini from './TagItemMini'
|
||||
import TwikooCommentCount from '@/components/TwikooCommentCount'
|
||||
|
||||
/**
|
||||
* 博客列表的文字内容
|
||||
@@ -26,17 +27,19 @@ export const BlogPostCardInfo = ({ post, showPreview, showPageCover, showSummary
|
||||
{/* 日期 */}
|
||||
<div
|
||||
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
|
||||
href={`/archive#${post?.date?.start_date?.substr(0, 7)}`}
|
||||
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" />
|
||||
{post.date?.start_date || post.lastEditedTime}
|
||||
|
||||
</Link>
|
||||
|
||||
<TwikooCommentCount className='text-sm hover:text-indigo-700 dark:hover:text-indigo-400' post={post}/>
|
||||
</div>
|
||||
|
||||
{/* 摘要 */}
|
||||
|
||||
@@ -25,21 +25,23 @@ const HexoRecentComments = (props) => {
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<Card >
|
||||
<div className=" mb-2 px-1 justify-between">
|
||||
<i className="mr-2 fas fas fa-comment" />
|
||||
{locale.COMMON.RECENT_COMMENTS}
|
||||
</div>
|
||||
<Card >
|
||||
<div className=" mb-2 px-1 justify-between">
|
||||
<i className="mr-2 fas fas fa-comment" />
|
||||
{locale.COMMON.RECENT_COMMENTS}
|
||||
</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 && 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-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>)}
|
||||
{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 && 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-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>)}
|
||||
|
||||
</Card>
|
||||
);
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export default HexoRecentComments
|
||||
|
||||
@@ -3,6 +3,7 @@ import Link from 'next/link'
|
||||
import React from 'react'
|
||||
import TagItemMini from './TagItemMini'
|
||||
import CONFIG_MATERY from '../config_matery'
|
||||
import TwikooCommentCount from '@/components/TwikooCommentCount'
|
||||
// import Image from 'next/image'
|
||||
|
||||
const BlogPostCard = ({ index, post, showSummary, siteInfo }) => {
|
||||
@@ -14,84 +15,87 @@ const BlogPostCard = ({ index, post, showSummary, siteInfo }) => {
|
||||
const showPageCover = CONFIG_MATERY.POST_LIST_COVER && post?.page_cover
|
||||
const delay = (index % 3) * 300
|
||||
return (
|
||||
<div
|
||||
data-aos="zoom-in"
|
||||
data-aos-duration="500"
|
||||
data-aos-delay={delay}
|
||||
data-aos-once="true"
|
||||
data-aos-anchor-placement="top-bottom"
|
||||
className="w-full mb-4 overflow-auto shadow-md border dark:border-black rounded-xl bg-white dark:bg-hexo-black-gray">
|
||||
<div
|
||||
data-aos="zoom-in"
|
||||
data-aos-duration="500"
|
||||
data-aos-delay={delay}
|
||||
data-aos-once="true"
|
||||
data-aos-anchor-placement="top-bottom"
|
||||
className="w-full mb-4 overflow-auto shadow-md border dark:border-black rounded-xl bg-white dark:bg-hexo-black-gray">
|
||||
|
||||
{/* 固定高度 ,空白用图片拉升填充 */}
|
||||
<div className="flex flex-col h-80 justify-between">
|
||||
{/* 固定高度 ,空白用图片拉升填充 */}
|
||||
<div className="flex flex-col h-80 justify-between">
|
||||
|
||||
{/* 头部图片 填充卡片 */}
|
||||
{showPageCover && (
|
||||
<Link href={`${BLOG.SUB_PATH}/${post.slug}`} passHref legacyBehavior>
|
||||
<div
|
||||
className="flex flex-grow w-full relative duration-200 bg-black rounded-t-md cursor-pointer transform overflow-hidden">
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img
|
||||
src={post?.page_cover}
|
||||
alt={post.title}
|
||||
className="opacity-50 h-full w-full hover:scale-125 rounded-t-md transform object-cover duration-500"
|
||||
/>
|
||||
<span className='absolute bottom-0 left-0 text-white p-6 text-2xl replace break-words w-full' > {post.title}</span>
|
||||
</div>
|
||||
</Link>
|
||||
)}
|
||||
{/* 头部图片 填充卡片 */}
|
||||
{showPageCover && (
|
||||
<Link href={`${BLOG.SUB_PATH}/${post.slug}`} passHref legacyBehavior>
|
||||
<div
|
||||
className="flex flex-grow w-full relative duration-200 bg-black rounded-t-md cursor-pointer transform overflow-hidden">
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img
|
||||
src={post?.page_cover}
|
||||
alt={post.title}
|
||||
className="opacity-50 h-full w-full hover:scale-125 rounded-t-md transform object-cover duration-500"
|
||||
/>
|
||||
<span className='absolute bottom-0 left-0 text-white p-6 text-2xl replace break-words w-full' > {post.title}</span>
|
||||
</div>
|
||||
</Link>
|
||||
)}
|
||||
|
||||
{/* 文字描述 */}
|
||||
<div >
|
||||
{/* 描述 */}
|
||||
<div className="px-4 flex flex-col w-full text-gray-700 dark:text-gray-300">
|
||||
{/* 文字描述 */}
|
||||
<div >
|
||||
{/* 描述 */}
|
||||
<div className="px-4 flex flex-col w-full text-gray-700 dark:text-gray-300">
|
||||
|
||||
{(!showPreview || showSummary) && post.summary && (
|
||||
<p style={{ overflow: 'hidden', textOverflow: 'ellipsis', display: '-webkit-box', WebkitLineClamp: '4', WebkitBoxOrient: 'vertical' }}
|
||||
className="replace my-2 text-sm font-light leading-7">
|
||||
{post.summary}
|
||||
</p>
|
||||
)}
|
||||
{(!showPreview || showSummary) && post.summary && (
|
||||
<p style={{ overflow: 'hidden', textOverflow: 'ellipsis', display: '-webkit-box', WebkitLineClamp: '4', WebkitBoxOrient: 'vertical' }}
|
||||
className="replace my-2 text-sm font-light leading-7">
|
||||
{post.summary}
|
||||
</p>
|
||||
)}
|
||||
|
||||
<div className='text-gray-800 justify-between flex my-2 dark:text-gray-300'>
|
||||
<Link
|
||||
href={`/archive#${post?.date?.start_date?.substr(0, 7)}`}
|
||||
passHref
|
||||
className="font-light hover:underline cursor-pointer text-sm leading-4 mr-3">
|
||||
<div className='text-gray-800 justify-between flex my-2 dark:text-gray-300'>
|
||||
<div>
|
||||
<Link
|
||||
href={`/archive#${post?.date?.start_date?.substr(0, 7)}`}
|
||||
passHref
|
||||
className="font-light hover:underline cursor-pointer text-sm leading-4 mr-3">
|
||||
|
||||
<i className="far fa-clock mr-1" />
|
||||
{post.date?.start_date || post.lastEditedTime}
|
||||
<i className="far fa-clock mr-1" />
|
||||
{post.date?.start_date || post.lastEditedTime}
|
||||
|
||||
</Link>
|
||||
<Link
|
||||
href={`/category/${post.category}`}
|
||||
passHref
|
||||
className="cursor-pointer font-light text-sm hover:underline hover:text-indigo-700 dark:hover:text-indigo-400 transform">
|
||||
</Link>
|
||||
<TwikooCommentCount post={post} className='hover:underline cursor-pointer text-sm'/>
|
||||
</div>
|
||||
<Link
|
||||
href={`/category/${post.category}`}
|
||||
passHref
|
||||
className="cursor-pointer font-light text-sm hover:underline hover:text-indigo-700 dark:hover:text-indigo-400 transform">
|
||||
|
||||
<i className="mr-1 far fa-folder" />
|
||||
{post.category}
|
||||
<i className="mr-1 far fa-folder" />
|
||||
{post.category}
|
||||
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{post?.tagItems && post?.tagItems.length > 0 && (<>
|
||||
<hr />
|
||||
<div className="text-gray-400 justify-between flex px-5 py-3">
|
||||
<div className="md:flex-nowrap flex-wrap md:justify-start inline-block">
|
||||
<div>
|
||||
{' '}
|
||||
{post.tagItems.map(tag => (
|
||||
<TagItemMini key={tag.name} tag={tag} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>)}
|
||||
</div>
|
||||
</div>
|
||||
{post?.tagItems && post?.tagItems.length > 0 && (<>
|
||||
<hr />
|
||||
<div className="text-gray-400 justify-between flex px-5 py-3">
|
||||
<div className="md:flex-nowrap flex-wrap md:justify-start inline-block">
|
||||
<div>
|
||||
{' '}
|
||||
{post.tagItems.map(tag => (
|
||||
<TagItemMini key={tag.name} tag={tag} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import React from 'react'
|
||||
import CONFIG_MEDIUM from '../config_medium'
|
||||
import CategoryItem from './CategoryItem'
|
||||
import TagItemMini from './TagItemMini'
|
||||
import TwikooCommentCount from '@/components/TwikooCommentCount'
|
||||
|
||||
const BlogPostCard = ({ post, showSummary }) => {
|
||||
const showPreview = CONFIG_MEDIUM.POST_LIST_PREVIEW && post.blockMap
|
||||
@@ -43,13 +44,9 @@ const BlogPostCard = ({ post, showSummary }) => {
|
||||
}
|
||||
>
|
||||
<div className="text-sm py-1">{post.date?.start_date}</div>
|
||||
{CONFIG_MEDIUM.POST_LIST_CATEGORY && (
|
||||
<CategoryItem category={post.category} />
|
||||
)}
|
||||
{CONFIG_MEDIUM.POST_LIST_TAG &&
|
||||
post?.tagItems?.map(tag => (
|
||||
<TagItemMini key={tag.name} tag={tag} />
|
||||
))}
|
||||
{CONFIG_MEDIUM.POST_LIST_CATEGORY && <CategoryItem category={post.category} />}
|
||||
{CONFIG_MEDIUM.POST_LIST_TAG && post?.tagItems?.map(tag => <TagItemMini key={tag.name} tag={tag} />)}
|
||||
<TwikooCommentCount post={post} className='hover:underline'/>
|
||||
</div>
|
||||
|
||||
<div className="flex"></div>
|
||||
|
||||
@@ -8,8 +8,7 @@ import SideAreaLeft from './components/SideAreaLeft'
|
||||
import SideAreaRight from './components/SideAreaRight'
|
||||
import TopNav from './components/TopNav'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import CONFIG_NEXT from './config_next'
|
||||
import Live2D from '@/components/Live2D'
|
||||
import BLOG from '@/blog.config'
|
||||
@@ -22,12 +21,13 @@ import BLOG from '@/blog.config'
|
||||
const LayoutBase = (props) => {
|
||||
const { children, headerSlot, meta, sideBarSlot, floatSlot, rightAreaSlot, siteInfo } = props
|
||||
const { onLoading } = useGlobal()
|
||||
const targetRef = React.useRef(null)
|
||||
const floatButtonGroup = React.useRef(null)
|
||||
const targetRef = useRef(null)
|
||||
const floatButtonGroup = useRef(null)
|
||||
const leftAreaSlot = <Live2D/>
|
||||
|
||||
const [show, switchShow] = React.useState(false)
|
||||
const [percent, changePercent] = React.useState(0) // 页面阅读百分比
|
||||
const [showRightFloat, switchShow] = useState(false)
|
||||
const [percent, changePercent] = useState(0) // 页面阅读百分比
|
||||
|
||||
const scrollListener = () => {
|
||||
const targetRef = document.getElementById('wrapper')
|
||||
const clientHeight = targetRef?.clientHeight
|
||||
@@ -37,13 +37,13 @@ const LayoutBase = (props) => {
|
||||
if (per > 100) per = 100
|
||||
const shouldShow = scrollY > 100 && per > 0
|
||||
|
||||
if (shouldShow !== show) {
|
||||
if (shouldShow !== showRightFloat) {
|
||||
switchShow(shouldShow)
|
||||
}
|
||||
changePercent(per)
|
||||
}
|
||||
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
// facebook messenger 插件需要调整右下角悬浮按钮的高度
|
||||
const fb = document.getElementsByClassName('fb-customerchat')
|
||||
if (fb.length === 0) {
|
||||
@@ -54,7 +54,7 @@ const LayoutBase = (props) => {
|
||||
|
||||
document.addEventListener('scroll', scrollListener)
|
||||
return () => document.removeEventListener('scroll', scrollListener)
|
||||
}, [show])
|
||||
}, [showRightFloat])
|
||||
|
||||
return (<div id='theme-next'>
|
||||
|
||||
@@ -78,7 +78,7 @@ const LayoutBase = (props) => {
|
||||
|
||||
{/* 右下角悬浮 */}
|
||||
<div ref={floatButtonGroup} className='right-8 bottom-12 lg:right-2 fixed justify-end z-20 font-sans'>
|
||||
<div className={(show ? 'animate__animated ' : 'hidden') + ' animate__fadeInUp rounded-md glassmorphism justify-center duration-500 animate__faster flex space-x-2 items-center cursor-pointer '}>
|
||||
<div className={(showRightFloat ? 'animate__animated ' : 'hidden') + ' animate__fadeInUp rounded-md glassmorphism justify-center duration-500 animate__faster flex space-x-2 items-center cursor-pointer '}>
|
||||
<JumpToTopButton percent={percent}/>
|
||||
<JumpToBottomButton />
|
||||
<FloatDarkModeButton/>
|
||||
@@ -91,8 +91,4 @@ const LayoutBase = (props) => {
|
||||
)
|
||||
}
|
||||
|
||||
LayoutBase.propTypes = {
|
||||
children: PropTypes.node
|
||||
}
|
||||
|
||||
export default LayoutBase
|
||||
|
||||
@@ -8,6 +8,7 @@ import TagItemMini from './TagItemMini'
|
||||
import CONFIG_NEXT from '../config_next'
|
||||
import NotionPage from '@/components/NotionPage'
|
||||
import NotionIcon from '@/components/NotionIcon'
|
||||
import TwikooCommentCount from '@/components/TwikooCommentCount'
|
||||
|
||||
const BlogPostCard = ({ post, showSummary }) => {
|
||||
const { locale } = useGlobal()
|
||||
@@ -31,7 +32,7 @@ const BlogPostCard = ({ post, showSummary }) => {
|
||||
|
||||
<div
|
||||
className={`flex mt-2 items-center ${showPreview ? 'justify-center' : 'justify-start'
|
||||
} flex-wrap dark:text-gray-500 text-gray-400 hover:text-blue-500 dark:hover:text-blue-400 `}
|
||||
} flex-wrap dark:text-gray-500 text-gray-400 `}
|
||||
>
|
||||
<div>
|
||||
{post.category && (
|
||||
@@ -39,7 +40,7 @@ const BlogPostCard = ({ post, showSummary }) => {
|
||||
<Link
|
||||
href={`/category/${post.category}`}
|
||||
passHref
|
||||
className="cursor-pointer font-light text-sm hover:underline transform">
|
||||
className="hover:text-blue-500 dark:hover:text-blue-400 cursor-pointer font-light text-sm hover:underline transform">
|
||||
|
||||
<i className="mr-1 fas fa-folder" />
|
||||
{post.category}
|
||||
@@ -48,22 +49,18 @@ const BlogPostCard = ({ post, showSummary }) => {
|
||||
<span className="mx-2">|</span>
|
||||
</>
|
||||
)}
|
||||
<Link
|
||||
href={`/archive#${post?.date?.start_date?.substr(0, 7)}`}
|
||||
passHref
|
||||
className="font-light hover:underline cursor-pointer text-sm leading-4 mr-3">
|
||||
|
||||
{post.date?.start_date}
|
||||
|
||||
</Link>
|
||||
<Link
|
||||
href={`/archive#${post?.date?.start_date?.substr(0, 7)}`}
|
||||
passHref
|
||||
className="hover:text-blue-500 dark:hover:text-blue-400 font-light hover:underline cursor-pointer text-sm leading-4 mr-3">
|
||||
{post.date?.start_date}
|
||||
</Link>
|
||||
</div>
|
||||
<div className="md:flex-nowrap flex-wrap md:justify-start inline-block">
|
||||
<div>
|
||||
{' '}
|
||||
<TwikooCommentCount post={post} className='hover:text-blue-500 dark:hover:text-blue-400 hover:underline text-sm'/>
|
||||
<div className="hover:text-blue-500 dark:hover:text-blue-400 md:flex-nowrap flex-wrap md:justify-start inline-block">
|
||||
{post.tagItems?.map(tag => (
|
||||
<TagItemMini key={tag.name} tag={tag} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import BLOG from '@/blog.config'
|
||||
import Link from 'next/link'
|
||||
import CONFIG_SIMPLE from '../config_simple'
|
||||
import TwikooCommentCount from '@/components/TwikooCommentCount'
|
||||
|
||||
export const BlogItem = props => {
|
||||
const { post } = props
|
||||
@@ -18,6 +19,7 @@ export const BlogItem = props => {
|
||||
<div className="mb-4 text-sm text-gray-700 dark:text-gray-300">
|
||||
<span> <i className="fa-regular fa-user"></i> <a href={CONFIG_SIMPLE.AUTHOR_LINK}>{BLOG.AUTHOR}</a></span>
|
||||
<span> - <i className="fa-regular fa-clock"></i> {post.date?.start_date || post.createdTime}</span>
|
||||
<span> - <TwikooCommentCount post={post}/></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>
|
||||
|
||||
Reference in New Issue
Block a user