Skip to content
This repository has been archived by the owner on Jul 20, 2020. It is now read-only.

Commit

Permalink
Merge pull request #53 from andela/ft-users-able-to-chat-170947581
Browse files Browse the repository at this point in the history
  • Loading branch information
jabichris committed Mar 24, 2020
2 parents 086cc84 + 4d4ecb3 commit 269cd21
Show file tree
Hide file tree
Showing 8 changed files with 278 additions and 2 deletions.
93 changes: 93 additions & 0 deletions public/chat.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
<!DOCTYPE html>
<html>
<head>
<title>Barefoot Nomad Chat</title>
</head>
<body>
<div class='wrap'>
<h1 id="bn-header">Chat with users</h1>
<h2>Online Users</h2>
<ul class="users"></ul>
<h2>Messages</h2>
<div id="chat-messages"></div>
<div class="messageInput">
<input type="text" name="" id="txt" placeholder="write your message" value="">
<button id="sendButton">Send</button>
</div>
</div>
<script src="/socket.io/socket.io.js"></script>
<script>
const socket = io({
transportOptions: {
polling: {
extraHeaders: {
token: localStorage.getItem("token")
}
}
}
});
socket.on('messageFromServer', (dataFromServer) => {
console.log(dataFromServer);
socket.emit('messageToServer', {data: 'this is comming from chat.html'})
})

socket.on('onlineUsers', (usernames) => {
const usernamesParsed = JSON.parse(usernames)
usernamesParsed.map((username) => {
let usersContainer = document.querySelector('.users');
let node = document.createElement('li');
node.innerHTML = `${username}`
usersContainer.appendChild(node)
})
});

socket.on('newUser', (usernames) => {
const currentUsers = JSON.parse(usernames)
const newUser = currentUsers[currentUsers.length - 1];
let usersContainer = document.querySelector('.users');
usersContainer.innerHTML = '';
currentUsers.map((username) => {
let node = document.createElement('li');
node.innerHTML = `${username}`
usersContainer.appendChild(node)
})
});

socket.on('chatHistory', (messageData) => {
const parseMessages = JSON.parse(messageData); //there is an error in the console log with this line
parseMessages.map((messages) => {
const messagesContainer = document.querySelector('#chat-messages');
let messageNode = document.createElement('div');
let senderNode = document.createElement('h3')
let chatText = document.createElement('p')
senderNode.innerHTML = `${messages.userName}`;
chatText.innerHTML = `${messages.message}`;
messageNode.appendChild(senderNode);
messageNode.appendChild(chatText)
messagesContainer.appendChild(messageNode)
})
});

const sendButton = document.querySelector('#sendButton');
sendButton.addEventListener('click', () => {
let messageText = document.querySelector('#txt').value;
socket.emit('message', {message: messageText, token: localStorage.token})
document.querySelector('#txt').value = '';
})

socket.on('sendMessage', (message) => {
const messagesContainer = document.querySelector('#chat-messages');
const messageInfo = JSON.parse(message);
let messageNode = document.createElement('div');
let senderNode = document.createElement('h3')
let chatText = document.createElement('p')
senderNode.innerHTML = `${messageInfo.senderFirstName} ${messageInfo.senderLastName}`;
chatText.innerHTML = messageInfo.message;
messageNode.appendChild(senderNode);
messageNode.appendChild(chatText)
messagesContainer.appendChild(messageNode)
});

</script>
</body>
</html>
6 changes: 5 additions & 1 deletion public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,11 @@
body: parsedData.content,
icon: "./bareicon.png"
})
});
});
socket.on('messageFromServer', (dataFromServer) => {
console.log(dataFromServer);
socket.emit('messageToServer', {data: 'this is comming from chat socket'})
})
</script>
</body>
</html>
1 change: 1 addition & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import notificationsRouter from './routes/notifications';
import './config/passport';
import { ioMiddleware } from './middlewares/io';


