Corp/backend/notifications/push_lifecycle.py

129 lines
3.8 KiB
Python

"""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,
)