Reputation: 1005
I am writing a django module that handles real time message paired with notification. So far:
a conversation can only take place between no more than 2 users.
a notification should be sent after each message.
I am currently working on getting the notifications to show up and the issue is that the notification gets rendered in the sender profile page and not in the recipient profile. I cant see where my error is
Here is what I have done: consumers.py
import json
from channels.generic.websocket import AsyncWebsocketConsumer
from channels.db import database_sync_to_async
from .models import Chat, ChatRoom
from accounts.models import User
from asgiref.sync import sync_to_async
from django.contrib.auth import get_user_model
from django.shortcuts import get_object_or_404
from asgiref.sync import async_to_sync
class ChatConsumer(AsyncWebsocketConsumer):
async def connect(self):
self.room_id = self.scope['url_route']['kwargs']['room_id']
self.room_group_name = 'chat_%s' % self.room_id
await self.channel_layer.group_add(
self.room_group_name,
self.channel_name
)
await self.accept()
async def disconnect(self, close_code):
await self.channel_layer.group_discard(
self.room_group_name,
self.channel_name
)
async def receive(self, text_data):
text_data_json = json.loads(text_data)
message = text_data_json['message']
recipient = text_data_json['recipient']
self.user_id = self.scope['user'].id
# Find room object
room = await database_sync_to_async(ChatRoom.objects.get)(pk=self.room_id)
print('ok1')
# Create new chat object
chat = Chat(
content=message,
sender=self.scope['user'],
room=room,
)
print('ok2')
await database_sync_to_async(chat.save)()
print("ok3")
# get the recipient user
recipient_user = await database_sync_to_async(User.objects.get)(id=recipient)
print("ok4")
await sync_to_async(chat.recipient.add)(recipient_user.id)
print("ok5")
await database_sync_to_async(chat.save)()
await self.channel_layer.group_send(
self.room_group_name,
{
'type': 'chat_message',
'message': message,
'user_id': self.user_id
})
# Send a notification to the recipient
await self.channel_layer.send(
recipient_user.username,
{
'type': 'notification',
'message': message
}
)
await self.send_notification(f'New message from {self.user_id}')
print('notification has been created')
async def chat_message(self, event):
message = event['message']
user_id = event['user_id']
await self.send(text_data=json.dumps({
'message': message,
'user_id': user_id
}))
async def send_notification(self, message):
await self.send(text_data=json.dumps({
'type': 'notification',
'message':message
}))
and here is my room.js code, which is the javascript code handling the logic to display the messages logic and the notifications:
chatSocket.onmessage = function(e) {
const data = JSON.parse(e.data);
console.log("data",data)
console.log("datatype",data.type)
var message_type = data.type;
console.log("message type",message_type)
if(message_type === 'notification'){
$("#notification-bar2").text(data.message);
$("#notification-bar2").show();
}
if(message_type !== 'notification'){
const messageElement = document.createElement('div')
const userId = data['user_id']
const loggedInUserId = JSON.parse(document.getElementById('user_id').textContent)
console.log(loggedInUserId)
messageElement.innerText = data.message
if (userId === loggedInUserId) {
messageElement.classList.add( 'relative', 'max-w-xl', 'px-4', 'py-2', 'text-gray-700', 'bg-gray-100','rounded', 'shadow','flex', 'justify-end','message', 'sender','block')
} else {
messageElement.classList.add('relative', 'max-w-xl', 'px-4', 'py-2', 'text-gray-700', 'bg-gray-100','rounded', 'shadow','flex', 'justify-start','message', 'receiver','block')
}
chatLog.appendChild(messageElement)
if (document.querySelector('#emptyText')) {
document.querySelector('#emptyText').remove()
}
}
};
I am super confused about why that would be, and be fresh starting with channels, there are still quite a bit of stuff that I dont understand super well, therefore any king help is greatly appreciated! I am more than happy to provide additionnal code if necessary
Upvotes: 1
Views: 445
Reputation: 6296
It seems you are attempting to send the notification in two different ways but doing it the wrong way.
# Send a notification to the recipient
await self.channel_layer.send(
recipient_user.username,
{
'type': 'notification',
'message': message
}
)
await self.send_notification(f'New message from {self.user_id}')
print('notification has been created')
The first method attempts to send it through the channel layer which is the correct way given that the current channel is that of the sender, so you need to pipe it through the channel layer to the channel of the recipient. The issue is that you are not using the correct name of the handler. You are using notification
as type instead of send_notification
so it is never handled.
In the second case, you are calling the send_notification
function directly in the current channel which is the sender's channel so it will get sent to the sender instead of the intended receiver.
The entire code block above should be replaced with:
# Send a notification to the recipient
await self.channel_layer.send(
recipient_user.username,
{
'type': 'send_notification',
'message': f'New message from {self.user_id}'
}
)
Upvotes: 1
Reputation: 474
In your receive function you only have a recipient and a connected user. This code takes the recipient that comes with the message and sends it to that user(the recipient) letting it know that it came from this user(the sender). The message already comes tagged with the recipient that it goes to when it hits the websocket. We need to get rid of that and keep track of all of our connected users within the ChatRoom object. You can simply handle this in the view or when the user connects to the websocket. When a message is sent, get each recipient user in the room, which should be every user except for the user that sent the object.
print('ok2')
await database_sync_to_async(chat.save)()
print("ok3")
# get the recipient user
# database_sync_to_async function that gets the list of users
# dont forget to call users.remove(sender)
for each in users:
recipient_user = each
print("ok4")
await sync_to_async(chat.recipient.add)(recipient_user.id)
print("ok5")
await database_sync_to_async(chat.save)()
Upvotes: 0