diff --git a/backend/chat/__pycache__/consumers.cpython-311.pyc b/backend/chat/__pycache__/consumers.cpython-311.pyc index 2997482a..df2d0d1b 100644 Binary files a/backend/chat/__pycache__/consumers.cpython-311.pyc and b/backend/chat/__pycache__/consumers.cpython-311.pyc differ diff --git a/backend/chat/__pycache__/routing.cpython-311.pyc b/backend/chat/__pycache__/routing.cpython-311.pyc index 6c45d4f8..e5d174ae 100644 Binary files a/backend/chat/__pycache__/routing.cpython-311.pyc and b/backend/chat/__pycache__/routing.cpython-311.pyc differ diff --git a/backend/chat/__pycache__/signals.cpython-311.pyc b/backend/chat/__pycache__/signals.cpython-311.pyc index f44542ca..df1734dd 100644 Binary files a/backend/chat/__pycache__/signals.cpython-311.pyc and b/backend/chat/__pycache__/signals.cpython-311.pyc differ diff --git a/backend/chat/consumers.py b/backend/chat/consumers.py index 0d3fba07..c4f06c27 100644 --- a/backend/chat/consumers.py +++ b/backend/chat/consumers.py @@ -1,22 +1,30 @@ from channels.generic.websocket import AsyncWebsocketConsumer import json -from .models import Message +from .models import Message, Chat +from django.db.models import Q from asgiref.sync import sync_to_async class ChatConsumer(AsyncWebsocketConsumer): async def connect(self): - self.room_name = self.scope['url_route']['kwargs']['room_name'] - self.room_group_name = f'chat_{self.room_name}' - - # Join room group - await self.channel_layer.group_add( - self.room_group_name, - self.channel_name - ) + self.user = self.scope["user"] + self.room_name = self.scope['url_route'].get('kwargs', {}).get('room_name') + + if self.room_name: + self.room_group_name = f'chat_{self.room_name}' + await self.channel_layer.group_add( + self.room_group_name, + self.channel_name + ) + else: + self.chat_group_name = f'user_{self.user.id}_chats' + await self.channel_layer.group_add( + self.chat_group_name, + self.channel_name + ) await self.accept() - print("webhook accepted") + print("WebSocket accepted") recent_messages = await fetch_recent_messages(self.room_name) for message in recent_messages: @@ -24,15 +32,25 @@ async def connect(self): async def disconnect(self, close_code): - # Leave room group - await self.channel_layer.group_discard( - self.room_group_name, - self.channel_name - ) + # Conditionally leave the appropriate group + if self.room_name: + await self.channel_layer.group_discard( + self.room_group_name, + self.channel_name + ) + else: + await self.channel_layer.group_discard( + self.chat_group_name, + self.channel_name + ) # Receive message from WebSocket (not used here but included for completeness) async def receive(self, text_data): - pass + text_data_json = json.loads(text_data) + message_type = text_data_json.get('type') + + if message_type == 'fetch_chats': + await self.fetch_and_send_chats() async def chat_message(self, event): await self.send(text_data=json.dumps({ @@ -46,6 +64,22 @@ async def send_chat_message(self, message): 'message': message })) + async def chat_update(self, event): + # Handle incoming chat update messages + # For simplicity, we're just directly forwarding the update + await self.send(text_data=json.dumps({ + 'type': 'chat_update', + 'data': event['data'] + })) + + async def fetch_and_send_chats(self): + # Fetch chat list for the user and send it + chats = await get_user_chats(self.user.id) + await self.send(text_data=json.dumps({ + 'type': 'chat_list', + 'chats': chats + })) + @sync_to_async def fetch_recent_messages(room_name, limit=10): print("Connecting to room:", room_name) @@ -59,3 +93,17 @@ def fetch_recent_messages(room_name, limit=10): print("Fetched messages:", recent_messages) # Debug print return recent_messages +@sync_to_async +def get_user_chats(user_id): + chats = Chat.objects.filter( + Q(employee_id=user_id) | Q(mhp_id=user_id) + ).order_by('-last_message_at') + + recent_chats = [{ + 'chat_id': chat.id, + 'last_message_at': chat.last_message_at.strftime("%Y-%m-%d %H:%M:%S"), + 'employee_id': chat.employee_id, + 'mhp_id': chat.mhp_id, + } for chat in chats] + print("Fetched chats:", recent_chats) + return recent_chats diff --git a/backend/chat/routing.py b/backend/chat/routing.py index f39bdd86..72334b38 100644 --- a/backend/chat/routing.py +++ b/backend/chat/routing.py @@ -2,5 +2,6 @@ from . import consumers websocket_urlpatterns = [ + path('ws/chat/', consumers.ChatConsumer.as_asgi()), path('ws/chat//', consumers.ChatConsumer.as_asgi()), ] diff --git a/backend/chat/signals.py b/backend/chat/signals.py index 091f7513..8775aaa9 100644 --- a/backend/chat/signals.py +++ b/backend/chat/signals.py @@ -1,6 +1,6 @@ -from django.db.models.signals import post_save +from django.db.models.signals import post_save, post_delete from django.dispatch import receiver -from .models import Message +from .models import Message, Chat from channels.layers import get_channel_layer from asgiref.sync import async_to_sync @@ -27,3 +27,23 @@ def send_message_update(sender, instance, created, **kwargs): message_content ) +@receiver(post_save, sender=Chat) +@receiver(post_delete, sender=Chat) # If you want to handle deletions +def send_chat_update(sender, instance, **kwargs): + channel_layer = get_channel_layer() + group_name = "chats_group" # This could be a general group name if broadcasting to all users + + chat_content = { + 'type': 'chat_update', # Corresponds to the method in the consumer + 'chat': { + 'id': instance.id, + 'last_updated_at': instance.last_updated_at.strftime("%Y-%m-%d %H:%M:%S"), + 'employee_id': instance.employee_id, + 'mhp_id': instance.mhp_id, + } + } + + async_to_sync(channel_layer.group_send)( + group_name, + chat_content + ) \ No newline at end of file diff --git a/backend/dump.rdb b/backend/dump.rdb index 24212ab4..c6f9cb02 100644 Binary files a/backend/dump.rdb and b/backend/dump.rdb differ diff --git a/web/src/pages/Messenger.js b/web/src/pages/Messenger.js index bd8897f5..53340197 100644 --- a/web/src/pages/Messenger.js +++ b/web/src/pages/Messenger.js @@ -13,6 +13,7 @@ function Messenger() { const webSocket = useRef(null); useEffect(() => { + /* const fetchChats = async () => { if (!token || !token.session.access_token) { console.error('Token not available'); @@ -34,12 +35,35 @@ function Messenger() { }; fetchChats(); + */ + + const chatsWsScheme = window.location.protocol === "https:" ? "wss" : "ws"; + const chatsWsUrl = `${chatsWsScheme}://localhost:8000/ws/chat/`; + + const chatsWebSocket = new WebSocket(chatsWsUrl); + + chatsWebSocket.open = (event) => { + console.log('Chats WebSocket opened'); + }; + + chatsWebSocket.onmessage = (event) => { + const data = JSON.parse(event.data); + console.log("Chat update received:", data); + setChats(data.chats); + }; + + chatsWebSocket.onclose = (event) => { + console.log('Chats WebSocket closed'); + }; + + chatsWebSocket.onerror = (event) => { + console.log('Chats WebSocket error', event); + }; + return () => { - if (webSocket.current) { - webSocket.current.close(); - } + chatsWebSocket.close(); }; - }, [token]); + }, []); useEffect(() => { if (selectedChatId) {