import { useState } from 'react'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { getComments, createComment, type CommentListResponse, } from '../services/commentService'; import { useUser } from '@/features/auth/hooks/useUser'; import { useToast } from '@/hooks/useToast'; import { useIsRateLimited } from '@/hooks/useIsRateLimited'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { MessageCircle, Send, Loader2 } from 'lucide-react'; import { LoadingSpinner } from '@/components/ui/loading-spinner'; import { CommentThread } from './CommentThread'; import type { TrackComment } from '../services/commentService'; import { ErrorDisplay } from '@/components/ui/ErrorDisplay'; // FE-PAGE-007: Complete Track Detail page implementation - Comments Section interface CommentSectionProps { trackId: string; } export function CommentSection({ trackId }: CommentSectionProps) { const { data: user } = useUser(); const toast = useToast(); const queryClient = useQueryClient(); const isRateLimited = useIsRateLimited(); const [newComment, setNewComment] = useState(''); const [mutationError, setMutationError] = useState(null); const [page, setPage] = useState(1); const limit = 20; const { data: commentsData, isLoading, error, } = useQuery({ queryKey: ['trackComments', trackId, page], queryFn: () => getComments(trackId, page, limit), enabled: !!trackId, }); // Action 3.4.1.3: Store last comment content for retry const [lastCommentContent, setLastCommentContent] = useState(''); const [retryCount, setRetryCount] = useState(0); // Action 4.4.1.5: Add optimistic update const createCommentMutation = useMutation({ mutationFn: (content: string) => createComment(trackId, content), onMutate: async (content: string) => { // Cancel outgoing refetches await queryClient.cancelQueries({ queryKey: ['trackComments', trackId] }); // Snapshot previous value const previousComments = queryClient.getQueryData([ 'trackComments', trackId, page, ]); // Optimistically add new comment if (previousComments && user) { const optimisticComment: TrackComment = { id: `temp-${Date.now()}`, track_id: trackId, user_id: user.id, content: content.trim(), is_edited: false, created_at: new Date().toISOString(), updated_at: new Date().toISOString(), user: { id: user.id, username: user.username || '', avatar: user.avatar_url, }, }; queryClient.setQueryData( ['trackComments', trackId, page], { ...previousComments, comments: [optimisticComment, ...previousComments.comments], total: (previousComments.total || 0) + 1, }, ); } return { previousComments }; }, onError: (error: any, _content, context) => { // Rollback on error if (context?.previousComments) { queryClient.setQueryData( ['trackComments', trackId, page], context.previousComments, ); } setMutationError( new Error(error.message || 'Erreur lors de la publication'), ); // Store content for retry setLastCommentContent(newComment.trim()); }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['trackComments', trackId] }); setNewComment(''); setMutationError(null); setRetryCount(0); setLastCommentContent(''); toast.success('Commentaire publié'); }, }); const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); if (!newComment.trim() || !user) return; setLastCommentContent(newComment.trim()); createCommentMutation.mutate(newComment.trim()); }; // Action 3.4.1.3: Retry handler for failed mutations const handleRetry = async () => { if (!lastCommentContent || retryCount >= 3) return; setRetryCount((prev) => prev + 1); try { await createCommentMutation.mutateAsync(lastCommentContent); } catch (error) { // Error will be handled by mutation's onError } }; // Filter to show only top-level comments (no parent_id) const topLevelComments = commentsData?.comments?.filter((c: TrackComment) => !c.parent_id) || []; const total = commentsData?.total || 0; const totalPages = Math.ceil(total / limit); return ( Commentaires ({commentsData?.total || 0}) {mutationError && ( { setMutationError(null); setRetryCount(0); setLastCommentContent(''); }} /> )} {/* Comment Form */} {user ? (
setNewComment(e.target.value)} placeholder="Écrire un commentaire..." maxLength={500} />
) : (

Connectez-vous pour commenter

)} {/* Comments List */} {isLoading ? (
) : error ? ( queryClient.invalidateQueries({ queryKey: ['trackComments', trackId], }) } /> ) : topLevelComments.length === 0 ? (
Aucun commentaire pour le moment. Soyez le premier à commenter !
) : (
{topLevelComments.map((comment: TrackComment) => ( ))} {/* Pagination */} {totalPages > 1 && (
Page {page} sur {totalPages}
)}
)}
); }