GitHub: https://github.com/Sokhavuth/chat
Heroku: https://khmerweb-chat.herokuapp.com/
When a user leaves the chat page, related socket client could inform socket server for it to broadcast this information to all connected socket clients. In this case, socket server could delete the user who left the chat page from the users collection object and broadcast this new users object to socket clients.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!--index.html--> | |
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8" /> | |
<meta name="viewport" content="width=device-width, initial-scale=1" /> | |
<title>Khmer Web Chat</title> | |
<link rel="stylesheet" href="/base.css" /> | |
<link rel="stylesheet" href="/chat.css" /> | |
<link href="/fonts/setup.css" rel="stylesheet" /> | |
<link href="/logo.png" rel="icon" /> | |
</head> | |
<body> | |
<section class="Chat region"> | |
<div class="main"> | |
<div class="title"> | |
<input type="button" onClick="endChat()" value="Leave chat" /> | |
</div> | |
<div class="outer"> | |
<div id="msg-board"></div> | |
<form action="" onSubmit="submitHandler(event)"> | |
<input type="text" id="chat-name" onChange="onChange()" | |
required placeholder="Chat name" /> | |
<input id="input" autocomplete="off" required | |
placeholder="Type your message here" /> | |
<input type="submit" value="Send" /> | |
</form> | |
</div> | |
</div> | |
<div class="sidebar"> | |
<div class="title">All people</div> | |
<div id="users" class="content"></div> | |
</div> | |
</section> | |
<script src="/socket.io/socket.io.js"></script> | |
<script> | |
const socket = io(); | |
function onChange(){ | |
const username = document.getElementById('chat-name'); | |
if(username.value){ | |
const obj = { | |
username: username.value, | |
} | |
socket.emit('new user', obj); | |
} | |
}; | |
socket.on('new user', (obj) => { | |
const msgBoard = document.getElementById('msg-board'); | |
let element = document.createElement('div'); | |
element.setAttribute("class", "new-user"); | |
const msg = `${obj.username} join the conversation`; | |
element.textContent = msg; | |
msgBoard.appendChild(element); | |
element.scrollIntoView(); | |
const users = document.getElementById('users'); | |
users.innerHTML = ''; | |
for(let key in obj.users){ | |
element = document.createElement('div'); | |
element.setAttribute("class", "user"); | |
const user = obj.users[key]; | |
element.textContent = user; | |
users.appendChild(element); | |
element.scrollIntoView(); | |
}; | |
}); | |
function submitHandler(e){ | |
e.preventDefault(); | |
const input = document.getElementById('input'); | |
const obj = { | |
message: input.value, | |
}; | |
if (input.value) { | |
socket.emit('chat message', obj); | |
input.value = ''; | |
} | |
}; | |
socket.on('chat message', function(obj){ | |
const msgBoard = document.getElementById('msg-board'); | |
const element = document.createElement('div'); | |
const msg = `${obj.chatName}: ${obj.message}`; | |
element.textContent = msg; | |
msgBoard.appendChild(element); | |
element.scrollIntoView(); | |
}); | |
function endChat(){ | |
const username = document.getElementById('chat-name'); | |
if(username.value){ | |
const userid = localStorage.getItem('userid'); | |
socket.emit("user left"); | |
} | |
}; | |
socket.on("user left", (obj) => { | |
const msgBoard = document.getElementById('msg-board'); | |
let element = document.createElement('div'); | |
element.setAttribute("class", "user-left"); | |
const msg = `${obj.username} left the conversation`; | |
element.textContent = msg; | |
msgBoard.appendChild(element); | |
element.scrollIntoView(); | |
const users = document.getElementById('users'); | |
users.innerHTML = ''; | |
for(let key in obj.users){ | |
element = document.createElement('div'); | |
element.setAttribute("class", "user"); | |
const user = obj.users[key]; | |
element.textContent = user; | |
users.appendChild(element); | |
element.scrollIntoView(); | |
}; | |
}); | |
</script> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// index.js | |
// npm install express | |
// npm install socket.io | |
// npm install nodemon | |
const express = require('express'); | |
const app = express(); | |
const http = require('http'); | |
const server = http.createServer(app); | |
const path = require('path'); | |
const { Server } = require("socket.io"); | |
const io = new Server(server); | |
const port = process.env.PORT || 3000; | |
app.use(express.static(path.join(__dirname, 'public'))); | |
app.get('/', (req, res) => { | |
res.sendFile(`${__dirname}/index.html`); | |
}); | |
const users = {}; | |
io.on('connection', (socket) => { | |
socket.emit('connection'); | |
socket.on('new user', (obj) => { | |
users[socket.id] = obj.username; | |
obj.users = users; | |
io.emit('new user', obj); | |
}); | |
socket.on('chat message', (obj) => { | |
obj.chatName = users[socket.id]; | |
io.emit('chat message', obj); | |
}); | |
socket.on('user left', () => { | |
deleteUser(); | |
socket.disconnect(); | |
}); | |
socket.on("disconnect", () => { | |
if(users[socket.id]){ | |
deleteUser(); | |
} | |
}); | |
function deleteUser(){ | |
const obj = {}; | |
const username = JSON.stringify(users[socket.id]); | |
obj.username = JSON.parse(username); | |
delete users[socket.id]; | |
obj.users = {...users}; | |
io.emit('user left', obj); | |
} | |
}); | |
server.listen(port, () => { | |
console.log(`listening on *${port}`); | |
}); |
Top comments (5)
Woah, nice one bro!
Regrads by My Pijari
Should extract all the server side "socket.on('user left'" code (except the socket.disconnect() )
into
or
That way u will also clean things up and emit if the user close the browser/tab
OK, I see in the case the user closes the browser.
Yes...
Scrolled through your code and seems u just generate the userid.. think u need to map the userid to socket.id though, using a hash map for this wld make most sense i guess.
But on other hand, i don't see why use a userid at all. If want cld persist a user name or whatever in local storage and for id just use the socket.id .. this will ofc change every time u connect with a new socket but if this is what u store temporarily on the server and maps the user to then it makes things much easier to handle as wont need any extra mapping between the socket.id and the userid.
Thank you for your advice. I will use socket.id instead of random uuid for socket client.