"use client"; import { useState } from "react"; import { toast } from "sonner"; const REACTIONS: Record = { like: "👍", love: "❤️", laugh: "😂", wow: "😮", sad: "😢", fire: "🔥", }; type Props = { recipeId: string; commentId: string; initialCounts?: Record; initialUserReactions?: string[]; }; export function CommentReactions({ recipeId, commentId, initialCounts = {}, initialUserReactions = [] }: Props) { const [counts, setCounts] = useState>(initialCounts); const [userReactions, setUserReactions] = useState(initialUserReactions); const [pending, setPending] = useState(null); async function toggle(type: string) { if (pending) return; setPending(type); // Optimistic update const hasReacted = userReactions.includes(type); setUserReactions((prev) => hasReacted ? prev.filter((r) => r !== type) : [...prev, type], ); setCounts((prev) => ({ ...prev, [type]: Math.max(0, (prev[type] ?? 0) + (hasReacted ? -1 : 1)), })); try { const res = await fetch(`/api/v1/recipes/${recipeId}/comments/${commentId}/reactions`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ type }), }); if (!res.ok) { // Revert optimistic update setUserReactions((prev) => hasReacted ? [...prev, type] : prev.filter((r) => r !== type), ); setCounts((prev) => ({ ...prev, [type]: Math.max(0, (prev[type] ?? 0) + (hasReacted ? 1 : -1)), })); if (res.status === 401) { toast.error("Sign in to react to comments"); } else { toast.error("Failed to update reaction"); } return; } const data = await res.json() as { counts: Record; added: boolean }; setCounts(data.counts); setUserReactions((prev) => { if (data.added && !prev.includes(type)) return [...prev, type]; if (!data.added && prev.includes(type)) return prev.filter((r) => r !== type); return prev; }); } catch { // Revert on network error setUserReactions((prev) => hasReacted ? [...prev, type] : prev.filter((r) => r !== type), ); setCounts((prev) => ({ ...prev, [type]: Math.max(0, (prev[type] ?? 0) + (hasReacted ? 1 : -1)), })); toast.error("Failed to update reaction"); } finally { setPending(null); } } return (
{Object.entries(REACTIONS).map(([type, emoji]) => { const reacted = userReactions.includes(type); const cnt = counts[type] ?? 0; return ( ); })}
); }