import React, { useState, useEffect, useRef } from 'react' import axios from 'axios' function formatRelative(isoString) { const diff = Date.now() - new Date(isoString).getTime() const s = Math.floor(diff / 1000) if (s < 60) return 'just now' const m = Math.floor(s / 60) if (m < 60) return `${m}m ago` const h = Math.floor(m / 60) if (h < 24) return `${h}h ago` const d = Math.floor(h / 24) return `${d}d ago` } export default function PostComments({ postId, isLoggedIn, isOwn = false, initialCount = 0 }) { const [comments, setComments] = useState([]) const [loading, setLoading] = useState(false) const [submitting, setSubmitting] = useState(false) const [body, setBody] = useState('') const [error, setError] = useState(null) const [page, setPage] = useState(1) const [hasMore, setHasMore] = useState(false) const [loaded, setLoaded] = useState(false) const textareaRef = useRef(null) const fetchComments = async (p = 1) => { setLoading(true) try { const { data } = await axios.get(`/api/posts/${postId}/comments`, { params: { page: p } }) setComments((prev) => p === 1 ? data.data : [...prev, ...data.data]) setHasMore(data.meta.current_page < data.meta.last_page) setPage(p) } catch { // } finally { setLoading(false) setLoaded(true) } } useEffect(() => { fetchComments(1) }, [postId]) const handleSubmit = async (e) => { e.preventDefault() if (!body.trim()) return setSubmitting(true) setError(null) try { const { data } = await axios.post(`/api/posts/${postId}/comments`, { body }) setComments((prev) => [...prev, data.comment]) setBody('') } catch (err) { setError(err.response?.data?.message ?? 'Failed to post comment.') } finally { setSubmitting(false) } } const handleDelete = async (commentId) => { if (!window.confirm('Delete this comment?')) return try { await axios.delete(`/api/posts/${postId}/comments/${commentId}`) setComments((prev) => prev.filter((c) => c.id !== commentId)) } catch { // } } const handleHighlight = async (comment) => { try { if (comment.is_highlighted) { await axios.delete(`/api/posts/${postId}/comments/${comment.id}/highlight`) setComments((prev) => prev.map((c) => c.id === comment.id ? { ...c, is_highlighted: false } : c), ) } else { await axios.post(`/api/posts/${postId}/comments/${comment.id}/highlight`) // Only one can be highlighted — clear others and set this one setComments((prev) => prev.map((c) => ({ ...c, is_highlighted: c.id === comment.id })), ) } } catch { // } } // Highlighted comment always first (server also orders this way, but keep client in sync) const sorted = [...comments].sort((a, b) => (b.is_highlighted ? 1 : 0) - (a.is_highlighted ? 1 : 0), ) return (
{/* Comment list */} {!loaded && loading && (
{[1, 2].map((i) => (
))}
)} {loaded && sorted.map((c) => (
{/* Avatar */} {c.author.name} {/* Body */}
{c.author.name || `@${c.author.username}`} {formatRelative(c.created_at)} {c.is_highlighted && ( Highlighted by author )}
{/* Actions: highlight (owner) + delete */}
{isOwn && ( )} {isLoggedIn && ( )}
))} {loaded && hasMore && ( )} {/* Composer */} {isLoggedIn ? (