import { useEffect, useRef, useState } from "react"; import { useLocation } from "react-router"; import { add } from "~/components/ui/notice/utils"; import { useHydrated } from "~/hooks/use-hydrated"; import { clsn } from "~/utils"; import CommentForm, { type CommentFormSubmitValues } from "../comment-form"; import CommentItem from "../comment-item"; const { APP_APIURL } = import.meta.env; interface CommentsProps { className?: string; refs: string; } export default function Comments({ className, refs }: CommentsProps) { const wrapperRef = useRef(null); const location = useLocation(); const hydrated = useHydrated(); const [list, setList] = useState([]); const [loading, setLoading] = useState(true); const prevRefsRef = useRef(""); const scrollToComment = (hash: string) => { const id = hash.replace(/^#/, ""); if (!id || !id.startsWith("c")) { return; } const commentItem = document.querySelector(`#${id}`); if (!commentItem) { console.log("定位评论失败"); return; } console.log("定位评论成功"); commentItem.scrollIntoView(); commentItem.classList.add("animate-scale"); const onAnimationEnd = (ev: AnimationEvent) => { const el = ev.target as HTMLDivElement; if (el) { el.classList.remove("animate-scale"); } commentItem.removeEventListener("animationend", onAnimationEnd); }; commentItem.addEventListener("animationend", onAnimationEnd); }; const getCommentRequest = () => { fetch(`${APP_APIURL}/api/comment/detail/?ref=${encodeURIComponent(refs)}`) .then((res) => res.json()) .then((res: API.PageResponse) => { if (res.status === "Success") { setList(res.data); setLoading(false); } if (location.hash.match(/^#c/)) { scrollToComment(location.hash); } }); }; useEffect(() => { if (!hydrated || !wrapperRef.current) { return; } if (location.hash.match(/^#c/)) { wrapperRef.current.scrollIntoView(); } const observer = new IntersectionObserver( (entries) => { entries.forEach((entry) => { if (entry.isIntersecting && prevRefsRef.current !== refs) { console.log("获取评论"); prevRefsRef.current = refs; getCommentRequest(); } }); }, { threshold: 0.1, }, ); observer.observe(wrapperRef.current); return () => { observer.disconnect(); }; }, [hydrated, refs, location.hash]); useEffect(() => { if (hydrated && location.hash.match(/^#c/) && wrapperRef.current) { scrollToComment(location.hash); } }, [hydrated, location.hash]); const addCommentRequest = (data: CommentFormSubmitValues) => { return new Promise((resolve, reject) => { fetch(`${APP_APIURL}/api/comment/add`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ ...data, ref: refs }), }) .then((res) => res.json()) .then((res: API.Response) => { if (res.status === "Success") { add({ title: "评论成功", type: "success", content: res.msg, }); if (res.data) { if (res.data.root) { setList((prev) => { const index = prev.findIndex((item) => item.uuid === res.data.root); if (index > -1) { const next = [...prev]; next[index] = { ...next[index], children: [res.data, ...next[index].children], }; return next; } return prev; }); } else { setList((prev) => [{ ...res.data, children: [] }, ...prev]); } } resolve(); } else { add({ title: "评论失败", type: "error", content: res.msg, }); reject(); } }) .catch(() => { reject(); }); }); }; return (

评论

{list.length > 0 ? (
{list.map((item) => ( ))}
) : (
{loading ? "评论获取中" : list.length === 0 ? "快来成为第一个评论的人吧" : null}
)}
); }