61 lines
2.2 KiB
Python
61 lines
2.2 KiB
Python
from __future__ import annotations
|
|
|
|
from django.db import transaction
|
|
from django.db.models import F, Q
|
|
from django.utils import timezone
|
|
|
|
from communications.mentions import mentioned_user_ids
|
|
from communications.models import ChatReadState, Message
|
|
from notifications.push_lifecycle import delete_message_notifications
|
|
|
|
|
|
def soft_delete_message(
|
|
message: Message,
|
|
*,
|
|
deleted_by,
|
|
replacement_content: str | None = None,
|
|
) -> bool:
|
|
"""Soft-delete a message and remove it from cached unread counters."""
|
|
with transaction.atomic():
|
|
locked_message = Message.objects.select_for_update().get(pk=message.pk)
|
|
if locked_message.is_deleted:
|
|
return False
|
|
|
|
unread_states = ChatReadState.objects.filter(
|
|
chat_id=locked_message.chat_id,
|
|
unread_count__gt=0,
|
|
).exclude(user_id=locked_message.author_id)
|
|
unread_states = unread_states.filter(
|
|
Q(last_read_message_id__isnull=True)
|
|
| Q(last_read_message_id__lt=locked_message.id)
|
|
)
|
|
unread_states.update(unread_count=F("unread_count") - 1)
|
|
|
|
mention_recipient_ids = mentioned_user_ids(locked_message.mentions)
|
|
if mention_recipient_ids:
|
|
unread_states.filter(
|
|
user_id__in=mention_recipient_ids,
|
|
unread_mention_count__gt=0,
|
|
).update(unread_mention_count=F("unread_mention_count") - 1)
|
|
|
|
locked_message.is_deleted = True
|
|
locked_message.deleted_at = timezone.now()
|
|
locked_message.deleted_by = deleted_by
|
|
update_fields = ["is_deleted", "deleted_at", "deleted_by"]
|
|
if replacement_content is not None:
|
|
locked_message.content = replacement_content
|
|
update_fields.append("content")
|
|
locked_message.save(update_fields=update_fields)
|
|
|
|
delete_message_notifications(
|
|
chat_id=locked_message.chat_id,
|
|
message_id=locked_message.id,
|
|
)
|
|
|
|
message.is_deleted = locked_message.is_deleted
|
|
message.deleted_at = locked_message.deleted_at
|
|
message.deleted_by = locked_message.deleted_by
|
|
if replacement_content is not None:
|
|
message.content = replacement_content
|
|
return True
|