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

Chat Application Finished #2

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
3 changes: 3 additions & 0 deletions .bowerrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"directory": "public/vendor"
}
45 changes: 45 additions & 0 deletions app/plugins/giphyPlugin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
var Promise = require('promise');
var request = require('request');

module.exports = function(context){
return new Promise(function(resolve, reject){;

var msg = context.msg;
var io = context.io;
if(!msg)return resolve(context);

// /giphy COMMAND
var regex = / *\/giphy (.+)/;
if(regex.test(msg.text)){
var giphyText = regex.exec(msg.text)[1];
request({
url : 'http://api.giphy.com/v1/gifs/search',
qs : {
api_key: 'dc6zaTOxFJmzC',
q : giphyText,
limit : 1,
},
json: true
}, function(error, response, json){
if( error ||
response.statusCode != 200 ||
json.meta.status != 200 ||
json.data.length == 0 ){
reject('A giphy could not be found with "' + giphyText + '"');
}else {
msg.attachments = msg.attachments || [];
msg.attachments.push({
type: 'giphy',
url : json.data[0].images.fixed_width.url
});
io.to(context.room).emit('message', msg);
resolve(context);
}

});
}else{
reject('The message should start with "/giphy" ' +
'and a message has to be provided after.');
}
});
}
135 changes: 135 additions & 0 deletions app/plugins/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
var Promise = require('promise');


// load plugins
var giphyPlugin = require('./giphyPlugin');
var socketPlugin = require('./socketPlugin');

// inti globals
var pluginRunId = 0;

var plugins = module.exports = {

// run all plugins against 'context'
runPlugins: function (context) { // {text : text}
return new Promise(function(resolve, reject){

var msg = context.msg;
var promise = metaDataPlugin(context);
if(!msg)return promise.then(resolve);

var regex = /(\/[^ ]+)/;
if(regex.test(msg.text)){
var command = regex.exec(msg.text)[1];
var plugin = plugins.map[command];
if(plugin){
// plugin will be applied
promise
.then(plugin)
.then(resolve)
.catch(getDefaultErrorPlugin(context))
.then(resolve)
.catch(reject);
}else{
// no plugin found with that name
promise
.then(uknownCommandPlugin)
.catch(getDefaultErrorPlugin(context))
.then(resolve)
.catch(reject);
}
} else {
promise
.then(defaultPlugin)
.catch(getDefaultErrorPlugin(context))
.then(resolve)
.catch(reject);
}
});
},

map : {
'/giphy' : giphyPlugin,
'/rename' : socketPlugin.rename,
}

}


//
// default plugins
//


// timestamp and id plugin
function metaDataPlugin(context){
return new Promise(function(resolve, reject){
var msg = context.msg;
if(!msg)return resolve(context);
msg.timestamp = new Date();
msg.id = new Date().getTime() + '' + (++pluginRunId);
resolve(context);
});
}

// a plugin that just send the message to the room
function defaultPlugin(context){
return new Promise(function(resolve, reject){
var msg = context.msg;
var room= context.room;
var io = context.io;
if(msg && room){
io.to(room).emit('message', msg);
resolve(context);
} else {
reject('Message cannot be empty');
}
});
}

// a plugin used when the command is not found.
// It generates an error to notify the user
function uknownCommandPlugin(context){
return new Promise(function(resolve, reject){
reject('Uknown command, messages that starts with "/" are commands. ' +
'If you want to start with "/" try preceding the "/" with a space');
});
}

// a plugin that will process errors generated as :
/*
{
// aditional fields could be added for logging
message: 'This is the error to the user'
}
*/
function getDefaultErrorPlugin(context){
return function defaultErrorPlugin(err) {
return new Promise(function(resolve, reject){
if(!context)return reject(err);

var socket = context.socket;


// normalize err object
var errMessage = null;
if(err instanceof Error){
console.error(err);
errMessage = 'An error has occurred.';
} else if(typeof err == 'string'){
errMessage = err;
} else {
errMessage = (err||{}).message || 'An error has occurred.';
}

var msg = {
timestamp: new Date(),
user : 'server',
text : errMessage,
onlyYou : true,
}
socket.emit('message', msg);
resolve();
});
}
}
77 changes: 77 additions & 0 deletions app/plugins/socketPlugin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
var Promise = require('promise');

