Files

109 lines
3.4 KiB
TypeScript
Raw Permalink Normal View History

'use client';
import Link from 'next/link';
import { useState } from 'react';
import type { PlazaComment } from '@/types/plaza';
import { formatCount, formatRelativeTime } from '@/lib/format';
import { fetchComments } from '@/lib/api';
import { CommentReportButton } from '@/components/post/CommentReportButton';
type CommentThreadProps = {
postId: string;
comment: PlazaComment;
depth?: number;
onReply: (comment: PlazaComment) => void;
onToggleLike: (comment: PlazaComment) => void;
};
export function CommentThread({
postId,
comment,
depth = 0,
onReply,
onToggleLike,
}: CommentThreadProps) {
const [replies, setReplies] = useState<PlazaComment[]>([]);
const [expanded, setExpanded] = useState(false);
const [loadingReplies, setLoadingReplies] = useState(false);
const loadReplies = async () => {
if (loadingReplies || expanded) return;
setLoadingReplies(true);
try {
const data = await fetchComments(postId, { parent_id: comment.id });
setReplies(data.comments);
setExpanded(true);
} finally {
setLoadingReplies(false);
}
};
const canReply = depth === 0;
return (
<div className={depth > 0 ? 'border-l border-[#ebe4d6] pl-4' : undefined}>
<div className="flex items-start justify-between gap-3">
<div className="min-w-0 flex-1">
<Link
href={`/u/${comment.author.slug}`}
className="font-semibold text-[#17221d] hover:text-[#2f6f57]"
>
{comment.author.display_name}
</Link>
<p className="mt-1 text-sm leading-relaxed text-[#4a5751]">{comment.content}</p>
<div className="mt-2 flex flex-wrap items-center gap-3 text-xs text-[#8a928c]">
<span>{formatRelativeTime(comment.created_at)}</span>
{canReply ? (
<button
type="button"
onClick={() => onReply(comment)}
className="font-semibold text-[#68716c] hover:text-[#2f6f57]"
>
回复
</button>
) : null}
<CommentReportButton commentId={comment.id} />
</div>
{canReply && comment.reply_count > 0 && !expanded ? (
<button
type="button"
onClick={() => void loadReplies()}
className="mt-2 text-xs font-semibold text-[#2f6f57]"
>
{loadingReplies
? '加载回复中…'
: `查看 ${formatCount(comment.reply_count)} 条回复`}
</button>
) : null}
</div>
<button
type="button"
onClick={() => onToggleLike(comment)}
className={`shrink-0 rounded-full px-3 py-1 text-xs font-semibold ${
comment.viewer_liked
? 'bg-[#2f6f57] text-white'
: 'border border-[#d6d0c3] text-[#68716c]'
}`}
>
👍 {formatCount(comment.like_count)}
</button>
</div>
{expanded && replies.length > 0 ? (
<ul className="mt-3 space-y-3">
{replies.map((reply) => (
<li key={reply.id}>
<CommentThread
postId={postId}
comment={reply}
depth={depth + 1}
onReply={onReply}
onToggleLike={onToggleLike}
/>
</li>
))}
</ul>
) : null}
</div>
);
}