"use client"; import { useState, useEffect, useCallback, use } from "react"; import { useRouter } from "next/navigation"; import { useFamily } from "../../FamilyProvider"; import type { CirclePost, CircleComment, Circle } from "@/types"; const REACTIONS = ["โค๏ธ", "๐Ÿ˜‚", "๐Ÿ‘", "๐Ÿ™", "๐Ÿ˜ฎ"]; function timeAgo(iso: string) { const diff = Date.now() - new Date(iso).getTime(); const m = Math.floor(diff / 60000); if (m < 1) return "just now"; if (m < 60) return `${m}m`; const h = Math.floor(m / 60); if (h < 24) return `${h}h`; return `${Math.floor(h / 24)}d`; } // โ”€โ”€ Post card โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ function PostCard({ post, myFamilyId, circleId, isAdmin, onDeleted, onReact, }: { post: CirclePost; myFamilyId: string; circleId: string; isAdmin: boolean; onDeleted: (id: string) => void; onReact: (postId: string, emoji: string) => void; }) { const [showComments, setShowComments] = useState(false); const [comments, setComments] = useState([]); const [commentText, setCommentText] = useState(""); const [posting, setPosting] = useState(false); const [showMenu, setShowMenu] = useState(false); const [reportSent, setReportSent] = useState(false); const [deleteConfirm, setDeleteConfirm] = useState(false); const [editMode, setEditMode] = useState(false); const [editBody, setEditBody] = useState(post.body ?? ""); const [savingEdit, setSavingEdit] = useState(false); const [lightbox, setLightbox] = useState(false); const loadComments = async () => { const res = await fetch(`/api/circles/${circleId}/posts/${post.id}/comments`); const data = await res.json(); setComments(data.comments ?? []); }; const toggleComments = () => { if (!showComments) loadComments(); setShowComments(v => !v); }; const addComment = async () => { if (!commentText.trim()) return; setPosting(true); await fetch(`/api/circles/${circleId}/posts/${post.id}/comments`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ body: commentText.trim() }), }); setCommentText(""); loadComments(); setPosting(false); }; const sendReport = async (reason: string) => { await fetch(`/api/circles/${circleId}/posts/${post.id}/report`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ reason }), }); setReportSent(true); setShowMenu(false); }; const deletePost = async () => { await fetch(`/api/circles/${circleId}/posts/${post.id}`, { method: "DELETE" }); onDeleted(post.id); }; const saveEdit = async () => { setSavingEdit(true); await fetch(`/api/circles/${circleId}/posts/${post.id}`, { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ body: editBody }), }); post.body = editBody; // optimistic local update setEditMode(false); setSavingEdit(false); }; const isOwn = post.authorFamilyId === myFamilyId; return ( <> {/* Fullscreen image lightbox */} {lightbox && post.imageUrl && (
setLightbox(false)} > Full size
)}
{/* Author row */}
๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ง

{post.authorFamilyName}

{timeAgo(post.createdAt)}{post.sourceKind ? ` ยท shared a ${post.sourceKind}` : ""}

{/* โ‹ฏ menu โ€” rendered outside overflow:hidden so it doesn't get clipped */}
{showMenu && ( <>
setShowMenu(false)} />
{isOwn && ( )} {(isOwn || isAdmin) && ( )} {!isOwn && !reportSent && ( )} {reportSent &&

Reported โ€” thank you

}
)}
{/* Delete confirmation */} {deleteConfirm && (

Delete this post?

)} {/* Body โ€” inline edit or display */} {editMode ? (