Add public following/followers pages

pull/226/head
Andrew Godwin 2022-12-21 20:36:10 +00:00
rodzic 932cfe9243
commit c9794c0fcf
8 zmienionych plików z 116 dodań i 28 usunięć

Wyświetl plik

@ -904,7 +904,11 @@ table.metadata td .emoji {
}
.view-options {
margin: 0 0 10px 3px;
margin: 0 0 10px 0px;
}
.view-options.follows {
margin: 0 0 20px 0px;
}
.view-options a {

Wyświetl plik

@ -161,6 +161,8 @@ urlpatterns = [
path("@<handle>/action/", identity.ActionIdentity.as_view()),
path("@<handle>/rss/", identity.IdentityFeed()),
path("@<handle>/report/", report.SubmitReport.as_view()),
path("@<handle>/following/", identity.IdentityFollows.as_view(inbound=False)),
path("@<handle>/followers/", identity.IdentityFollows.as_view(inbound=True)),
# Posts
path("compose/", compose.Compose.as_view(), name="compose"),
path(

Wyświetl plik

@ -0,0 +1,24 @@
{% extends "identity/view.html" %}
{% block title %}{% if self.inbound %}Followers{% else %}Following{% endif %} - {{ identity }}{% endblock %}
{% block subcontent %}
{% for identity in page_obj %}
{% include "activities/_identity.html" %}
{% empty %}
<span class="empty">
This person has no {% if self.inbound %}followers{% else %}follows{% endif %} yet.
</span>
{% endfor %}
<div class="load-more">
{% if page_obj.has_previous %}
<a class="button" href=".?page={{ page_obj.previous_page_number }}">Previous Page</a>
{% endif %}
{% if page_obj.has_next %}
<a class="button" href=".?page={{ page_obj.next_page_number }}">Next Page</a>
{% endif %}
</div>
{% endblock %}

Wyświetl plik

@ -20,7 +20,7 @@
{% if request.identity %}
{% if identity == request.identity %}
<form class="inline follow">
<form class="inline follow-profile">
<a class="button" href="{% url "settings_profile" %}">Edit Profile</a>
</form>
{% else %}
@ -72,13 +72,11 @@
</table>
{% endif %}
{% if identity.config_identity.visible_follows %}
<div class="stats">
<ul>
<li><strong>{{ following_count }}</strong> following</li>
<li><strong>{{ followers_count }}</strong> follower{{ followers_count|pluralize }}</li>
</ul>
</div>
{% if identity.local and identity.config_identity.visible_follows %}
<div class="view-options follows">
<a href="{{ identity.urls.following }}" {% if not inbound or not follows_page %}class="selected"{% endif %}><strong>{{ following_count }}</strong> following</a>
<a href="{{ identity.urls.followers }}" {% if inbound or not follows_page %}class="selected"{% endif %}><strong>{{ followers_count }}</strong> follower{{ followers_count|pluralize }}</a>
</div>
{% endif %}
{% if not identity.local %}
@ -94,24 +92,28 @@
{% endif %}
{% endif %}
{% for post in page_obj %}
{% include "activities/_post.html" %}
{% empty %}
<span class="empty">
{% if identity.local %}
No posts yet.
{% else %}
No posts have been received/retrieved by this server yet.
{% block subcontent %}
{% if identity.profile_uri %}
You might find historical posts at
<a href="{{ identity.profile_uri }}">their original profile ➔</a>
{% for post in page_obj %}
{% include "activities/_post.html" %}
{% empty %}
<span class="empty">
{% if identity.local %}
No posts yet.
{% else %}
No posts have been received/retrieved by this server yet.
{% if identity.profile_uri %}
You might find historical posts at
<a href="{{ identity.profile_uri }}">their original profile ➔</a>
{% endif %}
{% endif %}
{% endif %}
</span>
{% endfor %}
</span>
{% endfor %}
{% if page_obj.has_next %}
<div class="load-more"><a class="button" href=".?page={{ page_obj.next_page_number }}">Next Page</a></div>
{% endif %}
{% if page_obj.has_next %}
<div class="load-more"><a class="button" href=".?page={{ page_obj.next_page_number }}">Next Page</a></div>
{% endif %}
{% endblock %}
{% endblock %}

Wyświetl plik

@ -21,6 +21,9 @@ def test_visible_follows_disabled(client, identity):
"""
Tests that disabling visible follows hides it from profile
"""
Config.set_identity(identity, "visible_follows", True)
response = client.get(identity.urls.view)
assertContains(response, '<div class="view-options follows">', status_code=200)
Config.set_identity(identity, "visible_follows", False)
response = client.get(identity.urls.view)
assertNotContains(response, '<div class="stats">', status_code=200)
assertNotContains(response, '<div class="view-options follows">', status_code=200)

Wyświetl plik

@ -219,6 +219,8 @@ class Identity(StatorModel):
class urls(urlman.Urls):
view = "/@{self.username}@{self.domain_id}/"
action = "{view}action/"
followers = "{view}followers/"
following = "{view}following/"
activate = "{view}activate/"
admin = "/admin/identities/"
admin_edit = "{admin}{self.pk}/"

Wyświetl plik

@ -1,5 +1,7 @@
from typing import cast
from django.db import models
from users.models import Follow, FollowStates, Identity
@ -11,6 +13,16 @@ class IdentityService:
def __init__(self, identity: Identity):
self.identity = identity
def following(self) -> models.QuerySet[Identity]:
return Identity.objects.filter(
inbound_follows__source=self.identity
).not_deleted()
def followers(self) -> models.QuerySet[Identity]:
return Identity.objects.filter(
outbound_follows__target=self.identity
).not_deleted()
def follow_from(self, from_identity: Identity) -> Follow:
"""
Follows a user (or does nothing if already followed).

Wyświetl plik

@ -29,7 +29,7 @@ class ViewIdentity(ListView):
"""
template_name = "identity/view.html"
paginate_by = 5
paginate_by = 25
def get(self, request, handle):
# Make sure we understand this handle
@ -140,6 +140,45 @@ class IdentityFeed(Feed):
return item.published
class IdentityFollows(ListView):
"""
Shows following/followers for an identity.
"""
template_name = "identity/follows.html"
paginate_by = 25
inbound = False
def get(self, request, handle):
self.identity = by_handle_or_404(
self.request,
handle,
local=False,
)
if not Config.load_identity(self.identity).visible_follows:
raise Http404("Hidden follows")
return super().get(request, identity=self.identity)
def get_queryset(self):
if self.inbound:
return IdentityService(self.identity).followers()
else:
return IdentityService(self.identity).following()
def get_context_data(self):
context = super().get_context_data()
context["identity"] = self.identity
context["inbound"] = self.inbound
context["follows_page"] = True
context["followers_count"] = self.identity.inbound_follows.filter(
state__in=FollowStates.group_active()
).count()
context["following_count"] = self.identity.outbound_follows.filter(
state__in=FollowStates.group_active()
).count()
return context
@method_decorator(identity_required, name="dispatch")
class ActionIdentity(View):
def post(self, request, handle):