Removing Redundant Profile View:

This part we need to reduce some redundant codes.

  • We move contents of user_follow_view into profile_detail_api_view, and delete user_follow_view in /profiles/api/views.py ```python import random from django.http import HttpResponse, JsonResponse, HttpResponseRedirect from django.shortcuts import render, redirect from django.utils.http import is_safe_url from django.conf import settings from django.contrib.auth import get_user_model

from rest_framework.authentication import SessionAuthentication from rest_framework.decorators import api_view, permission_classes, authentication_classes from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response

from ..models import Profile from ..serializers import PublicProfileSerializer

User = get_user_model() ALLOWED_HOSTS = settings.ALLOWED_HOSTS

‘’’ @api_view([‘GET’, ‘POST’]) @permission_classes([IsAuthenticated]) def user_follow_view(request, username, args, *kwargs): me = request.user other_user_qs = User.objects.filter(username=username) if me.username == username: my_followers = me.profile.followers.all() return Response({“count”: my_followers.count()}, status=200) if not other_user_qs.exists(): return Response({}, status=404)

  1. other = other_user_qs.first()
  2. profile = other.profile
  3. data = request.data or {}
  4. action = data.get("action")
  5. if action == "follow":
  6. profile.followers.add(me)
  7. elif action == "unfollow":
  8. profile.followers.remove(me)
  9. else:
  10. pass
  11. data = PublicProfileSerializer(
  12. instance=profile, context={"request": request})
  13. return Response(data.data, status=200)

‘’’

@api_view([‘GET’])

@api_view([‘GET’, ‘POST’]) def profile_detail_api_view(request, username, args, *kwargs): qs = Profile.objects.filter(user__username=username) if not qs.exists(): return Response({“detail”: “User not found”}, status=404) profile_obj = qs.first()

  1. # data = PublicProfileSerializer(instance=profile_obj, context={"request": request})
  2. # new
  3. data = request.data or {}
  4. if request.method == "POST":
  5. me = request.user
  6. action = data.get("action")
  7. if profile_obj.user != me:
  8. if action == "follow":
  9. profile_obj.followers.add(me)
  10. elif action == "unfollow":
  11. profile_obj.followers.remove(me)
  12. else:
  13. pass
  14. serializer = PublicProfileSerializer(
  15. instance=profile_obj, context={"request": request})
  16. # return Response(data.data, status=200)
  17. return Response(serializer.data, status=200)
  1. - remove user_follow_view in /profiles/api/urls.py
  2. ```python
  3. from django.urls import path
  4. from .views import (
  5. # user_follow_view,
  6. profile_detail_api_view,
  7. )
  8. urlpatterns = [
  9. path('<str:username>/', profile_detail_api_view),
  10. # path('<str:username>/follow', user_follow_view),
  11. path('<str:username>/follow', profile_detail_api_view),
  12. ]

to test, npm start and runserver, access http://localhost/api/profiles/root/follow and http://localhost:30/
image.png
image.png
both work exactly same as before.

Display Follower Count with Numeral.js:

In this part, we can add the follower count into profile badge to see how many users are following a profile, or the user of this profile following how many other users.

