Files
SkinbaseNova/resources/js/components/social/CommentList.jsx
2026-03-20 21:17:26 +01:00

87 lines
3.4 KiB
JavaScript

import React, { useState } from 'react'
import LevelBadge from '../xp/LevelBadge'
import CommentForm from './CommentForm'
function CommentItem({ comment, canReply, onReply, onDelete }) {
const [replying, setReplying] = useState(false)
return (
<article id={`story-comment-${comment.id}`} className="rounded-2xl border border-white/[0.08] bg-white/[0.04] p-4">
<div className="flex gap-3">
<img
src={comment.user?.avatar_url || 'https://files.skinbase.org/default/avatar_default.webp'}
alt={comment.user?.display || 'User'}
className="h-10 w-10 rounded-full object-cover ring-1 ring-white/10"
/>
<div className="min-w-0 flex-1">
<div className="flex flex-wrap items-center gap-2">
{comment.user?.profile_url ? (
<a href={comment.user.profile_url} className="text-sm font-semibold text-white hover:text-sky-300">
{comment.user.display}
</a>
) : (
<span className="text-sm font-semibold text-white">{comment.user?.display || 'User'}</span>
)}
<LevelBadge level={comment.user?.level} rank={comment.user?.rank} compact />
<span className="text-xs uppercase tracking-[0.16em] text-white/30">{comment.time_ago}</span>
</div>
<div
className="prose prose-invert prose-sm mt-2 max-w-none text-white/80 prose-p:my-1.5 prose-a:text-sky-300"
dangerouslySetInnerHTML={{ __html: comment.rendered_content || '' }}
/>
<div className="mt-3 flex items-center gap-3 text-xs text-white/45">
{canReply ? (
<button type="button" onClick={() => setReplying((value) => !value)} className="transition hover:text-white">
Reply
</button>
) : null}
{comment.can_delete ? (
<button type="button" onClick={() => onDelete?.(comment.id)} className="transition hover:text-rose-300">
Delete
</button>
) : null}
</div>
{replying ? (
<div className="mt-3">
<CommentForm
compact
placeholder={`Reply to ${comment.user?.display || 'user'}`}
submitLabel="Reply"
onCancel={() => setReplying(false)}
onSubmit={async (content) => {
await onReply?.(comment.id, content)
setReplying(false)
}}
/>
</div>
) : null}
{Array.isArray(comment.replies) && comment.replies.length > 0 ? (
<div className="mt-4 space-y-3 border-l border-white/[0.08] pl-4">
{comment.replies.map((reply) => (
<CommentItem key={reply.id} comment={reply} canReply={canReply} onReply={onReply} onDelete={onDelete} />
))}
</div>
) : null}
</div>
</div>
</article>
)
}
export default function CommentList({ comments = [], canReply = false, onReply, onDelete, emptyMessage = 'No comments yet.' }) {
if (!comments.length) {
return <p className="text-sm text-white/45">{emptyMessage}</p>
}
return (
<div className="space-y-4">
{comments.map((comment) => (
<CommentItem key={comment.id} comment={comment} canReply={canReply} onReply={onReply} onDelete={onDelete} />
))}
</div>
)
}