aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/config.js4
-rw-r--r--src/environment.js8
-rwxr-xr-xsrc/features/settingsWS/actions.js10
-rwxr-xr-xsrc/features/settingsWS/index.js35
-rwxr-xr-xsrc/features/settingsWS/state.js13
-rwxr-xr-xsrc/features/settingsWS/store.js107
-rw-r--r--src/stores/FeaturesStore.js2
7 files changed, 179 insertions, 0 deletions
diff --git a/src/config.js b/src/config.js
index 4423e61e9..05ee07ee5 100644
--- a/src/config.js
+++ b/src/config.js
@@ -13,6 +13,10 @@ export const LOCAL_API = 'http://localhost:3000';
13export const DEV_API = 'https://dev.franzinfra.com'; 13export const DEV_API = 'https://dev.franzinfra.com';
14export const LIVE_API = 'https://api.franzinfra.com'; 14export const LIVE_API = 'https://api.franzinfra.com';
15 15
16export const LOCAL_WS_API = 'ws://localhost:3000';
17export const DEV_WS_API = 'wss://dev.franzinfra.com';
18export const LIVE_WS_API = 'wss://api.franzinfra.com';
19
16export const LOCAL_API_WEBSITE = 'http://localhost:3333'; 20export const LOCAL_API_WEBSITE = 'http://localhost:3333';
17export const DEV_API_WEBSITE = 'https://meetfranz.com'; 21export const DEV_API_WEBSITE = 'https://meetfranz.com';
18export const LIVE_API_WEBSITE = 'https://meetfranz.com'; 22export const LIVE_API_WEBSITE = 'https://meetfranz.com';
diff --git a/src/environment.js b/src/environment.js
index 68fa45173..e47b373b0 100644
--- a/src/environment.js
+++ b/src/environment.js
@@ -7,6 +7,9 @@ import {
7 LOCAL_API_WEBSITE, 7 LOCAL_API_WEBSITE,
8 DEV_API_WEBSITE, 8 DEV_API_WEBSITE,
9 LIVE_API_WEBSITE, 9 LIVE_API_WEBSITE,
10 LIVE_WS_API,
11 LOCAL_WS_API,
12 DEV_WS_API,
10} from './config'; 13} from './config';
11 14
12export const isDevMode = isDev; 15export const isDevMode = isDev;
@@ -26,17 +29,22 @@ export const ctrlKey = isMac ? '⌘' : 'Ctrl';
26export const cmdKey = isMac ? 'Cmd' : 'Ctrl'; 29export const cmdKey = isMac ? 'Cmd' : 'Ctrl';
27 30
28let api; 31let api;
32let wsApi;
29let web; 33let web;
30if (!isDevMode || (isDevMode && useLiveAPI)) { 34if (!isDevMode || (isDevMode && useLiveAPI)) {
31 api = LIVE_API; 35 api = LIVE_API;
36 wsApi = LIVE_WS_API;
32 web = LIVE_API_WEBSITE; 37 web = LIVE_API_WEBSITE;
33} else if (isDevMode && useLocalAPI) { 38} else if (isDevMode && useLocalAPI) {
34 api = LOCAL_API; 39 api = LOCAL_API;
40 wsApi = LOCAL_WS_API;
35 web = LOCAL_API_WEBSITE; 41 web = LOCAL_API_WEBSITE;
36} else { 42} else {
37 api = DEV_API; 43 api = DEV_API;
44 wsApi = DEV_WS_API;
38 web = DEV_API_WEBSITE; 45 web = DEV_API_WEBSITE;
39} 46}
40 47
41export const API = api; 48export const API = api;
49export const WS_API = wsApi;
42export const WEBSITE = web; 50export const WEBSITE = web;
diff --git a/src/features/settingsWS/actions.js b/src/features/settingsWS/actions.js
new file mode 100755
index 000000000..631670c8a
--- /dev/null
+++ b/src/features/settingsWS/actions.js
@@ -0,0 +1,10 @@
1import PropTypes from 'prop-types';
2import { createActionsFromDefinitions } from '../../actions/lib/actions';
3
4export const settingsWSActions = createActionsFromDefinitions({
5 greet: {
6 name: PropTypes.string.isRequired,
7 },
8}, PropTypes.checkPropTypes);
9
10export default settingsWSActions;
diff --git a/src/features/settingsWS/index.js b/src/features/settingsWS/index.js
new file mode 100755
index 000000000..1e268f184
--- /dev/null
+++ b/src/features/settingsWS/index.js
@@ -0,0 +1,35 @@
1import { reaction, runInAction } from 'mobx';
2import { SettingsWSStore } from './store';
3import state, { resetState } from './state';
4
5const debug = require('debug')('Franz:feature:settingsWS');
6
7let store = null;
8
9export default function initAnnouncements(stores, actions) {
10 const { features } = stores;
11
12 // Toggle workspace feature
13 reaction(
14 () => (
15 features.features.isSettingsWSEnabled
16 ),
17 (isEnabled) => {
18 if (isEnabled) {
19 debug('Initializing `settingsWS` feature');
20 store = new SettingsWSStore(stores, null, actions, state);
21 store.initialize();
22 runInAction(() => { state.isFeatureActive = true; });
23 } else if (store) {
24 debug('Disabling `settingsWS` feature');
25 runInAction(() => { state.isFeatureActive = false; });
26 store.teardown();
27 store = null;
28 resetState(); // Reset state to default
29 }
30 },
31 {
32 fireImmediately: true,
33 },
34 );
35}
diff --git a/src/features/settingsWS/state.js b/src/features/settingsWS/state.js
new file mode 100755
index 000000000..7b16b2b6e
--- /dev/null
+++ b/src/features/settingsWS/state.js
@@ -0,0 +1,13 @@
1import { observable } from 'mobx';
2
3const defaultState = {
4 isFeatureActive: false,
5};
6
7export const settingsWSState = observable(defaultState);
8
9export function resetState() {
10 Object.assign(settingsWSState, defaultState);
11}
12
13export default settingsWSState;
diff --git a/src/features/settingsWS/store.js b/src/features/settingsWS/store.js
new file mode 100755
index 000000000..21fd3440e
--- /dev/null
+++ b/src/features/settingsWS/store.js
@@ -0,0 +1,107 @@
1import { observable, reaction } from 'mobx';
2import WebSocket from 'ws';
3import ms from 'ms';
4
5import Store from '../../stores/lib/Store';
6import { WS_API } from '../../environment';
7
8const debug = require('debug')('Franz:feature:settingsWS:store');
9
10export class SettingsWSStore extends Store {
11 ws = null;
12
13 @observable connected = false;
14
15 pingTimeout = null;
16
17 reconnectTimeout = null;
18
19 constructor(stores, api, actions, state) {
20 super(stores, api, actions);
21 this.state = state;
22 }
23
24 setup() {
25 this.connect();
26
27 reaction(() => !this.connected, this.reconnect.bind(this));
28 }
29
30 connect() {
31 try {
32 const wsURL = `${WS_API}/ws/${this.stores.user.data.id}`;
33 debug('Setting up WebSocket to', wsURL);
34
35 this.ws = new WebSocket(wsURL);
36
37 this.ws.on('open', () => {
38 debug('Opened WebSocket');
39 this.send({
40 action: 'authorize',
41 token: this.stores.user.authToken,
42 });
43
44 this.connected = true;
45
46 this.heartbeat();
47 });
48
49 this.ws.on('message', (data) => {
50 const resp = JSON.parse(data);
51 debug('Received message', resp);
52
53 if (resp.id) {
54 this.stores.user.getUserInfoRequest.patch((result) => {
55 if (!result) return;
56
57 debug('Patching user object with new values');
58 Object.assign(result, resp);
59 });
60 }
61 });
62
63 this.ws.on('ping', this.heartbeat.bind(this));
64 } catch (err) {
65 console.err(err);
66 }
67 }
68
69 heartbeat() {
70 debug('Heartbeat');
71 clearTimeout(this.pingTimeout);
72
73 // Use `WebSocket#terminate()` and not `WebSocket#close()`. Delay should be
74 // equal to the interval at which your server sends out pings plus a
75 // conservative assumption of the latency.
76 this.pingTimeout = setTimeout(() => {
77 debug('Terminating connection reconnecting in 35');
78 this.ws.terminate();
79
80 this.connected = false;
81 }, ms('35s'));
82 }
83
84 send(data) {
85 if (this.ws) {
86 this.ws.send(JSON.stringify(data));
87 debug('Sending data', data);
88 } else {
89 debug('WebSocket is not initialized');
90 }
91 }
92
93 reconnect() {
94 if (!this.connected) {
95 debug('Trying to reconnect in 30s');
96 this.reconnectTimeout = setInterval(() => {
97 debug('Trying to reconnect');
98 this.connect();
99 }, ms('30s'));
100 } else {
101 debug('Clearing reconnect interval');
102 clearInterval(this.reconnectTimeout);
103 }
104 }
105}
106
107export default SettingsWSStore;
diff --git a/src/stores/FeaturesStore.js b/src/stores/FeaturesStore.js
index d2842083c..e60d79075 100644
--- a/src/stores/FeaturesStore.js
+++ b/src/stores/FeaturesStore.js
@@ -8,6 +8,7 @@ import spellchecker from '../features/spellchecker';
8import serviceProxy from '../features/serviceProxy'; 8import serviceProxy from '../features/serviceProxy';
9import basicAuth from '../features/basicAuth'; 9import basicAuth from '../features/basicAuth';
10import shareFranz from '../features/shareFranz'; 10import shareFranz from '../features/shareFranz';
11import settingsWS from '../features/settingsWS';
11 12
12import { DEFAULT_FEATURES_CONFIG } from '../config'; 13import { DEFAULT_FEATURES_CONFIG } from '../config';
13 14
@@ -58,5 +59,6 @@ export default class FeaturesStore extends Store {
58 serviceProxy(this.stores, this.actions); 59 serviceProxy(this.stores, this.actions);
59 basicAuth(this.stores, this.actions); 60 basicAuth(this.stores, this.actions);
60 shareFranz(this.stores, this.actions); 61 shareFranz(this.stores, this.actions);
62 settingsWS(this.stores, this.actions);
61 } 63 }
62} 64}