"use client" import { useState, useCallback } from "react" import Image from "next/image" import { ThumbsUpIcon, ReplyIcon, MoreHorizontalIcon, TrashIcon, ChevronDownIcon, ChevronUpIcon, } from "lucide-react" import { useSession } from "@/lib/auth-client" import { TiptapRenderer } from "@/components/tiptap-renderer" import { TiptapEditor } from "@/components/tiptap-editor" const MAX_DEPTH = 3 export interface CommentData { id: string gameId: string userId: string parentId: string | null content: Record upvotes: number createdAt: string updatedAt: string userName: string | null userImage: string | null } function formatDate(value: string | null | undefined): string { if (!value) return "" return new Date(value).toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric", }) } function getInitial(name: string | null | undefined): string { return name?.charAt(0)?.toUpperCase() || "?" } interface CommentItemProps { comment: CommentData depth?: number onReplyPosted: () => void gameId: string } export function CommentItem({ comment, depth = 0, onReplyPosted, gameId, }: CommentItemProps) { const { data: session } = useSession() const [upvotes, setUpvotes] = useState(comment.upvotes) const [hasUpvoted, setHasUpvoted] = useState(false) const [isReplying, setIsReplying] = useState(false) const [replyContent, setReplyContent] = useState | null>(null) const [replySubmitting, setReplySubmitting] = useState(false) const [showReplies, setShowReplies] = useState(false) const [replies, setReplies] = useState([]) const [loadingReplies, setLoadingReplies] = useState(false) const [menuOpen, setMenuOpen] = useState(false) const [isDeleted, setIsDeleted] = useState(false) const isOwner = session?.user?.id === comment.userId const isAdmin = session?.user?.role === "admin" const canModerate = isOwner || isAdmin const handleUpvote = useCallback(async () => { if (!session) return try { const res = await fetch( `/api/games/${gameId}/comments/${comment.id}/upvote`, { method: "POST" }, ) if (res.ok) { setUpvotes((prev) => (hasUpvoted ? prev - 1 : prev + 1)) setHasUpvoted((prev) => !prev) } } catch (err) { console.error("Failed to upvote comment:", err) } }, [session, gameId, comment.id, hasUpvoted]) const handleReplySubmit = useCallback(async () => { if (!replyContent || !session) return setReplySubmitting(true) try { const res = await fetch(`/api/games/${gameId}/comments`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ parentId: comment.id, content: replyContent }), }) if (res.ok) { setIsReplying(false) setReplyContent(null) onReplyPosted() if (showReplies) { // Refresh replies setLoadingReplies(true) const repliesRes = await fetch( `/api/games/${gameId}/comments/${comment.id}/replies`, ) if (repliesRes.ok) { const data = (await repliesRes.json()) as CommentData[] setReplies(data) } setLoadingReplies(false) } } } catch (err) { console.error("Failed to post reply:", err) } finally { setReplySubmitting(false) } }, [replyContent, session, gameId, comment.id, onReplyPosted, showReplies]) const handleDelete = useCallback(async () => { if (!canModerate) return try { const res = await fetch( `/api/games/${gameId}/comments/${comment.id}`, { method: "DELETE" }, ) if (res.ok) { setIsDeleted(true) } } catch (err) { console.error("Failed to delete comment:", err) } }, [canModerate, gameId, comment.id]) const handleLoadReplies = useCallback(async () => { if (showReplies) { setShowReplies(false) return } setLoadingReplies(true) try { const res = await fetch( `/api/games/${gameId}/comments/${comment.id}/replies`, ) if (res.ok) { const data = (await res.json()) as CommentData[] setReplies(data) setShowReplies(true) } } catch (err) { console.error("Failed to load replies:", err) } finally { setLoadingReplies(false) } }, [showReplies, gameId, comment.id]) if (isDeleted) { return (
Comment removed
) } return (
0 ? "ml-4 border-l border-border pl-3" : ""}>
{/* Avatar */}
{comment.userImage ? ( {comment.userName ) : (
{getInitial(comment.userName)}
)}
{/* Content */}
{comment.userName || "Unknown"} {formatDate(comment.createdAt)}
{/* Actions */}
{session && depth < MAX_DEPTH && ( )} {canModerate && (
{menuOpen && ( <>
setMenuOpen(false)} />
)}
)}
{/* Reply form */} {isReplying && (
setReplyContent(json)} className="min-h-[100px]" />
)} {/* Load replies */} {depth < MAX_DEPTH && (
{replies.length > 0 && !showReplies && ( )} {showReplies && replies.length > 0 && ( )} {replies.length === 0 && !showReplies && ( )}
)} {/* Replies list */} {depth < MAX_DEPTH && showReplies && replies.length > 0 && (
{replies.map((reply) => ( ))}
)}
) }