Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions backend/projects/urls.py
Original file line number Diff line number Diff line change
@@ -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('<str:pk>/', ProjectCRUDView.as_view(), name='project-detail'), # Read, Update, Delete project by ID
path('like/<str:project_id>', toggle_like, name='toggle_like'),
path('follow/<str:project_id>', toggle_follow, name='toggle_follow'),
path('member/manage/<str:project_id>', project_member_hander, name='project-member-handler'),
path('like/<str:project_id>/', toggle_like, name='toggle_like'),
path('follow/<str:project_id>/', toggle_follow, name='toggle_follow'),
path('member/manage/<str:project_id>/', project_member_hander, name='project-member-handler'),
path('likes/check/<str:user_id>/<str:project_id>/', check_like_status, name='check_like_status'),
path('follows/check/<str:user_id>/<str:project_id>/', check_follow_status, name='check_follow_status')
]
35 changes: 31 additions & 4 deletions backend/projects/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand All @@ -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)

Expand All @@ -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)

Expand All @@ -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)

Expand Down Expand Up @@ -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)
99 changes: 70 additions & 29 deletions frontend/bitmatch/src/views/IndividualProjectPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down Expand Up @@ -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);
}
};

Expand Down Expand Up @@ -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();

Expand Down Expand Up @@ -563,14 +593,25 @@ const ProjectDetailPage = () => {

<div className="flex gap-4 mb-2">
<div className="flex items-center gap-2">
<Button variant="ghost" size="icon" onClick={handleLike}>
<ThumbsUp className="h-6 w-6"></ThumbsUp>
<Button
variant="ghost"
size="icon"
onClick={handleLike}
className={likeStatus ? "text-blue-500" : ""}
>
<ThumbsUp className="h-6 w-6" />
</Button>
<span>{project.likes_count} Likes</span>
</div>

<div className="flex items-center gap-2">
<Button variant="ghost" size="icon" onClick={handleFollow}>
<UserRound className="h-6 w-6"></UserRound>
<Button
variant="ghost"
size="icon"
onClick={handleFollow}
className={following ? "text-blue-500" : ""}
>
<UserRound className="h-6 w-6" />
</Button>
<span>{project.followers_count} Followers</span>
</div>
Expand Down
Loading