Follows page
This commit is contained in:
parent
adf2449d37
commit
b3072c81ba
@ -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 {
|
||||
|
@ -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(),
|
||||
|
@ -13,16 +13,25 @@
|
||||
<img src="{{ identity.local_icon_url }}" class="icon">
|
||||
|
||||
{% if request.identity %}
|
||||
<form action="{{ identity.urls.action }}" method="POST" class="inline follow">
|
||||
{% csrf_token %}
|
||||
{% if follow %}
|
||||
<input type="hidden" name="action" value="unfollow">
|
||||
<button>Unfollow</button>
|
||||
{% else %}
|
||||
<input type="hidden" name="action" value="follow">
|
||||
<button>Follow</button>
|
||||
{% endif %}
|
||||
</form>
|
||||
{% if identity == request.identity %}
|
||||
<form class="inline follow">
|
||||
<a class="button" href="{% url "settings_profile" %}">Edit Profile</a>
|
||||
</form>
|
||||
{% else %}
|
||||
<form action="{{ identity.urls.action }}" method="POST" class="inline follow {% if reverse_follow %}has-reverse{% endif %}">
|
||||
{% csrf_token %}
|
||||
{% if reverse_follow %}
|
||||
<span class="reverse-follow">Follows You</span>
|
||||
{% endif %}
|
||||
{% if follow %}
|
||||
<input type="hidden" name="action" value="unfollow">
|
||||
<button>Unfollow</button>
|
||||
{% else %}
|
||||
<input type="hidden" name="action" value="follow">
|
||||
<button>Follow</button>
|
||||
{% endif %}
|
||||
</form>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{{ identity.name_or_handle }} <small>@{{ identity.handle }}</small>
|
||||
|
@ -6,6 +6,9 @@
|
||||
<a href="{% url "settings_interface" %}" {% if section == "interface" %}class="selected"{% endif %}>
|
||||
<i class="fa-solid fa-display"></i> Interface
|
||||
</a>
|
||||
<a href="{% url "settings_follows" %}" {% if section == "follows" %}class="selected"{% endif %}>
|
||||
<i class="fa-solid fa-arrow-right-arrow-left"></i> Follows
|
||||
</a>
|
||||
{% if request.user.admin %}
|
||||
<h3>Account</h3>
|
||||
<a href="{% url "settings_security" %}" {% if section == "security" %}class="selected"{% endif %}>
|
||||
|
25
templates/settings/follows.html
Normal file
25
templates/settings/follows.html
Normal file
@ -0,0 +1,25 @@
|
||||
{% extends "settings/base.html" %}
|
||||
|
||||
{% block subtitle %}Follows{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="icon-menu">
|
||||
{% for identity, details in identities %}
|
||||
<a class="option" href="{{ identity.urls.view }}">
|
||||
<img src="{{ identity.local_icon_url }}">
|
||||
<span class="handle">
|
||||
{{ identity.name_or_handle }}
|
||||
<small>@{{ identity.handle }}</small>
|
||||
</span>
|
||||
{% if details.outbound %}
|
||||
<span class="pill">Following</span>
|
||||
{% endif %}
|
||||
{% if details.inbound %}
|
||||
<span class="pill">Follows You</span>
|
||||
{% endif %}
|
||||
</a>
|
||||
{% empty %}
|
||||
<p class="option empty">You have no follows.</p>
|
||||
{% endfor %}
|
||||
</section>
|
||||
{% endblock %}
|
@ -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"):
|
||||
"""
|
||||
|
33
users/views/follows.py
Normal file
33
users/views/follows.py
Normal file
@ -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),
|
||||
}
|
@ -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,
|
||||
}
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user