updated chat andlayout

This commit is contained in:
David Bruno Vontor
2026-06-03 17:07:34 +02:00
parent 3d1965e5e6
commit bb09f0ccd3
13 changed files with 799 additions and 107 deletions

View File

@@ -120,9 +120,11 @@ class ChatConsumer(AsyncWebsocketConsumer):
"type": "new_chat_message",
"message_id": event["message_id"],
"message": event["message"],
"sender_id": event["sender_id"],
"sender_id": event.get("sender_id"),
"sender": event["sender"],
"sender_avatar": event["sender_avatar"],
"sender_avatar": event.get("sender_avatar"),
"reply_to_id": event.get("reply_to_id"),
"media_files": event.get("media_files", []),
}))
async def reply_chat_message(self, event):

View File

@@ -4,6 +4,7 @@ from drf_spectacular.utils import extend_schema_field
from .models import Chat, ChatReadStatus, Message, MessageFile, MessageHistory, MessageReaction
class MessageSenderSerializer(serializers.ModelSerializer):
avatar = serializers.SerializerMethodField()
@@ -51,10 +52,42 @@ class ReplyToSerializer(serializers.ModelSerializer):
class MessageSerializer(serializers.ModelSerializer):
sender = MessageSenderSerializer(read_only=True)
reply_to = ReplyToSerializer(read_only=True)
# reply_to is a SerializerMethodField so we can bypass ActiveManager and
# still surface a tombstone when the original message is soft-deleted.
reply_to = serializers.SerializerMethodField()
media_files = MessageFileSerializer(many=True, read_only=True)
reactions = MessageReactionSerializer(many=True, read_only=True)
@extend_schema_field(ReplyToSerializer)
def get_reply_to(self, obj):
"""
Fetch the reply-to message via all_objects so soft-deleted originals
are still accessible. Returns content=None when the message is deleted,
which the frontend renders as a '(zpráva smazána)' tombstone.
"""
reply_to_id = obj.reply_to_id
if not reply_to_id:
return None
try:
msg = Message.all_objects.select_related('sender').get(pk=reply_to_id)
except Message.DoesNotExist:
return None
sender_data = None
if msg.sender:
from django.conf import settings
avatar = (settings.MEDIA_URL + msg.sender.avatar.name) if msg.sender.avatar else None
sender_data = {'id': msg.sender.id, 'username': msg.sender.username, 'avatar': avatar}
else:
sender_data = {'id': 0, 'username': '', 'avatar': None}
return {
'id': msg.id,
# content=None signals the frontend to show the deleted tombstone
'content': None if msg.is_deleted else msg.content,
'sender': sender_data,
}
class Meta:
model = Message
fields = [

View File

@@ -241,12 +241,26 @@ class MessageViewSet(viewsets.ModelViewSet):
mt = 'FILE'
MessageFile.objects.create(message=message, file=f, media_type=mt)
from django.conf import settings
avatar_url = (settings.MEDIA_URL + request.user.avatar.name) if request.user.avatar else None
media_files_data = [
{
'id': f.id,
'file': settings.MEDIA_URL + f.file.name if f.file else '',
'media_type': f.media_type,
'uploaded_at': f.uploaded_at.isoformat(),
}
for f in message.media_files.all()
]
_broadcast(chat.id, {
'type': 'chat.message',
'message_id': message.id,
'message': message.content,
'sender_id': request.user.id,
'sender': request.user.username,
'has_files': message.media_files.exists(),
'sender_avatar': avatar_url,
'reply_to_id': message.reply_to_id,
'media_files': media_files_data,
})
return Response(