diff --git a/bookwyrm/migrations/0151_auto_20220705_0049.py b/bookwyrm/migrations/0151_auto_20220705_0049.py new file mode 100644 index 0000000000..6010e38e57 --- /dev/null +++ b/bookwyrm/migrations/0151_auto_20220705_0049.py @@ -0,0 +1,90 @@ +# Generated by Django 3.2.13 on 2022-07-05 00:49 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ("bookwyrm", "0150_readthrough_stopped_date"), + ] + + operations = [ + migrations.RemoveField( + model_name="notification", + name="related_book", + ), + migrations.AddField( + model_name="notification", + name="related_list_items", + field=models.ManyToManyField( + related_name="notifications", to="bookwyrm.ListItem" + ), + ), + migrations.AddField( + model_name="notification", + name="related_reports", + field=models.ManyToManyField(to="bookwyrm.Report"), + ), + migrations.AddField( + model_name="notification", + name="related_users", + field=models.ManyToManyField( + related_name="notifications", to=settings.AUTH_USER_MODEL + ), + ), + migrations.AlterField( + model_name="notification", + name="related_list_item", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="notifications_tmp", + to="bookwyrm.listitem", + ), + ), + migrations.AlterField( + model_name="notification", + name="related_report", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="notifications_tmp", + to="bookwyrm.report", + ), + ), + migrations.RunSQL( + sql=""" + INSERT INTO bookwyrm_notification_related_users (notification_id, user_id) + SELECT id, related_user_id + FROM bookwyrm_notification + WHERE bookwyrm_notification.related_user_id IS NOT NULL; + + INSERT INTO bookwyrm_notification_related_list_items (notification_id, listitem_id) + SELECT id, related_list_item_id + FROM bookwyrm_notification + WHERE bookwyrm_notification.related_list_item_id IS NOT NULL; + + INSERT INTO bookwyrm_notification_related_reports (notification_id, report_id) + SELECT id, related_report_id + FROM bookwyrm_notification + WHERE bookwyrm_notification.related_report_id IS NOT NULL; + + """, + reverse_sql=migrations.RunSQL.noop, + ), + migrations.RemoveField( + model_name="notification", + name="related_list_item", + ), + migrations.RemoveField( + model_name="notification", + name="related_report", + ), + migrations.RemoveField( + model_name="notification", + name="related_user", + ), + ] diff --git a/bookwyrm/migrations/0152_remove_notification_notification_type_valid.py b/bookwyrm/migrations/0152_remove_notification_notification_type_valid.py new file mode 100644 index 0000000000..f7471c0d25 --- /dev/null +++ b/bookwyrm/migrations/0152_remove_notification_notification_type_valid.py @@ -0,0 +1,17 @@ +# Generated by Django 3.2.13 on 2022-07-05 03:16 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ("bookwyrm", "0151_auto_20220705_0049"), + ] + + operations = [ + migrations.RemoveConstraint( + model_name="notification", + name="notification_type_valid", + ), + ] diff --git a/bookwyrm/migrations/0153_merge_20220706_2141.py b/bookwyrm/migrations/0153_merge_20220706_2141.py new file mode 100644 index 0000000000..03959f9ef8 --- /dev/null +++ b/bookwyrm/migrations/0153_merge_20220706_2141.py @@ -0,0 +1,13 @@ +# Generated by Django 3.2.13 on 2022-07-06 21:41 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ("bookwyrm", "0152_alter_report_user"), + ("bookwyrm", "0152_remove_notification_notification_type_valid"), + ] + + operations = [] diff --git a/bookwyrm/models/antispam.py b/bookwyrm/models/antispam.py index f506b6f193..dd2a6df263 100644 --- a/bookwyrm/models/antispam.py +++ b/bookwyrm/models/antispam.py @@ -3,7 +3,7 @@ import operator from django.apps import apps -from django.db import models +from django.db import models, transaction from django.db.models import Q from django.utils.translation import gettext_lazy as _ @@ -58,25 +58,20 @@ def automod_task(): return reporter = AutoMod.objects.first().created_by reports = automod_users(reporter) + automod_statuses(reporter) - if reports: - admins = User.objects.filter( - models.Q(user_permissions__name__in=["moderate_user", "moderate_post"]) - | models.Q(is_superuser=True) - ).all() - notification_model = apps.get_model( - "bookwyrm", "Notification", require_ready=True - ) + if not reports: + return + + admins = User.objects.filter( + models.Q(user_permissions__name__in=["moderate_user", "moderate_post"]) + | models.Q(is_superuser=True) + ).all() + notification_model = apps.get_model("bookwyrm", "Notification", require_ready=True) + with transaction.atomic(): for admin in admins: - notification_model.objects.bulk_create( - [ - notification_model( - user=admin, - related_report=r, - notification_type="REPORT", - ) - for r in reports - ] + notification, _ = notification_model.objects.get_or_create( + user=admin, notification_type=notification_model.REPORT, read=False ) + notification.related_repors.add(reports) def automod_users(reporter): diff --git a/bookwyrm/models/group.py b/bookwyrm/models/group.py index 05ed39a278..003b23d02c 100644 --- a/bookwyrm/models/group.py +++ b/bookwyrm/models/group.py @@ -140,16 +140,6 @@ def save(self, *args, **kwargs): # make an invitation super().save(*args, **kwargs) - # now send the invite - model = apps.get_model("bookwyrm.Notification", require_ready=True) - notification_type = "INVITE" - model.objects.create( - user=self.user, - related_user=self.group.user, - related_group=self.group, - notification_type=notification_type, - ) - @transaction.atomic def accept(self): """turn this request into the real deal""" @@ -157,25 +147,24 @@ def accept(self): model = apps.get_model("bookwyrm.Notification", require_ready=True) # tell the group owner - model.objects.create( - user=self.group.user, - related_user=self.user, + model.notify( + self.group.user, + self.user, related_group=self.group, - notification_type="ACCEPT", + notification_type=model.ACCEPT, ) # let the other members know about it for membership in self.group.memberships.all(): member = membership.user if member not in (self.user, self.group.user): - model.objects.create( - user=member, - related_user=self.user, + model.notify( + member, + self.user, related_group=self.group, - notification_type="JOIN", + notification_type=model.JOIN, ) def reject(self): """generate a Reject for this membership request""" - self.delete() diff --git a/bookwyrm/models/list.py b/bookwyrm/models/list.py index 0195020e00..63dd5b23f6 100644 --- a/bookwyrm/models/list.py +++ b/bookwyrm/models/list.py @@ -1,7 +1,6 @@ """ make a list of books!! """ import uuid -from django.apps import apps from django.core.exceptions import PermissionDenied from django.db import models from django.db.models import Q @@ -151,34 +150,12 @@ class ListItem(CollectionItemMixin, BookWyrmModel): collection_field = "book_list" def save(self, *args, **kwargs): - """create a notification too""" - created = not bool(self.id) + """Update the list's date""" super().save(*args, **kwargs) # tick the updated date on the parent list self.book_list.updated_date = timezone.now() self.book_list.save(broadcast=False, update_fields=["updated_date"]) - list_owner = self.book_list.user - model = apps.get_model("bookwyrm.Notification", require_ready=True) - # create a notification if somoene ELSE added to a local user's list - if created and list_owner.local and list_owner != self.user: - model.objects.create( - user=list_owner, - related_user=self.user, - related_list_item=self, - notification_type="ADD", - ) - - if self.book_list.group: - for membership in self.book_list.group.memberships.all(): - if membership.user != self.user: - model.objects.create( - user=membership.user, - related_user=self.user, - related_list_item=self, - notification_type="ADD", - ) - def raise_not_deletable(self, viewer): """the associated user OR the list owner can delete""" if self.book_list.user == viewer: diff --git a/bookwyrm/models/notification.py b/bookwyrm/models/notification.py index 417bf7591a..21a992b07d 100644 --- a/bookwyrm/models/notification.py +++ b/bookwyrm/models/notification.py @@ -1,77 +1,123 @@ """ alert a user to activity """ -from django.db import models +from django.db import models, transaction from django.dispatch import receiver from .base_model import BookWyrmModel -from . import Boost, Favorite, ImportJob, Report, Status, User - -# pylint: disable=line-too-long -NotificationType = models.TextChoices( - "NotificationType", - "FAVORITE REPLY MENTION TAG FOLLOW FOLLOW_REQUEST BOOST IMPORT ADD REPORT INVITE ACCEPT JOIN LEAVE REMOVE GROUP_PRIVACY GROUP_NAME GROUP_DESCRIPTION", -) +from . import Boost, Favorite, GroupMemberInvitation, ImportJob, ListItem, Report +from . import Status, User, UserFollowRequest class Notification(BookWyrmModel): """you've been tagged, liked, followed, etc""" + # Status interactions + FAVORITE = "FAVORITE" + BOOST = "BOOST" + REPLY = "REPLY" + MENTION = "MENTION" + TAG = "TAG" + + # Relationships + FOLLOW = "FOLLOW" + FOLLOW_REQUEST = "FOLLOW_REQUEST" + + # Imports + IMPORT = "IMPORT" + + # List activity + ADD = "ADD" + + # Admin + REPORT = "REPORT" + + # Groups + INVITE = "INVITE" + ACCEPT = "ACCEPT" + JOIN = "JOIN" + LEAVE = "LEAVE" + REMOVE = "REMOVE" + GROUP_PRIVACY = "GROUP_PRIVACY" + GROUP_NAME = "GROUP_NAME" + GROUP_DESCRIPTION = "GROUP_DESCRIPTION" + + # pylint: disable=line-too-long + NotificationType = models.TextChoices( + # there has got be a better way to do this + "NotificationType", + f"{FAVORITE} {REPLY} {MENTION} {TAG} {FOLLOW} {FOLLOW_REQUEST} {BOOST} {IMPORT} {ADD} {REPORT} {INVITE} {ACCEPT} {JOIN} {LEAVE} {REMOVE} {GROUP_PRIVACY} {GROUP_NAME} {GROUP_DESCRIPTION}", + ) + user = models.ForeignKey("User", on_delete=models.CASCADE) - related_book = models.ForeignKey("Edition", on_delete=models.CASCADE, null=True) - related_user = models.ForeignKey( - "User", on_delete=models.CASCADE, null=True, related_name="related_user" + read = models.BooleanField(default=False) + notification_type = models.CharField( + max_length=255, choices=NotificationType.choices + ) + + related_users = models.ManyToManyField( + "User", symmetrical=False, related_name="notifications" ) related_group = models.ForeignKey( "Group", on_delete=models.CASCADE, null=True, related_name="notifications" ) related_status = models.ForeignKey("Status", on_delete=models.CASCADE, null=True) related_import = models.ForeignKey("ImportJob", on_delete=models.CASCADE, null=True) - related_list_item = models.ForeignKey( - "ListItem", on_delete=models.CASCADE, null=True - ) - related_report = models.ForeignKey("Report", on_delete=models.CASCADE, null=True) - read = models.BooleanField(default=False) - notification_type = models.CharField( - max_length=255, choices=NotificationType.choices + related_list_items = models.ManyToManyField( + "ListItem", symmetrical=False, related_name="notifications" ) + related_reports = models.ManyToManyField("Report", symmetrical=False) - def save(self, *args, **kwargs): - """save, but don't make dupes""" - # there's probably a better way to do this - if self.__class__.objects.filter( - user=self.user, - related_book=self.related_book, - related_user=self.related_user, - related_group=self.related_group, - related_status=self.related_status, - related_import=self.related_import, - related_list_item=self.related_list_item, - related_report=self.related_report, - notification_type=self.notification_type, - ).exists(): + @classmethod + @transaction.atomic + def notify(cls, user, related_user, **kwargs): + """Create a notification""" + if related_user and (not user.local or user == related_user): return - super().save(*args, **kwargs) - - class Meta: - """checks if notifcation is in enum list for valid types""" + notification, _ = cls.objects.get_or_create(user=user, **kwargs) + if related_user: + notification.related_users.add(related_user) + notification.read = False + notification.save() - constraints = [ - models.CheckConstraint( - check=models.Q(notification_type__in=NotificationType.values), - name="notification_type_valid", + @classmethod + @transaction.atomic + def notify_list_item(cls, user, list_item): + """Group the notifications around the list items, not the user""" + related_user = list_item.user + notification = cls.objects.filter( + user=user, + related_users=related_user, + related_list_items__book_list=list_item.book_list, + notification_type=Notification.ADD, + ).first() + if not notification: + notification = cls.objects.create( + user=user, notification_type=Notification.ADD ) - ] + notification.related_users.add(related_user) + notification.related_list_items.add(list_item) + notification.read = False + notification.save() + + @classmethod + def unnotify(cls, user, related_user, **kwargs): + """Remove a user from a notification and delete it if that was the only user""" + try: + notification = cls.objects.filter(user=user, **kwargs).get() + except Notification.DoesNotExist: + return + notification.related_users.remove(related_user) + if not notification.related_users.count(): + notification.delete() @receiver(models.signals.post_save, sender=Favorite) # pylint: disable=unused-argument def notify_on_fav(sender, instance, *args, **kwargs): """someone liked your content, you ARE loved""" - if not instance.status.user.local or instance.status.user == instance.user: - return - Notification.objects.create( - user=instance.status.user, - notification_type="FAVORITE", - related_user=instance.user, + Notification.notify( + instance.status.user, + instance.user, related_status=instance.status, + notification_type=Notification.FAVORITE, ) @@ -81,15 +127,16 @@ def notify_on_unfav(sender, instance, *args, **kwargs): """oops, didn't like that after all""" if not instance.status.user.local: return - Notification.objects.filter( - user=instance.status.user, - related_user=instance.user, + Notification.unnotify( + instance.status.user, + instance.user, related_status=instance.status, - notification_type="FAVORITE", - ).delete() + notification_type=Notification.FAVORITE, + ) @receiver(models.signals.post_save) +@transaction.atomic # pylint: disable=unused-argument def notify_user_on_mention(sender, instance, *args, **kwargs): """creating and deleting statuses with @ mentions and replies""" @@ -105,22 +152,23 @@ def notify_user_on_mention(sender, instance, *args, **kwargs): and instance.reply_parent.user != instance.user and instance.reply_parent.user.local ): - Notification.objects.create( - user=instance.reply_parent.user, - notification_type="REPLY", - related_user=instance.user, + Notification.notify( + instance.reply_parent.user, + instance.user, related_status=instance, + notification_type=Notification.REPLY, ) + for mention_user in instance.mention_users.all(): # avoid double-notifying about this status if not mention_user.local or ( instance.reply_parent and mention_user == instance.reply_parent.user ): continue - Notification.objects.create( - user=mention_user, - notification_type="MENTION", - related_user=instance.user, + Notification.notify( + mention_user, + instance.user, + notification_type=Notification.MENTION, related_status=instance, ) @@ -135,11 +183,11 @@ def notify_user_on_boost(sender, instance, *args, **kwargs): ): return - Notification.objects.create( - user=instance.boosted_status.user, + Notification.notify( + instance.boosted_status.user, + instance.user, related_status=instance.boosted_status, - related_user=instance.user, - notification_type="BOOST", + notification_type=Notification.BOOST, ) @@ -147,12 +195,12 @@ def notify_user_on_boost(sender, instance, *args, **kwargs): # pylint: disable=unused-argument def notify_user_on_unboost(sender, instance, *args, **kwargs): """unboosting a status""" - Notification.objects.filter( - user=instance.boosted_status.user, + Notification.unnotify( + instance.boosted_status.user, + instance.user, related_status=instance.boosted_status, - related_user=instance.user, - notification_type="BOOST", - ).delete() + notification_type=Notification.BOOST, + ) @receiver(models.signals.post_save, sender=ImportJob) @@ -166,12 +214,13 @@ def notify_user_on_import_complete( return Notification.objects.create( user=instance.user, - notification_type="IMPORT", + notification_type=Notification.IMPORT, related_import=instance, ) @receiver(models.signals.post_save, sender=Report) +@transaction.atomic # pylint: disable=unused-argument def notify_admins_on_report(sender, instance, *args, **kwargs): """something is up, make sure the admins know""" @@ -181,8 +230,72 @@ def notify_admins_on_report(sender, instance, *args, **kwargs): | models.Q(is_superuser=True) ).all() for admin in admins: - Notification.objects.create( + notification, _ = Notification.objects.get_or_create( user=admin, - related_report=instance, - notification_type="REPORT", + notification_type=Notification.REPORT, + read=False, + ) + notification.related_reports.add(instance) + + +@receiver(models.signals.post_save, sender=GroupMemberInvitation) +# pylint: disable=unused-argument +def notify_user_on_group_invite(sender, instance, *args, **kwargs): + """Cool kids club here we come""" + Notification.notify( + instance.user, + instance.group.user, + related_group=instance.group, + notification_type=Notification.INVITE, + ) + + +@receiver(models.signals.post_save, sender=ListItem) +@transaction.atomic +# pylint: disable=unused-argument +def notify_user_on_list_item_add(sender, instance, created, *args, **kwargs): + """Someone added to your list""" + if not created: + return + + list_owner = instance.book_list.user + # create a notification if somoene ELSE added to a local user's list + if list_owner.local and list_owner != instance.user: + # keep the related_user singular, group the items + Notification.notify_list_item(list_owner, instance) + + if instance.book_list.group: + for membership in instance.book_list.group.memberships.all(): + if membership.user != instance.user: + Notification.notify_list_item(membership.user, instance) + + +@receiver(models.signals.post_save, sender=UserFollowRequest) +@transaction.atomic +# pylint: disable=unused-argument +def notify_user_on_follow(sender, instance, created, *args, **kwargs): + """Someone added to your list""" + if not created or not instance.user_object.local: + return + + manually_approves = instance.user_object.manually_approves_followers + if manually_approves: + # don't group notifications + notification = Notification.objects.filter( + user=instance.user_object, + related_users=instance.user_subject, + notification_type=Notification.FOLLOW_REQUEST, + ).first() + if not notification: + notification = Notification.objects.create( + user=instance.user_object, notification_type=Notification.FOLLOW_REQUEST + ) + notification.related_users.set([instance.user_subject]) + notification.read = False + notification.save() + else: + Notification.notify( + instance.user_object, + instance.user_subject, + notification_type=Notification.FOLLOW, ) diff --git a/bookwyrm/models/relationship.py b/bookwyrm/models/relationship.py index 3132005147..082294c0e0 100644 --- a/bookwyrm/models/relationship.py +++ b/bookwyrm/models/relationship.py @@ -1,5 +1,4 @@ """ defines relationships between users """ -from django.apps import apps from django.core.cache import cache from django.db import models, transaction, IntegrityError from django.db.models import Q @@ -148,14 +147,6 @@ def save(self, *args, broadcast=True, **kwargs): # pylint: disable=arguments-di if not manually_approves: self.accept() - model = apps.get_model("bookwyrm.Notification", require_ready=True) - notification_type = "FOLLOW_REQUEST" if manually_approves else "FOLLOW" - model.objects.create( - user=self.user_object, - related_user=self.user_subject, - notification_type=notification_type, - ) - def get_accept_reject_id(self, status): """get id for sending an accept or reject of a local user""" diff --git a/bookwyrm/settings.py b/bookwyrm/settings.py index 1254eabd79..140f715e10 100644 --- a/bookwyrm/settings.py +++ b/bookwyrm/settings.py @@ -11,7 +11,7 @@ env = Env() env.read_env() DOMAIN = env("DOMAIN") -VERSION = "0.4.1" +VERSION = "0.4.2" RELEASE_API = env( "RELEASE_API", diff --git a/bookwyrm/templates/landing/password_reset_request.html b/bookwyrm/templates/landing/password_reset_request.html index 5d877442fe..b06668e578 100644 --- a/bookwyrm/templates/landing/password_reset_request.html +++ b/bookwyrm/templates/landing/password_reset_request.html @@ -9,7 +9,13 @@

