diff options
author | 2019-06-04 14:26:56 +0200 | |
---|---|---|
committer | 2019-06-04 14:26:56 +0200 | |
commit | fd6a1e9ba24c7947af08a30829c860cad4726f67 (patch) | |
tree | 0b6276700ced8d13eaf0a1e3b706a69a5a47b1da /src/features/settingsWS | |
parent | fix(Windows): Replace tray icon with high-res version (diff) | |
parent | Merge pull request #1443 from meetfranz/feature/multilingual-announcements (diff) | |
download | ferdium-app-fd6a1e9ba24c7947af08a30829c860cad4726f67.tar.gz ferdium-app-fd6a1e9ba24c7947af08a30829c860cad4726f67.tar.zst ferdium-app-fd6a1e9ba24c7947af08a30829c860cad4726f67.zip |
Merge branch 'develop' of https://github.com/meetfranz/franz into develop
Diffstat (limited to 'src/features/settingsWS')
-rwxr-xr-x | src/features/settingsWS/actions.js | 10 | ||||
-rwxr-xr-x | src/features/settingsWS/index.js | 29 | ||||
-rwxr-xr-x | src/features/settingsWS/state.js | 13 | ||||
-rwxr-xr-x | src/features/settingsWS/store.js | 130 |
4 files changed, 182 insertions, 0 deletions
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 @@ | |||
1 | import PropTypes from 'prop-types'; | ||
2 | import { createActionsFromDefinitions } from '../../actions/lib/actions'; | ||
3 | |||
4 | export const settingsWSActions = createActionsFromDefinitions({ | ||
5 | greet: { | ||
6 | name: PropTypes.string.isRequired, | ||
7 | }, | ||
8 | }, PropTypes.checkPropTypes); | ||
9 | |||
10 | export default settingsWSActions; | ||
diff --git a/src/features/settingsWS/index.js b/src/features/settingsWS/index.js new file mode 100755 index 000000000..2064d2973 --- /dev/null +++ b/src/features/settingsWS/index.js | |||
@@ -0,0 +1,29 @@ | |||
1 | import { reaction } from 'mobx'; | ||
2 | import { SettingsWSStore } from './store'; | ||
3 | |||
4 | const debug = require('debug')('Franz:feature:settingsWS'); | ||
5 | |||
6 | export const settingsStore = new SettingsWSStore(); | ||
7 | |||
8 | export default function initSettingsWebSocket(stores, actions) { | ||
9 | const { features } = stores; | ||
10 | |||
11 | // Toggle SettingsWebSocket feature | ||
12 | reaction( | ||
13 | () => ( | ||
14 | features.features.isSettingsWSEnabled | ||
15 | ), | ||
16 | (isEnabled) => { | ||
17 | if (isEnabled) { | ||
18 | debug('Initializing `settingsWS` feature'); | ||
19 | settingsStore.start(stores, actions); | ||
20 | } else if (settingsStore) { | ||
21 | debug('Disabling `settingsWS` feature'); | ||
22 | settingsStore.stop(); | ||
23 | } | ||
24 | }, | ||
25 | { | ||
26 | fireImmediately: true, | ||
27 | }, | ||
28 | ); | ||
29 | } | ||
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 @@ | |||
1 | import { observable } from 'mobx'; | ||
2 | |||
3 | const defaultState = { | ||
4 | isFeatureActive: false, | ||
5 | }; | ||
6 | |||
7 | export const settingsWSState = observable(defaultState); | ||
8 | |||
9 | export function resetState() { | ||
10 | Object.assign(settingsWSState, defaultState); | ||
11 | } | ||
12 | |||
13 | export default settingsWSState; | ||
diff --git a/src/features/settingsWS/store.js b/src/features/settingsWS/store.js new file mode 100755 index 000000000..167a70d10 --- /dev/null +++ b/src/features/settingsWS/store.js | |||
@@ -0,0 +1,130 @@ | |||
1 | import { observable } from 'mobx'; | ||
2 | import WebSocket from 'ws'; | ||
3 | import ms from 'ms'; | ||
4 | |||
5 | import { FeatureStore } from '../utils/FeatureStore'; | ||
6 | import { createReactions } from '../../stores/lib/Reaction'; | ||
7 | import { WS_API } from '../../environment'; | ||
8 | |||
9 | const debug = require('debug')('Franz:feature:settingsWS:store'); | ||
10 | |||
11 | export class SettingsWSStore extends FeatureStore { | ||
12 | stores = null; | ||
13 | |||
14 | actions = null; | ||
15 | |||
16 | ws = null; | ||
17 | |||
18 | pingTimeout = null; | ||
19 | |||
20 | reconnectTimeout = null; | ||
21 | |||
22 | @observable connected = false; | ||
23 | |||
24 | start(stores, actions) { | ||
25 | this.stores = stores; | ||
26 | this.actions = actions; | ||
27 | |||
28 | this._registerReactions(createReactions([ | ||
29 | this._initialize.bind(this), | ||
30 | this._reconnect.bind(this), | ||
31 | this._close.bind(this), | ||
32 | ])); | ||
33 | } | ||
34 | |||
35 | connect() { | ||
36 | try { | ||
37 | const wsURL = `${WS_API}/ws/${this.stores.user.data.id}`; | ||
38 | debug('Setting up WebSocket to', wsURL); | ||
39 | |||
40 | this.ws = new WebSocket(wsURL); | ||
41 | |||
42 | this.ws.on('open', () => { | ||
43 | debug('Opened WebSocket'); | ||
44 | this.send({ | ||
45 | action: 'authorize', | ||
46 | token: this.stores.user.authToken, | ||
47 | }); | ||
48 | |||
49 | this.connected = true; | ||
50 | |||
51 | this.heartbeat(); | ||
52 | }); | ||
53 | |||
54 | this.ws.on('message', (data) => { | ||
55 | const resp = JSON.parse(data); | ||
56 | debug('Received message', resp); | ||
57 | |||
58 | if (resp.id) { | ||
59 | this.stores.user.getUserInfoRequest.patch((result) => { | ||
60 | if (!result) return; | ||
61 | |||
62 | debug('Patching user object with new values'); | ||
63 | Object.assign(result, resp); | ||
64 | }); | ||
65 | } | ||
66 | }); | ||
67 | |||
68 | this.ws.on('ping', this.heartbeat.bind(this)); | ||
69 | } catch (err) { | ||
70 | console.err(err); | ||
71 | } | ||
72 | } | ||
73 | |||
74 | heartbeat() { | ||
75 | debug('Heartbeat'); | ||
76 | clearTimeout(this.pingTimeout); | ||
77 | |||
78 | this.pingTimeout = setTimeout(() => { | ||
79 | debug('Terminating connection, reconnecting in 35'); | ||
80 | this.ws.terminate(); | ||
81 | |||
82 | this.connected = false; | ||
83 | }, ms('35s')); | ||
84 | } | ||
85 | |||
86 | send(data) { | ||
87 | if (this.ws && this.ws.readyState === 1) { | ||
88 | this.ws.send(JSON.stringify(data)); | ||
89 | debug('Sending data', data); | ||
90 | } else { | ||
91 | debug('WebSocket is not initialized'); | ||
92 | } | ||
93 | } | ||
94 | |||
95 | // Reactions | ||
96 | |||
97 | _initialize() { | ||
98 | if (this.stores.user.data.id && !this.ws) { | ||
99 | this.connect(); | ||
100 | } | ||
101 | } | ||
102 | |||
103 | _reconnect() { | ||
104 | if (!this.connected) { | ||
105 | debug('Trying to reconnect in 30s'); | ||
106 | this.reconnectTimeout = setInterval(() => { | ||
107 | debug('Trying to reconnect'); | ||
108 | this.connect(); | ||
109 | }, ms('30s')); | ||
110 | } else { | ||
111 | debug('Clearing reconnect interval'); | ||
112 | clearInterval(this.reconnectTimeout); | ||
113 | } | ||
114 | } | ||
115 | |||
116 | _close() { | ||
117 | if (!this.stores.user.isLoggedIn) { | ||
118 | debug('Stopping reactions'); | ||
119 | this._stopReactions(); | ||
120 | |||
121 | if (this.ws) { | ||
122 | debug('Terminating connection'); | ||
123 | this.ws.terminate(); | ||
124 | this.ws = null; | ||
125 | } | ||
126 | } | ||
127 | } | ||
128 | } | ||
129 | |||
130 | export default SettingsWSStore; | ||