Presence (Online & Typing)
Published in
3 min readOct 13, 2021
Prerequisites
Steps
- Add OnlineConsumer and TypingConsumer to consumers.py
class OnlineConsumer(AsyncWebsocketConsumer):
async def connect(self):
self.group_name = 'online'
# Join room group
await self.channel_layer.group_add(
self.group_name,
self.channel_name
)
await self.accept()
async def disconnect(self, close_code):
# Leave room group
await self.channel_layer.group_discard(
self.group_name,
self.channel_name
)
# Receive message from WebSocket
async def receive(self, text_data):
text_data_json = json.loads(text_data)
requested_user = text_data_json['requested_user']
await self.channel_layer.group_send(
self.group_name,
{
'type': 'request_created',
'requested_user': requested_user
}
)
# Receive message from room group
async def request_created(self, event):
if event['requested_user'] == str(self.scope['user'].pk):
await self.channel_layer.group_send(
self.group_name,
{
'type': 'request_response',
'requested_user': event['requested_user']
}
)
# Receive message from room group
async def request_response(self, event):
await self.send(text_data=json.dumps({
'requested_user': event['requested_user']
}))
class TypingConsumer(AsyncWebsocketConsumer):
async def connect(self):
self.connection_id = self.scope['url_route']['kwargs']['connection_id']
self.group_name = f'typing_{self.connection_id}'
# Join room group
await self.channel_layer.group_add(
self.group_name,
self.channel_name
)
await self.accept()
async def disconnect(self, close_code):
# Leave room group
await self.channel_layer.group_discard(
self.group_name,
self.channel_name
)
# Receive message from WebSocket
async def receive(self, text_data):
text_data_json = json.loads(text_data)
# Send message to room group
await self.channel_layer.group_send(
self.group_name,
{
'type': 'show_typing',
'typing_user': str(self.scope['user'].pk),
'is_typing': text_data_json['is_typing']
}
)
# Receive message from room group
async def show_typing(self, event):
# Send message to WebSocket
await self.send(text_data=json.dumps({
'reciever_is_typing': event['typing_user'] != str(self.scope['user'].pk),
'is_typing': event['is_typing']
}))
- Add the following JS to EVERY page where if the user is present then they should be shown online (typically the base.html which the other pages extend)
Start Online Socket JS
<script>
var url = window.location.href
var websocket_scheme = 'ws'
if(url.includes('https')){
websocket_scheme = 'wss'
}const onlineSocket = new WebSocket(
`${websocket_scheme}://`
+ window.location.host
+ '/ws/chat/online/'
);
</script>
- Add the following JS to chat.html
Online Socket JS
<script>
function send_request(){
onlineSocket.send(JSON.stringify({
'requested_user': '{{message_reciever_user}}'
}))
document.querySelector('#reciever-status').innerHTML = ''
//reciever-status is the id of the div where you can see the current status of the other user (blank, online or typing)
setTimeout(send_request, 5000);
}
setTimeout(send_request, 5000);
onlineSocket.onmessage = function(e) {
const data = JSON.parse(e.data);
if(data.requested_user=='{{message_reciever_user}}'){
document.querySelector('#reciever-status').innerHTML =
`
<span class="label label-sm label-dot label-success"></span>
<span class="font-weight-bold text-muted font-size-sm">Online</span>
`
}
};
onlineSocket.onclose = function(e) {
console.error('Online socket closed unexpectedly with error:', e);
};
</script>
Typing Socket JS
<script>
var url = window.location.href
var websocket_scheme = 'ws'
if(url.includes('https')){
websocket_scheme = 'wss'
}
var typingSocket = new WebSocket(
`${websocket_scheme}://`
+ window.location.host
+ '/ws/chat/typing/'
+ roomName
+ '/'
);
typingSocket.onmessage = function(e) {
const data = JSON.parse(e.data);
if(data.reciever_is_typing && data.is_typing){
document.querySelector('#reciever-status').innerHTML =
`
<span class="font-weight-bold text-warning font-size-sm">typing...</span>
`
}
};
typingSocket.onclose = function(e) {
console.error('Chat socket closed unexpectedly with error:', e);
};
</script>
- Update the Send Message JS from Upgrading Chat to include the following:
Add this to the js
document.querySelector('#content_input').onblur = function(e){
typingSocket.send(JSON.stringify({
'is_typing': false,
}));
- Update the (‘#content_input’).onkeydown function to this
document.querySelector('#content_input').onkeydown = function(e) {
typingSocket.send(JSON.stringify({
'is_typing': true,
}));
if (e.keyCode === 13) { // enter, return
document.querySelector('#chat-message-submit').click();
}
};
This sets and unsets a person as typing
- Update routing.py to include your new sockets
websocket_urlpatterns += [
path('typing/<uuid:connection_id>/', consumers.TypingConsumer.as_asgi()),
path('online/', consumers.OnlineConsumer.as_asgi()),
]