Files
zhaoguiyang.site/src/components/blog/Comments.tsx
2026-03-18 08:41:22 +08:00

66 lines
1.9 KiB
TypeScript

import { useEffect, useRef } from 'react';
import type { WalineInstance, WalineInitOptions } from '@waline/client';
import { init } from '@waline/client';
import '@waline/client/style';
import '@/styles/waline-custom.css';
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';
const getTheme = () => (document.documentElement.classList.contains('dark') ? 'html.dark' : false);
useEffect(() => {
if (!containerRef.current) return;
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(),
pageview: true,
comment: true,
path,
});
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();
const instance = walineInstanceRef.current;
walineInstanceRef.current = null;
if (instance) {
try {
instance.destroy();
} catch {
if (containerRef.current) {
containerRef.current.innerHTML = '';
}
}
}
};
}, [props, walineLang]);
return <div ref={containerRef} className="waline-container" />;
}