reactions
This commit is contained in:
parent
48e7094c5e
commit
c21e193fbf
9 changed files with 97 additions and 119 deletions
|
@ -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>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue