I am using JQuery/JS with Django Channels WebSockets to configure real-time notifications for chat messages.
When a user has any Notification
objects with nofication_read=False
, they are rendered out in the navbar with the code below.
{{ notification|length }}
displays the number of Notification
objects with nofication_read=False
.
navbar.html
<li id="notification_li" class="nav-item">
<a class="nav-link" href="#" id="notificationLink">
<span id="notification_id{{notification_id}}">{{ notification|length }}</span>
<div id="notificationContainer">
<div id="notificationTitle">Notifications</div>
<div id="notificationsBody" class="notifications">
{% for notifications in notification|slice:"0:10" %}
<span id="notification-{{notification.id}}">
{{ notifications.notification_chat.message }}
via {{ notifications.notification_chat.user }}
at {{ notifications.notification_chat.timestamp }}
</span>
{% endfor %}
I know this isn't realtime at the moment.
But the problem is that when the user refreshes the page and clicks on the red icon span id=notification_id
to display their list of unread notifications, the data is sent through the websocket (JSON.stringify
) as "type": "notification_read"
which calls the if message_type == "notification_read":
command in consumers.y
which marks them as nofication_read=True
in the database.
When the user refreshes the page, the red icon is no longer there and when they try to click on the background link, it's unclickable with the error in the browser console
Uncaught ReferenceError: notification_id is not defined
As soon as they get another notification and the page is refreshed, it obviously becomes clickable again.
Any time after that initial click, users should still be able to click and see the list of read
notifications.
consumers.py
class ChatConsumer(AsyncConsumer):
...
async def websocket_receive(self, event):
message_type = json.loads(event.get('text','{}')).get('type')
if message_type == "notification_read":
user = self.scope['user']
username = user.username if user.is_authenticated else 'default'
# Update the notification read status flag in Notification model.
notification = Notification.objects.filter(notification_user=user).update(notification_read=True)
print("notification read")
return
front_text = event.get('text', None)
if front_text is not None:
loaded_dict_data = json.loads(front_text)
msg = loaded_dict_data.get('message')
user = self.scope['user']
username = user.username if user.is_authenticated else 'default'
notification_id = 'default'
myResponse = {
'message': msg,
'username': username,
'notification': notification_id,
}
...
await self.create_chat_message(user, msg)
other_user = self.scope['url_route']['kwargs']['username']
other_user = User.objects.get(username=other_user)
await self.create_notification(other_user, msg)
...
@database_sync_to_async
def create_chat_message(self, me, msg):
thread_obj = self.thread_obj
return ChatMessage.objects.create(thread=thread_obj, user=me, message=msg)
@database_sync_to_async
def create_notification(self, other_user, msg):
last_chat = ChatMessage.objects.latest('id')
created_notification = Notification.objects.create(notification_user=other_user, notification_chat=last_chat)
return created_notification
context_processors.py
def notification(request):
if request.user.is_authenticated:
notification = Notification.objects.filter(notification_user=request.user, notification_read=False)
return {
'notification':notification,
return Notification.objects.none()
base.html
<script>
$(document).ready(function() {
$("#notificationLink").click(function() {
var data = {
"type": "notification_read",
"username": username,
"notification_id": notification_id,
}
socket.send(JSON.stringify(data));
$("#notificationContainer").fadeToggle(300);
$("#notification_id").fadeOut("slow");
return false;
});
...
var incomingMsg = $('.incoming_msg')
socket.onmessage = function(e) {
console.log("message", e)
var chatDataMsg = JSON.parse(e.data)
incomingMsg.append('<li>' + chatDataMsg.message + ' from ' + chatDataMsg.username + '</li>')
}