Why Build a Chat App
An easy way of learning something is trying to solve a problem. In my case, I wanted to build a [Node JS] App (https://nodejs.org/en/) and deploy it on cloud ( something like Heroku which I later π out).
So here is the problem >> Most of my friends were working in different organizations, to communicate with each other on desktop we tried apps like hangout ... but most of the apps were blocked in most of the organizations. WhatsApp Web was not launched by then (Aug 2014), so I decided to solve this problem by learning Node JS and socket.io.
Getting Started
- Install NodeJS and start a new node project with below package.json
{
"name": "Chat-App",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"express": "^4.13.3",
"moment": "^2.12.0",
"socket.io": "^1.3.7"
}
}
Set up Node JS file to setup application
var PORT = process.env.PORT || 3000;
var express = require("express");
var app = express(); // express app which is used boilerplate for HTTP
var http = require("http").Server(app);
// expose the folder via express thought
app.use(express.static(__dirname + '/public'));
http.listen(PORT, function() {
console.log("server started");
});
Set up client side public folder for the UI
- Add index.html to launch the landing page
- Add chat.html to open chat screen after logging from chat window
- Add app.js to show messages received from Node Server (using Socket.io).
Socket Communication
- Showing Welcome message
When a user joins a chat room, he is greeted by the system for logging into the system.
socket.emit("message", {
text: "Welcome to Chat Appliction !",
timestamp: moment().valueOf(),
name: "System"
});
server.js
Also if other person has joined room , client side has to emit an event
socket.on("connect", function() {
console.log("Connected to Socket I/O Server!");
console.log(name + " wants to join " + room);
// to join a specific room
socket.emit('joinRoom', {
name: name,
room: room
});
});
app.js
Now server broadcast this message to all the users joined in that room
socket.on('joinRoom', function(req) {
clientInfo[socket.id] = req;
socket.join(req.room);
//broadcast new user joined room
socket.broadcast.to(req.room).emit("message", {
name: "System",
text: req.name + ' has joined',
timestamp: moment().valueOf()
});
});
server.js
- Show notification when user is typing
As user is typing in the message field , client side emits that event :
$('#messagebox').keyup(function() {
console.log('happening');
typing = true;
$("#icon-type").removeClass();
//console.log("typing typing ....");
//socket.emit('typing', 'typing...');
socket.emit('typing', {
text: name + " is typing ..."
});
clearTimeout(timeout);
timeout = setTimeout(timeoutFunction, 1000);
});
app.js
The server then broadcasts this notification:
socket.on('typing', function(message) { // broadcast this message to all users in that room
socket.broadcast.to(clientInfo[socket.id].room).emit("typing", message);
});
server.js
- When a user sends a message When the user submits the message form then after sanitizing the input , the message is emitted to the server
var $form = $("#messageForm");
var $message1 = $form.find('input[name=message]');
$form.on("submit", function(event) {
event.preventDefault();
var msg = $message1.val();
//prevent js injection attack
msg = msg.replace(/</g, "<").replace(/>/g, ">").trim();
if (msg === "") return -1; //empty messages cannot be sent
socket.emit("message", {
text: msg,
name: name
});
// show user messageForm
var $messages = $(".messages");
var $message = $('<li class = "list-group-item"></li>');
var momentTimestamp = moment().format("h:mm a");
// $(".messages").append($('<p>').text(message.text));
$message.append("<strong>" + momentTimestamp + " " + name + "</strong>");
//$message.append("<p>" + $message1.val()+ "</p>");
$message.append($("<p>", {
class: "mymessages",
text: $message1.val()
}));
$messages.append($message);
$message1.val('');
// manage autoscroll
var obj = $("ul.messages.list-group");
var offset = obj.offset();
var scrollLength = obj[0].scrollHeight;
// offset.top += 20;
$("ul.messages.list-group").animate({
scrollTop: scrollLength - offset.top
});
});
app.js
Server listens to the above client event , then after checking if the input message is not a predefined command {@currentUsers gives list of users in current chat room}, broadcasts the received message.
socket.on("message", function(message) {
console.log("Message Received : " + message.text);
// to show all current users
if (message.text === "@currentUsers") {
sendCurrentUsers(socket);
} else {
//broadcast to all users except for sender
message.timestamp = moment().valueOf();
//socket.broadcast.emit("message",message);
// now message should be only sent to users who are in same room
socket.broadcast.to(clientInfo[socket.id].room).emit("message", message);
//socket.emit.to(clientInfo[socket.id].room).emit("message", message);
}
server.js
- Checking if the message has been by the user or not
On Client side, when the message is received, then a check is made to see if the user has opened the chat window or not. If chat window is open, then it means, the message is seen, then an event is emitted
// notify, only when the user has not open chat view
if (document[hidden]) {
notifyMe(message);
// also notify server that user has not seen messgae
var umsg = {
text: name + " has not seen message",
read: false
};
socket.emit("userSeen", umsg);
} else {
// notify server that user has seen message
var umsg = {
text: name + " has seen message",
read: true,
user: name
};
socket.emit("userSeen", umsg);
}
});
app.js
User gets a notification if the chat window is minimized
If user clicks on the notification window , then user seen message is emitted so that blue ticks (user not message) gets converted to green (user has seen the message)
function notifyMe(msg) {
// Let's check if the browser supports notifications
if (!("Notification" in window)) {
alert("This browser does not support desktop notification,try Chromium!");
}
// Let's check whether notification permissions have already been granted
else if (Notification.permission === "granted") {
// If it's okay let's create a notification
// var notification = new Notification(msg);
var notification = new Notification('Chat App', {
body: msg.name + ": " + msg.text,
icon: '/images/apple-icon.png' // optional
});
notification.onclick = function(event) {
event.preventDefault();
this.close();
// assume user would see message so broadcast userSeen event
var umsg = {
text: name + " has seen message",
read: true,
user: name
};
socket.emit("userSeen", umsg);
//window.open('http://www.mozilla.org', '_blank');
};
}
// Otherwise, we need to ask the user for permission
else if (Notification.permission !== 'denied') {
Notification.requestPermission(function(permission) {
// If the user accepts, let's create a notification
if (permission === "granted") {
var notification = new Notification('Chat App', {
body: msg.name + ": " + msg.text,
icon: '/images/apple-icon.png' // optional
});
notification.onclick = function(event) {
event.preventDefault();
this.close();
var umsg = {
text: name + " has seen message",
read: true,
user: name
};
socket.emit("userSeen", umsg);
// assume user would see message so broadcast userSeen event
};
}
});
}
// At last, if the user has denied notifications, and you
// want to be respectful there is no need to bother them any more.
}
app.js
Server side needs to be notified when user sees the message
// to check if user seen Message
socket.on("userSeen", function(msg) {
socket.broadcast.to(clientInfo[socket.id].room).emit("userSeen", msg);
//socket.emit("message", msg);
});
server.js
- Deploying to the Cloud
Now code needs to be deployed to the cloud , this can be easily done through Heroku. Five applications can be deployed at a time in Heroku for free.
Conclusion
I got really excited when I shared this with my friends, we could chat through a web application which is not blocked due to any organization policies.
This is a 4 year old project , when I came to know about dev.to decided to put my learning here.
To improve this chat application, below features could be added :
- Implement authentication using OAuth2
- Add the option to change username , add a profile picture.
- Add DB to persist the chat messages.
- any other feature ...
Source Code : https://github.com/bhupendra1011/Chat-App
Demo : https://bhupendra1011.herokuapp.com/
Top comments (4)
Hey , you can create room on deployed version : https://bhupendra1011.herokuapp.com/chat.html?name=test&room=room1. On your local machine , you need to spin a server . you can try with Web servre for chrome =>chrome.google.com/webstore/detail/... . Just give the folder and try to run with url generated
is the project open-source?
bbbb
nicee