wip: implment webmention display block

This commit is contained in:
sy
2023-03-31 19:31:14 +08:00
parent a7a6efe1fb
commit fd3b026154
6 changed files with 255 additions and 3 deletions

View File

@@ -1,2 +1,6 @@
# 环境变量 @see https://www.nextjs.cn/docs/basic-features/environment-variables
NEXT_PUBLIC_VERSION=3.12.4
NEXT_PUBLIC_THEME=nobelium
NEXT_PUBLIC_WEBMENTION_ENABLE=yes
NEXT_PUBLIC_WEBMENTION_AUTH=https://github.com/siygle
NEXT_PUBLIC_WEBMENTION_HOSTNAME=chat.sylee.dev

View File

@@ -178,9 +178,9 @@ const BLOG = {
COMMENT_WALINE_RECENT: process.env.NEXT_PUBLIC_WALINE_RECENT || false, // 最新评论
COMMENT_WEBMENTION: {
ENABLE: (process.env.WEBMENTION_ENABLE === 'yes'),
AUTH: process.env.WEBMENTION_AUTH || '',
HOSTNAME: process.env.WEBMENTION_HOSTNAME || ''
ENABLE: process.env.NEXT_PUBLIC_WEBMENTION_ENABLE || false,
AUTH: process.env.NEXT_PUBLIC_WEBMENTION_AUTH || '',
HOSTNAME: process.env.NEXT_PUBLIC_WEBMENTION_HOSTNAME || ''
},
// <---- 评论插件

View File

@@ -43,6 +43,12 @@ const GiscusComponent = dynamic(
},
{ ssr: false }
)
const WebMentionComponent = dynamic(
() => {
return import('@/components/WebMention')
},
{ ssr: false }
)
const ValineComponent = dynamic(() => import('@/components/ValineComponent'), {
ssr: false
@@ -100,6 +106,10 @@ const Comment = ({ frontMatter }) => {
{BLOG.COMMENT_GITALK_CLIENT_ID && (<div key='GitTalk'>
<GitalkComponent frontMatter={frontMatter}/>
</div>)}
{BLOG.COMMENT_WEBMENTION.ENABLE === 'yes' && (<div key='WebMention'>
<WebMentionComponent frontMatter={frontMatter} className="px-2" />
</div>)}
</Tabs>
</div>
)

View File

@@ -44,6 +44,7 @@ const CommonHead = ({ meta, children }) => {
<meta name="twitter:description" content={description} />
<meta name="twitter:title" content={title} />
{console.log(BLOG)}
{BLOG.COMMENT_WEBMENTION.ENABLE && (
<>
<link rel="webmention" href={`https://webmention.io/${BLOG.COMMENT_WEBMENTION.HOSTNAME}/webmention`} />

168
components/WebMention.js Normal file
View File

@@ -0,0 +1,168 @@
import BLOG from '@/blog.config'
import { useEffect, useState } from 'react'
import { useRouter } from 'next/router'
/**
* 评论插件
* @param issueTerm
* @param layout
* @returns {JSX.Element}
* @constructor
*/
const WebmentionCount = ({ target }) => {
const initialCounts = {
count: 0,
type: {
like: 0,
mention: 0,
reply: 0,
repost: 0
}
}
const [counts, setCounts] = useState(initialCounts)
const fetchCounts = async (target) => {
const responseData = await fetch(`https://webmention.io/api/count.json?target=${target}`)
console.log('!!!', responseData)
return (responseData.json) ? await responseData.json() : responseData
}
useEffect(() => {
async function getCounts() {
const responseCounts = await fetchCounts(target)
console.log('rere', responseCounts)
setCounts(responseCounts)
}
getCounts()
}, [target])
return (
<div className='webmention-counts'>
{counts
? (
<div className='counts'>
<span>
<span className='count'>{counts.type.like || 0}</span>Likes
</span>
<span>
<span className='count'>{counts.type.reply || 0}</span>Replies
</span>
<span>
<span className='count'>
{(counts.type.repost || 0) + (counts.type.mention || 0)}
</span>
Mentions
</span>
</div>
)
: (
<p>Failed to fetch Webmention counts</p>
)
}
</div>
)
}
const Avatar = ({ author }) => (
<a className='avattar-wrapper' href={author.url} key={author.name}>
<Image
className="avatar"
src={author.photo}
alt={author.name}
fill
sizes="(max-width: 768px) 100vw,
(max-width: 1200px) 50vw,
33vw"
/>
</a>
)
const WebmentionReplies = ({ target }) => {
const [mentions, setMentions] = useState([])
const fetchMentions = async (target) =>
fetch(
`https://webmention.io/api/mentions.jf2?per-page=500&target=${target}`
).then((response) => (response.json ? response.json() : response))
useEffect(() => {
async function getMentions() {
const responseMentions = await fetchMentions(target)
if (responseMentions.children) {
setMentions(responseMentions.children)
}
}
getMentions()
}, [target])
const distinctMentions = [
...new Map(mentions.map((item) => [item.author.url, item])).values()
].sort((a, b) => new Date(a['wm-received']) - new Date(b['wm-received']))
const replies = distinctMentions.filter(
(mention) => 'in-reply-to' in mention && 'content' in mention
)
return (
<div>
<p>
{distinctMentions.length > 0
? `Already ${distinctMentions.length} people liked, shared or talked about this article:`
: 'Be the first one to share this article!'}
</p>
<div className='webmention-avatars'>
{distinctMentions.map((reply) => (
<Avatar key={reply.author.name} author={reply.author} />
))}
</div>
{replies && replies.length
? (
<div className='webmention-replies'>
<h4>Replies</h4>
<ul className='replies'>
{replies.map((reply) => (
<li className='reply' key={reply.content.text}>
<div>
<Avatar key={reply.author.name} author={reply.author} />
</div>
<div className='text'>
<p className='reply-author-name'>{reply.author.name}</p>
<p className='reply-content'>{reply.content.text}</p>
</div>
</li>
))}
</ul>
</div>
)
: null}
</div>
)
}
const WebMentionBlock = ({ frontMatter }) => {
const router = useRouter()
const url = `https://${BLOG.COMMENT_WEBMENTION.HOSTNAME}/${router.asPath}`
const tweet = `${frontMatter.title} by @siygle ${url}`
return (
<div className='post-footer'>
<div className='post-footer-intro'>
This post is using{' '}
<a target="_blank" rel='noreferrer' href="https://webmention.io">
WebMention.io
</a>{' '}
as the comment system.{' '}
<a
target="_blank"
id='tweet-post-url'
href={`https://twitter.com/intent/tweet?text=${encodeURI(tweet)}`}
rel="noopener noreferrer"
>Tweet this post</a>
, the comments will show up here.
</div>
<div className='webmention-info'>
<WebmentionCount target={url} />
<WebmentionReplies target={url} />
</div>
</div>
)
}
export default WebMentionBlock

View File

@@ -222,4 +222,73 @@ nav {
/* twikoo 评论区超链接样式 */
.tk-main a {
@apply text-blue-700
}
/* Webmention style */
.post-footer {
background: rgba(0, 116, 222, .2);
padding: 1rem 2rem;
border-radius: 5px;
}
.webmention {
margin-top: 1rem;
padding-top: 1rem;
border-top: 1px solid rgba(0, 0, 0, 0.2);
}
.webmention-counts .count {
font-weight: bold;
margin-right: .2rem;
}
/* .webmention-counts .counts > span {
margin-right: .8rem;
} */
.webmention-counts .counts>span:not(:last-child):after {
content: " • ";
}
a.avatar-wrapper {
display: inline-block;
width: 50px;
height: 50px;
position: relative;
}
.webmention-avatars .avatar-wrapper {
margin-right: -8px;
}
.avatar {
border-radius: 50%;
margin: 0;
border: 3px solid rgba(0, 116, 222, .5);
}
.replies {
margin: 0;
padding: 0;
}
.reply {
list-style: none;
display: flex;
position: relative;
padding: 0;
align-items: flex-start;
margin-top: .6rem;
}
.reply p {
margin: 0;
}
.reply .text {
margin-left: 1rem;
font-size: 14px;
}
.reply-author-name {
font-weight: 500;
}