Undefined timestamp Issue in frontend with javascript
I have created django consumers.py and a frontend in html and css to disply messages sent by a user, the profile picture of the sender and the username of the sender but anytime i open the browser, the message displays well and the username appears well but the time since always appear as undefined even though i printed the time stamp from the websocket and it shows the actual timp. ususally when i send a message, i see the received timestamp on the developer console but immediately i refresh the page it changes to undefine, i simply want to be able to display the time elapsed since a essage was sent, example X seconds ago, X minutes ago, X hours ago my timestamp field is using timezone.now, Please i want a user to see the timesince a message was sent, the tmplate filere timesince isnt working though
this is my consumers.py
import os
import json
from channels.generic.websocket import AsyncWebsocketConsumer
from django.contrib.auth import get_user_model
from django.conf import settings
from channels.db import database_sync_to_async
from virtualrooms.models import VirtualRoom
from .models import Interaction
from django.templatetags.static import static
from datetime import datetime
User = get_user_model()
class DiscussionRoomConsumer(AsyncWebsocketConsumer):
async def connect(self):
self.room_name = self.scope['url_route']['kwargs']['room_name']
self.room_group_name = f"discussion_room_{self.room_name}"
# Join room group
await self.channel_layer.group_add(
self.room_group_name,
self.channel_name
)
await self.accept()
# Get or create the VirtualRoom instance based on the provided room_name
virtual_room = await self.get_virtual_room()
if virtual_room is not None:
# Send existing messages and old messages to the new user
old_messages = await self.get_old_messages(virtual_room)
for message in old_messages:
await self.send(text_data=json.dumps({
'message': message['content'],
'username': await self.get_username(message['user']),
'user_picture': await self.get_user_profile_picture(message['user']),
}))
async def disconnect(self, close_code):
# Leave room group
await self.channel_layer.group_discard(
self.room_group_name,
self.channel_name
)
# Receive message from WebSocket
async def receive(self, text_data):
text_data_json = json.loads(text_data)
message = text_data_json['message']
user_id = self.scope["user"].id
# Get the VirtualRoom instance based on the extracted room name asynchronously
virtual_room = await self.get_virtual_room()
# Check if the VirtualRoom instance exists
if virtual_room:
# Save message to the database with the VirtualRoom instance asynchronously
await self.save_interaction_to_database(user_id, virtual_room, message)
# Send message to the room group
time_stamp = datetime.now().isoformat()
username = await self.get_username(user_id)
user_picture = await self.get_user_profile_picture(user_id)
# Log the entire message data
print("Sending message data:", {
'message': message,
'username': username,
'user_picture': user_picture,
'time_stamp': time_stamp,
})
await self.channel_layer.group_send(
self.room_group_name,
{
'type': 'chat.message',
'message': message,
'username': username,
'user_picture': user_picture,
'time_stamp': time_stamp,
}
)
else:
# Handle the case where the VirtualRoom does not exist
print(f"VirtualRoom with slug '{self.room_name}' does not exist.")
@database_sync_to_async
def save_interaction_to_database(self, user_id, virtual_room, message):
# Save the message to the database with the VirtualRoom instance
Interaction.objects.create(
user_id=user_id,
room_name=virtual_room,
content=message
)
@database_sync_to_async
def get_old_messages(self, virtual_room):
# Fetch old messages based on the VirtualRoom
return list(Interaction.objects.filter(room_name=virtual_room).order_by('time_stamp').values('user', 'content'))
@database_sync_to_async
def get_username(self, user_id):
try:
user = User.objects.get(id=user_id)
return user.username
except User.DoesNotExist:
return None
@database_sync_to_async
def get_user_profile_picture(self, user_id):
try:
user = User.objects.get(id=user_id)
# Check if the user has a profile picture
if hasattr(user, 'user_picture') and user.user_picture:
user_picture = user.user_picture
# Use Django storage to get the correct file path
image_path = user_picture.path
if os.path.isfile(image_path):
# Return the actual user picture URL if the image file exists
return user_picture.url
else:
# Log that the image file doesn't exist and return the default avatar
print(f"Image file for user {user_id} does not exist. Using default avatar.")
return static('images/avatar.svg')
else:
# Log that the user does not have a profile picture and return the default avatar
print(f"User {user_id} does not have a profile picture. Using default avatar.")
return static('images/avatar.svg')
except User.DoesNotExist:
# Log that the user does not exist and return the default avatar
print(f"User {user_id} does not exist. Using default avatar.")
return static('images/avatar.svg')
@database_sync_to_async
def get_virtual_room(self):
try:
return VirtualRoom.objects.get(slug__iexact=self.room_name)
except VirtualRoom.DoesNotExist:
return None
# Receive message from the room group
async def chat_message(self, event):
message = event['message']
username = event['username']
user_picture = event['user_picture']
time_stamp = event['time_stamp']
print(f"Sending message with timestamp: {time_stamp}")
# Send the message to WebSocket
await self.send(text_data=json.dumps({
'message': message,
'username': username,
'user_picture': user_picture,
'time_stamp': time_stamp,
}))
and this is my frontend:
<!DOCTYPE html>
{% load static %}
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="{% static 'css/discussionroom.css' %}">
<title>Discussion Room</title>
</head>
<body>
<div id="discussion-room">
<div id="chat-box">
{% for message in messages %}
<div>
<img src="{{ message.user_picture.url|default:'/static/images/avatar.svg' }}" alt="User Picture" style="width: 30px; height: 30px;">
<strong>{{ message.username }}:</strong>
{{ message.message }}
<span class="timestamp">{{ message.time_stamp|timesince }}</span>
</div>
{% endfor %}
</div>
<input type="text" id="message-input" placeholder="Type your message...">
<button onclick="sendMessage()">Send</button>
</div>
<script>
// Get the room name from the URL
const pathArray = window.location.pathname.split('/');
const roomNameIndex = pathArray.indexOf('discussions') + 1;
const roomName = pathArray[roomNameIndex].split('@')[1];
// Log the extracted room name
console.log('Extracted room name:', roomName);
// Create a new WebSocket connection
const socket = new WebSocket(`ws://${window.location.host}/ws/discussions/${roomName}/`);
socket.onmessage = function(event) {
const data = JSON.parse(event.data);
console.log('Received data:', data);
displayMessage(data.user_picture, data.username, data.message, data.time_stamp);
// Save timestamp to local storage
localStorage.setItem('lastTimestamp', data.time_stamp);
};
function sendMessage() {
const message = document.getElementById('message-input').value;
if (message.trim() !== '') {
socket.send(JSON.stringify({'message': message}));
document.getElementById('message-input').value = '';
}
}
function displayMessage(userPicture, username, message, timestamp) {
console.log('Received timestamp:', timestamp);
console.log('Received user picture:', userPicture);
localStorage.setItem('lastTimestamp', timestamp);
const messageDiv = document.createElement('div');
// Set the userPicture URL
const imageUrl = userPicture || '/static/images/avatar.svg';
//console.log('Selected image URL:', imageUrl);
const imgElement = new Image();
imgElement.src = imageUrl;
// Add an event listener to handle image load errors
imgElement.addEventListener('error', function() {
//console.warn('Error loading image. Using default avatar.');
messageDiv.innerHTML = `<img src="/static/images/avatar.svg" alt="Default Avatar" style="width: 30px; height: 30px;"> ${message}`;
document.getElementById('chat-box').appendChild(messageDiv);
});
// Add an event listener to handle image load success
imgElement.addEventListener('load', function() {
imgElement.className = 'user-profile-img'; // Add this line
messageDiv.innerHTML = `<img src="${imageUrl}" alt="User Picture" style="width: 30px; height: 30px; border-radius: 50%; object-fit: cover; "> <strong>${username}:</strong> ${message}`;
document.getElementById('chat-box').appendChild(messageDiv);
});
}
const storedTimestamp = localStorage.getItem('lastTimestamp');
if (storedTimestamp) {
console.log('Retrieved timestamp from local storage:', storedTimestamp);
}
</script>
</body>
</html>