@{post.authorName}• {formattedDate} + {!hideViewButton && ( + <> + {' • '} + + View + + > + )} {post.content} @@ -99,7 +109,7 @@ function PostReactionButton({ emoji, didReact, onClick, count }: PostReactionBut diff --git a/src/app/feed/pages/PostPage.tsx b/src/app/feed/pages/PostPage.tsx new file mode 100644 index 0000000..6e51c75 --- /dev/null +++ b/src/app/feed/pages/PostPage.tsx @@ -0,0 +1,127 @@ +import { useEffect, useState } from 'react' +import { useNavigate, useParams } from 'react-router-dom' +import { Post } from '../posts/posts.ts' +import { PostsService } from '../posts/postsService.ts' +import SingleColumnLayout from '../../../layouts/SingleColumnLayout.tsx' +import NavBar from '../../../components/NavBar.tsx' +import AuthNavButtons from '../../auth/components/AuthNavButtons.tsx' +import PostItem from '../components/PostItem.tsx' + +interface PostPageProps { + postsService: PostsService +} + +export default function PostPage({ postsService }: PostPageProps) { + const { postId } = useParams<{ postId: string }>() + const navigate = useNavigate() + const [post, setPost] = useState(null) + const [loading, setLoading] = useState(true) + const [error, setError] = useState(null) + + useEffect(() => { + const fetchPost = async () => { + if (!postId) { + setError('Post ID is required') + setLoading(false) + return + } + + try { + // Load posts and find the one with matching ID + const { posts } = await postsService.loadPublicFeed(null, 100) + const foundPost = posts.find(p => p.postId === postId) + + if (foundPost) { + setPost(foundPost) + } else { + setError('Post not found') + } + } catch (e: unknown) { + setError((e as Error).message) + } finally { + setLoading(false) + } + } + + fetchPost() + }, [postId, postsService]) + + const onAddReaction = async (emoji: string) => { + if (!post) return + + await postsService.addReaction(post.postId, emoji) + + setPost(prevPost => { + if (!prevPost) return null + + const updatedReactions = [...prevPost.reactions] + const theReaction = updatedReactions.find(r => r.emoji === emoji) + + if (theReaction) { + theReaction.count++ + theReaction.didReact = true + } else { + updatedReactions.push({ emoji, count: 1, didReact: true }) + } + + return { + ...prevPost, + reactions: updatedReactions + } + }) + } + + const onClearReaction = async (emoji: string) => { + if (!post) return + + await postsService.removeReaction(post.postId, emoji) + + setPost(prevPost => { + if (!prevPost) return null + + const updatedReactions = [...prevPost.reactions] + const theReaction = updatedReactions.find(r => r.emoji === emoji) + + if (theReaction) { + theReaction.count = Math.max(theReaction.count - 1, 0) + theReaction.didReact = false + } + + return { + ...prevPost, + reactions: updatedReactions + } + }) + } + + return ( + + + + } + > + + {loading && Loading...} + + {error && ( + + Error: {error} + + )} + + {post && ( + + + + )} + + + ) +}