From b8f52ba9c18f9b0d9f43865fdd6db4cc3cfd0b53 Mon Sep 17 00:00:00 2001 From: William Garcia <102125255+ColumnSkunky@users.noreply.github.com> Date: Thu, 1 May 2025 01:02:44 -0700 Subject: [PATCH] feat: likes/follows implementation (#56) * Feat: check like status and check follow status * feat: frontend implementation --------- Co-authored-by: Larry La --- backend/projects/urls.py | 10 +- backend/projects/views.py | 35 ++++++- .../src/views/IndividualProjectPage.jsx | 99 +++++++++++++------ 3 files changed, 107 insertions(+), 37 deletions(-) diff --git a/backend/projects/urls.py b/backend/projects/urls.py index d441ef4..89a405d 100644 --- a/backend/projects/urls.py +++ b/backend/projects/urls.py @@ -1,11 +1,13 @@ from django.urls import path -from .views import ProjectCRUDView, get_projects, toggle_follow, toggle_like, project_member_hander +from .views import ProjectCRUDView, get_projects, toggle_follow, toggle_like, project_member_hander, check_like_status, check_follow_status urlpatterns = [ path('', get_projects, name='get_projects'), # Fetch all projects path('create/', ProjectCRUDView.as_view(), name='project-create'), # Create project path('/', ProjectCRUDView.as_view(), name='project-detail'), # Read, Update, Delete project by ID - path('like/', toggle_like, name='toggle_like'), - path('follow/', toggle_follow, name='toggle_follow'), - path('member/manage/', project_member_hander, name='project-member-handler'), + path('like//', toggle_like, name='toggle_like'), + path('follow//', toggle_follow, name='toggle_follow'), + path('member/manage//', project_member_hander, name='project-member-handler'), + path('likes/check///', check_like_status, name='check_like_status'), + path('follows/check///', check_follow_status, name='check_follow_status') ] diff --git a/backend/projects/views.py b/backend/projects/views.py index bbb9224..a17a9a9 100644 --- a/backend/projects/views.py +++ b/backend/projects/views.py @@ -78,7 +78,7 @@ def toggle_like(request, project_id): Like.objects.create(project=project, user=user) project.likes_count += 1 project.save() - return Response({'message': 'Project liked successfully'}, status=status.HTTP_200_OK) + return Response({'message': 'Project liked successfully','like_count': project.likes_count}, status=status.HTTP_200_OK) else: return Response({'message': 'You already liked this project.'}, status=status.HTTP_400_BAD_REQUEST) @@ -88,7 +88,7 @@ def toggle_like(request, project_id): like.delete() project.likes_count -= 1 project.save() - return Response({'message': 'Project unliked successfully'}, status=status.HTTP_200_OK) + return Response({'message': 'Project unliked successfully','like_count': project.likes_count}, status=status.HTTP_200_OK) else: return Response({'message': 'You already unliked this project.'}, status=status.HTTP_400_BAD_REQUEST) @@ -109,7 +109,7 @@ def toggle_follow(request, project_id): Follow.objects.create(project=project, user=user) project.followers_count += 1 project.save() - return Response({'message': 'Project followed successfully'}, status=status.HTTP_200_OK) + return Response({'message': 'Project followed successfully', 'follow_count': project.followers_count}, status=status.HTTP_200_OK) else: return Response({'message': 'You already followed this project.'}, status=status.HTTP_400_BAD_REQUEST) @@ -119,7 +119,7 @@ def toggle_follow(request, project_id): follow.delete() project.followers_count -= 1 project.save() - return Response({'message': 'Project unfollowed successfully'}, status=status.HTTP_200_OK) + return Response({'message': 'Project unfollowed successfully', 'follow_count': project.followers_count}, status=status.HTTP_200_OK) else: return Response({'message': 'You already unfollowed this project.'}, status=status.HTTP_400_BAD_REQUEST) @@ -155,3 +155,30 @@ def project_member_hander(request, project_id): except User.DoesNotExist: return Response({'error': 'User not found'}, status=404) +@api_view(['GET']) +@permission_classes([AllowAny]) +def check_like_status(request, user_id, project_id): + try: + project = get_object_or_404(Project, id=project_id) + user = get_object_or_404(User, id=user_id) + + liked = Like.objects.filter(project=project, user=user).exists() + + return Response({'liked': liked}, status=status.HTTP_200_OK) + + except Exception as e: + return Response({'error': str(e)}, status=status.HTTP_400_BAD_REQUEST) + +@api_view(['GET']) +@permission_classes([AllowAny]) +def check_follow_status(request, user_id, project_id): + try: + project = get_object_or_404(Project, id=project_id) + user = get_object_or_404(User, id=user_id) + + followed = Follow.objects.filter(project=project, user=user).exists() + + return Response({'followed': followed}, status=status.HTTP_200_OK) + + except Exception as e: + return Response({'error': str(e)}, status=status.HTTP_400_BAD_REQUEST) diff --git a/frontend/bitmatch/src/views/IndividualProjectPage.jsx b/frontend/bitmatch/src/views/IndividualProjectPage.jsx index 3d3ffcb..66b8c5e 100644 --- a/frontend/bitmatch/src/views/IndividualProjectPage.jsx +++ b/frontend/bitmatch/src/views/IndividualProjectPage.jsx @@ -103,6 +103,28 @@ const ProjectDetailPage = () => { const ownerData = await fetchUserByUUID(projectData.owner); setOwnerData(ownerData); + // Check initial like status + const likeStatusResponse = await fetch( + `${SERVER_HOST}/projects/likes/check/${userData.id}/${id}` + ); + if (likeStatusResponse.ok) { + const likeData = await likeStatusResponse.json(); + setLiked(likeData.liked); + } else { + console.error("Error checking like status"); + } + + // Check initial follow status + const followStatusResponse = await fetch( + `${SERVER_HOST}/projects/follows/check/${userData.id}/${id}/` + ); + if (followStatusResponse.ok) { + const followData = await followStatusResponse.json(); + setFollowing(followData.followed); + } else { + console.error("Error checking follow status"); + } + setIsReady(true); } catch (error) { setError("Error loading data"); @@ -131,22 +153,49 @@ const ProjectDetailPage = () => { const handleFollow = async () => { setFollowing(!following); - console.log("current following status:", following); const fdata = { action: following ? "unfollow" : "follow", - user_id: 1, + user_id: userUuid, }; - console.log("id is ", id); try { const response = await axios.post( - `${SERVER_HOST}/projects/follow/${id}`, + `${SERVER_HOST}/projects/follow/${id}/`, fdata ); - console.log("response: ", response.data); - window.location.reload(); + if (response.data && response.data.follow_count !== undefined) { + setProject((prevProject) => ({ + ...prevProject, + followers_count: response.data.follow_count, + })); + } } catch (error) { console.error("Error updating follows: ", error); + setFollowing(!following); + } + }; + + const handleLike = async () => { + setLiked(!likeStatus); + const fdata = { + action: likeStatus ? "unlike" : "like", + user_id: userUuid, + }; + + try { + const response = await axios.post( + `${SERVER_HOST}/projects/like/${id}/`, + fdata + ); + if (response.data && response.data.like_count !== undefined) { + setProject((prevProject) => ({ + ...prevProject, + likes_count: response.data.like_count, + })); + } + } catch (error) { + console.error("Error updating like status: ", error); + setLiked(!likeStatus); } }; @@ -365,25 +414,6 @@ const ProjectDetailPage = () => { return () => clearTimeout(timer); }, [cooldown]); - const handleLike = async () => { - setLiked(!likeStatus); - const fdata = { - action: likeStatus ? "unlike" : "like", - user_id: 1, - }; - - try { - const response = await axios.post( - `${SERVER_HOST}/projects/like/${id}`, - fdata - ); - console.log("response: ", response.data); - window.location.reload(); - } catch (error) { - console.error("Error updating likes: ", error); - } - }; - const handleSave = async (data) => { const formData = new FormData(); @@ -563,14 +593,25 @@ const ProjectDetailPage = () => {
- {project.likes_count} Likes
+
- {project.followers_count} Followers