dotenv.config();
i18n.configure({
locales: ['fr', 'en'],
Expand Down
35 changes: 35 additions & 0 deletions src/middlewares/io.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import dotenv from 'dotenv';
import db from '../models';
import { verifyToken } from '../utils/tokenHandler';


dotenv.config();

export const connectedUsers = {};
Expand All @@ -10,12 +11,46 @@ export const ioMiddleware = async (socket) => {
try {
const { token } = socket.handshake.headers;
const decoded = verifyToken(token);
const userInfo = await db.User.findOne({ where: { id: decoded.id } });
if (!decoded.error) {
if (!connectedUsers[decoded.id]) {
connectedUsers[decoded.id] = [];
}
connectedUsers[decoded.id].push(socket.id);

// display users that are online
const userIds = Object.keys(connectedUsers);

const usersOnline = Promise.all(userIds.map(async (id) => {
const user = await db.User.findOne({ where: { id } });
return `${user.firstName} ${user.lastName}`;
})).then((users) => users);

const onlineUsers = JSON.stringify((await usersOnline).map((user) => user));
socket.emit('onlineUsers', onlineUsers);
socket.broadcast.emit('newUser', onlineUsers);

socket.on('message', async (dataFromClient) => {
if (dataFromClient.message !== undefined && dataFromClient.message.trim().length > 0) {
const sender = verifyToken(dataFromClient.token);
const senderInfo = await db.User.findOne({ where: { id: sender.id } });
await db.Chat.create({
message: dataFromClient.message,
userId: senderInfo.id,
userName: `${senderInfo.firstName} ${senderInfo.lastName}`
});
socket.broadcast.emit('sendMessage', JSON.stringify({ senderFirstName: userInfo.firstName, senderLastName: userInfo.lastName, message: dataFromClient.message }));
socket.emit('sendMessage', JSON.stringify({ senderFirstName: userInfo.firstName, senderLastName: userInfo.lastName, message: dataFromClient.message }));
} else {
socket.emit('sendMessage', JSON.stringify({ senderFirstName: 'server', senderLastName: '', message: 'message can\'t be empty or contain only spaces' }));
}
});

const chatData = JSON.stringify(await db.Chat.findAll());
socket.emit('chatHistory', chatData);

socket.emit('initialize', JSON.stringify({ notif: await db.Notifications.findAll({ where: { receiverId: decoded.id } }) }));

socket.on('disconnect', () => {
process.stdout.write('a user is disconnected');
connectedUsers[decoded.id].forEach((el, index, arr) => {
Expand Down
33 changes: 33 additions & 0 deletions src/migrations/20200315174530-create-chat.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Chats', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
message: {
type: Sequelize.STRING
},
userId: {
type: Sequelize.STRING
},
userName: {
type: Sequelize.STRING
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Chats');
}
};
11 changes: 11 additions & 0 deletions src/models/chat.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module.exports = (sequelize, DataTypes) => {
const Chat = sequelize.define('Chat', {
message: DataTypes.STRING,
userId: DataTypes.STRING,
userName: DataTypes.STRING
}, {});
Chat.associate = () => {
// associations can be defined here
};
return Chat;
};
3 changes: 2 additions & 1 deletion src/services/localesServices/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -170,5 +170,6 @@
"Request found": "Request found",
"Requests not found": "Requests not found",
"Request not found or not yours to approve": "Request not found or not yours to approve",
"Trips statistics": "Trips statistics"
"Trips statistics": "Trips statistics",
"Request not found or not yours to manage": "Request not found or not yours to manage"
}
98 changes: 98 additions & 0 deletions src/tests/chat.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import chai from 'chai';
import chaiHttp from 'chai-http';
import dotenv from 'dotenv';
import ioClient from 'socket.io-client';
import sinon from 'sinon';
import { server } from '../index';
import { provideToken } from '../utils/tokenHandler';

const socketToken = provideToken('79660e6f-4b7d-4g21-81re-74f54jk91c8a', true, 'jdev@andela.com', 'requester');
const socketToken2 = provideToken('0119b84a-99a4-41c0-8a0e-6e0b6c385165', true, 'umuhozad@andela.com', 'manager');

dotenv.config();
const { expect } = chai;
chai.use(chaiHttp);
let clientSocket;
let clientSocket2;
let clientSocket3;

describe('CHAT TESTS', () => {
const BASE_URL = `http://localhost:${server.address().port}`;
beforeEach(() => {
clientSocket = ioClient.connect(BASE_URL, {
transportOptions: {
polling:
{ extraHeaders: { token: socketToken } }
},
'force new connection': true,
});
clientSocket.on('initialize', () => {
clientSocket.emit('message', { message: 'this is a message', token: socketToken });
});

clientSocket2 = ioClient.connect(BASE_URL, {
transportOptions: {
polling:
{ extraHeaders: { token: socketToken2 } }
},
'force new connection': true,
});
clientSocket2.on('initialize', () => {
clientSocket2.emit('message', { message: ' ', token: socketToken2 });
});

clientSocket3 = ioClient.connect(BASE_URL, {
transportOptions: {
polling:
{ extraHeaders: { token: 'safdljasdf' } }
},
'force new connection': true,
});
});
afterEach(() => {
clientSocket.disconnect();
clientSocket2.disconnect();
clientSocket3.disconnect();
sinon.restore();
});

it('should receive a chat message', (done) => {
clientSocket.on('sendMessage', (data) => {
const result = JSON.parse(data);
expect(JSON.parse(data)).to.be.an('object');
expect(result.message).to.equal('this is a message');
done();
});
});
it('should recieve chat history', (done) => {
clientSocket.on('chatHistory', (data) => {
const result = JSON.parse(data);
expect(result).to.be.an('array');

done();
});
});
it('should show users that are online', (done) => {
clientSocket.on('onlineUsers', (data) => {
const result = JSON.parse(data);
expect(result).to.be.an('array');
done();
});
});
it('should return error for invalid token', (done) => {
clientSocket3.on('initialize', (data) => {
const result = JSON.parse(data);
expect(result).to.be.an('object');
expect(result.error).to.equal('The token is not provided or the token provided is an invalid token');
done();
});
});
it('should return an error for empty message', (done) => {
clientSocket2.on('sendMessage', (data) => {
const result = JSON.parse(data);
expect(result).to.be.an('object');
expect(result.message).to.equal('message can\'t be empty or contain only spaces');
done();
});
});
});

0 comments on commit 269cd21

Please sign in to comment.