Skip to content

Commit ac2ed92

Browse files
authored
Merge pull request #407 from ever-works/fix/Rating-in-item-detail
refactor: improve rating display sync and add SSR safety for custom events
1 parent a478d4d commit ac2ed92

File tree

2 files changed

+35
-15
lines changed

2 files changed

+35
-15
lines changed

hooks/use-comments.ts

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,13 @@ interface UpdateCommentData {
1616
rating?: number;
1717
}
1818

19+
const COMMENT_MUTATION_EVENT = "comment:mutated";
20+
21+
const dispatchCommentEvent = (comment: CommentWithUser) => {
22+
if (typeof window === "undefined") return;
23+
window.dispatchEvent(new CustomEvent(COMMENT_MUTATION_EVENT, { detail: comment }));
24+
};
25+
1926
export function useComments(itemId: string) {
2027
const queryClient = useQueryClient();
2128
const loginModal = useLoginModal();
@@ -53,7 +60,7 @@ export function useComments(itemId: string) {
5360
// response.data contains the API response: { success: true, comment: CommentWithUser }
5461
return response.data.comment;
5562
},
56-
onSuccess: (newComment) => {
63+
onSuccess: async (newComment) => {
5764
if (newComment) {
5865
// Optimistically update cache with server-returned comment data
5966
queryClient.setQueryData<CommentWithUser[]>(["comments", itemId], (old = []) => {
@@ -65,9 +72,9 @@ export function useComments(itemId: string) {
6572
// Add new comment at the beginning
6673
return [newComment, ...old];
6774
});
68-
// Update rating caches to reflect new rating immediately
69-
queryClient.setQueryData(["commentRating", itemId], newComment.rating);
70-
queryClient.invalidateQueries({ queryKey: ["itemRating", itemId], exact: true });
75+
dispatchCommentEvent(newComment);
76+
// Force refetch rating query to show updated data immediately
77+
await queryClient.refetchQueries({ queryKey: ["item-rating", itemId] });
7178
}
7279
},
7380
});
@@ -108,16 +115,17 @@ export function useComments(itemId: string) {
108115

109116
return response.data;
110117
},
111-
onSuccess: (updatedComment) => {
118+
onSuccess: async (updatedComment) => {
112119
if (updatedComment) {
113120
// Optimistically update cache with server-returned comment data
114121
queryClient.setQueryData<CommentWithUser[]>(["comments", itemId], (old = []) => {
115122
return old.map(comment =>
116123
comment.id === updatedComment.id ? updatedComment : comment
117124
);
118125
});
119-
// Invalidate rating caches to reflect updated rating
120-
queryClient.invalidateQueries({ queryKey: ["itemRating", itemId], exact: true });
126+
dispatchCommentEvent(updatedComment);
127+
// Force refetch rating to reflect updated rating immediately
128+
await queryClient.refetchQueries({ queryKey: ["item-rating", itemId] });
121129
}
122130
},
123131
});
@@ -137,9 +145,9 @@ export function useComments(itemId: string) {
137145

138146
return response.data;
139147
},
140-
onSuccess: () => {
148+
onSuccess: async () => {
141149
queryClient.invalidateQueries({ queryKey: ["comments", itemId] });
142-
queryClient.invalidateQueries({ queryKey: ["itemRating", itemId] });
150+
await queryClient.refetchQueries({ queryKey: ["item-rating", itemId] });
143151
},
144152
});
145153

@@ -158,9 +166,9 @@ export function useComments(itemId: string) {
158166

159167
return response.data;
160168
},
161-
onSuccess: () => {
169+
onSuccess: async () => {
162170
queryClient.invalidateQueries({ queryKey: ["comments", itemId] });
163-
queryClient.invalidateQueries({ queryKey: ["itemRating", itemId] });
171+
await queryClient.refetchQueries({ queryKey: ["item-rating", itemId] });
164172
},
165173
});
166174

hooks/use-item-rating.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,29 +8,41 @@ interface RatingData {
88
totalRatings: number;
99
}
1010

11-
export function useItemRating(itemId: string) {
11+
export function useItemRating(itemId: string, enabled: boolean = true) {
1212
const { features } = useFeatureFlagsWithSimulation();
1313

1414
const {
1515
data: rating = { averageRating: 0, totalRatings: 0 },
1616
isLoading,
1717
error,
18+
refetch,
1819
} = useQuery<RatingData>({
1920
queryKey: ["item-rating", itemId],
20-
enabled: features.ratings, // Only fetch when ratings feature is enabled
21-
queryFn: async () => {
21+
enabled: features.ratings && enabled, // Only fetch when ratings feature is enabled
22+
queryFn: async ({ signal }) => {
2223
const encodedItemId = encodeURIComponent(itemId);
23-
const response = await serverClient.get<RatingData>(`/api/items/${encodedItemId}/comments/rating`);
24+
// Avoid HTTP cache; rely on React Query invalidation for freshness
25+
const response = await serverClient.get<RatingData>(`/api/items/${encodedItemId}/comments/rating`, {
26+
cache: "no-store",
27+
headers: { "Cache-Control": "no-store" },
28+
signal,
29+
});
2430
if (!apiUtils.isSuccess(response)) {
2531
throw new Error(apiUtils.getErrorMessage(response) || "Failed to fetch rating");
2632
}
2733
return response.data;
2834
},
35+
// Use moderate defaults; mutations and explicit refetch handle immediates
36+
staleTime: 5 * 60 * 1000, // 5 minutes
37+
refetchOnMount: false,
38+
refetchOnWindowFocus: false,
39+
refetchOnReconnect: false,
2940
});
3041

3142
return {
3243
rating,
3344
isLoading,
3445
error,
46+
refetch,
3547
};
3648
}

0 commit comments

Comments
 (0)