follower and following:

  • add follower and following display in /twittme-web/src/profiles/badge.js

    1. function ProfileBadge(props) {
    2. const { user, didFollowToggle, profileLoading } = props;
    3. let currentVerb = user && user.is_following ? "Unfollow" : "Follow";
    4. currentVerb = profileLoading ? "Loading..." : currentVerb;
    5. const handleFollowToggle = (event) => {
    6. event.preventDefault();
    7. if (didFollowToggle && !profileLoading) {
    8. didFollowToggle(currentVerb);
    9. }
    10. };
    11. /*
    12. return user ? (
    13. <div>
    14. <UserPicture user={user} hideLink />
    15. <p>
    16. <UserDisplay user={user} includeFullName hideLink />
    17. </p>
    18. <button className="btn btn-primary" onClick={handleFollowToggle}>
    19. {currentVerb}
    20. </button>
    21. </div>
    22. ) : null;
    23. */
    24. return user ? (
    25. <div>
    26. <UserPicture user={user} hideLink />
    27. <p>
    28. <UserDisplay user={user} includeFullName hideLink />
    29. </p>
    30. <p>
    31. Followers: {user.follower_count}
    32. </p>
    33. <p>
    34. Following: {user.following_count}
    35. </p>
    36. <button className="btn btn-primary" onClick={handleFollowToggle}>
    37. {currentVerb}
    38. </button>
    39. </div>
    40. ) : null;
    41. }

    runserver and npm start, access http://localhost:30/
    image.png

    for large numbers:

    because we are build this individually, so we mostly don’t have a large count of followings and followers, we need to consider some extreme cases.

  • temporarily change returns of get_following_count in /profiles/serializers.py

    • just some randomly big numbers

      1. class PublicProfileSerializer(serializers.ModelSerializer):
      2. # ...
      3. def get_following_count(self, obj):
      4. return 1234567898712386786385 # obj.user.following.count()
      5. def get_follower_count(self, obj):
      6. return 912467362897 # obj.followers.count()

      access http://localhost:30/
      image.png
      so if the number is too big, it might be shown as the following count above with “e+n” or something, and actually users don’t need to get too detailed numbers.

  • we npm install “numeral” in /twittme-web ```bash [root@localhost twittme-web]# npm install numeral npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.2.13 (node_modules/chokidar/node_modules/fsevents): npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.13: wanted {“os”:”darwin”,”arch”:”any”} (current: {“os”:”linux”,”arch”:”x64”}) npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@2.1.3 (node_modules/watchpack/node_modules/fsevents): npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@2.1.3: wanted {“os”:”darwin”,”arch”:”any”} (current: {“os”:”linux”,”arch”:”x64”}) npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.2.4 (node_modules/fsevents): npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.4: wanted {“os”:”darwin”,”arch”:”any”} (current: {“os”:”linux”,”arch”:”x64”})

  • numeral@2.0.6 added 1 package from 1 contributor and audited 1951 packages in 24.243s

35 packages are looking for funding run npm fund for details

found 1041 vulnerabilities (1032 low, 2 moderate, 7 high) run npm audit fix to fix them, or npm audit for details

  1. - apply this in /twittme-web/src/profiles/badge.js
  2. ```javascript
  3. import React, { useState, useEffect } from "react";
  4. import { apiProfileDetail, apiProfileFollowToggle } from "./lookup";
  5. import numeral from "numeral"; //new
  6. import { UserDisplay, UserPicture } from "./components";
  7. //new
  8. function DisplayCount(props) {
  9. return (
  10. <span className={props.className}>
  11. {numeral(props.children).format("0a")}
  12. </span>
  13. );
  14. }
  15. function ProfileBadge(props) {
  16. const { user, didFollowToggle, profileLoading } = props;
  17. let currentVerb = user && user.is_following ? "Unfollow" : "Follow";
  18. currentVerb = profileLoading ? "Loading..." : currentVerb;
  19. const handleFollowToggle = (event) => {
  20. event.preventDefault();
  21. if (didFollowToggle && !profileLoading) {
  22. didFollowToggle(currentVerb);
  23. }
  24. };
  25. /*
  26. return user ? (
  27. <div>
  28. <UserPicture user={user} hideLink />
  29. <p>
  30. <UserDisplay user={user} includeFullName hideLink />
  31. </p>
  32. <p>Followers: {user.follower_count}</p>
  33. <p>Following: {user.following_count}</p>
  34. <button className="btn btn-primary" onClick={handleFollowToggle}>
  35. {currentVerb}
  36. </button>
  37. </div>
  38. ) : null;
  39. */
  40. return user ? (
  41. <div>
  42. <UserPicture user={user} hideLink />
  43. <p>
  44. <UserDisplay user={user} includeFullName hideLink />
  45. </p>
  46. <p>
  47. Followers: <DisplayCount>{user.follower_count}</DisplayCount>
  48. </p>
  49. <p>
  50. Following: <DisplayCount>{user.following_count}</DisplayCount>
  51. </p>
  52. <button className="btn btn-primary" onClick={handleFollowToggle}>
  53. {currentVerb}
  54. </button>
  55. </div>
  56. ) : null;
  57. }

access http://localhost:30/
image.png
b as “billion”
t as “thousand”

  • now we can change methods in /profiles/serializers.py back to return correct values

    more details:

  • in /twittme-web/src/profiles/badge.js

    • if only 1 follower, show “1 follower” instead of “1 followers
    • show user location and bio infomation ```javascript function ProfileBadge(props) { const { user, didFollowToggle, profileLoading } = props;

    let currentVerb = user && user.is_following ? “Unfollow” : “Follow”; currentVerb = profileLoading ? “Loading…” : currentVerb; const handleFollowToggle = (event) => { event.preventDefault(); if (didFollowToggle && !profileLoading) {

    1. didFollowToggle(currentVerb);

    } };

    /* return user ? (

    1. <UserPicture user={user} hideLink />
    2. <p>
    3. <UserDisplay user={user} includeFullName hideLink />
    4. </p>
    5. <p>
    6. Followers: <DisplayCount>{user.follower_count}</DisplayCount>
    7. </p>
    8. <p>
    9. Following: <DisplayCount>{user.following_count}</DisplayCount>
    10. </p>
    11. <button className="btn btn-primary" onClick={handleFollowToggle}>
    12. {currentVerb}
    13. </button>

    ) : null; */ return user ? (

    1. <UserPicture user={user} hideLink />
    2. <p>
    3. <UserDisplay user={user} includeFullName hideLink />
    4. </p>
    5. <p>
    6. <DisplayCount>{user.follower_count}</DisplayCount>{" "}
    7. {user.follower_count === 1 ? "follower" : "followers"}{" "}
    8. </p>
    9. <p>
    10. <DisplayCount>{user.following_count}</DisplayCount> following
    11. </p>
    12. <p>{user.location}</p>
    13. <p>{user.bio}</p>
    14. <button className="btn btn-primary" onClick={handleFollowToggle}>
    15. {currentVerb}
    16. </button>

    ) : null; } ``` access http://localhost:30/
    image.png

  • in order to increase code reuse

    • create /twittme-web/src/profiles/utils.js
      • copy DisplayCount from /twittme-web/src/profiles/badge.js and export ```javascript import React from “react”; import numeral from “numeral”;

export function DisplayCount(props) { return ( {numeral(props.children).format(“0a”)} ); }

  1. - import DisplayCount in /twittme-web/src/profiles/badge.js
  2. ```javascript
  3. //...
  4. //import numeral from "numeral";
  5. //...
  6. import { DisplayCount } from "./utils"; //new
  7. /*
  8. function DisplayCount(props) {
  9. return (
  10. <span className={props.className}>
  11. {numeral(props.children).format("0a")}
  12. </span>
  13. );
  14. }
  15. */
  16. //...