tangosource/tangoestimate

View on GitHub
server/components/sockets/main.js

Summary

Maintainability
A
3 hrs
Test Coverage
var uuid  = require('node-uuid'),
    _     = require('lodash'),
    rooms = {};

exports.register = function(socket, io) {
  socket.on('newSession',    function(data){onNewSession(socket, data);});
  socket.on('joinSession',   function(data){onJoinSession(io, socket, data);});
  socket.on('leaveSession',  function(data){onLeaveSession(socket);});
  socket.on('disconnect',    function(data){onLeaveSession(socket);});
  socket.on('reconnect',     function(data){onReconnect(socket);});
  socket.on('verifySession', function(data){onVerifyession(socket, data);});

  //Pointing sessions listeners
  socket.on('updateDescription', function(data){updateDescription(socket, data);});
  socket.on('vote',              function(data){onVote(io, socket, data);});
  socket.on('revealVotes',       function(data){onRevealVotes(io, socket, data);});
  socket.on('clearSession',      function(data){onClearSession(socket, data)});

  //Retrospective sessions listeners
  socket.on('reveal',           function(data){onReveal(io, data);});
  socket.on('hide',             function(data){onHide(io, data);});
  socket.on('newEntry',         function(data){onNewEntry(socket, data);});
  socket.on('updateEntry',      function(data){onUpdateEntry(socket, data);});
  socket.on('deleteEntry',      function(data){onDeleteEntry(socket, data);});
  socket.on('closeEntry',       function(data){onCloseEntry(socket, data);});
  socket.on('openEntry',        function(data){onOpenEntry(socket, data);});
  socket.on('moveCurrentEntry', function(data){onMoveCurrentEntry(socket, data);});
};

function onVerifyession(socket, data){
  if(!rooms[data.id]){ return socket.emit('errorMsg', {message: "Session does not exist"}); }
  socket.emit('sessionVerified', {id: data.id, data: rooms[data.id].hasOwnProperty('good') ? 'retrospective': 'pointing'});
};

function onNewSession(socket, data) {
  var roomId = uuid.v1();

  if(data == 'retrospective'){
    rooms[roomId] = {players: [], moderators:[], good: [], bad: [], improvements: []};
  }else{
    rooms[roomId] = {players: [], moderators:[], votes: {}, voteValues: data};
  }

  socket.emit('sessionCreated', {id: roomId, data: data});
};

function onJoinSession(io, socket, data) {
  if(!rooms[data.roomId]){ return socket.emit('errorMsg', {message: "Session does not exist"}); }
  if(!data.username || !data.type){ return socket.emit('errorMsg', {message: "Missing information"}); }

  socket.join(data.roomId);
  //Save user in room
  rooms[data.roomId][data.type + "s"].push({id: socket.id, username:data.username, type:data.type});

  //Send previous information from room
  if(data.sessionType == 'retrospective'){
    socket.emit('joinedSession', {id: socket.id, session: getRetrospectiveData(rooms[data.roomId])});

    if(rooms[data.roomId].revealed){
      socket.emit('reveal', {session: _.pick(rooms[data.roomId], 'good', 'bad', 'improvements')} );
    }

    return io.to(data.roomId).emit('updateUsers', {players: rooms[data.roomId].players, moderators: rooms[data.roomId].moderators});
  }

  socket.emit('joinedSession', {id: socket.id, description: rooms[data.roomId].description, voteValues: rooms[data.roomId].voteValues});
  io.to(data.roomId).emit('hideVotes'); //hide votes if more users joined to room
  io.to(data.roomId).emit('updateUsers', {players: rooms[data.roomId].players, moderators: rooms[data.roomId].moderators});
};

//Pointing listeners

function updateDescription(socket, data) {
  rooms[data.id].description = data.description;
  socket.broadcast.to(data.id).emit('descriptionUpdated', data.description);
};