module.exports = {

rename: function(context){

return new Promise(function(resolve, reject){

var socket = context.socket;
if(!socket) return resolve(context);
var msg = context.msg;
var session= socket.session;
if(!session)return reject(); // throw default error

// /rename COMMAND
var rename = / *\/rename (.+)/;
if(msg && rename.test(msg.text)){
// get newNickname
var nickname = rename.exec(msg.text)[1];
var oldNick = session.nickname;

if(nickname.length > 100){
reject('New name is too long');
return;
}

// update nickname in session
session.nickname = nickname;

// emit to the socket for rename
socket.emit('changeName',
createRenameSocketMessage(
'you have changed the name to ' + nickname, session
)
);

// dont bother others if change is for the same name
if(nickname == oldNick)return; // no error

// send to everyone in the room
for(var room in socket.rooms){

// message to others for new nickname for the user
context.socket.to(room).emit(
createRenameSocketMessage(
oldNick + ' has renamed to ' + nickname, session
)
);

// update name in room
if(context.rooms[room] && context.rooms[room][session.id]){
context.rooms[room][session.id] = nickname;
}

}
// remove message so that it wont be sent
context.msg = null;
resolve(context);

} else {
reject('Message should start with "/rename" and a name has to be provided');
}
});


function createRenameSocketMessage(text, session){
return {
text : text,
timestamp: new Date(),
user : {
id : session.id,
nickname: session.nickname
}
}
}
}
}
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@
"homepage": "https://github.com/cnverg/chat-challenge#readme",
"dependencies": {
"express": "^4.13.3",
"lodash": "^4.0.1",
"promise": "^7.1.1",
"request": "^2.69.0",
"socket.io": "^1.4.4"
}
}
6 changes: 6 additions & 0 deletions public/app/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/*globals anfular*/
(function(){'use strict'

angular.module('chatApp', ['angularModalService','ngAnimate']);

})();
77 changes: 77 additions & 0 deletions public/app/controllers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*globals angular*/
(function(){ 'use strict'

angular.module('chatApp')
.controller('MainController',
function($scope, ChatService, DialogService){

// objects used in view
var user = $scope.user = ChatService.user;
var currentRoom = $scope.currentRoom = ChatService.room;
var chatService = $scope.chatService = ChatService;

// ng-models for inputs
$scope.newRoom = "Lobby";
$scope.newMessage = "";
$scope.newNickname = null;

// enter the lobby
ChatService.join('Lobby');


// functions for the view
$scope.join = function(room){
ChatService.join(room);
}
$scope.leave = function(room){
ChatService.leave(room);
}
$scope.send = function(msg){
if(currentRoom.connected && msg){
ChatService.send(currentRoom.name, msg);
$scope.newMessage = '';
}
}
$scope.changeName = function(nickname){
if(nickname){
ChatService.changeName(nickname);
}
$scope.newNickname = '';
}
$scope.usersCount = function(){
return currentRoom.users ? Object.keys(currentRoom.users).length : 0;
}
$scope.leave = function(){
DialogService.showConfirmModal({
text : 'Are you sure you want to leave this room',
title: 'Leave the Room'
})
.then(function(){
ChatService.leave(currentRoom.name);
});
}
$scope.changeNickname = function(){
DialogService.showInputModal({
text: user.nickname,
title: 'Write the new nickname'
})
.then(function(newNickname){
ChatService.changeName(newNickname);
});
}
$scope.joinRoom = function(){
DialogService.showInputModal({
text: currentRoom.name,
title: 'Write the name of the room to join'
})
.then(function(newRoom){
ChatService.join(newRoom);
});
}


}
)


})();
20 changes: 20 additions & 0 deletions public/app/directives/attachments.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*globals angular*/
(function(){ 'use strict'

var app = angular.module('chatApp');

app
.directive('giphyAttachment', function(){
return {
restrict: 'E',
scope: {
content: '='
},
templateUrl: 'app/directives/templates/attachments/giphy.html',
controller: function($scope){ }
};
});



})();
Loading