import React from 'react'; import { Link } from 'react-router-dom'; import { ArrowUp, ArrowDown, MessageSquare, Clipboard } from 'lucide-react'; import { formatDistanceToNow } from 'date-fns'; import { Post } from 'opchan-core/types/forum'; import { useForumActions, usePermissions, useUserVotes, useForumData, usePostBookmark, } from '@/hooks'; 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 { usePending, usePendingVote } from '@/hooks/usePending'; import { ShareButton } from '@/components/ui/ShareButton'; interface PostCardProps { post: Post; commentCount?: number; } const PostCard: React.FC = ({ post, commentCount = 0 }) => { const { cellsWithStats } = useForumData(); const { votePost, isVoting } = useForumActions(); const { canVote } = usePermissions(); const userVotes = useUserVotes(); const { isBookmarked, loading: bookmarkLoading, toggleBookmark, } = usePostBookmark(post, post.cellId); // ✅ Get pre-computed cell data const cell = cellsWithStats.find(c => c.id === post.cellId); const cellName = cell?.name || 'unknown'; // ✅ Use pre-computed vote data (assuming post comes from useForumData) const score = 'voteScore' in post ? (post.voteScore as number) : post.upvotes.length - post.downvotes.length; const { isPending } = usePending(post.id); const votePending = usePendingVote(post.id); // ✅ Get user vote status from hook const userVoteType = userVotes.getPostVoteType(post.id); const userUpvoted = userVoteType === 'upvote'; const userDownvoted = userVoteType === 'downvote'; // ✅ Content truncation (simple presentation logic is OK) const contentPreview = post.content.length > 200 ? post.content.substring(0, 200) + '...' : post.content; const handleVote = async (e: React.MouseEvent, isUpvote: boolean) => { e.preventDefault(); // ✅ All validation and permission checking handled in hook await votePost(post.id, isUpvote); }; const handleBookmark = async (e?: React.MouseEvent) => { if (e) { e.preventDefault(); e.stopPropagation(); } await toggleBookmark(); }; return (
{/* Voting column */}
0 ? 'text-cyber-accent' : score < 0 ? 'text-blue-400' : 'text-cyber-neutral' }`} > {score} {votePending.isPending && ( syncing… )}
{/* Content column */}
{/* Post metadata */}
r/{cellName} Posted by u/ {formatDistanceToNow(new Date(post.timestamp), { addSuffix: true, })} {post.relevanceScore !== undefined && ( <> )}
{/* Post title and content - clickable to navigate to post */}

{post.title}

{/* Post content preview */}

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