diff --git a/src/client/actions.js b/src/client/actions.js index 26e99e3..5a36f3f 100644 --- a/src/client/actions.js +++ b/src/client/actions.js @@ -8,3 +8,6 @@ export const removeUser = createAction('remove user'); export const newMessage = createAction('new message'); export const sendMessage = createAction('send message'); + +export const ws_disconnected = createAction('ws is disconnected'); +export const connecting = createAction('connecting'); diff --git a/src/client/app.js b/src/client/app.js index 932e4f6..82258e2 100644 --- a/src/client/app.js +++ b/src/client/app.js @@ -3,6 +3,7 @@ import { connect } from 'react-redux'; import { AppBar, FlatButton } from 'material-ui'; import { Welcome, Room } from './views'; import { logout } from './actions'; +import CircularProgress from 'material-ui/CircularProgress'; class App extends Component { handleLogout() { @@ -10,12 +11,19 @@ class App extends Component { } render() { - const { username } = this.props; + const { username, connecting } = this.props; let body, right; if (username) { - body = ; - right = ; + if (connecting) { + + + body =
; + } + else { + body = ; + } + right = ; } else { body = ; } diff --git a/src/client/reducers.js b/src/client/reducers.js index 701ced3..d36ce18 100644 --- a/src/client/reducers.js +++ b/src/client/reducers.js @@ -1,12 +1,13 @@ import { combineReducers } from 'redux'; import { createReducer } from 'redux-act'; import { - login, logout, addUser, removeUser, newMessage + login, logout, addUser, removeUser, newMessage, connecting } from './actions'; const initial = { app: { - username: null + username: null, + connecting: false }, users: {}, messages: { @@ -20,7 +21,10 @@ const app = createReducer({ return { ...state, username: payload.username }; }, [logout]: (state, payload) => { - return { ...state, username: null }; + return { ...state, username: null, connecting: false }; + }, + [connecting]: (state, payload) => { + return { ...state, connecting: payload.connecting }; }, }, initial.app); diff --git a/src/client/sagas.js b/src/client/sagas.js index e3b645f..30eace5 100644 --- a/src/client/sagas.js +++ b/src/client/sagas.js @@ -1,17 +1,30 @@ import io from 'socket.io-client'; -import { eventChannel } from 'redux-saga'; +import { eventChannel, END } from 'redux-saga'; import { fork, take, call, put, cancel } from 'redux-saga/effects'; import { - login, logout, addUser, removeUser, newMessage, sendMessage + login, logout, addUser, removeUser, newMessage, sendMessage, + connecting, ws_disconnected } from './actions'; function connect() { - const socket = io('http://localhost:3000'); - return new Promise(resolve => { + const socket = io('http://localhost:3000', { + 'reconnection': true, + 'reconnectionDelay': 1000, + 'reconnectionDelayMax': 5000, + 'reconnectionAttempts': 5 + }); + return new Promise((resolve, reject) => { socket.on('connect', () => { resolve(socket); }); - }); + socket.on('reconnect_failed', (err) => { + reject(new Error('ws:reconnect_failed ')) + }); + }).then( + response => ({ socket: response }) + ).catch( + error => ({ socket, error }) + ); } function subscribe(socket) { @@ -26,24 +39,34 @@ function subscribe(socket) { emit(newMessage({ message })); }); socket.on('disconnect', e => { - // TODO: handle + emit(END) }); - return () => {}; + return () => { }; }); } function* read(socket) { const channel = yield call(subscribe, socket); - while (true) { - let action = yield take(channel); - yield put(action); + try { + while (true) { + let action = yield take(channel); + yield put(action); + } + } + finally { + yield put(ws_disconnected()) } } function* write(socket) { - while (true) { - const { payload } = yield take(`${sendMessage}`); - socket.emit('message', payload); + try { + while (true) { + const { payload } = yield take(`${sendMessage}`); + socket.emit('message', payload); + } + } + finally { + } } @@ -52,20 +75,46 @@ function* handleIO(socket) { yield fork(write, socket); } -function* flow() { - while (true) { - let { payload } = yield take(`${login}`); - const socket = yield call(connect); - socket.emit('login', { username: payload.username }); +function* flow(username) { + try { + while (true) { + yield put(connecting({ connecting: true })); + const { socket, error } = yield call(connect); + yield put(connecting({ connecting: false })); + + if (error) { + yield call([socket, socket.disconnect]); + yield put(logout()); + break; + } - const task = yield fork(handleIO, socket); + if (socket) { + socket.emit('login', { username }); + + const task = yield fork(handleIO, socket); + + let action = yield take([`${logout}`, `${ws_disconnected}`]); + yield cancel(task); + socket.emit('logout'); + yield call([socket, socket.disconnect]); + if (action.type == logout().type) { + break; + } + } + } + } + finally { - let action = yield take(`${logout}`); - yield cancel(task); - socket.emit('logout'); } } export default function* rootSaga() { - yield fork(flow); + let myFlow; + while (true) { + let {payload} = yield take(`${login}`); + if (myFlow) { + yield cancel(myFlow); + } + myFlow = yield fork(flow, payload.username); + } }