feat(comments): 添加 Waline 评论组件,替换 Giscus 评论组件
This commit is contained in:
61
src/components/blog/Comments.tsx
Normal file
61
src/components/blog/Comments.tsx
Normal file
@@ -0,0 +1,61 @@
|
||||
import { useEffect, useRef } from 'react';
|
||||
import type { WalineInstance, WalineInitOptions } from '@waline/client';
|
||||
import { init } from '@waline/client';
|
||||
import '@waline/client/style';
|
||||
|
||||
import type { Lang } from '@/types/i18n';
|
||||
|
||||
export type CommentsProps = Partial<Omit<WalineInitOptions, 'el' | 'serverURL'>> & { lang?: Lang };
|
||||
|
||||
export default function Comments({ lang = 'en', ...props }: CommentsProps) {
|
||||
const walineInstanceRef = useRef<WalineInstance | null>(null);
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const walineLang = lang === 'zh' ? 'zh-CN' : 'en';
|
||||
|
||||
useEffect(() => {
|
||||
if (!containerRef.current) return;
|
||||
|
||||
const getTheme = () => document.documentElement.classList.contains('dark') ? 'html.dark' : false;
|
||||
|
||||
const pathname = window.location.pathname;
|
||||
const postsMatch = pathname.match(/\/posts\/([^/]+)/);
|
||||
const path = postsMatch ? postsMatch[1] : pathname;
|
||||
|
||||
walineInstanceRef.current = init({
|
||||
...props,
|
||||
el: containerRef.current,
|
||||
serverURL: import.meta.env.PUBLIC_WALINE_SERVER_URL,
|
||||
lang: walineLang,
|
||||
dark: getTheme(),
|
||||
path,
|
||||
});
|
||||
|
||||
return () => walineInstanceRef.current?.destroy();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const getTheme = () => document.documentElement.classList.contains('dark') ? 'html.dark' : false;
|
||||
|
||||
walineInstanceRef.current?.update({
|
||||
...props,
|
||||
lang: walineLang,
|
||||
dark: getTheme(),
|
||||
});
|
||||
|
||||
const handleThemeChange = () => {
|
||||
walineInstanceRef.current?.update({
|
||||
...props,
|
||||
lang: walineLang,
|
||||
dark: getTheme(),
|
||||
});
|
||||
};
|
||||
|
||||
const observer = new MutationObserver(handleThemeChange);
|
||||
observer.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] });
|
||||
|
||||
return () => observer.disconnect();
|
||||
}, [lang, props, walineLang]);
|
||||
|
||||
return <div ref={containerRef} className="waline-container" />;
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
import Giscus from '@giscus/react';
|
||||
import type { Lang } from '@/types/i18n';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
export interface GiscusCommentsProps {
|
||||
lang?: Lang;
|
||||
}
|
||||
|
||||
export default function GiscusComments({ lang = 'en' }: GiscusCommentsProps) {
|
||||
const [theme, setTheme] = useState<'light' | 'dark'>('light');
|
||||
const [term, setTerm] = useState<string>('');
|
||||
|
||||
const giscusLang = lang === 'zh' ? 'zh-CN' : lang;
|
||||
|
||||
useEffect(() => {
|
||||
const getTheme = () => document.documentElement.classList.contains('dark') ? 'dark' : 'light';
|
||||
|
||||
const pathname = window.location.pathname;
|
||||
const postsMatch = pathname.match(/\/posts\/([^/]+)/);
|
||||
const discussionTerm = postsMatch ? postsMatch[1] : pathname;
|
||||
|
||||
setTheme(getTheme());
|
||||
setTerm(discussionTerm);
|
||||
|
||||
const observer = new MutationObserver(() => {
|
||||
setTheme(getTheme());
|
||||
});
|
||||
|
||||
observer.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] });
|
||||
|
||||
return () => observer.disconnect();
|
||||
}, []);
|
||||
|
||||
|
||||
return (
|
||||
<Giscus
|
||||
repo="zguiyang/blog-giscus"
|
||||
repoId="R_kgDOQ2Wnxw"
|
||||
category="Announcements"
|
||||
categoryId="DIC_kwDOQ2Wnx84C0vSJ"
|
||||
mapping="specific"
|
||||
term={term}
|
||||
strict="0"
|
||||
reactionsEnabled="1"
|
||||
emitMetadata="0"
|
||||
inputPosition="top"
|
||||
theme={theme}
|
||||
lang={giscusLang}
|
||||
loading="lazy"
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -12,7 +12,7 @@ import TableOfContents from '@/components/layout/TableOfContents.astro';
|
||||
import BlogNavigation from '@/components/layout/BlogNavigation.astro';
|
||||
import PostMeta from '@/components/blog/PostMeta.astro';
|
||||
import Container from '../components/ui/Container';
|
||||
import GiscusComments from '@/components/blog/GiscusComments';
|
||||
import Comments from '@/components/blog/Comments';
|
||||
|
||||
// Use Astro's MarkdownLayoutProps for proper type safety
|
||||
export type Props = MarkdownLayoutProps<FrontmatterProps>;
|
||||
@@ -107,7 +107,7 @@ const finalReadingTime = readTime ? parseInt(readTime.replace(/\D/g, '')) : unde
|
||||
|
||||
<!-- Comments Section -->
|
||||
<div class="mt-10 sm:mt-16 border-t border-border pt-10">
|
||||
<GiscusComments client:only="react" lang={lang} />
|
||||
<Comments client:only="react" lang={lang} />
|
||||
</div>
|
||||
|
||||
<!-- Author Card moved to bottom with enhanced styling -->
|
||||
|
||||
@@ -55,7 +55,7 @@ const tags = extractTags(allPostsArray);
|
||||
|
||||
---
|
||||
|
||||
<BlogLayout title="博客 - 赵桂阳" description="深入我对编程、技术趋势和开发者生活的思考。探索我的最新文章。">
|
||||
<BlogLayout title="博客 - Joy Zhao" description="深入我对编程、技术趋势和开发者生活的思考。探索我的最新文章。">
|
||||
<main class="min-h-screen">
|
||||
<!-- 头部区域 -->
|
||||
<Container client:load className="pt-24 pb-12">
|
||||
|
||||
Reference in New Issue
Block a user