feat: collapsible post cards in circle feed

Posts start collapsed showing author, timestamp, a thumbnail (if image)
and truncated text preview with ▼ chevron. Tap the author row to expand
the full post (body, full image, reactions, comments). Tap again to
collapse. Lets users scan many posts quickly and expand only what
interests them.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Manohar Gupta 2026-05-24 08:01:56 +05:30
parent 581fdb074d
commit 560761968b

View file

@ -29,6 +29,7 @@ function PostCard({
onDeleted: (id: string) => void; onDeleted: (id: string) => void;
onReact: (postId: string, emoji: string) => void; onReact: (postId: string, emoji: string) => void;
}) { }) {
const [collapsed, setCollapsed] = useState(true);
const [showComments, setShowComments] = useState(false); const [showComments, setShowComments] = useState(false);
const [comments, setComments] = useState<CircleComment[]>([]); const [comments, setComments] = useState<CircleComment[]>([]);
const [commentText, setCommentText] = useState(""); const [commentText, setCommentText] = useState("");
@ -112,15 +113,31 @@ function PostCard({
)} )}
<div className="bg-white dark:bg-gray-800 rounded-2xl shadow-sm"> <div className="bg-white dark:bg-gray-800 rounded-2xl shadow-sm">
{/* Author row */} {/* Author row — tap to collapse/expand */}
<div className="flex items-center justify-between px-4 pt-3 pb-2"> <div className="flex items-center justify-between px-4 pt-3 pb-2">
<div className="flex items-center gap-2"> <button
<div className="w-8 h-8 bg-rose-100 dark:bg-rose-900/40 rounded-full flex items-center justify-center text-sm">👨👩👧</div> onClick={() => setCollapsed(v => !v)}
<div> className="flex items-center gap-2 flex-1 min-w-0 text-left"
>
<div className="w-8 h-8 bg-rose-100 dark:bg-rose-900/40 rounded-full flex items-center justify-center text-sm flex-shrink-0">👨👩👧</div>
<div className="flex-1 min-w-0">
<p className="text-sm font-medium">{post.authorFamilyName}</p> <p className="text-sm font-medium">{post.authorFamilyName}</p>
<p className="text-xs text-gray-400">{timeAgo(post.createdAt)}{post.sourceKind ? ` · shared a ${post.sourceKind}` : ""}</p> <p className="text-xs text-gray-400">{timeAgo(post.createdAt)}{post.sourceKind ? ` · shared a ${post.sourceKind}` : ""}</p>
</div> </div>
{/* Collapsed preview */}
{collapsed && (
<div className="flex items-center gap-2 flex-shrink-0 max-w-[45%]">
{post.imageUrl && (
<img src={post.imageUrl} alt="" className="w-10 h-10 rounded-lg object-cover flex-shrink-0" />
)}
{post.body && (
<span className="text-xs text-gray-400 truncate">{post.body}</span>
)}
<span className="text-gray-300 text-xs flex-shrink-0"></span>
</div> </div>
)}
{!collapsed && <span className="text-gray-300 text-xs flex-shrink-0 ml-2"></span>}
</button>
{/* ⋯ menu — rendered outside overflow:hidden so it doesn't get clipped */} {/* ⋯ menu — rendered outside overflow:hidden so it doesn't get clipped */}
<div className="relative"> <div className="relative">
<button onClick={() => setShowMenu(v => !v)} className="p-2 text-gray-400 text-lg"></button> <button onClick={() => setShowMenu(v => !v)} className="p-2 text-gray-400 text-lg"></button>
@ -153,6 +170,12 @@ function PostCard({
</div> </div>
</div> </div>
{/* Collapsed divider */}
{collapsed && <div className="border-t border-gray-50 dark:border-gray-700 rounded-b-2xl" />}
{/* Expanded content */}
{!collapsed && <>
{/* Delete confirmation */} {/* Delete confirmation */}
{deleteConfirm && ( {deleteConfirm && (
<div className="mx-4 mb-3 p-3 bg-red-50 dark:bg-red-900/20 rounded-xl space-y-2"> <div className="mx-4 mb-3 p-3 bg-red-50 dark:bg-red-900/20 rounded-xl space-y-2">
@ -257,6 +280,7 @@ function PostCard({
</div> </div>
</div> </div>
)} )}
</>} {/* end !collapsed */}
</div> </div>
</> </>
); );