Skip to content

Commit 21c8c91

Browse files
authored
Merge pull request #428 from CodebilityDev/miguel/fix-upvote-implementation
Miguel/fix upvote implementation
2 parents 853d875 + 4c8126c commit 21c8c91

2 files changed

Lines changed: 77 additions & 62 deletions

File tree

‎apps/codebility/app/home/feeds/_components/PostUpvote.tsx‎

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22

33
import { useEffect, useState } from "react";
44
import { useUserStore } from "@/store/codev-store";
5+
import { useFeedsStore } from "@/store/feeds-store";
56
import { ArrowBigUp } from "lucide-react";
67

78
import {
89
AddPostUpvote,
10+
countUpvotes,
911
hasUserUpvoted,
1012
removePostUpvote,
1113
} from "../_services/action";
@@ -19,7 +21,11 @@ export default function PostUpvote({ post }: PostUpvoteProps) {
1921
const { user } = useUserStore();
2022

2123
const [isUpvoted, setIsUpvoted] = useState(false);
22-
const [upvotes, setUpvotes] = useState(post.upvoters_id?.length || 0);
24+
const [upvotes, setUpvotes] = useState(0);
25+
const { posts, fetchPosts } = useFeedsStore((state) => ({
26+
posts: state.posts,
27+
fetchPosts: state.fetchPosts,
28+
}));
2329

2430
useEffect(() => {
2531
const checkUpvote = async () => {
@@ -34,33 +40,43 @@ export default function PostUpvote({ post }: PostUpvoteProps) {
3440
} else {
3541
setIsUpvoted(false);
3642
}
37-
};
3843

44+
if (post?.id) {
45+
try {
46+
const upvotesCount = await countUpvotes(post.id);
47+
setUpvotes(upvotesCount);
48+
} catch (error) {
49+
console.error("Error counting upvotes:", error);
50+
}
51+
}
52+
};
3953
checkUpvote();
40-
}, [user?.id, post?.id]);
54+
}, [user?.id, post?.id, posts]);
4155

