From 64de2cd49ebc9829064339d0375c0f859c33c9cb Mon Sep 17 00:00:00 2001 From: Mohammed Abdoon Date: Tue, 30 Jun 2026 03:36:33 +0100 Subject: [PATCH] fixed unfollow issue --- backend/data/follows.py | 11 +++++++++++ backend/main.py | 2 ++ front-end/components/profile.mjs | 17 +++++++++++------ front-end/tests/profile.spec.mjs | 15 +++++++++++++++ 4 files changed, 39 insertions(+), 6 deletions(-) diff --git a/backend/data/follows.py b/backend/data/follows.py index a4b6314e..43c566b7 100644 --- a/backend/data/follows.py +++ b/backend/data/follows.py @@ -21,6 +21,17 @@ def follow(follower: User, followee: User): pass +def unfollow(follower: User, followee: User): + with db_cursor() as cur: + cur.execute( + "DELETE FROM follows WHERE follower = %(follower_id)s AND followee = %(followee_id)s", + dict( + follower_id=follower.id, + followee_id=followee.id, + ), + ) + + def get_followed_usernames(follower: User) -> List[str]: """get_followed_usernames returns a list of usernames followee follows.""" with db_cursor() as cur: diff --git a/backend/main.py b/backend/main.py index 7ba155fa..a8e07215 100644 --- a/backend/main.py +++ b/backend/main.py @@ -4,6 +4,7 @@ from data.users import lookup_user from endpoints import ( do_follow, + do_unfollow, get_bloom, hashtag, home_timeline, @@ -54,6 +55,7 @@ def main(): app.add_url_rule("/profile", view_func=self_profile) app.add_url_rule("/profile/", view_func=other_profile) app.add_url_rule("/follow", methods=["POST"], view_func=do_follow) + app.add_url_rule("/unfollow/", methods=["POST"], view_func=do_unfollow) app.add_url_rule("/suggested-follows/", view_func=suggested_follows) app.add_url_rule("/bloom", methods=["POST"], view_func=send_bloom) diff --git a/front-end/components/profile.mjs b/front-end/components/profile.mjs index ec4f2009..2b7b81dc 100644 --- a/front-end/components/profile.mjs +++ b/front-end/components/profile.mjs @@ -27,11 +27,10 @@ function createProfile(template, {profileData, whoToFollow, isLoggedIn}) { followerCountEl.textContent = profileData.followers?.length || 0; followingCountEl.textContent = profileData.follows?.length || 0; followButtonEl.setAttribute("data-username", profileData.username || ""); - followButtonEl.hidden = profileData.is_self || profileData.is_following; + followButtonEl.dataset.isFollowing = profileData.is_following ? "true" : "false"; + followButtonEl.textContent = profileData.is_following ? "Unfollow" : "Follow"; + followButtonEl.hidden = profileData.is_self || !isLoggedIn; followButtonEl.addEventListener("click", handleFollow); - if (!isLoggedIn) { - followButtonEl.style.display = "none"; - } if (whoToFollow.length > 0) { const whoToFollowList = whoToFollowContainer.querySelector("[data-who-to-follow]"); @@ -62,8 +61,14 @@ async function handleFollow(event) { const username = button.getAttribute("data-username"); if (!username) return; - await apiService.followUser(username); - await apiService.getWhoToFollow(); + const isFollowing = button.dataset.isFollowing === "true"; + + if (isFollowing) { + await apiService.unfollowUser(username); + } else { + await apiService.followUser(username); + await apiService.getWhoToFollow(); + } } export {createProfile, handleFollow}; diff --git a/front-end/tests/profile.spec.mjs b/front-end/tests/profile.spec.mjs index a90f69d3..c24dc479 100644 --- a/front-end/tests/profile.spec.mjs +++ b/front-end/tests/profile.spec.mjs @@ -43,4 +43,19 @@ test.describe("Profile View", () => { // And bloom form is not attached await expect(page.locator("#bloom-form-container form")).not.toBeAttached(); }); + + test("allows a user to unfollow a profile after following it", async ({page}) => { + await loginAsJustSomeGuy(page); + await page.goto("/#/profile/sample"); + + const followButton = page.locator("#profile-container [data-action='follow']"); + await expect(followButton).toBeVisible(); + await expect(followButton).toHaveText("Follow"); + + await followButton.click(); + await expect(followButton).toHaveText("Unfollow"); + + await followButton.click(); + await expect(followButton).toHaveText("Follow"); + }); });