chat implemented, testing needed

This commit is contained in:
2026-05-20 00:52:56 +02:00
parent c7de2dbcdc
commit d52af2c495
14 changed files with 144 additions and 17 deletions

View File

@@ -3,8 +3,9 @@ import json
from channels.db import database_sync_to_async
from channels.generic.websocket import AsyncWebsocketConsumer
from django.utils import timezone
from .models import Chat, Message
from .models import Chat, ChatReadStatus, Message
class ChatConsumer(AsyncWebsocketConsumer):
@@ -95,6 +96,14 @@ class ChatConsumer(AsyncWebsocketConsumer):
"user": user.username,
})
elif msg_type == "mark_read":
await _mark_read(chat_id=self.chat_id, user=user)
await self.channel_layer.group_send(self.chat_name, {
"type": "read.status",
"user": user.username,
"chat_id": int(self.chat_id),
})
else:
await self.send(text_data=json.dumps({"error": "Unsupported message type."}))
@@ -155,6 +164,13 @@ class ChatConsumer(AsyncWebsocketConsumer):
"user": event["user"],
}))
async def read_status(self, event):
await self.send(text_data=json.dumps({
"type": "read_status",
"user": event["user"],
"chat_id": event["chat_id"],
}))
# ---------------------------------------------------------------------------
# DB helpers (run in thread pool via database_sync_to_async)
@@ -179,3 +195,12 @@ def _create_message(chat_id, sender, content, reply_to_id=None):
def _toggle_reaction(message_id, user, emoji):
message = Message.objects.get(pk=message_id)
return message.toggle_reaction(user, emoji)
@database_sync_to_async
def _mark_read(chat_id, user):
ChatReadStatus.objects.update_or_create(
user=user,
chat_id=chat_id,
defaults={"last_read_at": timezone.now()},
)

View File

@@ -0,0 +1,28 @@
# Generated by Django 5.2.7 on 2026-05-19 22:40
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('chat', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='ChatReadStatus',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('last_read_at', models.DateTimeField(auto_now=True)),
('chat', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='read_statuses', to='chat.chat')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='chat_read_statuses', to=settings.AUTH_USER_MODEL)),
],
options={
'unique_together': {('user', 'chat')},
},
),
]

View File

@@ -213,4 +213,24 @@ class MessageFile(SoftDeleteModel):
uploaded_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return f"Media {self.id} for Message {self.message.id}"
return f"Media {self.id} for Message {self.message.id}"
class ChatReadStatus(models.Model):
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name='chat_read_statuses',
)
chat = models.ForeignKey(
Chat,
on_delete=models.CASCADE,
related_name='read_statuses',
)
last_read_at = models.DateTimeField(auto_now=True)
class Meta:
unique_together = ('user', 'chat')
def __str__(self):
return f"{self.user} read {self.chat} at {self.last_read_at}"

View File

@@ -1,5 +1,6 @@
from rest_framework import serializers
from .models import Chat, Message, MessageFile, MessageHistory, MessageReaction
from drf_spectacular.utils import extend_schema_field
from .models import Chat, ChatReadStatus, Message, MessageFile, MessageHistory, MessageReaction
class MessageFileSerializer(serializers.ModelSerializer):
@@ -60,14 +61,28 @@ class MessageSendSerializer(serializers.Serializer):
class ChatSerializer(serializers.ModelSerializer):
unread_count = serializers.SerializerMethodField(read_only=True)
@extend_schema_field(serializers.IntegerField())
def get_unread_count(self, obj):
request = self.context.get('request')
if not request or not request.user.is_authenticated:
return 0
last_read = ChatReadStatus.objects.filter(
user=request.user, chat=obj
).values_list('last_read_at', flat=True).first()
if last_read is None:
return obj.messages.filter(is_deleted=False).count()
return obj.messages.filter(created_at__gt=last_read, is_deleted=False).count()
class Meta:
model = Chat
fields = [
'id', 'chat_type', 'owner', 'name',
'icon', 'banner', 'members', 'moderators',
'hub', 'created_at', 'updated_at',
'hub', 'created_at', 'updated_at', 'unread_count',
]
read_only_fields = ['owner', 'created_at', 'updated_at']
read_only_fields = ['owner', 'created_at', 'updated_at', 'unread_count']
class ChatMemberSerializer(serializers.Serializer):