fix: rating not shown after refresh, comment reactions not shown until clicked

- The interactive RatingStars widget was always initialized with
  initialScore={0}, ignoring the current user's existing rating.
  Fetch it via a third parallel query and pass it through — the
  rating was persisted correctly all along, just never displayed
  back.
- CommentReactions never fetched its counts on mount, relying solely
  on initialCounts/initialUserReactions props that comments-section
  always passed as empty — so every comment showed zero reactions
  until you clicked one yourself (which returns fresh counts from the
  POST response). Fetch on mount via the existing GET endpoint.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Arnaud
2026-07-08 22:33:36 +02:00
parent 1677e40668
commit e67145493e
2 changed files with 19 additions and 3 deletions
+4 -2
View File
@@ -55,7 +55,7 @@ export default async function RecipePage({ params }: Params) {
const VISIBILITY_LABEL = m.recipe.visibility;
const DIETARY_LABELS = m.recipe.dietary;
const [recipe, ratingData, favoriteData] = await Promise.all([
const [recipe, ratingData, favoriteData, myRating] = await Promise.all([
db.query.recipes.findFirst({
where: and(
eq(recipes.id, id),
@@ -70,6 +70,7 @@ export default async function RecipePage({ params }: Params) {
}),
db.select({ avgScore: avg(ratings.score), total: count() }).from(ratings).where(eq(ratings.recipeId, id)),
db.query.favorites.findFirst({ where: and(eq(favorites.userId, session.user.id), eq(favorites.recipeId, id)) }),
db.query.ratings.findFirst({ where: and(eq(ratings.recipeId, id), eq(ratings.userId, session.user.id)) }),
]);
if (!recipe) notFound();
@@ -79,6 +80,7 @@ export default async function RecipePage({ params }: Params) {
const avgScore = ratingData[0]?.avgScore ? parseFloat(ratingData[0].avgScore) : null;
const ratingCount = ratingData[0]?.total ?? 0;
const isFavorited = !!favoriteData;
const myScore = myRating?.score ?? 0;
const cover = recipe.photos.find((p) => p.isCover) ?? recipe.photos[0];
const totalMins = (recipe.prepMins ?? 0) + (recipe.cookMins ?? 0);
@@ -384,7 +386,7 @@ export default async function RecipePage({ params }: Params) {
<>
<Separator />
<div className="space-y-4">
<RatingStars recipeId={id} initialScore={0} />
<RatingStars recipeId={id} initialScore={myScore} />
</div>
<Separator />
<CommentsSection recipeId={id} currentUserId={session.user.id} />
@@ -1,6 +1,6 @@
"use client";
import { useState } from "react";
import { useState, useEffect } from "react";
import { toast } from "sonner";
const REACTIONS: Record<string, string> = {
@@ -24,6 +24,20 @@ export function CommentReactions({ recipeId, commentId, initialCounts = {}, init
const [userReactions, setUserReactions] = useState<string[]>(initialUserReactions);
const [pending, setPending] = useState<string | null>(null);
useEffect(() => {
let cancelled = false;
fetch(`/api/v1/recipes/${recipeId}/comments/${commentId}/reactions`)
.then((res) => (res.ok ? res.json() : null))
.then((data: { counts: Record<string, number>; userReactions: string[] } | null) => {
if (!data || cancelled) return;
setCounts(data.counts);
setUserReactions(data.userReactions);
})
.catch(() => {});
return () => { cancelled = true; };
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [recipeId, commentId]);
async function toggle(type: string) {
if (pending) return;
setPending(type);