'use client'; import { useState, useEffect } from 'react'; import Link from 'next/link'; import { useParams, useRouter } from 'next/navigation'; import { ArrowLeftIcon } from '@/components/Icons'; import { PostCard } from '@/components/PostCard'; import { Compose } from '@/components/Compose'; import { useAuth } from '@/lib/contexts/AuthContext'; import { Post } from '@/lib/types'; export default function PostDetailPage() { const params = useParams(); const router = useRouter(); const { user } = useAuth(); const handle = params.handle as string; const id = params.id as string; const [post, setPost] = useState(null); const [replies, setReplies] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const fetchPostDetail = async () => { try { const res = await fetch(`/api/posts/${id}`); if (!res.ok) { throw new Error('Post not found'); } const data = await res.json(); setPost(data.post); setReplies(data.replies || []); } catch (err) { setError(err instanceof Error ? err.message : 'Failed to load post'); } finally { setLoading(false); } }; useEffect(() => { fetchPostDetail(); }, [id]); const handlePost = async (content: string, mediaIds: string[], linkPreview?: any, replyToId?: string, isNsfw?: boolean) => { // Check if we're replying to a swarm post let swarmReplyTo: { postId: string; nodeDomain: string; content?: string; author?: any } | undefined; let localReplyToId: string | undefined = replyToId; if (post?.isSwarm && post.nodeDomain && post.originalPostId) { // This is a reply to a swarm post - send to the origin node swarmReplyTo = { postId: post.originalPostId, nodeDomain: post.nodeDomain, content: post.content, author: post.author, }; localReplyToId = undefined; // Can't use UUID foreign key for swarm posts } const res = await fetch('/api/posts', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ content, mediaIds, linkPreview, replyToId: localReplyToId, swarmReplyTo, isNsfw }), }); if (res.ok) { const data = await res.json(); // Add the new reply to the top of the list or re-fetch setReplies([{ ...data.post, author: user }, ...replies]); if (post) { setPost({ ...post, repliesCount: post.repliesCount + 1 }); } } }; const handleLike = async (postId: string, currentLiked: boolean) => { const method = currentLiked ? 'DELETE' : 'POST'; await fetch(`/api/posts/${postId}/like`, { method }); }; const handleRepost = async (postId: string, currentReposted: boolean) => { const method = currentReposted ? 'DELETE' : 'POST'; await fetch(`/api/posts/${postId}/repost`, { method }); }; const handleDelete = (postId: string) => { if (postId === id) { router.push(`/u/${handle}`); } else { setReplies(prev => prev.filter(r => r.id !== postId)); if (post) { setPost({ ...post, repliesCount: Math.max(0, post.repliesCount - 1) }); } } }; if (loading) { return (
Loading...
); } if (error || !post) { return (

{error || 'Post not found'}

); } return ( <>

Post

{ const composer = document.querySelector('.compose-input') as HTMLTextAreaElement; composer?.focus(); }} /> {user && (
)}
{replies.map((reply) => ( { // In detail view, commenting on a reply should probably just focus the main composer // but we could also implement nested replies later. // For now, let's keep it simple. const composer = document.querySelector('.compose-input') as HTMLTextAreaElement; composer?.focus(); }} /> ))}
); }