Skip to content

Commit 59a8b52

Browse files
committed
set clientId on remote actions for duplicate vote prevention
1 parent 84c51e7 commit 59a8b52

7 files changed

+42
-3
lines changed

package.json

+3-1
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,12 @@
2525
"dependencies": {
2626
"classnames": "^2.1.3",
2727
"immutable": "^3.7.5",
28+
"object-assign": "^4.0.1",
2829
"react": "^0.13.3",
2930
"react-redux": "^2.1.0",
3031
"react-router": "^0.13.3",
3132
"redux": "^2.0.0",
32-
"socket.io-client": "^1.3.6"
33+
"socket.io-client": "^1.3.6",
34+
"uuid": "^2.0.1"
3335
}
3436
}

src/action_creators.js

+7
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
export function setClientId(clientId) {
2+
return {
3+
type: 'SET_CLIENT_ID',
4+
clientId
5+
};
6+
}
7+
18
export function setState(state) {
29
return {
310
type: 'SET_STATE',

src/client_id.js

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import uuid from 'uuid';
2+
3+
export default function getClientId() {
4+
let id = localStorage.getItem('clientId');
5+
if (!id) {
6+
id = uuid.v4();
7+
localStorage.setItem('clientId', id);
8+
}
9+
return id;
10+
}

src/index.jsx

+3-1
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@ import {createStore, applyMiddleware} from 'redux';
44
import {Provider} from 'react-redux';
55
import io from 'socket.io-client';
66
import reducer from './reducer';
7-
import {setState} from './action_creators';
7+
import {setClientId, setState} from './action_creators';
88
import remoteActionMiddleware from './remote_action_middleware';
9+
import getClientId from './client_id';
910
import App from './components/App';
1011
import {VotingContainer} from './components/Voting';
1112
import {ResultsContainer} from './components/Results';
@@ -21,6 +22,7 @@ const createStoreWithMiddleware = applyMiddleware(
2122
remoteActionMiddleware(socket)
2223
)(createStore);
2324
const store = createStoreWithMiddleware(reducer);
25+
store.dispatch(setClientId(getClientId()));
2426

2527
const routes = <Route handler={App}>
2628
<Route path="/results" handler={ResultsContainer} />

src/reducer.js

+2
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ function resetVote(state) {
2929

3030
export default function(state = Map(), action) {
3131
switch (action.type) {
32+
case 'SET_CLIENT_ID':
33+
return state.set('clientId', action.clientId);
3234
case 'SET_STATE':
3335
return resetVote(setState(state, action.state));
3436
case 'VOTE':

src/remote_action_middleware.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1+
import objectAssign from 'object-assign';
2+
13
export default socket => store => next => action => {
24
if (action.meta && action.meta.remote) {
3-
socket.emit('action', action);
5+
const clientId = store.getState().get('clientId');
6+
socket.emit('action', objectAssign({}, action, {clientId}));
47
}
58
return next(action);
69
}

test/reducer_spec.js

+13
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,19 @@ import reducer from '../src/reducer';
55

66
describe('reducer', () => {
77

8+
it('handles SET_CLIENT_ID', () => {
9+
const initialState = Map();
10+
const action = {
11+
type: 'SET_CLIENT_ID',
12+
clientId: '1234'
13+
};
14+
const nextState = reducer(initialState, action);
15+
16+
expect(nextState).to.equal(fromJS({
17+
clientId: '1234'
18+
}));
19+
});
20+
821
it('handles SET_STATE', () => {
922
const initialState = Map();
1023
const action = {

0 commit comments

Comments
 (0)