import React, { useState } from 'react' import FollowButton from './FollowButton' import LikeButton from './LikeButton' import BookmarkButton from './BookmarkButton' import CommentForm from './CommentForm' import CommentList from './CommentList' export default function StorySocialPanel({ story, creator, initialState, initialComments, isAuthenticated = false }) { const [state, setState] = useState({ liked: Boolean(initialState?.liked), bookmarked: Boolean(initialState?.bookmarked), likesCount: Number(initialState?.likes_count || 0), commentsCount: Number(initialState?.comments_count || 0), bookmarksCount: Number(initialState?.bookmarks_count || 0), }) const [comments, setComments] = useState(Array.isArray(initialComments) ? initialComments : []) const csrfToken = typeof document !== 'undefined' ? document.querySelector('meta[name="csrf-token"]')?.getAttribute('content') : null const postJson = async (url, method = 'POST', body = null) => { const response = await fetch(url, { method, headers: { Accept: 'application/json', 'Content-Type': 'application/json', 'X-CSRF-TOKEN': csrfToken || '', }, credentials: 'same-origin', body: body ? JSON.stringify(body) : null, }) if (!response.ok) { const payload = await response.json().catch(() => ({})) throw new Error(payload?.message || 'Request failed.') } return response.json() } const refreshCounts = (nextComments) => { const countReplies = (items) => items.reduce((sum, item) => sum + 1 + countReplies(item.replies || []), 0) return countReplies(nextComments) } const insertReply = (items, parentId, newComment) => items.map((item) => { if (item.id === parentId) { return { ...item, replies: [...(item.replies || []), newComment] } } if (Array.isArray(item.replies) && item.replies.length > 0) { return { ...item, replies: insertReply(item.replies, parentId, newComment) } } return item }) const deleteCommentRecursive = (items, commentId) => items .filter((item) => item.id !== commentId) .map((item) => ({ ...item, replies: Array.isArray(item.replies) ? deleteCommentRecursive(item.replies, commentId) : [], })) return (
{ if (!isAuthenticated) { window.location.href = '/login' return } const payload = await postJson(`/api/stories/${story.id}/like`, 'POST', { state: !state.liked }) setState((current) => ({ ...current, liked: Boolean(payload?.liked), likesCount: Number(payload?.likes_count || 0), })) }} /> { if (!isAuthenticated) { window.location.href = '/login' return } const payload = await postJson(`/api/stories/${story.id}/bookmark`, 'POST', { state: !state.bookmarked }) setState((current) => ({ ...current, bookmarked: Boolean(payload?.bookmarked), bookmarksCount: Number(payload?.bookmarks_count || 0), })) }} /> {creator?.username ? ( ) : null}

Discussion

{state.commentsCount.toLocaleString()} comments on this story

{isAuthenticated ? (
{ const payload = await postJson(`/api/stories/${story.id}/comments`, 'POST', { content }) const nextComments = [payload.data, ...comments] setComments(nextComments) setState((current) => ({ ...current, commentsCount: refreshCounts(nextComments) })) }} />
) : (

Sign in to join the discussion.

)} { const payload = await postJson(`/api/stories/${story.id}/comments`, 'POST', { content, parent_id: parentId }) const nextComments = insertReply(comments, parentId, payload.data) setComments(nextComments) setState((current) => ({ ...current, commentsCount: refreshCounts(nextComments) })) }} onDelete={async (commentId) => { await postJson(`/api/stories/${story.id}/comments/${commentId}`, 'DELETE') const nextComments = deleteCommentRecursive(comments, commentId) setComments(nextComments) setState((current) => ({ ...current, commentsCount: refreshCounts(nextComments) })) }} />
) }