Add support to dismiss notifications (#605)

This commit is contained in:
Humberto Rocha 2023-07-11 18:37:03 -04:00 committed by GitHub
parent 1cc9c16b8c
commit 5f49f9b2bb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 102 additions and 9 deletions

View File

@ -0,0 +1,18 @@
# Generated by Django 4.2.2 on 2023-07-09 17:25
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("activities", "0017_stator_next_change"),
]
operations = [
migrations.AddField(
model_name="timelineevent",
name="dismissed",
field=models.BooleanField(default=False),
),
]

View File

@ -55,6 +55,7 @@ class TimelineEvent(models.Model):
published = models.DateTimeField(default=timezone.now) published = models.DateTimeField(default=timezone.now)
seen = models.BooleanField(default=False) seen = models.BooleanField(default=False)
dismissed = models.BooleanField(default=False)
created = models.DateTimeField(auto_now_add=True) created = models.DateTimeField(auto_now_add=True)

View File

@ -77,7 +77,7 @@ class TimelineService:
def notifications(self, types: list[str]) -> models.QuerySet[TimelineEvent]: def notifications(self, types: list[str]) -> models.QuerySet[TimelineEvent]:
return ( return (
self.event_queryset() self.event_queryset()
.filter(identity=self.identity, type__in=types) .filter(identity=self.identity, type__in=types, dismissed=False)
.order_by("-created") .order_by("-created")
) )

View File

@ -76,7 +76,9 @@ urlpatterns = [
path("v1/statuses/<id>/source", statuses.status_source), path("v1/statuses/<id>/source", statuses.status_source),
# Notifications # Notifications
path("v1/notifications", notifications.notifications), path("v1/notifications", notifications.notifications),
path("v1/notifications/clear", notifications.dismiss_notifications),
path("v1/notifications/<id>", notifications.get_notification), path("v1/notifications/<id>", notifications.get_notification),
path("v1/notifications/<id>/dismiss", notifications.dismiss_notification),
# Polls # Polls
path("v1/polls/<id>", polls.get_poll), path("v1/polls/<id>", polls.get_poll),
path("v1/polls/<id>/votes", polls.vote_poll), path("v1/polls/<id>/votes", polls.vote_poll),

View File

@ -72,3 +72,29 @@ def get_notification(
id=id, id=id,
) )
return schemas.Notification.from_timeline_event(notification) return schemas.Notification.from_timeline_event(notification)
@scope_required("write:notifications")
@api_view.post
def dismiss_notifications(request: HttpRequest) -> dict:
TimelineService(request.identity).notifications(
list(NOTIFICATION_TYPES.values())
).update(dismissed=True)
return {}
@scope_required("write:notifications")
@api_view.post
def dismiss_notification(request: HttpRequest, id: str) -> dict:
notification = get_object_or_404(
TimelineService(request.identity).notifications(
list(NOTIFICATION_TYPES.values())
),
id=id,
)
notification.dismissed = True
notification.save()
return {}

View File

@ -11,11 +11,10 @@ def test_notifications(api_client, identity, remote_identity):
subject_identity=remote_identity, subject_identity=remote_identity,
) )
response = api_client.get("/api/v1/notifications").json() data = api_client.get("/api/v1/notifications").json()
assert len(data) == 1
assert len(response) == 1 assert data[0]["type"] == "follow"
assert response[0]["type"] == "follow" assert data[0]["account"]["id"] == str(remote_identity.id)
assert response[0]["account"]["id"] == str(remote_identity.id)
event.delete() event.delete()
@ -28,8 +27,55 @@ def test_get_notification(api_client, identity, remote_identity):
subject_identity=remote_identity, subject_identity=remote_identity,
) )
response = api_client.get(f"/api/v1/notifications/{event.id}").json() data = api_client.get(f"/api/v1/notifications/{event.id}").json()
assert response["type"] == "follow" assert data["type"] == "follow"
assert response["account"]["id"] == str(remote_identity.id) assert data["account"]["id"] == str(remote_identity.id)
event.delete() event.delete()
@pytest.mark.django_db
def test_dismiss_notifications(api_client, identity, identity2, remote_identity):
TimelineEvent.objects.create(
identity=identity,
type=TimelineEvent.Types.followed,
subject_identity=identity2,
)
TimelineEvent.objects.create(
identity=identity,
type=TimelineEvent.Types.followed,
subject_identity=remote_identity,
)
data = api_client.get("/api/v1/notifications").json()
assert len(data) == 2
response = api_client.post("/api/v1/notifications/clear", {})
assert response.status_code == 200
assert response.json() == {}
data = api_client.get("/api/v1/notifications").json()
assert len(data) == 0
TimelineEvent.objects.filter(identity=identity).delete()
@pytest.mark.django_db
def test_dismiss_notification(api_client, identity, remote_identity):
event = TimelineEvent.objects.create(
identity=identity,
type=TimelineEvent.Types.followed,
subject_identity=remote_identity,
)
data = api_client.get("/api/v1/notifications").json()
assert len(data) == 1
response = api_client.post(f"/api/v1/notifications/{event.id}/dismiss", {})
assert response.status_code == 200
assert response.json() == {}
data = api_client.get("/api/v1/notifications").json()
assert len(data) == 0
TimelineEvent.objects.filter(identity=identity).delete()