Corp/backend/communications/services/chat_notifications.py
lizerk 2768d2f1b3
Some checks failed
Deploy production / deploy (push) Failing after 14m49s
Restore reliable chat realtime and media playback
2026-09-10 20:26:58 +03:00

95 lines
3.0 KiB
Python

from dataclasses import dataclass
from django.db import transaction
from django.db.models import Q
from django.utils import timezone
from communications.notifications.config import NotificationVerbs
from notifications.cache import invalidate_unread_summary
from notifications.models import Notification
from notifications.push_lifecycle import dismiss_chat_push
from notifications.realtime import send_notifications_read_all_event
@dataclass(frozen=True)
class ChatNotificationReadResult:
notification_ids: list[int]
count: int
class ChatNotificationReadService:
"""Use case for reading notifications related to a specific chat."""
chat_related_verbs = (
NotificationVerbs.NEW_MESSAGE,
NotificationVerbs.ANNOUNCEMENT,
NotificationVerbs.MENTION,
NotificationVerbs.REPLY,
NotificationVerbs.ADDED_TO_CHAT,
NotificationVerbs.COMMENTED,
)
def mark_chat_notifications_as_read(
self,
*,
user,
chat_id: int,
up_to_message_id: int | None = None,
) -> ChatNotificationReadResult:
queryset = self._get_unread_queryset(user_id=user.id, chat_id=chat_id)
candidates = list(queryset.only("id", "data"))
notification_ids = [
notification.id
for notification in candidates
if self._is_at_or_before_message(
notification.data,
up_to_message_id=up_to_message_id,
)
]
if not notification_ids:
return ChatNotificationReadResult(notification_ids=[], count=0)
read_at = timezone.now()
count = queryset.filter(id__in=notification_ids).update(
unread=False,
timestamp_read=read_at,
)
invalidate_unread_summary(user.id)
def notify_after_commit():
invalidate_unread_summary(user.id)
send_notifications_read_all_event(user.id, notification_ids)
dismiss_chat_push(
user_id=user.id,
chat_id=chat_id,
notification_ids=notification_ids,
)
transaction.on_commit(notify_after_commit)
return ChatNotificationReadResult(
notification_ids=notification_ids,
count=count,
)
@staticmethod
def _is_at_or_before_message(data, *, up_to_message_id: int | None) -> bool:
if up_to_message_id is None:
return True
if not isinstance(data, dict) or data.get("message_id") is None:
return True
try:
return int(data["message_id"]) <= up_to_message_id
except (TypeError, ValueError):
return False
def _get_unread_queryset(self, *, user_id: int, chat_id: int):
return Notification.objects.filter(
Q(data__chat_id=chat_id) | Q(data__chat_id=str(chat_id)),
recipient_id=user_id,
unread=True,
deleted=False,
verb__in=self.chat_related_verbs,
)