"use client"; import { formatDistanceToNow } from "date-fns"; import { fr } from "date-fns/locale"; import { Heart, MoreHorizontal, Send, Trash2 } from "lucide-react"; import * as React from "react"; import { toast } from "sonner"; import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; import { Button } from "@/components/ui/button"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { Textarea } from "@/components/ui/textarea"; import { cn } from "@/lib/utils"; import { useAuth } from "@/providers/auth-provider"; import { useSocket } from "@/providers/socket-provider"; import { type Comment, CommentService } from "@/services/comment.service"; interface CommentSectionProps { contentId: string; } export function CommentSection({ contentId }: CommentSectionProps) { const { user, isAuthenticated } = useAuth(); const { socket } = useSocket(); const [comments, setComments] = React.useState([]); const [newComment, setNewComment] = React.useState(""); const [replyingTo, setReplyingTo] = React.useState(null); const [isSubmitting, setIsSubmitting] = React.useState(false); const [isLoading, setIsLoading] = React.useState(true); const fetchComments = React.useCallback(async () => { try { const data = await CommentService.getByContentId(contentId); setComments(data); } catch (_error) { toast.error("Impossible de charger les commentaires"); } finally { setIsLoading(false); } }, [contentId]); React.useEffect(() => { fetchComments(); }, [fetchComments]); // Gestion du WebSocket React.useEffect(() => { if (socket) { socket.emit("join_content", contentId); socket.on("new_comment", (comment: Comment) => { setComments((prev) => { // Éviter les doublons si l'auteur reçoit son propre commentaire via WS if (prev.some((c) => c.id === comment.id)) return prev; return [comment, ...prev]; }); }); return () => { socket.emit("leave_content", contentId); socket.off("new_comment"); }; } }, [socket, contentId]); const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (!newComment.trim() || isSubmitting) return; setIsSubmitting(true); try { const comment = await CommentService.create( contentId, newComment.trim(), replyingTo?.id, ); setComments((prev) => [comment, ...prev]); setNewComment(""); setReplyingTo(null); toast.success("Commentaire publié !"); } catch (_error) { toast.error("Erreur lors de la publication du commentaire"); } finally { setIsSubmitting(false); } }; const handleDelete = async (commentId: string) => { try { await CommentService.remove(commentId); setComments((prev) => prev.filter((c) => c.id !== commentId)); toast.success("Commentaire supprimé"); } catch (_error) { toast.error("Erreur lors de la suppression"); } }; const handleLike = async (comment: Comment) => { if (!isAuthenticated) { toast.error("Vous devez être connecté pour liker"); return; } try { if (comment.isLiked) { await CommentService.unlike(comment.id); setComments((prev) => prev.map((c) => c.id === comment.id ? { ...c, isLiked: false, likesCount: c.likesCount - 1 } : c, ), ); } else { await CommentService.like(comment.id); setComments((prev) => prev.map((c) => c.id === comment.id ? { ...c, isLiked: true, likesCount: c.likesCount + 1 } : c, ), ); } } catch (_error) { toast.error("Une erreur est survenue"); } }; // Organiser les commentaires : Parents d'abord const rootComments = comments.filter((c) => !c.parentId); const renderComment = (comment: Comment, depth = 0) => { const replies = comments.filter((c) => c.parentId === comment.id); return (
0 && "ml-10")}>
{comment.user.username[0].toUpperCase()}
{comment.user.displayName || comment.user.username} {formatDistanceToNow(new Date(comment.createdAt), { addSuffix: true, locale: fr, })}
{(user?.uuid === comment.user.uuid || user?.role === "admin" || user?.role === "moderator") && ( handleDelete(comment.id)} className="text-destructive" > Supprimer )}

{comment.text}

{comment.likesCount > 0 && ( {comment.likesCount} like{comment.likesCount > 1 ? "s" : ""} )} {isAuthenticated && depth < 1 && ( )}
{replies.length > 0 && (
{replies.map((reply) => renderComment(reply, depth + 1))}
)}
); }; return (

Commentaires ({comments.length})

{isAuthenticated ? (
{replyingTo && (
En réponse à{" "} @{replyingTo.user.username}
)}
{user?.username[0].toUpperCase()}