42-
const handleUpvote = (e: React.MouseEvent) => {
56+
const handleUpvote = async (e: React.MouseEvent) => {
4357
if (user) {
4458
e.stopPropagation();
4559
e.preventDefault();
4660

61+
setIsUpvoted((prev) => !prev);
62+
setUpvotes((prev) => prev + (isUpvoted ? -1 : 1));
63+
4764
if (!isUpvoted) {
48-
AddPostUpvote(post.id, user.id);
65+
const postUpvote = await AddPostUpvote(post.id, user.id);
4966
} else {
50-
removePostUpvote(post.id, user.id);
67+
await removePostUpvote(post.id, user.id);
5168
}
5269

53-
setIsUpvoted((prev) => !prev);
54-
setUpvotes((prev) => prev + (isUpvoted ? -1 : 1));
70+
await fetchPosts();
5571
}
5672
};
5773

5874
return (
5975
<button
6076
className={`flex items-center space-x-1 ${
6177
isUpvoted
62-
? "text-customBlue-500 dark:text-customBlue-400"
63-
: "hover:text-customBlue-500 dark:hover:text-customBlue-400"
78+
? "text-customBlue-300 dark:text-customBlue-200"
79+
: "hover:text-customBlue-300 dark:hover:text-customBlue-200"
6480
}`}
6581
onClick={handleUpvote}
6682
>

‎apps/codebility/app/home/feeds/_services/action.ts‎

Lines changed: 51 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -207,93 +207,92 @@ export const AddPostUpvote = async (postId: string, userId: string) => {
207207
try {
208208
const supabase = await createClientServerComponent();
209209

210-
// Fetch current upvoters_id array
211-
const { data: post, error: fetchError } = await supabase
212-
.from("posts")
213-
.select("upvoters_id")
214-
.eq("id", postId)
215-
.single();
210+
const { data: postUpvote, error: fetchError } = await supabase
211+
.from("post_upvotes")
212+
.select("*")
213+
.eq("post_id", postId)
214+
.eq("upvoter_id", userId)
215+
.maybeSingle();
216216

217217
if (fetchError) throw fetchError;
218218

219-
const currentUpvoters = post.upvoters_id || [];
220-
221219
// Avoid duplicate upvotes
222-
if (currentUpvoters.includes(userId)) {
220+
if (postUpvote) {
223221
return { message: "User has already upvoted this post." };
224222
}
225223

226-
const updatedUpvoters = [...currentUpvoters, userId];
227-
228-
// Update the post with new upvoter
229-
const { data: updatedPost, error: updateError } = await supabase
230-
.from("posts")
231-
.update({ upvoters_id: updatedUpvoters })
232-
.eq("id", postId)
233-
.select();
224+
// Add post upvote
225+
const { data: newUpvote, error: insertError } = await supabase
226+
.from("post_upvotes")
227+
.insert([{ post_id: postId, upvoter_id: userId }])
228+
.select()
229+
.single();
234230

235-
if (updateError) throw updateError;
231+
if (insertError) throw insertError;
236232

237-
return updatedPost;
233+
return newUpvote;
238234
} catch (error) {
239-
console.error(error);
235+
console.error("Failed to add upvote:", error);
240236
throw error;
241237
}
242238
};
243239

244240
export const removePostUpvote = async (postId: string, userId: string) => {
245-
try {
241+
try {
246242
const supabase = await createClientServerComponent();
247243

248-
// Fetch current upvoters_id array
249-
const { data: post, error: fetchError } = await supabase
250-
.from("posts")
251-
.select("upvoters_id")
252-
.eq("id", postId)
253-
.single();
244+
const { error } = await supabase
245+
.from("post_upvotes")
246+
.delete()
247+
.eq("post_id", postId)
248+
.eq("upvoter_id", userId);
254249

255-
if (fetchError) throw fetchError;
250+
if (error) throw error;
256251

257-
const currentUpvoters = post.upvoters_id || [];
258252

259-
// If user hasn't upvoted, nothing to remove
260-
if (!currentUpvoters.includes(userId)) {
261-
return { message: "User hasn't upvoted this post." };
262-
}
253+
} catch (error) {
254+
console.error("Failed to remove upvote:", error);
255+
throw error;
256+
}
257+
};
263258

264-
const updatedUpvoters = currentUpvoters.filter(id => id !== userId);
259+
export const hasUserUpvoted = async (postId: string, userId: string): Promise<boolean> => {
260+
try {
261+
const supabase = await createClientServerComponent();
265262

266-
// Update the post with new upvoter array
267-
const { data: updatedPost, error: updateError } = await supabase
268-
.from("posts")
269-
.update({ upvoters_id: updatedUpvoters })
270-
.eq("id", postId)
271-
.select();
263+
const { data: postUpvote, error } = await supabase
264+
.from("post_upvotes")
265+
.select("*")
266+
.eq("post_id", postId)
267+
.eq("upvoter_id", userId)
268+
.maybeSingle();
272269

273-
if (updateError) throw updateError;
270+
if (error) throw error;
274271

275-
return updatedPost;
272+
if (postUpvote) {
273+
return true;
274+
} else {
275+
return false;
276+
}
276277
} catch (error) {
277278
console.error(error);
278279
throw error;
279280
}
280281
};
281282

282-
export const hasUserUpvoted = async (postId: string, userId: string): Promise<boolean> => {
283-
try {
283+
export const countUpvotes = async (postId: string): Promise<number> => {
284+
try {
284285
const supabase = await createClientServerComponent();
285286

286-
const { data: post, error } = await supabase
287-
.from("posts")
288-
.select("upvoters_id")
289-
.eq("id", postId)
290-
.single();
287+
const { count, error } = await supabase
288+
.from("post_upvotes")
289+
.select("*", { count: "exact", head: true })
290+
.eq("post_id", postId);
291291

292292
if (error) throw error;
293293

294-
const upvoters: string[] = post.upvoters_id || [];
295-
296-
return upvoters.includes(userId);
294+
if (count) return count
295+
else return 0;
297296
} catch (error) {
298297
console.error(error);
299298
throw error;

0 commit comments

Comments
 (0)