import React, { useState } from 'react';
import { Link, useParams } from 'react-router-dom';
import { usePermissions, useAuth, useContent } from '@/hooks';
import type { Post as ForumPost, Cell as ForumCell, VoteMessage } from '@opchan/core';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Textarea } from '@/components/ui/textarea';
import { Skeleton } from '@/components/ui/skeleton';
import { LinkRenderer } from '@/components/ui/link-renderer';
import { RelevanceIndicator } from '@/components/ui/relevance-indicator';
import { ShareButton } from '@/components/ui/ShareButton';
import {
ArrowLeft,
MessageSquare,
MessageCircle,
ArrowUp,
ArrowDown,
RefreshCw,
Shield,
UserX,
} from 'lucide-react';
import { formatDistanceToNow } from 'date-fns';
import { CypherImage } from './ui/CypherImage';
import { AuthorDisplay } from './ui/author-display';
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from '@/components/ui/tooltip';
const PostList = () => {
const { cellId } = useParams<{ cellId: string }>();
// ✅ Use reactive hooks for data and actions
const { createPost, vote, moderate, refresh, commentsByPost, cells, posts } = useContent();
const cell = cells.find((c: ForumCell) => c.id === cellId);
const isCreatingPost = false;
const isVoting = false;
const { canPost, canVote, canModerate } = usePermissions();
const { currentUser } = useAuth();
const [newPostTitle, setNewPostTitle] = useState('');
const [newPostContent, setNewPostContent] = useState('');
if (!cellId) {
return (
{[...Array(3)].map((_, i) => (
))}
);
}
if (!cell) {
return (
Cell Not Found
The cell you're looking for doesn't exist.
);
}
const handleCreatePost = async (e: React.FormEvent) => {
e.preventDefault();
if (!newPostContent.trim()) return;
// ✅ All validation handled in hook
const post = await createPost({ cellId, title: newPostTitle, content: newPostContent });
if (post) {
setNewPostTitle('');
setNewPostContent('');
}
};
// Handle keyboard shortcuts
const handleKeyDown = (e: React.KeyboardEvent) => {
// Enter inserts newline by default. Send on Ctrl+Enter or Shift+Enter.
const isSendCombo =
(e.ctrlKey || e.metaKey || e.shiftKey) && e.key === 'Enter';
if (isSendCombo) {
e.preventDefault();
if (!isCreatingPost && newPostContent.trim() && newPostTitle.trim()) {
handleCreatePost(e as React.FormEvent);
}
}
};
const handleVotePost = async (postId: string, isUpvote: boolean) => {
await vote({ targetId: postId, isUpvote });
};
const getPostVoteType = (postId: string) => {
if (!currentUser) return null;
const p = posts.find((p: ForumPost) => p.id === postId);
if (!p) return null;
const up = p.upvotes.some((v: VoteMessage) => v.author === currentUser.address);
const down = p.downvotes.some((v: VoteMessage) => v.author === currentUser.address);
return up ? 'upvote' : down ? 'downvote' : null;
};
// ✅ Posts already filtered by hook based on user permissions
const visiblePosts = posts
.filter((p: ForumPost) => p.cellId === cellId)
.sort((a: ForumPost, b: ForumPost) => {
const ar = a.relevanceScore ?? 0;
const br = b.relevanceScore ?? 0;
return br - ar || b.timestamp - a.timestamp;
});
const handleModerate = async (postId: string) => {
const reason =
window.prompt('Enter a reason for moderation (optional):') || undefined;
if (!cell) return;
// ✅ All validation handled in hook
await moderate.post(cell.id, postId, reason);
};
const handleUnmoderate = async (postId: string) => {
const reason =
window.prompt('Optional note for unmoderation?') || undefined;
if (!cell) return;
await moderate.unpost(cell.id, postId, reason);
};
const handleModerateUser = async (userAddress: string) => {
const reason =
window.prompt('Reason for moderating this user? (optional)') || undefined;
if (!cell) return;
// ✅ All validation handled in hook
await moderate.user(cell.id, userAddress, reason);
};
return (
{cell.name}
{cell.description}
{canPost && (
)}
{!canPost && !currentUser && (
Connect your wallet to post
)}
{visiblePosts.length === 0 ? (
No Threads Yet
{canPost
? 'Be the first to post in this cell!'
: 'Connect your wallet to start a thread.'}
) : (
visiblePosts.map((post: ForumPost) => (
{post.upvotes.length - post.downvotes.length}
{post.title}
{formatDistanceToNow(post.timestamp, {
addSuffix: true,
})}
by
•
{commentsByPost[post.id]?.length || 0} comments
{typeof post.relevanceScore === 'number' && (
<>
•
>
)}
{canModerate(cell.id) && !post.moderated && (
Moderate post
)}
{canModerate(cell.id) && post.author !== cell.author && (
Moderate user
)}
{canModerate(cell.id) && post.moderated && (
Unmoderate post
)}
))
)}
);
};
export default PostList;