reactions

This commit is contained in:
john 2025-05-28 22:05:51 +02:00
parent 48e7094c5e
commit c21e193fbf
9 changed files with 97 additions and 119 deletions

View file

@ -1,20 +1,23 @@
import { useCallback, useState } from 'react'
import { useCallback, useRef, useState } from 'react'
import FeedView from '../components/FeedView.tsx'
import { PostsService } from '../posts/postsService.ts'
import { useUser } from '../../user/user.ts'
import { MediaService } from '../../media/mediaService.ts'
import NewPostWidget from '../../../components/NewPostWidget.tsx'
import { useFeedViewModel } from '../components/FeedView.ts'
import SingleColumnLayout from '../../../layouts/SingleColumnLayout.tsx'
import NavBar from '../../../components/NavBar.tsx'
import AuthNavButtons from '../../auth/components/AuthNavButtons.tsx'
import { useSaveSignupCodeToLocalStorage } from '../../../hooks/useSaveSignupCodeToLocalStorage.ts'
import { Post } from '../posts/posts.ts'
import { produce, WritableDraft } from 'immer'
interface HomePageProps {
postsService: PostsService
mediaService: MediaService
}
const PageSize = 20
export default function HomePage({ postsService, mediaService }: HomePageProps) {
const user = useUser()
useSaveSignupCodeToLocalStorage()
@ -27,7 +30,31 @@ export default function HomePage({ postsService, mediaService }: HomePageProps)
[postsService],
)
const { pages, setPages, loadNextPage } = useFeedViewModel(fetchPosts)
const [posts, setPosts] = useState<Post[]>([])
const [hasMore, setHasMore] = useState(true)
const [error, setError] = useState<string | null>(null)
const cursor = useRef<string | null>(null)
const loading = useRef(false)
const loadNextPage = useCallback(async () => {
if (loading.current || !hasMore || error) return
loading.current = true
try {
const delay = new Promise((resolve) => setTimeout(resolve, 500))
const pagePromise = fetchPosts(cursor.current, PageSize)
const [page] = await Promise.all([pagePromise, delay])
setHasMore(page.length >= PageSize)
cursor.current = page.at(-1)?.postId ?? null
setPosts((prev) => [...prev, ...page])
} catch (e: unknown) {
const err = e as Error
setError(err.message)
} finally {
loading.current = false
}
}, [fetchPosts, hasMore, error])
const onCreatePost = useCallback(
async (
@ -51,24 +78,52 @@ export default function HomePage({ postsService, mediaService }: HomePageProps)
}),
)
const post = await postsService.createNew(user.id, content, media, isPublic)
setPages((pages) => [[post], ...pages])
setPosts((pages) => [post, ...pages])
} catch (error) {
console.error('Failed to create post:', error)
} finally {
setIsSubmitting(false)
}
},
[mediaService, postsService, setPages, user],
[mediaService, postsService, setPosts, user],
)
const isLoggedIn = user != null
const addReaction = async (postId: string, emoji: string) => {
const onAddReaction = async (postId: string, emoji: string) => {
await postsService.addReaction(postId, emoji)
setPosts((prev) =>
produce(prev, (draft: WritableDraft<Post[]>) => {
const post = draft.find((p) => p.postId === postId)
if (!post) return
const theReaction = post.reactions.find((r) => r.emoji === emoji)
if (theReaction) {
theReaction.count++
theReaction.didReact = true
} else {
post.reactions.push({ emoji, count: 1, didReact: true })
}
}),
)
}
const clearReaction = async (postId: string, emoji: string) => {
const onClearReaction = async (postId: string, emoji: string) => {
await postsService.removeReaction(postId, emoji)
setPosts((prev) =>
produce(prev, (draft: WritableDraft<Post[]>) => {
const post = draft.find((p) => p.postId === postId)
if (!post) return
const theReaction = post.reactions.find((r) => r.emoji === emoji)
if (theReaction) {
theReaction.count = Math.max(theReaction.count - 1, 0)
theReaction.didReact = false
}
}),
)
}
return (
@ -82,10 +137,10 @@ export default function HomePage({ postsService, mediaService }: HomePageProps)
<main className={`w-full max-w-3xl mx-auto`}>
{isLoggedIn && <NewPostWidget onSubmit={onCreatePost} isSubmitting={isSubmitting} />}
<FeedView
pages={pages}
posts={posts}
onLoadMore={loadNextPage}
addReaction={addReaction}
clearReaction={clearReaction}
addReaction={onAddReaction}
clearReaction={onClearReaction}
/>
</main>
</SingleColumnLayout>