"""Synchronize stored and native browser notifications with chat events.""" from collections import defaultdict from django.db import transaction from django.utils import timezone from notifications.cache import invalidate_unread_summary from notifications.models import Notification from notifications.realtime import send_notifications_deleted_event PUSH_CONTROL_ID_BATCH_SIZE = 150 def queue_push_control( *, user_id: int, action: str, scope: dict, replacement_notification_id: int | None = None, ) -> None: def enqueue(): from notifications.tasks import send_push_control send_push_control.delay( user_id, action, scope, replacement_notification_id, ) transaction.on_commit(enqueue) def dismiss_chat_push( *, user_id: int, chat_id: int, notification_ids=None, ) -> None: scope = {"chat_id": chat_id} if notification_ids: scope["notification_ids"] = [ int(item) for item in list(notification_ids)[:PUSH_CONTROL_ID_BATCH_SIZE] ] queue_push_control( user_id=user_id, action="dismiss_chat", scope=scope, ) def dismiss_notifications_push(*, user_id: int, notification_ids) -> None: normalized_ids = [int(notification_id) for notification_id in notification_ids] for start in range(0, len(normalized_ids), PUSH_CONTROL_ID_BATCH_SIZE): queue_push_control( user_id=user_id, action="dismiss_notifications", scope={ "notification_ids": normalized_ids[ start : start + PUSH_CONTROL_ID_BATCH_SIZE ] }, ) def delete_message_notifications(*, chat_id: int, message_id: int) -> None: notifications = list( Notification.objects.filter( deleted=False, data__chat_id=chat_id, data__message_id=message_id, ).values("id", "recipient_id") ) if not notifications: return notification_ids = [item["id"] for item in notifications] Notification.objects.filter(id__in=notification_ids).update( deleted=True, unread=False, timestamp_read=timezone.now(), ) notifications_by_recipient = defaultdict(list) for item in notifications: notifications_by_recipient[item["recipient_id"]].append(item["id"]) for recipient_id, recipient_notification_ids in notifications_by_recipient.items(): invalidate_unread_summary(recipient_id) send_notifications_deleted_event( recipient_id, recipient_notification_ids, ) queue_push_control( user_id=recipient_id, action="dismiss_message", scope={"chat_id": chat_id, "message_id": message_id}, ) def replace_message_notifications(message) -> None: notifications = list( Notification.objects.filter( deleted=False, data__chat_id=message.chat_id, data__message_id=message.id, ) ) if not notifications: return description = (message.content or "").strip() if len(description) > 150: description = description[:147] + "..." grouped = defaultdict(list) for notification in notifications: notification.description = description grouped[notification.recipient_id].append(notification) Notification.objects.bulk_update(notifications, ["description"]) for recipient_id, recipient_notifications in grouped.items(): latest = max(recipient_notifications, key=lambda item: item.id) queue_push_control( user_id=recipient_id, action="replace_message", scope={"chat_id": message.chat_id, "message_id": message.id}, replacement_notification_id=latest.id, )