Added basic voting implementation

This commit is contained in:
Chris Blanchard 2015-07-27 12:55:36 +01:00
parent 6ccd424db3
commit cf2b9643fb
10 changed files with 266 additions and 66 deletions

View file

@ -27,13 +27,14 @@ npm start
- ENSL.org: Authentication - ENSL.org: Authentication
- ENSL.org: Pull user data and bans - ENSL.org: Pull user data and bans
- ENSL.org: API to allow ENSL to pull current gather status - ENSL.org: API to allow ENSL to pull current gather status
- Steam Integration: Pull hive stats - Hive Integration: Pull stats
- Steam Integration: Outbound steam messaging - Steam Integration: Outbound steam messaging
- Admin tools - Admin tools - kick user/edit messages
- Add a backend datastore to persist gather data and messages - Add a backend datastore to persist gather data and messages
- Add sounds - Add sounds
- Add WebRTC for internal voice comms - WebRTC?
- Add user lifeform + div information - Add user lifeform + div information
- Show online status
## License ## License

View file

@ -2,29 +2,35 @@
var winston = require("winston"); var winston = require("winston");
var User = require("../lib/user/user"); var User = require("../lib/user/user");
var enslClient = require("../lib/ensl/client")(); var client = require("../lib/ensl/client")();
var chatController = require("../lib/chat/controller"); var chatController = require("../lib/chat/controller");
var gatherController = require("../lib/gather/controller"); var gatherController = require("../lib/gather/controller");
var userController = require("../lib/user/controller"); var userController = require("../lib/user/controller");
var createUser = require("../spec/helpers/index.js").createUser; var getRandomUser = function (callback) {
var id = Math.floor(Math.random() * 5000) + 1;
console.log(id);
client.getUserById({
id: id
}, function (error, response, body) {
if (response.statusCode !== 200) return getRandomUser(callback);
return callback(error, response, body);
});
};
module.exports = function (io) { module.exports = function (io) {
var rootNamespace = io.of('/') var rootNamespace = io.of('/')
// Authorisation // Authorisation
io.use(function (socket, next) { io.use(function (socket, next) {
var id = Math.floor(Math.random() * 5000) + 1; getRandomUser(function (error, _, body) {
enslClient.getUserById({
id: id
}, function (error, response, body) {
if (error) { if (error) {
winston.error(error); winston.error(error);
return next(error) return next(error)
}; };
socket._user = new User(body); socket._user = new User(body);
next(); next();
}); })
}); });
userController(rootNamespace); userController(rootNamespace);

View file

@ -16,12 +16,56 @@
var Gather = require("./gather"); var Gather = require("./gather");
var gather = new Gather(); var gather = new Gather();
// ***** Temporary code to test voting *****
var User = require("../user/user");
var client = require("../ensl/client")();
var async = require("async");
var getRandomUser = function (callback) {
var id = Math.floor(Math.random() * 5000) + 1;
console.log(id);
client.getUserById({
id: id
}, function (error, response, body) {
if (response.statusCode !== 200) return getRandomUser(callback);
return callback(error, response, body);
});
};
var instructions = [];
for (var i = 0; i < 11; i++) {
instructions.push(function (callback) {
getRandomUser(function (error, response, body) {
if (error) return callback(error);
if (gather.can("addGatherer")) {
gather.addGatherer(new User(body));
}
callback();
});
});
};
async.parallel(instructions, function (error) {
if (error) {
console.log("Error while adding gatherers", error);
} else {
console.log("Loaded gatherers");
}
});
// ***** Temporary code to test voting *****
module.exports = function (namespace) { module.exports = function (namespace) {
var refreshGather = function () { var refreshGather = function () {
namespace.sockets.forEach(function (socket) { namespace.sockets.forEach(function (socket) {
socket.emit("gather:refresh", { socket.emit("gather:refresh", {
gather: gather.toJson(), gather: gather.toJson(),
user: socket._user currentUser: socket._user
}); });
}); });
}; };
@ -46,7 +90,10 @@ module.exports = function (namespace) {
}); });
socket.on("gather:vote", function (data) { socket.on("gather:vote", function (data) {
if (data.leader) {
gather.selectLeader(socket._user, data.leader.candidate);
}
refreshGather();
}); });
refreshGather(); refreshGather();

View file

@ -156,6 +156,7 @@ Gather.prototype.leaderVotes = function () {
}; };
Gather.prototype.voteForLeader = function (voter, candidate) { Gather.prototype.voteForLeader = function (voter, candidate) {
// Find voter and then assign their vote to candidate id
this.gatherers.forEach(function (gatherer, index, array) { this.gatherers.forEach(function (gatherer, index, array) {
if (gatherer.id === voter.id) { if (gatherer.id === voter.id) {
array[index].voteForLeader(candidate); array[index].voteForLeader(candidate);

View file

@ -23,8 +23,14 @@ function Gatherer (user) {
this.team = "lobby"; this.team = "lobby";
} }
Gatherer.prototype.voteForLeader = function (user) { Gatherer.prototype.voteForLeader = function (candidate) {
this.leaderVote = user.id; if (candidate === null) {
return this.leaderVote = null;
}
if (typeof candidate === 'number') {
return this.leaderVote = candidate;
}
this.leaderVote = candidate.id;
}; };
module.exports = Gatherer; module.exports = Gatherer;

View file

@ -64,7 +64,7 @@ var UserMenu = React.createClass({
}; };
}, },
componentDidMount: function () { componentDidMount: function () {
socket.on('userCount', this.updateUsers); socket.on('users:update', this.updateUsers);
}, },
updateUsers: function (data) { updateUsers: function (data) {
this.setProps({ this.setProps({
@ -197,22 +197,65 @@ var ChatMessage = React.createClass({
}); });
var CurrentUser = React.createClass({ var CurrentUser = React.createClass({
getDefaultProps: function () {
return {
username: "",
avatar: ""
}
},
componentDidMount: function () { componentDidMount: function () {
var self = this;
socket.on("users:update", function (data) {
self.setProps({
user: data.currentUser
});
});
socket.emit("users:refresh", {});
}, },
render: function () { render: function () {
return ( if (this.props.user) {
<a href="#">{this.props.user.username} &nbsp;<img src={this.props.user.avatar} return (
alt="User Avatar" <a href="#">{this.props.user.username} &nbsp;<img src={this.props.user.avatar}
height="20" alt="User Avatar"
width="20" /></a> height="20"
); width="20" /></a>
);
} else {
return false;
}
}
});
var VoteButton = React.createClass({
cancelVote: function (e) {
socket.emit("gather:vote", {
leader: {
candidate: null
}
});
},
vote: function (e) {
e.preventDefault();
socket.emit("gather:vote", {
leader: {
candidate: parseInt(e.target.value, 10)
}
});
},
render: function () {
if (this.props.currentGatherer === null) {
return false;
}
if (this.props.currentGatherer.leaderVote === this.props.candidate.id) {
return (
<button
onClick={this.cancelVote}
className="btn btn-xs btn-success">Voted
</button>
);
} else {
return (
<button
onClick={this.vote}
className="btn btn-xs btn-default"
value={this.props.candidate.id}>Vote
</button>
);
}
} }
}); });
@ -351,7 +394,7 @@ var Gather = React.createClass({
joinedGather: function () { joinedGather: function () {
var self = this; var self = this;
return this.props.gather.gatherers.some(function (gatherer) { return this.props.gather.gatherers.some(function (gatherer) {
return gatherer.user.id === self.props.user.id; return gatherer.user.id === self.props.currentUser.id;
}); });
}, },
componentDidMount: function () { componentDidMount: function () {
@ -359,7 +402,7 @@ var Gather = React.createClass({
socket.on("gather:refresh", function (data) { socket.on("gather:refresh", function (data) {
self.setProps({ self.setProps({
gather: data.gather, gather: data.gather,
user: data.user currentUser: data.currentUser
}); });
}); });
}, },
@ -384,6 +427,14 @@ var Gather = React.createClass({
inviteToGather: function (e) { inviteToGather: function (e) {
e.preventDefault(); e.preventDefault();
}, },
currentGatherer: function () {
var current = null;
var self = this;
this.props.gather.gatherers.forEach(function (gatherer) {
if (gatherer.id === self.props.currentUser.id) current = gatherer;
});
return current;
},
render: function () { render: function () {
var joinButton; var joinButton;
if (this.joinedGather()) { if (this.joinedGather()) {
@ -407,7 +458,7 @@ var Gather = React.createClass({
<br /> <br />
{this.stateDescription()} {this.stateDescription()}
</div> </div>
<Gatherers gatherers={this.props.gather.gatherers} /> <Gatherers gather={this.props.gather} currentGatherer={this.currentGatherer()} />
<GatherProgress gather={this.props.gather} /> <GatherProgress gather={this.props.gather} />
<div className="panel-footer text-right"> <div className="panel-footer text-right">
<ul className="list-inline"> <ul className="list-inline">
@ -431,13 +482,14 @@ var LeaderPoll = React.createClass({
var Gatherers = React.createClass({ var Gatherers = React.createClass({
render: function () { render: function () {
var gatherers = this.props.gatherers.map(function (gatherer) { var self = this;
var gatherers = this.props.gather.gatherers.map(function (gatherer) {
var lifeforms = ( var lifeforms = (
gatherer.user.ability.lifeforms.map(function (lifeform) { gatherer.user.ability.lifeforms.map(function (lifeform) {
return (<span className="label label-default">{lifeform}</span>); return (<span className="label label-default">{lifeform}</span>);
}) })
); );
var division = (<span className="label label-primary">{gatherer.user.ability.division}</span>);
var commBadge; var commBadge;
if (gatherer.user.ability.commander) { if (gatherer.user.ability.commander) {
commBadge = (<img src="/images/commander.png" commBadge = (<img src="/images/commander.png"
@ -446,16 +498,32 @@ var Gatherers = React.createClass({
width="20" />); width="20" />);
} }
var division = (<span className="label label-primary">{gatherer.user.ability.division}</span>);
var action = lifeforms;
if (self.props.gather.state === "election") {
var votes = self.props.gather.gatherers.reduce(function (acc, voter) {
if (voter.leaderVote === gatherer.id) acc++;
return acc;
}, 0)
action = (
<div className="text-right">
<small>{votes + " votes"} &nbsp;</small>
<VoteButton currentGatherer={self.props.currentGatherer} candidate={gatherer} />
</div>
);
}
return ( return (
<tr key={gatherer.user.id}> <tr key={gatherer.user.id}>
<td className="col-md-1">{commBadge}</td> <td className="col-md-1">{commBadge}</td>
<td className="col-md-5">{gatherer.user.username}</td> <td className="col-md-5">{gatherer.user.username}</td>
<td className="col-md-3">{division}&nbsp;</td> <td className="col-md-3">{division}&nbsp;</td>
<td className="col-md-3">{lifeforms}&nbsp;</td> <td className="col-md-2">{action}&nbsp;</td>
</tr> </tr>
); );
}) })
if (this.props.gatherers.length) { if (this.props.gather.gatherers.length) {
return ( return (
<div className="panel-body"> <div className="panel-body">
<div className="panel panel-default"> <div className="panel panel-default">

View file

@ -20,7 +20,7 @@ var enslClient = require("../ensl/client")();
module.exports = function (namespace) { module.exports = function (namespace) {
var refreshUsers = function (socket) { var refreshUsers = function (socket) {
var receiver = (socket !== undefined) ? socket : namespace; var receivers = (socket !== undefined) ? [socket] : namespace.sockets;
var newCache = {}; var newCache = {};
namespace.sockets.forEach(function (socket) { namespace.sockets.forEach(function (socket) {
@ -37,16 +37,19 @@ module.exports = function (namespace) {
} }
} }
receiver.emit('userCount', { receivers.forEach(function (socket) {
count: users.length, socket.emit('users:update', {
users: users count: users.length,
}); users: users,
currentUser: socket._user
});
});
}; };
namespace.on('connection', function (socket) { namespace.on('connection', function (socket) {
refreshUsers(); refreshUsers();
socket.on('refreshUsers', refreshUsers.bind(null, socket)); socket.on('users:refresh', refreshUsers.bind(null, socket));
socket.on("users:authorize", function (data) { socket.on("users:authorize", function (data) {
var id = parseInt(data.id, 10); var id = parseInt(data.id, 10);

View file

@ -25,6 +25,7 @@
}, },
"homepage": "https://github.com/cblanc/sws_gathers", "homepage": "https://github.com/cblanc/sws_gathers",
"dependencies": { "dependencies": {
"async": "^1.4.0",
"express": "~4.13.1", "express": "~4.13.1",
"express-handlebars": "~2.0.1", "express-handlebars": "~2.0.1",
"extend": "^3.0.0", "extend": "^3.0.0",

File diff suppressed because one or more lines are too long

View file

@ -231,9 +231,8 @@ describe("Gather Model:", function () {
describe("toJson", function () { describe("toJson", function () {
it ("returns a json representation of the gather instance", function () { it ("returns a json representation of the gather instance", function () {
var output = gather.toJson(); var output = gather.toJson();
assert.isArray(output.lobby); assert.isArray(output.gatherers);
assert.isArray(output.marines); assert.isString(output.state);
assert.isArray(output.aliens);
}); });
}); });
describe("leaderVotes", function () { describe("leaderVotes", function () {