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,36 +1,5 @@
import { useCallback, useRef, useState } from 'react'
import { Post } from '../posts/posts.ts'
import { produce, WritableDraft } from 'immer'
const PageSize = 20
export function useFeedViewModel(
loadMore: (cursor: string | null, amount: number) => Promise<Post[]>,
) {
const [pages, setPages] = 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 = loadMore(cursor.current, PageSize)
const [page] = await Promise.all([pagePromise, delay])
setHasMore(page.length >= PageSize)
cursor.current = page.at(-1)?.postId ?? null
setPages((prev) => [...prev, page])
} catch (e: unknown) {
const err = e as Error
setError(err.message)
} finally {
loading.current = false
}
}, [loadMore, hasMore, error])
return { pages, setPages, loadNextPage, error } as const
}

View file

@ -4,15 +4,14 @@ import { Post } from '../posts/posts.ts'
import PostItem from './PostItem.tsx'
interface FeedViewProps {
pages: Post[][]
posts: Post[]
onLoadMore: () => Promise<void>
addReaction: (postId: string, emoji: string) => void
clearReaction: (postId: string, emoji: string) => void
}
export default function FeedView({ pages, onLoadMore, addReaction, clearReaction }: FeedViewProps) {
export default function FeedView({ posts, onLoadMore, addReaction, clearReaction }: FeedViewProps) {
const sentinelRef = useRef<HTMLDivElement | null>(null)
const posts = pages.flat()
useIntersectionLoad(onLoadMore, sentinelRef)

View file

@ -1,5 +1,4 @@
import { Post, PostMedia } from '../posts/posts.ts'
import { Link } from 'react-router-dom'
import { useEffect, useState } from 'react'
interface PostItemProps {
@ -31,10 +30,7 @@ export default function PostItem({ post, addReaction, clearReaction }: PostItemP
return (
<article className={`w-full p-4 ${opacity} transition-opacity duration-500`} key={post.postId}>
<div className="text-sm text-gray-500 mb-3">
<Link to={`/u/${post.authorName}`} className="text-gray-400 hover:underline mr-2">
@{post.authorName}
</Link>
{formattedDate}
<span className="text-gray-400 mr-2">@{post.authorName}</span> {formattedDate}
</div>
<div className="text-gray-800 mb-4 whitespace-pre-wrap">{post.content}</div>
@ -77,10 +73,11 @@ function PostReactions({ post, addReaction, clearReaction }: PostReactionsProps)
return (
<PostReactionButton
key={emoji}
emoji={emoji}
didReact={didReact}
count={count}
onClick={() => onClick()}
onClick={onClick}
/>
)
})}