function onVote(io, socket, data) {
  var user = _.findWhere(rooms[data.id].players, {id: data.userId});
  rooms[data.id].votes[data.userId] = data.vote;
  user.voted = true;

  var numVotes = _.groupBy(rooms[data.id].players, 'voted').true.length;

  //Reveal votes if all user has voted
  rooms[data.id].revealed = numVotes ==  rooms[data.id].players.length;
  if(rooms[data.id].revealed){ io.to(data.id).emit('updateVotes', rooms[data.id].votes); }

  //update users to see what users already vote
  socket.broadcast.to(data.id).emit('updateUsers', {players: rooms[data.id].players, moderators: rooms[data.id].moderators});
};

function onRevealVotes(io, socket, data){
  rooms[data.id].revealed = true;
  io.to(data.id).emit('updateVotes', rooms[data.id].votes);
};

function onClearSession(socket, data){
  rooms[data.id].players = _.map(rooms[data.id].players, function(u){ u.voted = false; return u;});
  rooms[data.id].votes = {};
  socket.broadcast.to(data.id).emit('clearVotes');
};

//Retrospective listeners
function getRetrospectiveData(data){
  return {good: hideText(data.good), bad: hideText(data.bad), improvements: hideText(data.improvements)}
};

function hideText(data){
   var entry;
   return _.map(data, function(value){
    entry = _.clone(value);
    entry.text = '';
    return entry;
  }) || [];
};

function onNewEntry(socket, data) {
  rooms[data.id][data.type].push(data.entry);
  socket.broadcast.to(data.id).emit('newEntry', {type: data.type, username: data.entry.username});
};

function onCloseEntry(socket, data) {
  socket.broadcast.to(data.id).emit('closeEntry');
};

function onReveal(io, data) {
  rooms[data.id].revealed = true;
  io.to(data.id).emit('reveal', {session: _.pick(rooms[data.id], 'good', 'bad', 'improvements')} );
};

function onHide(io, data) {
  io.to(data.id).emit('hide', {session: getRetrospectiveData(rooms[data.id])} );
};

function onOpenEntry(socket, data) {
  socket.broadcast.to(data.id).emit('openEntry', data);
};

function onDeleteEntry(socket, data) {
  rooms[data.id][data.type] =  _.reject(rooms[data.id][data.type], {id: data.entry.id});
  socket.broadcast.to(data.id).emit('deleteEntry', data);
};

function onMoveCurrentEntry(socket, data) {
  socket.broadcast.to(data.id).emit('moveCurrentEntry', data);
};

function onUpdateEntry(socket, data) {
  var entry = _.findWhere(rooms[data.id][data.entryType], {id: data.entry.id});
  entry.read = data.entry.read;
  entry.text = data.entry.text;
  socket.broadcast.to(data.id).emit('updateEntry', data);
};

function onReconnect(){
  console.log("Reconecting");
}

function onLeaveSession(socket){
  var match, union;
  var roomId = _.findKey(rooms, function(room){//Find user in rooms
    union = _.union(room.players, room.moderators);
    match = _.findWhere(union, {id: socket.id});
    if(match){ return room;}
  });

  if(roomId && union.length > 1){ //Remove user from rooms and delete rooms if is the last user in the room
    rooms[roomId][match.type + "s"]= _.reject(rooms[roomId][match.type + "s"], {id: socket.id}); //remove user from room
    socket.broadcast.to(roomId).emit('updateUsers', {players: rooms[roomId].players, moderators: rooms[roomId].moderators});
    if(match.type == 'player' && rooms[roomId].votes){ //delete votes from user, and update clients
      delete rooms[roomId].votes[socket.id];
      socket.broadcast.to(roomId).emit('updateVotes', rooms[roomId].votes);
    }

    if(roomId && rooms[roomId].good){
      socket.broadcast.to(roomId).emit('updateEntries', _.pick(rooms[roomId], 'good', 'bad', 'improvements'));
    }
  }else{
    delete rooms[roomId];
  }
};