I am creating an application where students can communicate with tutors, I however can't seem to figure out how to create an effective socket between the 2 clients (a tutor and a student). Most socket tutorials use a single file for the clients. I am new to WebSockets, kindly assist. Here is my FastAPI backend API endpoint
connected_clients = {}
def generate_room_id(sender_username, recipient_username):
return '_'.join(sorted([sender_username, recipient_username]))
@router.websocket("/ws/")
async def chat_websocket(websocket: WebSocket, db: Session = Depends(get_db)):
await websocket.accept()
recipientUsername = None
try:
while True:
data = await websocket.receive_text()
message_data = json.loads(data)
recipientUsername = message_data["recipientUsername"]
content = message_data["content"]
senderUsername = message_data["senderUsername"]
room_id = generate_room_id(senderUsername, recipientUsername)
connected_clients.setdefault(room_id, []).append(websocket)
timestamp = datetime.utcnow()
user = db.query(User).filter(
User.username == recipientUsername).first()
message = Message(user_id=user.id,
content=content,
timestamp=timestamp)
db.add(message)
db.commit()
# Broadcast the message to all users in the same room
await broadcast_message(room_id, data, websocket)
print(connected_clients)
except WebSocketDisconnect:
if recipientUsername in connected_clients:
del connected_clients[recipientUsername]
await websocket.close()
async def broadcast_message(
room_id: str,
message_data: str,
sender_websocket: WebSocket):
recipient_websockets = connected_clients.get(room_id, [])
for recipient_websocket in recipient_websockets:
try:
# Skip broadcasting to the sender's WebSocket
if recipient_websocket != sender_websocket:
await recipient_websocket.send_text(message_data)
except WebSocketDisconnect:
# Handle recipient disconnection
connected_clients[room_id].remove(recipient_websocket)
Here is my student.html JavaScript
const scrollToBottom = () => {
const chatContainer = document.getElementById('scrollContainer');
chatContainer.scrollTop = chatContainer.scrollHeight;
};
function createChatMessage({senderUsername, content}, isSender) {
const messageContainer = document.createElement('div');
messageContainer.classList.add('chat__message');
messageContainer.classList.add('chat__message_type_operator');
messageContainer.classList.add('js-msg-cont');
messageContainer.style.display = 'flex';
if(isSender){
messageContainer.style.flexDirection = 'row-reverse';
}else{
messageContainer.style.flexDirection = 'row';
messageContainer.classList.add('chat__message_type_user');
}
messageContainer.setAttribute('data-is-new', '0');
messageContainer.setAttribute('data-msg-id', '5766');
const avatarContainer = document.createElement('div');
avatarContainer.classList.add('answer__avatar', 'chat__avatar');
avatarContainer.classList.add('chat__avatar_type_operator');
//avatarContainer.classList.add('chat__avatar_type_user');
const avatarImage = document.createElement('img');
avatarImage.src = `/static/images/${1}.png`; // Replace with appropriate image URL
avatarImage.alt = senderUsername;
avatarImage.width = 32;
avatarImage.height = 32;
avatarImage.classList.add("mCS_img_loaded")
avatarContainer.appendChild(avatarImage);
const textContainer = document.createElement('div');
textContainer.classList.add('chat__text');
//textContainer.classList.add('chat__text_type_operator');
textContainer.classList.add('chat__text_type_user');
const messageParagraph = document.createElement('p');
messageParagraph.textContent = content;
const timeElement = document.createElement('time');
timeElement.classList.add('chat__time');
timeElement.textContent = "3:13am";
textContainer.appendChild(messageParagraph);
textContainer.appendChild(timeElement);
messageContainer.appendChild(avatarContainer);
messageContainer.appendChild(textContainer);
return messageContainer;
}
// JavaScript code to handle form submission using WebSocket
const form = document.querySelector('#message-form-{{ tutor.username }}');
const chatArea = document.querySelector('.chat-area-{{ tutor.username }}');
const socketURL = `ws://${window.location.host}/ws/`;
const formData = new FormData(form);
const recipientUsername = formData.get('recipientUsername');
const senderUsername = formData.get("senderUsername");
const msgContent = document.getElementById("messageform-body");
let content;
const task_id = form.getAttribute('data-task-id');
const bid_id = form.getAttribute('data-bid-id');
const queryParams = `task_id=${task_id}&bid_id=${bid_id}&username=${recipientUsername}`;
const wsURL = `${socketURL}?${queryParams}`;
const socket = new WebSocket(wsURL);
const sendMessage = () => {
content = msgContent.value
const message = { recipientUsername, senderUsername, content };
const messageData = JSON.stringify(message);
socket.send(messageData);
chatArea.appendChild(createChatMessage(message, true));
scrollToBottom();
}
socket.onopen = () => {
console.log("Websocket connected");
};
socket.onmessage = event => {
console.log("Message received:", event.data);
const message = JSON.parse(event.data);
chatArea.appendChild(createChatMessage(message, false));
scrollToBottom();
};
form.addEventListener("submit", (event) => {
event.preventDefault();
sendMessage();
form.reset();
})
socket.onclose = () => console.log("WebSocket connection closed.");
And here is my tutor.html JavaScript
const scrollToBottom = () => {
const chatContainer = document.getElementById('scrollContainer');
chatContainer.scrollTop = chatContainer.scrollHeight;
};
function createChatMessage({senderUsername, content}, isSender) {
const messageContainer = document.createElement('div');
messageContainer.classList.add('chat__message', 'js-msg-cont');
messageContainer.classList.add('chat__message_type_operator');
if(isSender){
messageContainer.style.flexDirection = 'row-reverse';
}else{
messageContainer.style.flexDirection = 'row';
messageContainer.classList.add('chat__message_type_user');
}
//messageContainer.style.flexDirection = 'row-reverse';
//messageContainer.classList.add('chat__message_type_user');
messageContainer.setAttribute('data-is-new', '0');
messageContainer.setAttribute('data-msg-id', '1714757');
const avatarContainer = document.createElement('div');
avatarContainer.classList.add('answer__avatar', 'chat__avatar');
avatarContainer.classList.add('chat__avatar_type_operator');
//avatarContainer.classList.add('chat__avatar_type_user');
const avatarImage = document.createElement('img');
avatarImage.src = `/static/images/${1}.png`; // Replace with appropriate image URL
avatarImage.alt = senderUsername;
avatarImage.width = 32;
avatarImage.height = 32;
avatarContainer.appendChild(avatarImage);
const textContainer = document.createElement('div');
textContainer.classList.add('chat__text');
//textContainer.classList.add('chat__text_type_operator');
textContainer.classList.add('chat__text_type_user');
textContainer.id = `chat-area-${senderUsername}`;
const messageParagraph = document.createElement('p');
messageParagraph.textContent = content;
const timeElement = document.createElement('time');
timeElement.classList.add('chat__time');
timeElement.textContent = "3:13am";
const readSpan = document.createElement('span');
readSpan.classList.add('js-read-msg-mark-1714757');
readSpan.textContent = ', Read';
timeElement.appendChild(readSpan);
textContainer.appendChild(messageParagraph);
textContainer.appendChild(timeElement);
messageContainer.appendChild(avatarContainer);
messageContainer.appendChild(textContainer);
return messageContainer;
}
const socketURL = `ws://${window.location.host}/ws/`;
const form = document.querySelector('.js-chat-form');
const button = form.querySelector('#submit');
const chatArea = document.querySelector('.chat-area-{{student.username}}');
const formData = new FormData(form);
const recipientUsername = formData.get('recipientUsername');
const senderUsername = formData.get("senderUsername");
const task_id = form.getAttribute('data-task-id');
const bid_id = form.getAttribute('data-bid-id');
const queryParams = `task_id=${task_id}&bid_id=${bid_id}&recipientUsername=${recipientUsername}`;
const wsURL = `${socketURL}?${queryParams}`;
const socket = new WebSocket(wsURL);
const msgContent = document.getElementById("messageform-body");
let content;
const sendMessage = () => {
content = msgContent.value
message = { recipientUsername, senderUsername, content }
const messageData = JSON.stringify(message);
socket.send(messageData);
chatArea.appendChild(createChatMessage(message, true));
scrollToBottom();
}
socket.onopen = () => {
console.log("Websocket connected");
};
socket.onmessage = event => {
console.log("Message received:", event.data);
const message = JSON.parse(event.data);
chatArea.appendChild(createChatMessage(message, false));
scrollToBottom();
};
form.addEventListener("submit", (event) => {
event.preventDefault();
sendMessage();
form.reset();
})
socket.onclose = () => console.log("WebSocket connection closed.");
The connection is working ok but the second client cannot receive the first client's message until it has also sent a message. That is the onmessage event on both JavaScripts isn't being executed until the message is send by the second client. Kindly help in debugging this.