aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMaximilian Hils <git@maximilianhils.com>2016-06-02 21:12:59 -0700
committerMaximilian Hils <git@maximilianhils.com>2016-06-02 21:12:59 -0700
commit7c63aa3708a09b997575cb85cb6dfaaa41082fab (patch)
treef785caa03b667fb38487575ae7030e9e46bbd84d
parent6a91ab166f4479eb50846fed184aa197dd018bfe (diff)
downloadmitmproxy-7c63aa3708a09b997575cb85cb6dfaaa41082fab.tar.gz
mitmproxy-7c63aa3708a09b997575cb85cb6dfaaa41082fab.tar.bz2
mitmproxy-7c63aa3708a09b997575cb85cb6dfaaa41082fab.zip
web: reduxify WebSocket
-rw-r--r--mitmproxy/web/static/app.js176
-rw-r--r--web/src/js/app.js10
-rw-r--r--web/src/js/components/header.js24
-rw-r--r--web/src/js/connection.js16
-rw-r--r--web/src/js/ducks/flows.js15
-rw-r--r--web/src/js/ducks/index.js8
-rw-r--r--web/src/js/ducks/websocket.js34
7 files changed, 212 insertions, 71 deletions
diff --git a/mitmproxy/web/static/app.js b/mitmproxy/web/static/app.js
index b7e0f24e..d97a567d 100644
--- a/mitmproxy/web/static/app.js
+++ b/mitmproxy/web/static/app.js
@@ -455,10 +455,6 @@ var _redux = require('redux');
var _reactRedux = require('react-redux');
-var _jquery = require('jquery');
-
-var _jquery2 = _interopRequireDefault(_jquery);
-
var _connection = require('./connection');
var _connection2 = _interopRequireDefault(_connection);
@@ -475,8 +471,8 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
var store = (0, _redux.createStore)(_index2.default);
-(0, _jquery2.default)(function () {
- window.ws = new _connection2.default("/updates");
+document.addEventListener('DOMContentLoaded', function () {
+ window.ws = new _connection2.default("/updates", store.dispatch);
window.onerror = function (msg) {
_actions.EventLogActions.add_event(msg);
@@ -489,7 +485,7 @@ var store = (0, _redux.createStore)(_index2.default);
), document.getElementById("mitmproxy"));
});
-},{"./actions.js":2,"./components/proxyapp.js":20,"./connection":21,"./ducks/index":24,"jquery":"jquery","react":"react","react-dom":"react-dom","react-redux":"react-redux","redux":"redux"}],4:[function(require,module,exports){
+},{"./actions.js":2,"./components/proxyapp.js":20,"./connection":21,"./ducks/index":25,"react":"react","react-dom":"react-dom","react-redux":"react-redux","redux":"redux"}],4:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
@@ -708,7 +704,7 @@ ToggleInputButton.propTypes = {
onToggleChanged: _react2.default.PropTypes.func.isRequired
};
-},{"../utils.js":29,"lodash":"lodash","react":"react","react-dom":"react-dom"}],5:[function(require,module,exports){
+},{"../utils.js":31,"lodash":"lodash","react":"react","react-dom":"react-dom"}],5:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
@@ -966,7 +962,7 @@ var ValueEditor = exports.ValueEditor = _react2.default.createClass({
}
});
-},{"../utils.js":29,"react":"react","react-dom":"react-dom"}],6:[function(require,module,exports){
+},{"../utils.js":31,"react":"react","react-dom":"react-dom"}],6:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
@@ -1206,7 +1202,7 @@ var EventLogContainer = (0, _reactRedux.connect)(undefined, function (dispatch)
exports.default = EventLogContainer;
-},{"../ducks/eventLog":23,"../store/view.js":28,"./common":4,"./helpers/AutoScroll":16,"./helpers/VirtualScroll":17,"react":"react","react-dom":"react-dom","react-redux":"react-redux","shallowequal":"shallowequal"}],7:[function(require,module,exports){
+},{"../ducks/eventLog":23,"../store/view.js":30,"./common":4,"./helpers/AutoScroll":16,"./helpers/VirtualScroll":17,"react":"react","react-dom":"react-dom","react-redux":"react-redux","shallowequal":"shallowequal"}],7:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
@@ -1475,7 +1471,7 @@ var all_columns = [TLSColumn, IconColumn, PathColumn, MethodColumn, StatusColumn
exports.default = all_columns;
-},{"../flow/utils.js":26,"../utils.js":29,"react":"react"}],8:[function(require,module,exports){
+},{"../flow/utils.js":28,"../utils.js":31,"react":"react"}],8:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
@@ -1767,7 +1763,7 @@ FlowTable.defaultProps = {
};
exports.default = (0, _AutoScroll2.default)(FlowTable);
-},{"../utils.js":29,"./flowtable-columns.js":7,"./helpers/AutoScroll":16,"./helpers/VirtualScroll":17,"classnames":"classnames","lodash":"lodash","react":"react","react-dom":"react-dom","shallowequal":"shallowequal"}],9:[function(require,module,exports){
+},{"../utils.js":31,"./flowtable-columns.js":7,"./helpers/AutoScroll":16,"./helpers/VirtualScroll":17,"classnames":"classnames","lodash":"lodash","react":"react","react-dom":"react-dom","shallowequal":"shallowequal"}],9:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
@@ -2121,7 +2117,7 @@ var ContentView = _react2.default.createClass({
exports.default = ContentView;
-},{"../../flow/utils.js":26,"../../utils.js":29,"lodash":"lodash","react":"react"}],10:[function(require,module,exports){
+},{"../../flow/utils.js":28,"../../utils.js":31,"lodash":"lodash","react":"react"}],10:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
@@ -2389,7 +2385,7 @@ var Details = _react2.default.createClass({
exports.default = Details;
-},{"../../utils.js":29,"lodash":"lodash","react":"react"}],11:[function(require,module,exports){
+},{"../../utils.js":31,"lodash":"lodash","react":"react"}],11:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
@@ -2883,7 +2879,7 @@ var Error = exports.Error = _react2.default.createClass({
}
});
-},{"../../actions.js":2,"../../flow/utils.js":26,"../../utils.js":29,"../editor.js":5,"./contentview.js":9,"lodash":"lodash","react":"react","react-dom":"react-dom"}],13:[function(require,module,exports){
+},{"../../actions.js":2,"../../flow/utils.js":28,"../../utils.js":31,"../editor.js":5,"./contentview.js":9,"lodash":"lodash","react":"react","react-dom":"react-dom"}],13:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
@@ -3066,7 +3062,7 @@ function Footer(_ref) {
);
}
-},{"../utils.js":29,"./common.js":4,"react":"react"}],15:[function(require,module,exports){
+},{"../utils.js":31,"./common.js":4,"react":"react"}],15:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
@@ -3387,39 +3383,39 @@ var OptionMenu = exports.OptionMenu = function OptionMenu(props) {
_react2.default.createElement(
"div",
{ className: "menu-row" },
- _react2.default.createElement(_common.ToggleButton, { name: "showhost",
+ _react2.default.createElement(_common.ToggleButton, { text: "showhost",
checked: showhost,
- onToggleChanged: function onToggleChanged() {
+ onToggle: function onToggle() {
return _actions.SettingsActions.update({ showhost: !showhost });
}
}),
- _react2.default.createElement(_common.ToggleButton, { name: "no_upstream_cert",
+ _react2.default.createElement(_common.ToggleButton, { text: "no_upstream_cert",
checked: no_upstream_cert,
- onToggleChanged: function onToggleChanged() {
+ onToggle: function onToggle() {
return _actions.SettingsActions.update({ no_upstream_cert: !no_upstream_cert });
}
}),
- _react2.default.createElement(_common.ToggleButton, { name: "rawtcp",
+ _react2.default.createElement(_common.ToggleButton, { text: "rawtcp",
checked: rawtcp,
- onToggleChanged: function onToggleChanged() {
+ onToggle: function onToggle() {
return _actions.SettingsActions.update({ rawtcp: !rawtcp });
}
}),
- _react2.default.createElement(_common.ToggleButton, { name: "http2",
+ _react2.default.createElement(_common.ToggleButton, { text: "http2",
checked: http2,
- onToggleChanged: function onToggleChanged() {
+ onToggle: function onToggle() {
return _actions.SettingsActions.update({ http2: !http2 });
}
}),
- _react2.default.createElement(_common.ToggleButton, { name: "anticache",
+ _react2.default.createElement(_common.ToggleButton, { text: "anticache",
checked: anticache,
- onToggleChanged: function onToggleChanged() {
+ onToggle: function onToggle() {
return _actions.SettingsActions.update({ anticache: !anticache });
}
}),
- _react2.default.createElement(_common.ToggleButton, { name: "anticomp",
+ _react2.default.createElement(_common.ToggleButton, { text: "anticomp",
checked: anticomp,
- onToggleChanged: function onToggleChanged() {
+ onToggle: function onToggle() {
return _actions.SettingsActions.update({ anticomp: !anticomp });
}
}),
@@ -3609,7 +3605,7 @@ var Header = exports.Header = _react2.default.createClass({
}
});
-},{"../actions.js":2,"../filt/filt.js":25,"../utils.js":29,"./common.js":4,"./eventlog":6,"jquery":"jquery","react":"react","react-dom":"react-dom","react-redux":"react-redux"}],16:[function(require,module,exports){
+},{"../actions.js":2,"../filt/filt.js":27,"../utils.js":31,"./common.js":4,"./eventlog":6,"jquery":"jquery","react":"react","react-dom":"react-dom","react-redux":"react-redux"}],16:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
@@ -4025,7 +4021,7 @@ var MainView = _react2.default.createClass({
exports.default = MainView;
-},{"../actions.js":2,"../filt/filt.js":25,"../store/view.js":28,"../utils.js":29,"./common.js":4,"./flowtable.js":8,"./flowview/index.js":11,"react":"react"}],19:[function(require,module,exports){
+},{"../actions.js":2,"../filt/filt.js":27,"../store/view.js":30,"../utils.js":31,"./common.js":4,"./flowtable.js":8,"./flowview/index.js":11,"react":"react"}],19:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
@@ -4161,7 +4157,7 @@ var Prompt = _react2.default.createClass({
exports.default = Prompt;
-},{"../utils.js":29,"lodash":"lodash","react":"react","react-dom":"react-dom"}],20:[function(require,module,exports){
+},{"../utils.js":31,"lodash":"lodash","react":"react","react-dom":"react-dom"}],20:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
@@ -4357,29 +4353,38 @@ var App = exports.App = _react2.default.createElement(
)
);
-},{"../store/store.js":27,"../utils.js":29,"./common.js":4,"./eventlog.js":6,"./footer.js":14,"./header.js":15,"./mainview.js":18,"lodash":"lodash","react":"react","react-dom":"react-dom","react-redux":"react-redux","react-router":"react-router"}],21:[function(require,module,exports){
+},{"../store/store.js":29,"../utils.js":31,"./common.js":4,"./eventlog.js":6,"./footer.js":14,"./header.js":15,"./mainview.js":18,"lodash":"lodash","react":"react","react-dom":"react-dom","react-redux":"react-redux","react-router":"react-router"}],21:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
+exports.default = Connection;
var _actions = require("./actions.js");
var _dispatcher = require("./dispatcher.js");
-function Connection(url) {
+var _websocket = require("./ducks/websocket");
+
+var websocketActions = _interopRequireWildcard(_websocket);
+
+function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
+
+function Connection(url, dispatch) {
if (url[0] === "/") {
url = location.origin.replace("http", "ws") + url;
}
var ws = new WebSocket(url);
ws.onopen = function () {
+ dispatch(websocketActions.connected());
_actions.ConnectionActions.open();
};
- ws.onmessage = function (message) {
- var m = JSON.parse(message.data);
- _dispatcher.AppDispatcher.dispatchServerAction(m);
+ ws.onmessage = function (m) {
+ var message = JSON.parse(m.data);
+ _dispatcher.AppDispatcher.dispatchServerAction(message);
+ dispatch(websocketActions.receiveMessage(message));
};
ws.onerror = function () {
_actions.ConnectionActions.error();
@@ -4388,13 +4393,12 @@ function Connection(url) {
ws.onclose = function () {
_actions.ConnectionActions.close();
_actions.EventLogActions.add_event("WebSocket connection closed.");
+ dispatch(websocketActions.disconnected());
};
return ws;
}
-exports.default = Connection;
-
-},{"./actions.js":2,"./dispatcher.js":22}],22:[function(require,module,exports){
+},{"./actions.js":2,"./dispatcher.js":22,"./ducks/websocket":26}],22:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
@@ -4475,6 +4479,32 @@ function toggleEventLogVisibility() {
}
},{}],24:[function(require,module,exports){
+"use strict";
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.default = reducer;
+var defaultState = {
+ list: [],
+ isFetching: false,
+ updateBeforeFetch: [],
+ byId: {},
+ indexOf: {},
+ views: {}
+};
+
+function reducer() {
+ var state = arguments.length <= 0 || arguments[0] === undefined ? defaultState : arguments[0];
+ var action = arguments[1];
+
+ switch (action.type) {
+ default:
+ return state;
+ }
+}
+
+},{}],25:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, "__esModule", {
@@ -4487,15 +4517,71 @@ var _eventLog = require('./eventLog.js');
var _eventLog2 = _interopRequireDefault(_eventLog);
+var _websocket = require('./websocket.js');
+
+var _websocket2 = _interopRequireDefault(_websocket);
+
+var _flows = require('./flows.js');
+
+var _flows2 = _interopRequireDefault(_flows);
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var rootReducer = (0, _redux.combineReducers)({
- eventLog: _eventLog2.default
+ eventLog: _eventLog2.default,
+ flows: _flows2.default,
+ websocket: _websocket2.default
});
exports.default = rootReducer;
-},{"./eventLog.js":23,"redux":"redux"}],25:[function(require,module,exports){
+},{"./eventLog.js":23,"./flows.js":24,"./websocket.js":26,"redux":"redux"}],26:[function(require,module,exports){
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.default = reducer;
+exports.connected = connected;
+exports.disconnected = disconnected;
+exports.receiveMessage = receiveMessage;
+var CONNECTED = 'WEBSOCKET_CONNECTED';
+var DISCONNECTED = 'WEBSOCKET_DISCONNECTED';
+var RECEIVE_MESSAGE = 'RECEIVE_WEBSOCKET_MESSAGE';
+
+var defaultState = {
+ connected: true
+};
+/* we may want to have an error message attribute here at some point */
+function reducer() {
+ var state = arguments.length <= 0 || arguments[0] === undefined ? defaultState : arguments[0];
+ var action = arguments[1];
+
+ switch (action.type) {
+ case CONNECTED:
+ return {
+ connected: true
+ };
+ case DISCONNECTED:
+ return {
+ connected: false
+ };
+ default:
+ return state;
+ }
+}
+
+function connected() {
+ return { type: CONNECTED };
+}
+function disconnected() {
+ return { type: DISCONNECTED };
+}
+function receiveMessage(message) {
+ return { type: RECEIVE_MESSAGE, message: message };
+}
+
+},{}],27:[function(require,module,exports){
"use strict";
module.exports = function () {
@@ -6399,7 +6485,7 @@ module.exports = function () {
};
}();
-},{"../flow/utils.js":26}],26:[function(require,module,exports){
+},{"../flow/utils.js":28}],28:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
@@ -6533,7 +6619,7 @@ var parseHttpVersion = exports.parseHttpVersion = function parseHttpVersion(http
});
};
-},{"jquery":"jquery","lodash":"lodash"}],27:[function(require,module,exports){
+},{"jquery":"jquery","lodash":"lodash"}],29:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
@@ -6720,7 +6806,7 @@ _lodash2.default.extend(EventLogStore.prototype, LiveListStore.prototype, {
}
});
-},{"../actions.js":2,"../dispatcher.js":22,"events":1,"jquery":"jquery","lodash":"lodash"}],28:[function(require,module,exports){
+},{"../actions.js":2,"../dispatcher.js":22,"events":1,"jquery":"jquery","lodash":"lodash"}],30:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
@@ -6850,7 +6936,7 @@ _lodash2.default.extend(StoreView.prototype, _events.EventEmitter.prototype, {
}
});
-},{"../utils.js":29,"events":1,"lodash":"lodash"}],29:[function(require,module,exports){
+},{"../utils.js":31,"events":1,"lodash":"lodash"}],31:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
diff --git a/web/src/js/app.js b/web/src/js/app.js
index 5b5d1fc8..f631b2c5 100644
--- a/web/src/js/app.js
+++ b/web/src/js/app.js
@@ -3,7 +3,6 @@ import {render} from 'react-dom'
import {createStore} from 'redux'
import {Provider} from 'react-redux'
-import $ from "jquery"
import Connection from "./connection"
import {App} from "./components/proxyapp.js"
import {EventLogActions} from "./actions.js"
@@ -11,8 +10,8 @@ import rootReducer from './ducks/index';
let store = createStore(rootReducer);
-$(function () {
- window.ws = new Connection("/updates");
+document.addEventListener('DOMContentLoaded', () => {
+ window.ws = new Connection("/updates", store.dispatch);
window.onerror = function (msg) {
EventLogActions.add_event(msg);
@@ -20,6 +19,7 @@ $(function () {
render(
<Provider store={store}>{App}</Provider>,
- document.getElementById("mitmproxy"));
-});
+ document.getElementById("mitmproxy")
+ );
+});
diff --git a/web/src/js/components/header.js b/web/src/js/components/header.js
index 76c4744a..e329b3f5 100644
--- a/web/src/js/components/header.js
+++ b/web/src/js/components/header.js
@@ -243,29 +243,29 @@ export const OptionMenu = (props) => {
return (
<div>
<div className="menu-row">
- <ToggleButton name="showhost"
+ <ToggleButton text="showhost"
checked={showhost}
- onToggleChanged={() => SettingsActions.update({showhost: !showhost})}
+ onToggle={() => SettingsActions.update({showhost: !showhost})}
/>
- <ToggleButton name="no_upstream_cert"
+ <ToggleButton text="no_upstream_cert"
checked={no_upstream_cert}
- onToggleChanged={() => SettingsActions.update({no_upstream_cert: !no_upstream_cert})}
+ onToggle={() => SettingsActions.update({no_upstream_cert: !no_upstream_cert})}
/>
- <ToggleButton name="rawtcp"
+ <ToggleButton text="rawtcp"
checked={rawtcp}
- onToggleChanged={() => SettingsActions.update({rawtcp: !rawtcp})}
+ onToggle={() => SettingsActions.update({rawtcp: !rawtcp})}
/>
- <ToggleButton name="http2"
+ <ToggleButton text="http2"
checked={http2}
- onToggleChanged={() => SettingsActions.update({http2: !http2})}
+ onToggle={() => SettingsActions.update({http2: !http2})}
/>
- <ToggleButton name="anticache"
+ <ToggleButton text="anticache"
checked={anticache}
- onToggleChanged={() => SettingsActions.update({anticache: !anticache})}
+ onToggle={() => SettingsActions.update({anticache: !anticache})}
/>
- <ToggleButton name="anticomp"
+ <ToggleButton text="anticomp"
checked={anticomp}
- onToggleChanged={() => SettingsActions.update({anticomp: !anticomp})}
+ onToggle={() => SettingsActions.update({anticomp: !anticomp})}
/>
<ToggleInputButton name="stickyauth" placeholder="Sticky auth filter"
checked={Boolean(stickyauth)}
diff --git a/web/src/js/connection.js b/web/src/js/connection.js
index 6177938e..71d20f46 100644
--- a/web/src/js/connection.js
+++ b/web/src/js/connection.js
@@ -1,19 +1,22 @@
import {ConnectionActions, EventLogActions} from "./actions.js";
import {AppDispatcher} from "./dispatcher.js";
+import * as websocketActions from "./ducks/websocket"
-function Connection(url) {
+export default function Connection(url, dispatch) {
if (url[0] === "/") {
url = location.origin.replace("http", "ws") + url;
}
var ws = new WebSocket(url);
ws.onopen = function () {
+ dispatch(websocketActions.connected());
ConnectionActions.open();
};
- ws.onmessage = function (message) {
- var m = JSON.parse(message.data);
- AppDispatcher.dispatchServerAction(m);
+ ws.onmessage = function (m) {
+ var message = JSON.parse(m.data);
+ AppDispatcher.dispatchServerAction(message);
+ dispatch(websocketActions.receiveMessage(message));
};
ws.onerror = function () {
ConnectionActions.error();
@@ -22,8 +25,7 @@ function Connection(url) {
ws.onclose = function () {
ConnectionActions.close();
EventLogActions.add_event("WebSocket connection closed.");
+ dispatch(websocketActions.disconnected());
};
return ws;
-}
-
-export default Connection; \ No newline at end of file
+} \ No newline at end of file
diff --git a/web/src/js/ducks/flows.js b/web/src/js/ducks/flows.js
new file mode 100644
index 00000000..c4077f7a
--- /dev/null
+++ b/web/src/js/ducks/flows.js
@@ -0,0 +1,15 @@
+const defaultState = {
+ list: [],
+ isFetching: false,
+ updateBeforeFetch: [],
+ byId: {},
+ indexOf: {},
+ views: {}
+}
+
+export default function reducer(state = defaultState, action) {
+ switch (action.type) {
+ default:
+ return state
+ }
+}
diff --git a/web/src/js/ducks/index.js b/web/src/js/ducks/index.js
index c82d42bb..0074bda4 100644
--- a/web/src/js/ducks/index.js
+++ b/web/src/js/ducks/index.js
@@ -1,8 +1,12 @@
import {combineReducers} from 'redux'
-import eventLog from './eventLog.js';
+import eventLog from './eventLog.js'
+import websocket from './websocket.js'
+import flows from './flows.js'
const rootReducer = combineReducers({
- eventLog
+ eventLog,
+ flows,
+ websocket,
})
export default rootReducer \ No newline at end of file
diff --git a/web/src/js/ducks/websocket.js b/web/src/js/ducks/websocket.js
new file mode 100644
index 00000000..281d1f2c
--- /dev/null
+++ b/web/src/js/ducks/websocket.js
@@ -0,0 +1,34 @@
+const CONNECTED = 'WEBSOCKET_CONNECTED'
+const DISCONNECTED = 'WEBSOCKET_DISCONNECTED'
+const RECEIVE_MESSAGE = 'RECEIVE_WEBSOCKET_MESSAGE'
+
+
+const defaultState = {
+ connected: true,
+ /* we may want to have an error message attribute here at some point */
+}
+export default function reducer(state = defaultState, action) {
+ switch (action.type) {
+ case CONNECTED:
+ return {
+ connected: true
+ }
+ case DISCONNECTED:
+ return {
+ connected: false
+ }
+ default:
+ return state
+ }
+}
+
+
+export function connected() {
+ return {type: CONNECTED}
+}
+export function disconnected() {
+ return {type: DISCONNECTED}
+}
+export function receiveMessage(message) {
+ return {type: RECEIVE_MESSAGE, message}
+} \ No newline at end of file