From 7c6b463acbc52281f66792e6921525d3d960c03d Mon Sep 17 00:00:00 2001 From: chahinebrini Date: Sat, 16 May 2026 00:48:14 +0200 Subject: [PATCH] Revert "fix(native/community): derive heart state from props + store-optimistic delta" This reverts commit d28d1f145d9bdaa45fb788aaef69c645719f56bb. --- apps/rebreak-native/components/PostCard.tsx | 52 +++++++-------------- 1 file changed, 16 insertions(+), 36 deletions(-) diff --git a/apps/rebreak-native/components/PostCard.tsx b/apps/rebreak-native/components/PostCard.tsx index 9132139..8b6e6ce 100644 --- a/apps/rebreak-native/components/PostCard.tsx +++ b/apps/rebreak-native/components/PostCard.tsx @@ -26,20 +26,8 @@ function PostCardImpl({ post, onCommentPress }: Props) { const revertOptimisticLike = useCommunityStore((s) => s.revertOptimisticLike); const clearOptimisticLike = useCommunityStore((s) => s.clearOptimisticLike); - // Display state is DERIVED from props + the store-level optimistic delta. - // No useState mirror — that's exactly what created the foreign-likes-don't- - // update bug (seed-once from props) and the post-action flicker (useEffect - // re-sync racing with the React-Query cache patch). Realtime patches the - // cache → prop changes → we re-render automatically. Optimistic UI lives - // for the ms between tap and API-response in the community-store's - // `optimisticLikes` map (delta + userLike). - const optimistic = useCommunityStore((s) => s.optimisticLikes[post.id]); - const displayedLike: 'like' | null = optimistic - ? optimistic.userLike - : post.userLike === 'like' - ? 'like' - : null; - const displayedCount = (post.likesCount ?? 0) + (optimistic?.delta ?? 0); + const [localLike, setLocalLike] = useState<'like' | null>(post.userLike === 'like' ? 'like' : null); + const [localCount, setLocalCount] = useState(post.likesCount); const [isLiking, setIsLiking] = useState(false); // Heart-Pop Animation — Insta-Style: quick scale-up + spring-bounce back @@ -148,7 +136,9 @@ function PostCardImpl({ post, onCommentPress }: Props) { const handleLike = useCallback(async () => { if (isLiking) return; triggerHeartPop(); - applyOptimisticLike(post.id, displayedLike, displayedCount); + const { newLike, newCount } = applyOptimisticLike(post.id, localLike, localCount); + setLocalLike(newLike); + setLocalCount(newCount); setIsLiking(true); try { const res = await apiFetch<{ @@ -159,29 +149,19 @@ function PostCardImpl({ post, onCommentPress }: Props) { method: 'POST', body: { postId: post.id, type: 'like' }, }); - // Patch the React-Query cache synchronously with server truth so the - // prop reflects it BEFORE we clear the optimistic delta. Without this, - // there'd be a millisecond window between clearOptimistic (delta → 0) - // and the Realtime broadcast patching the cache — during which the - // displayed count would briefly snap back to the old prop value. - queryClient.setQueriesData( - { queryKey: ['community-posts'] }, - (data) => - Array.isArray(data) - ? data.map((p) => - p.id === post.id - ? { ...p, likesCount: res.likesCount, dislikesCount: res.dislikesCount, userLike: res.userLike } - : p, - ) - : data, - ); + setLocalCount(res.likesCount); + setLocalLike(res.userLike === 'like' ? 'like' : null); clearOptimisticLike(post.id); + // KEIN queryClient.invalidateQueries — würde die komplette Liste neu laden, + // PostCard remounted, Heart-Pop-Animation abgebrochen. Local-State reicht. } catch { revertOptimisticLike(post.id); + setLocalLike(post.userLike === 'like' ? 'like' : null); + setLocalCount(post.likesCount); } finally { setIsLiking(false); } - }, [isLiking, displayedLike, displayedCount, post.id, applyOptimisticLike, clearOptimisticLike, revertOptimisticLike, queryClient, triggerHeartPop]); + }, [isLiking, localLike, localCount, post.id, post.userLike, post.likesCount, applyOptimisticLike, clearOptimisticLike, revertOptimisticLike, queryClient, triggerHeartPop]); return ( @@ -298,13 +278,13 @@ function PostCardImpl({ post, onCommentPress }: Props) { > - {displayedCount > 0 && ( - {displayedCount} + {localCount > 0 && ( + {localCount} )}