Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
9 changes: 9 additions & 0 deletions backend/data/follows.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,15 @@ def follow(follower: User, followee: User):
# Already following - treat as idempotent request.
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."""
Expand Down
25 changes: 24 additions & 1 deletion backend/endpoints.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from typing import Dict, Union
from data import blooms
from data.follows import follow, get_followed_usernames, get_inverse_followed_usernames
from data.follows import follow, unfollow, get_followed_usernames, get_inverse_followed_usernames
from data.users import (
UserRegistrationError,
get_suggested_follows,
Expand Down Expand Up @@ -149,6 +149,29 @@ def do_follow():
}
)

@jwt_required()
def do_unfollow():
type_check_error = verify_request_fields({"unfollow_username": str})
if type_check_error is not None:
return type_check_error

current_user = get_current_user()

unfollow_username = request.json["unfollow_username"]

unfollow_user = get_user(unfollow_username)
if unfollow_user is None:
return make_response(
(f"Cannot unfollow {unfollow_username} - user does not exist", 404)
)

unfollow(current_user, unfollow_user)

return jsonify(
{
"success": True,
}
)

@jwt_required()
def send_bloom():
Expand Down
2 changes: 2 additions & 0 deletions backend/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from data.users import lookup_user
from endpoints import (
do_follow,
do_unfollow,
get_bloom,
hashtag,
home_timeline,
Expand Down Expand Up @@ -54,6 +55,7 @@ def main():
app.add_url_rule("/profile", view_func=self_profile)
app.add_url_rule("/profile/<profile_username>", 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/<limit_str>", view_func=suggested_follows)

app.add_url_rule("/bloom", methods=["POST"], view_func=send_bloom)
Expand Down
28 changes: 26 additions & 2 deletions front-end/components/profile.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ function createProfile(template, {profileData, whoToFollow, isLoggedIn}) {
);
const followerCountEl = profileElement.querySelector("[data-follower-count]");
const followButtonEl = profileElement.querySelector("[data-action='follow']");
const unfollowButtonEl = profileElement.querySelector("[data-action='unfollow']");
const whoToFollowContainer = profileElement.querySelector(".profile__who-to-follow");
// Populate with data
usernameEl.querySelector("h2").textContent = profileData.username || "";
Expand All @@ -27,10 +28,25 @@ 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;
if (unfollowButtonEl) {
unfollowButtonEl.setAttribute("data-username", profileData.username || "");
}
if (profileData.is_self) {
followButtonEl.hidden = true;
if (unfollowButtonEl) unfollowButtonEl.hidden = true;
} else {
followButtonEl.hidden = profileData.is_following;
if (unfollowButtonEl) {
unfollowButtonEl.hidden = !profileData.is_following;
}
}
followButtonEl.addEventListener("click", handleFollow);
if (unfollowButtonEl) {
unfollowButtonEl.addEventListener("click", handleUnfollow);
}
if (!isLoggedIn) {
followButtonEl.style.display = "none";
if (unfollowButtonEl) unfollowButtonEl.style.display = "none";
}

if (whoToFollow.length > 0) {
Expand Down Expand Up @@ -66,4 +82,12 @@ async function handleFollow(event) {
await apiService.getWhoToFollow();
}

export {createProfile, handleFollow};
async function handleUnfollow(event) {
const button = event.target;
const username = button.getAttribute("data-username");
if (!username) return;

await apiService.unfollowUser(username);
}

export {createProfile, handleFollow, handleUnfollow};
1 change: 1 addition & 0 deletions front-end/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ <h1 id="signup-heading" class="signup__title">Create your account</h1>
</dl>
<div class="profile__actions">
<button type="button" data-action="follow">Follow</button>
<button type="button" data-action="unfollow" hidden>Unfollow</button>
Comment thread
illicitonion marked this conversation as resolved.
Outdated
</div>
<div class="profile__who-to-follow">
<h4>Who to follow</h4>
Expand Down
3 changes: 2 additions & 1 deletion front-end/lib/api.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -261,8 +261,9 @@ async function followUser(username) {

async function unfollowUser(username) {
try {
const data = await _apiRequest(`/unfollow/${username}`, {
const data = await _apiRequest("/unfollow", {
method: "POST",
body: JSON.stringify({unfollow_username: username}),
});

if (data.success) {
Expand Down