{% trans "Reset Password" %}

- {% if message %}

{{ message }}

{% endif %} + {% if sent_message %} +

+ {% blocktrans trimmed %} + A password reset link will be sent to {{ email }} if there is an account using that email address. + {% endblocktrans %} +

+ {% endif %}

{% trans "A link to reset your password will be sent to your email address" %}

diff --git a/bookwyrm/templates/notifications/items/accept.html b/bookwyrm/templates/notifications/items/accept.html index 5f26008f47..f97c93c1cb 100644 --- a/bookwyrm/templates/notifications/items/accept.html +++ b/bookwyrm/templates/notifications/items/accept.html @@ -13,8 +13,34 @@ {% block description %} +{% if other_user_count == 0 %} + + {% blocktrans trimmed with group_name=notification.related_group.name group_path=notification.related_group.local_path %} + {{ related_user }} + accepted your invitation to join group + "{{ group_name }}" + {% endblocktrans %} + +{% elif other_user_count == 1 %} + {% blocktrans trimmed with group_name=notification.related_group.name group_path=notification.related_group.local_path %} - accepted your invitation to join group "{{ group_name }}" + {{ related_user }} + and + {{ second_user }} + accepted your invitation to join group + "{{ group_name }}" {% endblocktrans %} +{% else %} + + {% blocktrans trimmed with group_name=notification.related_group.name group_path=notification.related_group.local_path %} + {{ related_user }} + and + {{ other_user_display_count }} others + accepted your invitation to join group + "{{ group_name }}" + {% endblocktrans %} + +{% endif %} + {% endblock %} diff --git a/bookwyrm/templates/notifications/items/add.html b/bookwyrm/templates/notifications/items/add.html index 6a0183ebe9..fdd480ee11 100644 --- a/bookwyrm/templates/notifications/items/add.html +++ b/bookwyrm/templates/notifications/items/add.html @@ -1,14 +1,16 @@ {% extends 'notifications/items/layout.html' %} - {% load i18n %} {% load utilities %} +{% load humanize %} {% block primary_link %}{% spaceless %} -{% if notification.related_list_item.approved %} - {{ notification.related_list_item.book_list.local_path }} +{% with related_list=notification.related_list_items.first.book_list %} +{% if related_list.curation != "curated" %} + {{ related_list.local_path }} {% else %} - {% url 'list-curate' notification.related_list_item.book_list.id %} + {% url 'list-curate' related_list.id %} {% endif %} +{% endwith %} {% endspaceless %}{% endblock %} {% block icon %} @@ -16,25 +18,89 @@ {% endblock %} {% block description %} -{% with book_path=notification.related_list_item.book.local_path %} -{% with book_title=notification.related_list_item.book|book_title %} -{% with list_name=notification.related_list_item.book_list.name %} +{% with related_list=notification.related_list_items.first.book_list %} +{% with book_path=notification.related_list_items.first.book.local_path %} +{% with book_title=notification.related_list_items.first.book|book_title %} +{% with second_book_path=notification.related_list_items.all.1.book.local_path %} +{% with second_book_title=notification.related_list_items.all.1.book|book_title %} +{% with list_name=related_list.name %} + +{% url 'list' related_list.id as list_path %} +{% url 'list-curate' related_list.id as list_curate_path %} - {% if notification.related_list_item.approved %} - {% blocktrans trimmed with list_path=notification.related_list_item.book_list.local_path %} +{% if notification.related_list_items.count == 1 %} + {% if related_list.curation != "curated" %} + {% blocktrans trimmed %} + {{ related_user }} + added {{ book_title }} + to your list "{{ list_name }}" + {% endblocktrans %} + {% else %} + {% blocktrans trimmed %} + {{ related_user }} + suggested adding {{ book_title }} + to your list "{{ list_name }}" + {% endblocktrans %} + {% endif %} +{% elif notification.related_list_items.count == 2 %} + {% if related_list.curation != "curated" %} + {% blocktrans trimmed %} + {{ related_user }} + added {{ book_title }} + and {{ second_book_title }} + to your list "{{ list_name }}" + {% endblocktrans %} + {% else %} + {% blocktrans trimmed %} + {{ related_user }} + suggested adding {{ book_title }} + and {{ second_book_title }} + to your list "{{ list_name }}" + {% endblocktrans %} + {% endif %} +{% else %} + {% with count=notification.related_list_items.count|add:"-2" %} + {% with display_count=count|intcomma %} + {% if related_list.curation != "curated" %} - added {{ book_title }} to your list "{{ list_name }}" + {% blocktrans trimmed count counter=count %} + {{ related_user }} + added {{ book_title }}, + {{ second_book_title }}, + and {{ display_count }} other book + to your list "{{ list_name }}" + {% plural %} + {{ related_user }} + added {{ book_title }}, + {{ second_book_title }}, + and {{ display_count }} other books + to your list "{{ list_name }}" + {% endblocktrans %} - {% endblocktrans %} {% else %} - {% url 'list-curate' notification.related_list_item.book_list.id as list_path %} - {% blocktrans trimmed with list_path=list_path %} - suggested adding {{ book_title }} to your list "{{ list_name }}" + {% blocktrans trimmed count counter=count %} + {{ related_user }} + suggested adding {{ book_title }}, + {{ second_book_title }}, + and {{ display_count }} other book + to your list "{{ list_name }}" + {% plural %} + {{ related_user }} + suggested adding {{ book_title }}, + {{ second_book_title }}, + and {{ display_count }} other books + to your list "{{ list_name }}" + {% endblocktrans %} - {% endblocktrans %} {% endif %} + {% endwith %} + {% endwith %} +{% endif %} +{% endwith %} +{% endwith %} +{% endwith %} {% endwith %} {% endwith %} {% endwith %} diff --git a/bookwyrm/templates/notifications/items/boost.html b/bookwyrm/templates/notifications/items/boost.html index 5e3e115137..a1a0a24fbf 100644 --- a/bookwyrm/templates/notifications/items/boost.html +++ b/bookwyrm/templates/notifications/items/boost.html @@ -16,29 +16,97 @@ {% with related_status.local_path as related_path %} {% if related_status.status_type == 'Review' %} - {% blocktrans trimmed %} + {% if other_user_count == 0 %} - boosted your review of {{ book_title }} + {% blocktrans trimmed %} + {{ related_user }} boosted your review of {{ book_title }} + {% endblocktrans %} - {% endblocktrans %} + {% elif other_user_count == 1 %} + + {% blocktrans trimmed %} + {{ related_user }} + and + {{ second_user }} + boosted your review of {{ book_title }} + {% endblocktrans %} + + {% else %} + + {% blocktrans trimmed %} + {{ related_user }} and {{ other_user_display_count }} others boosted your review of {{ book_title }} + {% endblocktrans %} + + {% endif %} {% elif related_status.status_type == 'Comment' %} - {% blocktrans trimmed %} + {% if other_user_count == 0 %} + + {% blocktrans trimmed %} + {{ related_user }} boosted your comment on {{ book_title }} + {% endblocktrans %} + + {% elif other_user_count == 1 %} + + {% blocktrans trimmed %} + {{ related_user }} + and + {{ second_user }} + boosted your comment on {{ book_title }} + {% endblocktrans %} + + {% else %} - boosted your comment on{{ book_title }} + {% blocktrans trimmed %} + {{ related_user }} and {{ other_user_display_count }} others boosted your comment on {{ book_title }} + {% endblocktrans %} - {% endblocktrans %} + {% endif %} {% elif related_status.status_type == 'Quotation' %} - {% blocktrans trimmed %} + {% if other_user_count == 0 %} - boosted your quote from {{ book_title }} + {% blocktrans trimmed %} + {{ related_user }} boosted your quote from {{ book_title }} + {% endblocktrans %} - {% endblocktrans %} + {% elif other_user_count == 1 %} + + {% blocktrans trimmed %} + {{ related_user }} + and + {{ second_user }} + boosted your quote from {{ book_title }} + {% endblocktrans %} + + {% else %} + + {% blocktrans trimmed %} + {{ related_user }} and {{ other_user_display_count }} others boosted your quote from {{ book_title }} + {% endblocktrans %} + + {% endif %} {% else %} - {% blocktrans trimmed %} + {% if other_user_count == 0 %} + + {% blocktrans trimmed %} + {{ related_user }} boosted your status + {% endblocktrans %} + + {% elif other_user_count == 1 %} + + {% blocktrans trimmed %} + {{ related_user }} + and + {{ second_user }} + boosted your status + {% endblocktrans %} + + {% else %} - boosted your status + {% blocktrans trimmed %} + {{ related_user }} and {{ other_user_display_count }} others boosted your status + {% endblocktrans %} - {% endblocktrans %} + {% endif %} {% endif %} {% endwith %} diff --git a/bookwyrm/templates/notifications/items/fav.html b/bookwyrm/templates/notifications/items/fav.html index eb29ebc26f..0bface201f 100644 --- a/bookwyrm/templates/notifications/items/fav.html +++ b/bookwyrm/templates/notifications/items/fav.html @@ -16,29 +16,98 @@ {% with related_status.local_path as related_path %} {% if related_status.status_type == 'Review' %} - {% blocktrans trimmed %} + {% if other_user_count == 0 %} - liked your review of {{ book_title }} + {% blocktrans trimmed %} + {{ related_user }} liked your review of {{ book_title }} + {% endblocktrans %} - {% endblocktrans %} + {% elif other_user_count == 1 %} + + {% blocktrans trimmed %} + {{ related_user }} + and + {{ second_user }} + liked your review of {{ book_title }} + {% endblocktrans %} + + {% else %} + + {% blocktrans trimmed %} + {{ related_user }} and {{ other_user_display_count }} others liked your review of {{ book_title }} + {% endblocktrans %} + + {% endif %} {% elif related_status.status_type == 'Comment' %} - {% blocktrans trimmed %} + {% if other_user_count == 0 %} + + {% blocktrans trimmed %} + {{ related_user }} liked your comment on {{ book_title }} + {% endblocktrans %} - liked your comment on {{ book_title }} + {% elif other_user_count == 1 %} - {% endblocktrans %} + {% blocktrans trimmed %} + {{ related_user }} + and + {{ second_user }} + liked your comment on {{ book_title }} + {% endblocktrans %} + + {% else %} + + {% blocktrans trimmed %} + {{ related_user }} and {{ other_user_display_count }} others liked your comment on {{ book_title }} + {% endblocktrans %} + + {% endif %} {% elif related_status.status_type == 'Quotation' %} - {% blocktrans trimmed %} + {% if other_user_count == 0 %} + + {% blocktrans trimmed %} + {{ related_user }} liked your quote from {{ book_title }} + {% endblocktrans %} - liked your quote from {{ book_title }} + {% elif other_user_count == 1 %} - {% endblocktrans %} + {% blocktrans trimmed %} + {{ related_user }} + and + {{ second_user }} + liked your quote from {{ book_title }} + {% endblocktrans %} + + {% else %} + + {% blocktrans trimmed %} + {{ related_user }} and {{ other_user_display_count }} others liked your quote from {{ book_title }} + {% endblocktrans %} + + {% endif %} {% else %} - {% blocktrans trimmed %} + {% if other_user_count == 0 %} + + {% blocktrans trimmed %} + {{ related_user }} liked your status + {% endblocktrans %} + + {% elif other_user_count == 1 %} + + {% blocktrans trimmed %} + {{ related_user }} + and + {{ second_user }} + liked your status + {% endblocktrans %} + + {% else %} + + {% blocktrans trimmed %} + {{ related_user }} and {{ other_user_display_count }} others liked your status + {% endblocktrans %} - liked your status + {% endif %} - {% endblocktrans %} {% endif %} {% endwith %} diff --git a/bookwyrm/templates/notifications/items/follow.html b/bookwyrm/templates/notifications/items/follow.html index 3518e7b1b7..7303bb8b85 100644 --- a/bookwyrm/templates/notifications/items/follow.html +++ b/bookwyrm/templates/notifications/items/follow.html @@ -4,7 +4,7 @@ {% load utilities %} {% block primary_link %}{% spaceless %} - {{ notification.related_user.local_path }} +{% url 'user-followers' request.user.localname %} {% endspaceless %}{% endblock %} {% block icon %} @@ -12,6 +12,19 @@ {% endblock %} {% block description %} - {% trans "followed you" %} - {% include 'snippets/follow_button.html' with user=notification.related_user %} +{% if related_user_count == 1 %} + {% blocktrans trimmed %} + {{ related_user }} followed you + {% endblocktrans %} +{% elif related_user_count == 2 %} + {% blocktrans trimmed %} + {{ related_user }} and + {{ second_user }} followed you + {% endblocktrans %} +{% else %} + {% blocktrans trimmed %} + {{ related_user }} and {{ other_user_display_count }} others followed you + {% endblocktrans %} +{% endif %} + {% endblock %} diff --git a/bookwyrm/templates/notifications/items/follow_request.html b/bookwyrm/templates/notifications/items/follow_request.html index 9cec8116aa..320c69e430 100644 --- a/bookwyrm/templates/notifications/items/follow_request.html +++ b/bookwyrm/templates/notifications/items/follow_request.html @@ -3,13 +3,19 @@ {% load i18n %} {% load utilities %} +{% block primary_link %}{% spaceless %} +{% url 'user-followers' request.user.localname %} +{% endspaceless %}{% endblock %} + {% block icon %} {% endblock %} {% block description %} - {% trans "sent you a follow request" %} + {% blocktrans trimmed %} + {{ related_user }} sent you a follow request + {% endblocktrans %}
- {% include 'snippets/follow_request_buttons.html' with user=notification.related_user %} + {% include 'snippets/follow_request_buttons.html' with user=notification.related_users.first %}
{% endblock %} diff --git a/bookwyrm/templates/notifications/items/invite.html b/bookwyrm/templates/notifications/items/invite.html index aff416b07c..ab92be5a1e 100644 --- a/bookwyrm/templates/notifications/items/invite.html +++ b/bookwyrm/templates/notifications/items/invite.html @@ -12,11 +12,15 @@ {% endblock %} {% block description %} + {% blocktrans trimmed with group_name=notification.related_group.name group_path=notification.related_group.local_path %} -invited you to join the group "{{ group_name }}" + {{ related_user }} + invited you to join the group + "{{ group_name }}" {% endblocktrans %} +
{% include 'snippets/join_invitation_buttons.html' with group=notification.related_group %}
-{% endblock %} +{% endblock %} diff --git a/bookwyrm/templates/notifications/items/layout.html b/bookwyrm/templates/notifications/items/layout.html index e7c3b31478..3830e7e40a 100644 --- a/bookwyrm/templates/notifications/items/layout.html +++ b/bookwyrm/templates/notifications/items/layout.html @@ -1,5 +1,9 @@ {% load notification_page_tags %} +{% load humanize %} {% related_status notification as related_status %} + +{% with related_users=notification.related_users.all.distinct %} +{% with related_user_count=notification.related_users.count %}
@@ -9,14 +13,43 @@
+ {% if related_user_count > 1 %} +
+ +
+ {% endif %}
-

