Notifications: unsubscribe from extension approval activity (#177) #207

Merged
Oleg-Komarov merged 5 commits from approval-follow into main 2024-07-30 12:11:05 +02:00
4 changed files with 93 additions and 2 deletions

View File

@ -79,7 +79,27 @@
{% block extension_activity %} {% block extension_activity %}
<section id="activity" class="mt-4"> <section id="activity" class="mt-4">
<hr class="my-4"> <hr class="my-4">
<div class="align-items-center d-flex w-100">
<h2>Activity</h2> <h2>Activity</h2>
{% if request.user.is_authenticated and not is_maintainer %}
<form class="d-flex flex-column flex-grow-1" method="post" action="{% url 'reviewers:approval-follow' extension.slug %}">
{% csrf_token %}
{% if user_is_following %}
<button class="btn ms-auto" type="submit">
<i class="i-bell-off"></i> Unsubscribe
<input type="hidden" name="follow" value="" />
</button>
<small class="text-muted text-end">{% trans "You're receiving notifications because you're subscribed to this thread." %}</small>
{% else %}
<button class="btn ms-auto" type="submit">
<i class="i-bell"></i> Subscribe
<input type="hidden" name="follow" value="True" />
</button>
<small class="text-muted text-end">{% trans "You're not receiving notifications from this thread." %}</small>
{% endif %}
</form>
{% endif %}
</div>
{% if review_activity %} {% if review_activity %}
<ul class="activity-list"> <ul class="activity-list">

View File

@ -0,0 +1,50 @@
from django.test import TestCase
from django.urls import reverse
from common.tests.factories.extensions import create_version
from common.tests.factories.users import UserFactory
from notifications.models import Notification
from reviewers.models import ApprovalActivity
class TestNotifications(TestCase):
def test_unsubscribe(self):
version = create_version()
extension = version.extension
some_user = UserFactory()
some_user2 = UserFactory()
self.client.force_login(some_user)
url = reverse('reviewers:approval-comment', args=[extension.slug])
self.client.post(url, {'type': ApprovalActivity.ActivityType.COMMENT, 'message': 'lala'})
notification_nr = Notification.objects.filter(recipient=some_user).count()
self.client.force_login(some_user2)
url = reverse('reviewers:approval-comment', args=[extension.slug])
self.client.post(url, {'type': ApprovalActivity.ActivityType.COMMENT, 'message': 'lala2'})
new_notification_nr = Notification.objects.filter(recipient=some_user).count()
self.assertEqual(new_notification_nr, notification_nr + 1)
# unsubscribe and see what happens
self.client.force_login(some_user)
url = reverse('reviewers:approval-follow', args=[extension.slug])
self.client.post(url, {'follow': ''})
self.client.force_login(some_user2)
url = reverse('reviewers:approval-comment', args=[extension.slug])
self.client.post(url, {'type': ApprovalActivity.ActivityType.COMMENT, 'message': 'lala3'})
new_notification_nr2 = Notification.objects.filter(recipient=some_user).count()
self.assertEqual(new_notification_nr2, new_notification_nr)
# subscribe back
self.client.force_login(some_user)
url = reverse('reviewers:approval-follow', args=[extension.slug])
self.client.post(url, {'follow': '1'})
self.client.force_login(some_user2)
url = reverse('reviewers:approval-comment', args=[extension.slug])
self.client.post(url, {'type': ApprovalActivity.ActivityType.COMMENT, 'message': 'lala4'})
new_notification_nr3 = Notification.objects.filter(recipient=some_user).count()
self.assertEqual(new_notification_nr3, new_notification_nr2 + 1)

View File

@ -15,4 +15,9 @@ urlpatterns = [
views.ExtensionsApprovalFormView.as_view(), views.ExtensionsApprovalFormView.as_view(),
name='approval-comment', name='approval-comment',
), ),
path(
'approval-queue-follow/<slug:slug>/',
views.ExtensionsApprovalFollowView.as_view(),
name='approval-follow',
),
] ]

View File

@ -1,12 +1,14 @@
from collections import defaultdict from collections import defaultdict
import logging import logging
from actstream.actions import follow, is_following, unfollow
from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic.list import ListView from django.views.generic.list import ListView
from django.views.generic import DetailView, FormView from django.views.generic import DetailView, FormView
from django.shortcuts import reverse from django.shortcuts import redirect, reverse
import django.forms import django.forms
from constants.activity import Flag
from extensions.models import Extension from extensions.models import Extension
from reviewers.forms import CommentForm from reviewers.forms import CommentForm
from reviewers.models import ApprovalActivity from reviewers.models import ApprovalActivity
@ -134,6 +136,7 @@ class ExtensionsApprovalDetailView(DetailView):
form.fields['type'].widget.choices = choices form.fields['type'].widget.choices = choices
if len(choices) == 1: if len(choices) == 1:
form.fields['type'].widget = django.forms.HiddenInput() form.fields['type'].widget = django.forms.HiddenInput()
ctx['user_is_following'] = is_following(user, self.object, flag=Flag.REVIEWER)
return ctx return ctx
@ -158,3 +161,16 @@ class ExtensionsApprovalFormView(LoginRequiredMixin, FormView):
form.save() form.save()
self.approve_if_allowed(form) self.approve_if_allowed(form)
return super().form_valid(form) return super().form_valid(form)
class ExtensionsApprovalFollowView(LoginRequiredMixin, DetailView):
model = Extension
def post(self, request, *args, **kwargs):
extension = self.get_object()
user = request.user
if request.POST.get('follow'):
follow(user, extension, send_action=False, flag=Flag.REVIEWER)
else:
unfollow(user, extension, send_action=False, flag=Flag.REVIEWER)
return redirect(extension.get_review_url() + '#activity')