chat/apps/chat/views/conversations.py
2026-07-28 13:17:01 +03:30

155 lines
5.3 KiB
Python

from uuid import UUID
from drf_spectacular.utils import OpenApiParameter, extend_schema
from rest_framework import status
from rest_framework.response import Response
from rest_framework.views import APIView
from apps.chat.models import ConversationParticipant
from apps.chat.serializers.conversations import (
ConversationListSerializer,
ConversationSerializer,
ConversationUserActionSerializer,
CreateConversationSerializer,
build_participants_info,
)
from apps.chat.services.account import AccountService
from apps.chat.services.conversation import ConversationService
from apps.chat.services.message import MessageService
from apps.chat.services.read_state import ReadStateService
class ConversationCreateView(APIView):
authentication_classes = []
permission_classes = []
@extend_schema(
request=CreateConversationSerializer,
responses={201: ConversationSerializer},
)
def post(self, request):
serializer = CreateConversationSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
conversation = ConversationService().create(
serializer.validated_data["user_1_uuid"],
serializer.validated_data["user_2_uuid"],
title=serializer.validated_data.get("title"),
)
return Response(
ConversationSerializer(conversation).data,
status=status.HTTP_201_CREATED,
)
class UserConversationListView(APIView):
authentication_classes = []
permission_classes = []
@extend_schema(
parameters=[
OpenApiParameter("page", int, description="0-based page number", default=0),
OpenApiParameter("page_size", int, description="Results per page", default=20),
],
responses={200: ConversationListSerializer(many=True)},
)
def get(self, request, user_uuid):
uid = UUID(str(user_uuid))
page = int(request.query_params.get("page", 0))
page_size = int(request.query_params.get("page_size", 20))
conv_svc = ConversationService()
read_svc = ReadStateService()
msg_svc = MessageService()
conversations = conv_svc.list_for_user(uid, page, page_size)
participants_by_conv = {
conv.uuid: list(
ConversationParticipant.objects.filter(conversation_id=conv.uuid)
.order_by("joined_at")
.values_list("user_uuid", flat=True)
)
for conv in conversations
}
all_participant_uuids = {
p for participants in participants_by_conv.values() for p in participants
}
# Single bulk lookup (cache-backed) across the whole page, instead of
# one accounts-service round trip per conversation.
info_map = AccountService().get_public_info(list(all_participant_uuids))
result = []
for conv in conversations:
participants = participants_by_conv[conv.uuid]
participants_info = build_participants_info(participants, info_map=info_map)
try:
has_unread = read_svc.has_unread(conv.uuid, uid)
except Exception:
has_unread = False
try:
last_msgs = msg_svc.list_messages(conv.uuid, page=0, per_page=1)
last_message = last_msgs[0] if last_msgs else None
except Exception:
last_message = None
result.append(
{
"uuid": conv.uuid,
"type": conv.type,
"status": conv.status,
"title": conv.title,
"closed_by_uuid": conv.closed_by_uuid,
"created_at": conv.created_at,
"participants": participants,
"participants_info": participants_info,
"has_unread": has_unread,
"last_message": last_message,
}
)
return Response(ConversationListSerializer(result, many=True).data)
class ConversationCloseView(APIView):
authentication_classes = []
permission_classes = []
@extend_schema(
request=ConversationUserActionSerializer,
responses={200: ConversationSerializer},
)
def post(self, request, chat_uuid):
serializer = ConversationUserActionSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
conversation = ConversationService().close(
UUID(str(chat_uuid)), serializer.validated_data["user_uuid"]
)
return Response(ConversationSerializer(conversation).data)
class ConversationReopenView(APIView):
authentication_classes = []
permission_classes = []
@extend_schema(
request=ConversationUserActionSerializer,
responses={200: ConversationSerializer, 403: None},
)
def post(self, request, chat_uuid):
serializer = ConversationUserActionSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
try:
conversation = ConversationService().reopen(
UUID(str(chat_uuid)), serializer.validated_data["user_uuid"]
)
except PermissionError as exc:
return Response({"detail": str(exc)}, status=status.HTTP_403_FORBIDDEN)
return Response(ConversationSerializer(conversation).data)