Add news article comments and reactions
This commit is contained in:
@@ -85,6 +85,12 @@ export default function CommentForm({
|
||||
replyTo = null,
|
||||
onCancelReply = null,
|
||||
compact = false,
|
||||
submitUrl = null,
|
||||
contentField = 'content',
|
||||
maxLength = 10000,
|
||||
placeholder = 'Share your thoughts…',
|
||||
submitLabel = 'Post comment',
|
||||
submittingLabel = 'Posting…',
|
||||
}) {
|
||||
const [content, setContent] = useState('')
|
||||
const [tab, setTab] = useState('write') // 'write' | 'preview'
|
||||
@@ -92,6 +98,8 @@ export default function CommentForm({
|
||||
const [errors, setErrors] = useState([])
|
||||
const textareaRef = useRef(null)
|
||||
const formRef = useRef(null)
|
||||
const resolvedSubmitUrl = submitUrl || (artworkId ? `/api/artworks/${artworkId}/comments` : null)
|
||||
const warningThreshold = Math.max(1, Math.floor(maxLength * 0.9))
|
||||
|
||||
// Auto-focus when entering reply mode
|
||||
useEffect(() => {
|
||||
@@ -237,14 +245,14 @@ export default function CommentForm({
|
||||
}
|
||||
|
||||
const trimmed = content.trim()
|
||||
if (!trimmed) return
|
||||
if (!trimmed || !resolvedSubmitUrl) return
|
||||
|
||||
setSubmitting(true)
|
||||
setErrors([])
|
||||
|
||||
try {
|
||||
const { data } = await axios.post(`/api/artworks/${artworkId}/comments`, {
|
||||
content: trimmed,
|
||||
const { data } = await axios.post(resolvedSubmitUrl, {
|
||||
[contentField]: trimmed,
|
||||
parent_id: parentId || null,
|
||||
})
|
||||
|
||||
@@ -255,7 +263,12 @@ export default function CommentForm({
|
||||
} catch (err) {
|
||||
if (err.response?.status === 422) {
|
||||
const fieldErrors = err.response.data?.errors ?? {}
|
||||
const allErrors = Object.values(fieldErrors).flat()
|
||||
const allErrors = [
|
||||
...(Array.isArray(fieldErrors[contentField]) ? fieldErrors[contentField] : []),
|
||||
...Object.entries(fieldErrors)
|
||||
.filter(([field]) => field !== contentField)
|
||||
.flatMap(([, messages]) => Array.isArray(messages) ? messages : []),
|
||||
]
|
||||
setErrors(allErrors.length ? allErrors : ['Invalid content.'])
|
||||
} else {
|
||||
setErrors(['Something went wrong. Please try again.'])
|
||||
@@ -264,7 +277,7 @@ export default function CommentForm({
|
||||
setSubmitting(false)
|
||||
}
|
||||
},
|
||||
[artworkId, content, isLoggedIn, loginUrl, onPosted, parentId, onCancelReply],
|
||||
[content, contentField, isLoggedIn, loginUrl, onPosted, parentId, onCancelReply, resolvedSubmitUrl],
|
||||
)
|
||||
|
||||
/* ── Logged-out state ─────────────────────────────────────────────────── */
|
||||
@@ -342,10 +355,10 @@ export default function CommentForm({
|
||||
<span
|
||||
className={[
|
||||
'text-[11px] tabular-nums font-medium transition-colors',
|
||||
content.length > 9000 ? 'text-amber-400/80' : 'text-white/20',
|
||||
content.length > warningThreshold ? 'text-amber-400/80' : 'text-white/20',
|
||||
].join(' ')}
|
||||
>
|
||||
{content.length > 0 && `${content.length.toLocaleString()}/10,000`}
|
||||
{content.length > 0 && `${content.length.toLocaleString()}/${maxLength.toLocaleString()}`}
|
||||
</span>
|
||||
<EmojiPickerButton onEmojiSelect={handleEmojiSelect} disabled={submitting} />
|
||||
</div>
|
||||
@@ -385,9 +398,9 @@ export default function CommentForm({
|
||||
value={content}
|
||||
onChange={(e) => setContent(e.target.value)}
|
||||
onKeyDown={handleKeyDown}
|
||||
placeholder={replyTo ? `Reply to ${replyTo}…` : 'Share your thoughts…'}
|
||||
placeholder={replyTo ? `Reply to ${replyTo}…` : placeholder}
|
||||
rows={compact ? 2 : 4}
|
||||
maxLength={10000}
|
||||
maxLength={maxLength}
|
||||
disabled={submitting}
|
||||
aria-label="Comment text"
|
||||
className="w-full resize-none bg-transparent px-4 py-3 text-[13px] leading-relaxed text-white/90 placeholder-white/25 focus:outline-none disabled:opacity-50"
|
||||
@@ -451,10 +464,10 @@ export default function CommentForm({
|
||||
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="3" />
|
||||
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z" />
|
||||
</svg>
|
||||
Posting…
|
||||
{submittingLabel}
|
||||
</span>
|
||||
) : (
|
||||
'Post comment'
|
||||
submitLabel
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user