Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
set clientId on remote actions for duplicate vote prevention
  • Loading branch information
teropa committed Sep 10, 2015
1 parent 84c51e7 commit 59a8b52
Show file tree
Hide file tree
Showing 7 changed files with 42 additions and 3 deletions.
4 changes: 3 additions & 1 deletion package.json
Expand Up @@ -25,10 +25,12 @@
"dependencies": {
"classnames": "^2.1.3",
"immutable": "^3.7.5",
"object-assign": "^4.0.1",
"react": "^0.13.3",
"react-redux": "^2.1.0",
"react-router": "^0.13.3",
"redux": "^2.0.0",
"socket.io-client": "^1.3.6"
"socket.io-client": "^1.3.6",
"uuid": "^2.0.1"
}
}
7 changes: 7 additions & 0 deletions src/action_creators.js
@@ -1,3 +1,10 @@
export function setClientId(clientId) {
return {
type: 'SET_CLIENT_ID',
clientId
};
}

export function setState(state) {
return {
type: 'SET_STATE',
Expand Down
10 changes: 10 additions & 0 deletions src/client_id.js
@@ -0,0 +1,10 @@
import uuid from 'uuid';

export default function getClientId() {
let id = localStorage.getItem('clientId');
if (!id) {
id = uuid.v4();
localStorage.setItem('clientId', id);
}
return id;
}
4 changes: 3 additions & 1 deletion src/index.jsx
Expand Up @@ -4,8 +4,9 @@ import {createStore, applyMiddleware} from 'redux';
import {Provider} from 'react-redux';
import io from 'socket.io-client';
import reducer from './reducer';
import {setState} from './action_creators';
import {setClientId, setState} from './action_creators';
import remoteActionMiddleware from './remote_action_middleware';
import getClientId from './client_id';
import App from './components/App';
import {VotingContainer} from './components/Voting';
import {ResultsContainer} from './components/Results';
Expand All @@ -21,6 +22,7 @@ const createStoreWithMiddleware = applyMiddleware(
remoteActionMiddleware(socket)
)(createStore);
const store = createStoreWithMiddleware(reducer);
store.dispatch(setClientId(getClientId()));

const routes = <Route handler={App}>
<Route path="/results" handler={ResultsContainer} />
Expand Down
2 changes: 2 additions & 0 deletions src/reducer.js
Expand Up @@ -29,6 +29,8 @@ function resetVote(state) {

export default function(state = Map(), action) {
switch (action.type) {
case 'SET_CLIENT_ID':
return state.set('clientId', action.clientId);
case 'SET_STATE':
return resetVote(setState(state, action.state));
case 'VOTE':
Expand Down
5 changes: 4 additions & 1 deletion src/remote_action_middleware.js
@@ -1,6 +1,9 @@
import objectAssign from 'object-assign';

export default socket => store => next => action => {
if (action.meta && action.meta.remote) {
socket.emit('action', action);
const clientId = store.getState().get('clientId');
socket.emit('action', objectAssign({}, action, {clientId}));
}
return next(action);
}
13 changes: 13 additions & 0 deletions test/reducer_spec.js
Expand Up @@ -5,6 +5,19 @@ import reducer from '../src/reducer';

describe('reducer', () => {

it('handles SET_CLIENT_ID', () => {
const initialState = Map();
const action = {
type: 'SET_CLIENT_ID',
clientId: '1234'
};
const nextState = reducer(initialState, action);

expect(nextState).to.equal(fromJS({
clientId: '1234'
}));
});

it('handles SET_STATE', () => {
const initialState = Map();
const action = {
Expand Down

1 comment on commit 59a8b52

@andiwinata
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey, is it necessary to put client ID inside redux store?
isn't that will always be the same for each client regardless any state?

Please sign in to comment.