- {% if notification.related_user %} - {% include 'snippets/avatar.html' with user=notification.related_user %} - {{ notification.related_user.display_name }} - {% endif %} + {% if related_user_count == 1 %} + {% with user=related_users.first %} + {% spaceless %} + + {% include 'snippets/avatar.html' with user=user %} + + {% endspaceless %} + {% endwith %} + {% endif %} + + {% with related_user=related_users.first.display_name %} + {% with related_user_link=related_users.first.local_path %} + {% with second_user=related_users.1.display_name %} + {% with second_user_link=related_users.1.local_path %} + {% with other_user_count=related_user_count|add:"-1" %} + {% with other_user_display_count=other_user_count|intcomma %} {% block description %}{% endblock %} -

+ {% endwith %} + {% endwith %} + {% endwith %} + {% endwith %} + {% endwith %} + {% endwith %}
{% if related_status %} @@ -27,4 +60,5 @@
- +{% endwith %} +{% endwith %} diff --git a/bookwyrm/templates/notifications/items/leave.html b/bookwyrm/templates/notifications/items/leave.html index c17a1986ea..99b6a23242 100644 --- a/bookwyrm/templates/notifications/items/leave.html +++ b/bookwyrm/templates/notifications/items/leave.html @@ -13,8 +13,34 @@ {% block description %} +{% if other_user_count == 0 %} + + {% blocktrans trimmed with group_name=notification.related_group.name group_path=notification.related_group.local_path %} + {{ related_user }} + has left your group + "{{ group_name }}" + {% endblocktrans %} + +{% elif other_user_count == 1 %} + {% blocktrans trimmed with group_name=notification.related_group.name group_path=notification.related_group.local_path %} - has left your group "{{ group_name }}" + {{ related_user }} + and + {{ second_user }} + have left your group + "{{ group_name }}" {% endblocktrans %} +{% else %} + + {% blocktrans trimmed with group_name=notification.related_group.name group_path=notification.related_group.local_path %} + {{ related_user }} + and + {{ other_user_display_count }} others + have left your group + "{{ group_name }}" + {% endblocktrans %} + +{% endif %} + {% endblock %} diff --git a/bookwyrm/templates/notifications/items/mention.html b/bookwyrm/templates/notifications/items/mention.html index e4e78a11ed..8640526358 100644 --- a/bookwyrm/templates/notifications/items/mention.html +++ b/bookwyrm/templates/notifications/items/mention.html @@ -19,25 +19,25 @@ {% if related_status.status_type == 'Review' %} {% blocktrans trimmed %} - mentioned you in a review of {{ book_title }} + {{ related_user }} mentioned you in a review of {{ book_title }} {% endblocktrans %} {% elif related_status.status_type == 'Comment' %} {% blocktrans trimmed %} - mentioned you in a comment on {{ book_title }} + {{ related_user }} mentioned you in a comment on {{ book_title }} {% endblocktrans %} {% elif related_status.status_type == 'Quotation' %} {% blocktrans trimmed %} - mentioned you in a quote from {{ book_title }} + {{ related_user }} mentioned you in a quote from {{ book_title }} {% endblocktrans %} {% else %} {% blocktrans trimmed %} - mentioned you in a status + {{ related_user }} mentioned you in a status {% endblocktrans %} {% endif %} diff --git a/bookwyrm/templates/notifications/items/reply.html b/bookwyrm/templates/notifications/items/reply.html index 30e7eff78a..099e22078c 100644 --- a/bookwyrm/templates/notifications/items/reply.html +++ b/bookwyrm/templates/notifications/items/reply.html @@ -20,25 +20,25 @@ {% if related_status.reply_parent.status_type == 'Review' %} {% blocktrans trimmed %} - replied to your review of {{ book_title }} + {{ related_user }} replied to your review of {{ book_title }} {% endblocktrans %} {% elif related_status.reply_parent.status_type == 'Comment' %} {% blocktrans trimmed %} - replied to your comment on {{ book_title }} + {{ related_user }} replied to your comment on {{ book_title }} {% endblocktrans %} {% elif related_status.reply_parent.status_type == 'Quotation' %} {% blocktrans trimmed %} - replied to your quote from {{ book_title }} + {{ related_user }} replied to your quote from {{ book_title }} {% endblocktrans %} {% else %} {% blocktrans trimmed %} - replied to your status + {{ related_user }} replied to your status {% endblocktrans %} {% endif %} diff --git a/bookwyrm/templates/notifications/items/report.html b/bookwyrm/templates/notifications/items/report.html index fdd5f00946..44756ce167 100644 --- a/bookwyrm/templates/notifications/items/report.html +++ b/bookwyrm/templates/notifications/items/report.html @@ -1,9 +1,9 @@ {% extends 'notifications/items/layout.html' %} - +{% load humanize %} {% load i18n %} {% block primary_link %}{% spaceless %} - {% url 'settings-report' notification.related_report.id %} +{% url 'settings-reports' %} {% endspaceless %}{% endblock %} {% block icon %} @@ -11,6 +11,10 @@ {% endblock %} {% block description %} - {% url 'settings-report' notification.related_report.id as path %} - {% blocktrans %}A new report needs moderation.{% endblocktrans %} + {% url 'settings-reports' as path %} + {% blocktrans trimmed count counter=notification.related_reports.count with display_count=notification.related_reports.count|intcomma %} + A new report needs moderation + {% plural %} + {{ display_count }} new reports need moderation + {% endblocktrans %} {% endblock %} diff --git a/bookwyrm/templates/search/barcode_modal.html b/bookwyrm/templates/search/barcode_modal.html index 07e95f59ef..70481b20a8 100644 --- a/bookwyrm/templates/search/barcode_modal.html +++ b/bookwyrm/templates/search/barcode_modal.html @@ -16,11 +16,11 @@
- +