Skip to content

Commit a6ffed0

Browse files
committed
connection state indication
1 parent f314bce commit a6ffed0

File tree

7 files changed

+103
-2
lines changed

7 files changed

+103
-2
lines changed

src/action_creators.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,14 @@ export function setClientId(clientId) {
55
};
66
}
77

8+
export function setConnectionState(state, connected) {
9+
return {
10+
type: 'SET_CONNECTION_STATE',
11+
state,
12+
connected
13+
};
14+
}
15+
816
export function setState(state) {
917
return {
1018
type: 'SET_STATE',

src/components/App.jsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
import React from 'react/addons';
22
import {RouteHandler} from 'react-router';
3+
import {ConnectionStateContainer} from './ConnectionState';
34

45
export default React.createClass({
56
render: function() {
6-
return <RouteHandler />
7+
return <div>
8+
<ConnectionStateContainer />
9+
<RouteHandler />
10+
</div>
711
}
812
});

src/components/ConnectionState.jsx

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import React from 'react/addons';
2+
import {connect} from 'react-redux';
3+
import {Map} from 'immutable';
4+
5+
export const ConnectionState = React.createClass({
6+
mixins: [React.addons.PureRenderMixin],
7+
isVisible: function() {
8+
return !this.props.connected;
9+
},
10+
getMessage: function() {
11+
return `Not connected (${this.props.state})`;
12+
},
13+
render: function() {
14+
return <div className="connectionState"
15+
style={{display: this.isVisible() ? 'block' : 'none'}}>
16+
{this.getMessage()}
17+
</div>
18+
}
19+
});
20+
21+
22+
export const ConnectionStateContainer = connect(
23+
state => state.get('connection', Map()).toJS()
24+
)(ConnectionState);

src/index.jsx

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ 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 {setClientId, setState} from './action_creators';
7+
import {setClientId, setState, setConnectionState} from './action_creators';
88
import remoteActionMiddleware from './remote_action_middleware';
99
import getClientId from './client_id';
1010
import App from './components/App';
@@ -17,6 +17,17 @@ const socket = io(`${location.protocol}//${location.hostname}:8090`);
1717
socket.on('state', state =>
1818
store.dispatch(setState(state))
1919
);
20+
[
21+
'connect',
22+
'connect_error',
23+
'connect_timeout',
24+
'reconnect',
25+
'reconnecting',
26+
'reconnect_error',
27+
'reconnect_failed'
28+
].forEach(ev =>
29+
socket.on(ev, () => store.dispatch(setConnectionState(ev, socket.connected)))
30+
);
2031

2132
const createStoreWithMiddleware = applyMiddleware(
2233
remoteActionMiddleware(socket)

src/reducer.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
import {List, Map} from 'immutable';
22

3+
function setConnectionState(state, connectionState, connected) {
4+
return state.set('connection', Map({
5+
state: connectionState,
6+
connected
7+
}));
8+
}
9+
310
function setState(state, newState) {
411
return state.merge(newState);
512
}
@@ -31,6 +38,8 @@ export default function(state = Map(), action) {
3138
switch (action.type) {
3239
case 'SET_CLIENT_ID':
3340
return state.set('clientId', action.clientId);
41+
case 'SET_CONNECTION_STATE':
42+
return setConnectionState(state, action.state, action.connected);
3443
case 'SET_STATE':
3544
return resetVote(setState(state, action.state));
3645
case 'VOTE':

src/style.css

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,3 +121,18 @@ body {
121121
font-size: 4rem;
122122
text-align: center;
123123
}
124+
125+
/* Connection State */
126+
127+
.connectionState {
128+
position: fixed;
129+
top: 0;
130+
left: 0;
131+
right: 0;
132+
133+
padding: 5px;
134+
135+
text-align: center;
136+
137+
background-color: #B71C1C;
138+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import React from 'react/addons';
2+
import {expect} from 'chai';
3+
import {ConnectionState} from '../../src/components/ConnectionState';
4+
5+
const {renderIntoDocument, findRenderedDOMComponentWithTag}
6+
= React.addons.TestUtils;
7+
8+
describe('ConnectionState', () => {
9+
10+
it('is not visible when connected', () => {
11+
const component = renderIntoDocument(<ConnectionState connected={true} />);
12+
const div = findRenderedDOMComponentWithTag(component, 'div');
13+
expect(div.getDOMNode().style.display).to.equal('none');
14+
});
15+
16+
it('is visible when not connected', () => {
17+
const component = renderIntoDocument(<ConnectionState connected={false} />);
18+
const div = findRenderedDOMComponentWithTag(component, 'div');
19+
expect(div.getDOMNode().style.display).to.equal('block');
20+
});
21+
22+
it('contains connection state message', () => {
23+
const component = renderIntoDocument(
24+
<ConnectionState connected={false} state="Fail" />
25+
);
26+
const div = findRenderedDOMComponentWithTag(component, 'div');
27+
expect(div.getDOMNode().textContent).to.contain('Fail');
28+
});
29+
30+
});

0 commit comments

Comments
 (0)