From b3072c81ba73a16381366960841b6c294cc1fa6e Mon Sep 17 00:00:00 2001 From: Andrew Godwin Date: Thu, 17 Nov 2022 20:04:01 -0700 Subject: Follows page --- static/css/style.css | 26 ++++++++++++++++++++++++++ takahe/urls.py | 7 ++++++- templates/identity/view.html | 29 +++++++++++++++++++---------- templates/settings/_menu.html | 3 +++ templates/settings/follows.html | 25 +++++++++++++++++++++++++ users/models/follow.py | 4 ++++ users/views/follows.py | 33 +++++++++++++++++++++++++++++++++ users/views/identity.py | 14 +++++++++----- 8 files changed, 125 insertions(+), 16 deletions(-) create mode 100644 templates/settings/follows.html create mode 100644 users/views/follows.py diff --git a/static/css/style.css b/static/css/style.css index 43d4448..eba0e4d 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -335,6 +335,18 @@ nav a i { font-size: 200%; } +.icon-menu .option .handle { + margin-right: 20px; +} + +.icon-menu .option .pill { + display: inline-block; + padding: 5px 8px; + background: var(--color-highlight); + border-radius: 5px; + margin: 0 5px 0 5px; +} + .handle { vertical-align: middle; display: inline-block; @@ -375,6 +387,20 @@ form.follow { float: right; margin: 20px 0 0 0; font-size: 16px; + text-align: center; +} + +form.follow.has-reverse { + margin-top: 0; +} + +form.follow .reverse-follow { + display: block; + margin: 0 0 5px 0; +} + +form.follow button { + margin: 0; } form h1 { diff --git a/takahe/urls.py b/takahe/urls.py index 0ea49d0..044599a 100644 --- a/takahe/urls.py +++ b/takahe/urls.py @@ -6,7 +6,7 @@ from django.views.static import serve from activities.views import posts, search, timelines from core import views as core from stator import views as stator -from users.views import activitypub, admin, auth, identity, settings +from users.views import activitypub, admin, auth, follows, identity, settings urlpatterns = [ path("", core.homepage), @@ -31,6 +31,11 @@ urlpatterns = [ settings.ProfilePage.as_view(), name="settings_profile", ), + path( + "settings/follows/", + follows.FollowsPage.as_view(), + name="settings_follows", + ), path( "settings/interface/", settings.InterfacePage.as_view(), diff --git a/templates/identity/view.html b/templates/identity/view.html index f877f59..d584022 100644 --- a/templates/identity/view.html +++ b/templates/identity/view.html @@ -13,16 +13,25 @@ {% if request.identity %} -
- {% csrf_token %} - {% if follow %} - - - {% else %} - - - {% endif %} -
+ {% if identity == request.identity %} +
+ Edit Profile +
+ {% else %} +
+ {% csrf_token %} + {% if reverse_follow %} + Follows You + {% endif %} + {% if follow %} + + + {% else %} + + + {% endif %} +
+ {% endif %} {% endif %} {{ identity.name_or_handle }} @{{ identity.handle }} diff --git a/templates/settings/_menu.html b/templates/settings/_menu.html index cc87941..dd43912 100644 --- a/templates/settings/_menu.html +++ b/templates/settings/_menu.html @@ -6,6 +6,9 @@ Interface + + Follows + {% if request.user.admin %}

Account

diff --git a/templates/settings/follows.html b/templates/settings/follows.html new file mode 100644 index 0000000..5f43d05 --- /dev/null +++ b/templates/settings/follows.html @@ -0,0 +1,25 @@ +{% extends "settings/base.html" %} + +{% block subtitle %}Follows{% endblock %} + +{% block content %} +
+ {% for identity, details in identities %} + + + + {{ identity.name_or_handle }} + @{{ identity.handle }} + + {% if details.outbound %} + Following + {% endif %} + {% if details.inbound %} + Follows You + {% endif %} + + {% empty %} +

You have no follows.

+ {% endfor %} +
+{% endblock %} diff --git a/users/models/follow.py b/users/models/follow.py index d2ee493..e741c56 100644 --- a/users/models/follow.py +++ b/users/models/follow.py @@ -23,6 +23,10 @@ class FollowStates(StateGraph): accepted.transitions_to(undone) undone.transitions_to(undone_remotely) + @classmethod + def group_active(cls): + return [cls.unrequested, cls.local_requested, cls.accepted] + @classmethod async def handle_unrequested(cls, instance: "Follow"): """ diff --git a/users/views/follows.py b/users/views/follows.py new file mode 100644 index 0000000..9030efe --- /dev/null +++ b/users/views/follows.py @@ -0,0 +1,33 @@ +from django.utils.decorators import method_decorator +from django.views.generic import TemplateView + +from users.decorators import identity_required +from users.models import FollowStates + + +@method_decorator(identity_required, name="dispatch") +class FollowsPage(TemplateView): + """ + Shows followers/follows. + """ + + template_name = "settings/follows.html" + + def get_context_data(self): + # Gather all identities with a following relationship with us + identities = {} + for outbound_follow in self.request.identity.outbound_follows.filter( + state__in=FollowStates.group_active() + ): + identities.setdefault(outbound_follow.target, {})[ + "outbound" + ] = outbound_follow + for inbound_follow in self.request.identity.inbound_follows.filter( + state__in=FollowStates.group_active() + ): + identities.setdefault(inbound_follow.source, {})["inbound"] = inbound_follow + + return { + "section": "follows", + "identities": sorted(identities.items(), key=lambda i: i[0].username), + } diff --git a/users/views/identity.py b/users/views/identity.py index b83ba9a..ae8e5b0 100644 --- a/users/views/identity.py +++ b/users/views/identity.py @@ -28,18 +28,22 @@ class ViewIdentity(TemplateView): if identity.data_age > Config.system.identity_max_age: identity.transition_perform(IdentityStates.outdated) follow = None + reverse_follow = None if self.request.identity: follow = Follow.maybe_get(self.request.identity, identity) - if follow and follow.state not in [ - FollowStates.unrequested, - FollowStates.local_requested, - FollowStates.accepted, - ]: + if follow and follow.state not in FollowStates.group_active(): follow = None + reverse_follow = Follow.maybe_get(identity, self.request.identity) + if ( + reverse_follow + and reverse_follow.state not in FollowStates.group_active() + ): + reverse_follow = None return { "identity": identity, "posts": posts, "follow": follow, + "reverse_follow": reverse_follow, } -- cgit v1.2.3