import React from 'react'; import { Link } from 'react-router-dom'; import { ArrowUp, ArrowDown, MessageSquare } from 'lucide-react'; import { formatDistanceToNow } from 'date-fns'; import type { Post, PostMessage } from '@opchan/core'; import { RelevanceIndicator } from '@/components/ui/relevance-indicator'; import { AuthorDisplay } from '@/components/ui/author-display'; import { BookmarkButton } from '@/components/ui/bookmark-button'; import { LinkRenderer } from '@/components/ui/link-renderer'; import { useContent, usePermissions } from '@/hooks'; import { ShareButton } from '@/components/ui/ShareButton'; interface PostCardProps { post: Post | PostMessage; commentCount?: number; } const PostCard: React.FC = ({ post, commentCount = 0 }) => { const content = useContent(); const permissions = usePermissions(); // Get cell data from content const cell = content.cells.find((c) => c.id === post.cellId); const cellName = cell?.name || 'unknown'; // Use pre-computed vote data or safely compute from arrays when available const computedVoteScore = 'voteScore' in post && typeof (post as Post).voteScore === 'number' ? (post as Post).voteScore : undefined; const upvoteCount = 'upvotes' in post && Array.isArray((post as Post).upvotes) ? (post as Post).upvotes.length : 0; const downvoteCount = 'downvotes' in post && Array.isArray((post as Post).downvotes) ? (post as Post).downvotes.length : 0; const score = computedVoteScore ?? upvoteCount - downvoteCount; // Use library pending API const isPending = content.pending.isPending(post.id); // Get user vote status from post data const userUpvoted = (post as unknown as { userUpvoted?: boolean }).userUpvoted || false; const userDownvoted = (post as unknown as { userDownvoted?: boolean }).userDownvoted || false; // Check if bookmarked const isBookmarked = content.bookmarks.some((b) => b.targetId === post.id && b.type === 'post'); const [bookmarkLoading, setBookmarkLoading] = React.useState(false); // Remove duplicate vote status logic // ✅ Content truncation (simple presentation logic is OK) const contentText = typeof post.content === 'string' ? post.content : String(post.content ?? ''); const contentPreview = contentText.length > 200 ? contentText.substring(0, 200) + '...' : contentText; const handleVote = async (e: React.MouseEvent, isUpvote: boolean) => { e.preventDefault(); await content.vote({ targetId: post.id, isUpvote }); }; const handleBookmark = async (e?: React.MouseEvent) => { if (e) { e.preventDefault(); e.stopPropagation(); } setBookmarkLoading(true); try { await content.togglePostBookmark(post, post.cellId); } finally { setBookmarkLoading(false); } }; return (
{/* Voting column */}
0 ? 'text-cyber-accent' : score < 0 ? 'text-blue-400' : 'text-cyber-neutral' }`} > {score} {isPending && ( syncing… )}
{/* Content column */}
{/* Post metadata */}
r/{cellName} Posted by u/ {formatDistanceToNow(new Date(post.timestamp), { addSuffix: true, })} {('relevanceScore' in post) && typeof (post as Post).relevanceScore === 'number' && ( <> )}
{/* Post title and content - clickable to navigate to post */}

{post.title}

{/* Post content preview */}

{/* Post actions */}
{commentCount} comments
{isPending && ( syncing… )}
); }; export default PostCard;