diff --git a/src/App.tsx b/src/App.tsx index 3694fdf..2e407da 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,5 +1,6 @@ import { BrowserRouter, Route, Routes } from 'react-router-dom' import HomePage from './app/feed/pages/HomePage.tsx' +import PostPage from './app/feed/pages/PostPage.tsx' import SignupPage from './app/auth/pages/SignupPage.tsx' import LoginPage from './app/auth/pages/LoginPage.tsx' import LogoutPage from './app/auth/pages/LogoutPage.tsx' @@ -22,6 +23,10 @@ export default function App() { path={'/'} element={} /> + } + /> } /> } /> } /> diff --git a/src/app/feed/components/PostItem.tsx b/src/app/feed/components/PostItem.tsx index d97c109..05fcbb3 100644 --- a/src/app/feed/components/PostItem.tsx +++ b/src/app/feed/components/PostItem.tsx @@ -1,13 +1,15 @@ import { Post, PostMedia } from '../posts/posts.ts' import { useEffect, useState } from 'react' +import { Link } from 'react-router-dom' interface PostItemProps { post: Post addReaction: (emoji: string) => void clearReaction: (emoji: string) => void + hideViewButton?: boolean } -export default function PostItem({ post, addReaction, clearReaction }: PostItemProps) { +export default function PostItem({ post, addReaction, clearReaction, hideViewButton = false }: PostItemProps) { const formattedDate = post.createdAt.toLocaleString('en-US', { year: 'numeric', month: 'short', @@ -31,6 +33,14 @@ export default function PostItem({ post, addReaction, clearReaction }: PostItemP
@{post.authorName}• {formattedDate} + {!hideViewButton && ( + <> + {' • '} + + View + + + )}
{post.content}
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 && ( +
+ +
+ )} +
+
+ ) +}