From a1aa140848e85e91813e6fea2a6c582cbca4d362 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Fri, 8 Sep 2017 13:55:47 +0900 Subject: [PATCH] Handle connection loss gracefully --- src/actions/socket.js | 13 +++++++++---- src/index.js | 10 +++++++--- src/reducers/resource.js | 4 ++++ src/reducers/socket.js | 16 +++++++++------- src/socket.js | 6 +++--- src/store.js | 14 ++++++++++---- src/ui/connection.js | 21 +++++++++++++++++++-- 7 files changed, 61 insertions(+), 23 deletions(-) diff --git a/src/actions/socket.js b/src/actions/socket.js index 172e772..533719d 100644 --- a/src/actions/socket.js +++ b/src/actions/socket.js @@ -1,5 +1,10 @@ -export const SOCKET_CONNECTED = "SOCKET_CONNECTED"; -export const SOCKET_DISCONNECTED = "SOCKET_DISCONNECTED"; +export const SOCKET_STATE = { + CONNECTED: "SOCKET_CONNECTED", + CONNECTING: "SOCKET_CONNECTING", + DISCONNECTED: "SOCKET_DISCONNECTED" +}; -export const socket_connected = () => ({ type: SOCKET_CONNECTED }); -export const socket_disconnected = () => ({ type: SOCKET_DISCONNECTED }); +export const SOCKET_UPDATE = "SOCKET_UPDATE"; + +export const socket_update = (state, reason=null) => + ({ type: SOCKET_UPDATE, state, reason }); diff --git a/src/index.js b/src/index.js index c550c3c..614f74a 100644 --- a/src/index.js +++ b/src/index.js @@ -6,21 +6,25 @@ import { ConnectedRouter } from 'react-router-redux'; import 'preact/devtools'; import './polyfills'; -import store, { history } from './store'; +import store, { create, history } from './store'; import scss from '../scss/main.scss'; import { ws_init } from './socket'; import { filter_subscribe } from './actions/filter_subscribe'; -import { socket_connected, socket_disconnected } from './actions/socket'; +import { socket_update, SOCKET_STATE } from './actions/socket'; import Nav from './ui/navigation'; import Main from './ui/main'; import Connection from './ui/connection'; export function initialize(uri) { + store.dispatch(socket_update(SOCKET_STATE.CONNECTING)); ws_init(uri, () => { - store.dispatch(socket_connected()); + store.dispatch(socket_update(SOCKET_STATE.CONNECTED)); store.dispatch(filter_subscribe()); store.dispatch(filter_subscribe('server')); + }, () => { + store.dispatch(socket_update(SOCKET_STATE.DISCONNECTED, + "You were disconnected.")); }); } diff --git a/src/reducers/resource.js b/src/reducers/resource.js index 3b5a6b7..a06f4fb 100644 --- a/src/reducers/resource.js +++ b/src/reducers/resource.js @@ -1,4 +1,5 @@ import { UPDATE_RESOURCES, RESOURCES_REMOVED } from '../actions/resources'; +import { SOCKET_STATE, SOCKET_UPDATE } from '../actions/socket'; export default function resourceReducer(type) { return (state = {}, action) => { @@ -17,6 +18,9 @@ export default function resourceReducer(type) { return Object.values(state) .filter(r => action.ids.indexOf(r.id) === -1) .reduce((s, r) => ({ ...s, [r.id]: r }), {}); + case SOCKET_UPDATE: + const _state = action.state; + return _state === SOCKET_STATE.CONNECTING ? {} : state; } return state; }; diff --git a/src/reducers/socket.js b/src/reducers/socket.js index 2d332fd..378326e 100644 --- a/src/reducers/socket.js +++ b/src/reducers/socket.js @@ -1,12 +1,14 @@ -import { SOCKET_CONNECTED, SOCKET_DISCONNECTED } from '../actions/socket'; +import { SOCKET_STATE, SOCKET_UPDATE } from '../actions/socket'; -export default function socket(state = { connected: false }, action) { +export default function socket(_state = { + state: SOCKET_STATE.DISCONNECTED, + reason: null +}, action) { + const { state, reason } = action; switch (action.type) { - case SOCKET_CONNECTED: - return { ...state, connected: true }; - case SOCKET_DISCONNECTED: - return { ...state, connected: false }; + case SOCKET_UPDATE: + return { ..._state, state, reason }; default: - return state; + return _state; } } diff --git a/src/socket.js b/src/socket.js index 5724ce6..1a255aa 100644 --- a/src/socket.js +++ b/src/socket.js @@ -35,9 +35,9 @@ function ws_recv(e) { handler && handler(msg); } -export function ws_init(uri, cb) { +export function ws_init(uri, open, close) { ws = new WebSocket(uri); - ws.addEventListener("open", cb); + ws.addEventListener("open", open); ws.addEventListener("message", ws_recv); - ws.addEventListener("close", () => console.log("ws closed")); + ws.addEventListener("close", close); } diff --git a/src/store.js b/src/store.js index 4e46058..0ca46eb 100644 --- a/src/store.js +++ b/src/store.js @@ -13,10 +13,16 @@ export const history = createHistory(); const _compose = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; -const store = createStore( - reducer, - _compose(applyMiddleware(thunk, routerMiddleware(history))), -); + +let store; + +export const create = () => { + store = createStore( + reducer, + _compose(applyMiddleware(thunk, routerMiddleware(history))), + ); +}; +create(); if (module.hot) { // Enable webpack hot module replacement for reducers diff --git a/src/ui/connection.js b/src/ui/connection.js index 4dea7a3..4be45dd 100644 --- a/src/ui/connection.js +++ b/src/ui/connection.js @@ -1,14 +1,17 @@ import React, { Component } from 'react'; import { connect } from 'react-redux'; import { + Alert, Card, CardHeader, CardBlock, FormGroup, + Progress, Label, Input } from 'reactstrap'; import { initialize } from '..'; +import { SOCKET_STATE } from '../actions/socket'; class ConnectionOverlay extends Component { constructor() { @@ -25,15 +28,29 @@ class ConnectionOverlay extends Component { render() { const { socket } = this.props; - if (socket.connected) { + if (socket.state === SOCKET_STATE.CONNECTED) { return null; } + if (socket.state === SOCKET_STATE.CONNECTING) { + return ( +
+ + Connect to synapse + +

Connecting...

+ +
+
+
+ ); + } const { uri, autoconnect } = this.state; return (
Connect to synapse + {socket.reason && {socket.reason}} initialize(this.state.uri)} - >Connect + >{socket.reason ? "Reconnect" : "Connect"}