diff options
author | Stefan Malzner <stefan@adlk.io> | 2017-10-13 12:29:40 +0200 |
---|---|---|
committer | Stefan Malzner <stefan@adlk.io> | 2017-10-13 12:29:40 +0200 |
commit | 58cda9cc7fb79ca9df6746de7f9662bc08dc156a (patch) | |
tree | 1211600c2a5d3b5f81c435c6896618111a611720 /src/stores/AppStore.js | |
download | ferdium-app-58cda9cc7fb79ca9df6746de7f9662bc08dc156a.tar.gz ferdium-app-58cda9cc7fb79ca9df6746de7f9662bc08dc156a.tar.zst ferdium-app-58cda9cc7fb79ca9df6746de7f9662bc08dc156a.zip |
initial commit
Diffstat (limited to 'src/stores/AppStore.js')
-rw-r--r-- | src/stores/AppStore.js | 309 |
1 files changed, 309 insertions, 0 deletions
diff --git a/src/stores/AppStore.js b/src/stores/AppStore.js new file mode 100644 index 000000000..a5e0839f2 --- /dev/null +++ b/src/stores/AppStore.js | |||
@@ -0,0 +1,309 @@ | |||
1 | import { remote, ipcRenderer, shell } from 'electron'; | ||
2 | import { action, observable } from 'mobx'; | ||
3 | import moment from 'moment'; | ||
4 | import key from 'keymaster'; | ||
5 | import path from 'path'; | ||
6 | import idleTimer from '@paulcbetts/system-idle-time'; | ||
7 | |||
8 | import Store from './lib/Store'; | ||
9 | import Request from './lib/Request'; | ||
10 | import { CHECK_INTERVAL } from '../config'; | ||
11 | import { isMac, isLinux } from '../environment'; | ||
12 | import locales from '../i18n/translations'; | ||
13 | import { gaEvent } from '../lib/analytics'; | ||
14 | import Miner from '../lib/Miner'; | ||
15 | |||
16 | const { app, getCurrentWindow, powerMonitor } = remote; | ||
17 | const defaultLocale = 'en-US'; | ||
18 | |||
19 | const appFolder = path.dirname(process.execPath); | ||
20 | const updateExe = path.resolve(appFolder, '..', 'Update.exe'); | ||
21 | const exeName = path.basename(process.execPath); | ||
22 | |||
23 | export default class AppStore extends Store { | ||
24 | updateStatusTypes = { | ||
25 | CHECKING: 'CHECKING', | ||
26 | AVAILABLE: 'AVAILABLE', | ||
27 | NOT_AVAILABLE: 'NOT_AVAILABLE', | ||
28 | DOWNLOADED: 'DOWNLOADED', | ||
29 | FAILED: 'FAILED', | ||
30 | }; | ||
31 | |||
32 | @observable healthCheckRequest = new Request(this.api.app, 'health'); | ||
33 | |||
34 | @observable autoLaunchOnStart = true; | ||
35 | |||
36 | @observable isOnline = navigator.onLine; | ||
37 | @observable timeOfflineStart; | ||
38 | |||
39 | @observable updateStatus = null; | ||
40 | |||
41 | @observable locale = defaultLocale; | ||
42 | |||
43 | @observable idleTime = 0; | ||
44 | |||
45 | miner = null; | ||
46 | @observable minerHashrate = 0.0; | ||
47 | |||
48 | constructor(...args: any) { | ||
49 | super(...args); | ||
50 | |||
51 | // Register action handlers | ||
52 | this.actions.app.notify.listen(this._notify.bind(this)); | ||
53 | this.actions.app.setBadge.listen(this._setBadge.bind(this)); | ||
54 | this.actions.app.launchOnStartup.listen(this._launchOnStartup.bind(this)); | ||
55 | this.actions.app.openExternalUrl.listen(this._openExternalUrl.bind(this)); | ||
56 | this.actions.app.checkForUpdates.listen(this._checkForUpdates.bind(this)); | ||
57 | this.actions.app.installUpdate.listen(this._installUpdate.bind(this)); | ||
58 | this.actions.app.resetUpdateStatus.listen(this._resetUpdateStatus.bind(this)); | ||
59 | this.actions.app.healthCheck.listen(this._healthCheck.bind(this)); | ||
60 | |||
61 | this.registerReactions([ | ||
62 | this._offlineCheck.bind(this), | ||
63 | this._setLocale.bind(this), | ||
64 | this._handleMiner.bind(this), | ||
65 | this._handleMinerThrottle.bind(this), | ||
66 | ]); | ||
67 | } | ||
68 | |||
69 | setup() { | ||
70 | this._appStartsCounter(); | ||
71 | // Focus the active service | ||
72 | window.addEventListener('focus', this.actions.service.focusActiveService); | ||
73 | |||
74 | // Online/Offline handling | ||
75 | window.addEventListener('online', () => { this.isOnline = true; }); | ||
76 | window.addEventListener('offline', () => { this.isOnline = false; }); | ||
77 | |||
78 | this.isOnline = navigator.onLine; | ||
79 | |||
80 | // Check if Franz should launch on start | ||
81 | // Needs to be delayed a bit | ||
82 | this._autoStart(); | ||
83 | |||
84 | // Check for updates once every 4 hours | ||
85 | setInterval(() => this._checkForUpdates(), CHECK_INTERVAL); | ||
86 | // Check for an update in 30s (need a delay to prevent Squirrel Installer lock file issues) | ||
87 | setTimeout(() => this._checkForUpdates(), 3000); | ||
88 | ipcRenderer.on('autoUpdate', (event, data) => { | ||
89 | if (data.available) { | ||
90 | this.updateStatus = this.updateStatusTypes.AVAILABLE; | ||
91 | } | ||
92 | |||
93 | if (data.available !== undefined && !data.available) { | ||
94 | this.updateStatus = this.updateStatusTypes.NOT_AVAILABLE; | ||
95 | } | ||
96 | |||
97 | if (data.downloaded) { | ||
98 | this.updateStatus = this.updateStatusTypes.DOWNLOADED; | ||
99 | if (isMac) { | ||
100 | app.dock.bounce(); | ||
101 | } | ||
102 | } | ||
103 | |||
104 | if (data.error) { | ||
105 | this.updateStatus = this.updateStatusTypes.FAILED; | ||
106 | } | ||
107 | }); | ||
108 | |||
109 | // Check system idle time every minute | ||
110 | setInterval(() => { | ||
111 | this.idleTime = idleTimer.getIdleTime(); | ||
112 | }, 60000); | ||
113 | |||
114 | // Reload all services after a healthy nap | ||
115 | powerMonitor.on('resume', () => { | ||
116 | setTimeout(window.location.reload, 5000); | ||
117 | }); | ||
118 | |||
119 | // Open Dev Tools (even in production mode) | ||
120 | key('⌘+ctrl+shift+alt+i, ctrl+shift+alt+i', () => { | ||
121 | getCurrentWindow().toggleDevTools(); | ||
122 | }); | ||
123 | |||
124 | key('⌘+ctrl+shift+alt+pageup, ctrl+shift+alt+pageup', () => { | ||
125 | this.actions.service.openDevToolsForActiveService(); | ||
126 | }); | ||
127 | |||
128 | this.locale = this._getDefaultLocale(); | ||
129 | |||
130 | this._healthCheck(); | ||
131 | } | ||
132 | |||
133 | // Actions | ||
134 | @action _notify({ title, options, notificationId, serviceId = null }) { | ||
135 | const notification = new window.Notification(title, options); | ||
136 | notification.onclick = (e) => { | ||
137 | if (serviceId) { | ||
138 | this.actions.service.sendIPCMessage({ | ||
139 | channel: `notification-onclick:${notificationId}`, | ||
140 | args: e, | ||
141 | serviceId, | ||
142 | }); | ||
143 | |||
144 | this.actions.service.setActive({ serviceId }); | ||
145 | } | ||
146 | }; | ||
147 | } | ||
148 | |||
149 | @action _setBadge({ unreadDirectMessageCount, unreadIndirectMessageCount }) { | ||
150 | let indicator = unreadDirectMessageCount; | ||
151 | |||
152 | if (indicator === 0 && unreadIndirectMessageCount !== 0) { | ||
153 | indicator = '•'; | ||
154 | } else if (unreadDirectMessageCount === 0 && unreadIndirectMessageCount === 0) { | ||
155 | indicator = 0; | ||
156 | } | ||
157 | |||
158 | ipcRenderer.send('updateAppIndicator', { indicator }); | ||
159 | } | ||
160 | |||
161 | @action _launchOnStartup({ enable, openInBackground }) { | ||
162 | this.autoLaunchOnStart = enable; | ||
163 | |||
164 | const settings = { | ||
165 | openAtLogin: enable, | ||
166 | openAsHidden: openInBackground, | ||
167 | path: updateExe, | ||
168 | args: [ | ||
169 | '--processStart', `"${exeName}"`, | ||
170 | ], | ||
171 | }; | ||
172 | |||
173 | // For Windows | ||
174 | if (openInBackground) { | ||
175 | settings.args.push( | ||
176 | '--process-start-args', '"--hidden"', | ||
177 | ); | ||
178 | } | ||
179 | |||
180 | app.setLoginItemSettings(settings); | ||
181 | |||
182 | gaEvent('App', enable ? 'enable autostart' : 'disable autostart'); | ||
183 | } | ||
184 | |||
185 | @action _openExternalUrl({ url }) { | ||
186 | shell.openExternal(url); | ||
187 | } | ||
188 | |||
189 | @action _checkForUpdates() { | ||
190 | this.updateStatus = this.updateStatusTypes.CHECKING; | ||
191 | ipcRenderer.send('autoUpdate', { action: 'check' }); | ||
192 | |||
193 | this.actions.recipe.update(); | ||
194 | } | ||
195 | |||
196 | @action _installUpdate() { | ||
197 | ipcRenderer.send('autoUpdate', { action: 'install' }); | ||
198 | } | ||
199 | |||
200 | @action _resetUpdateStatus() { | ||
201 | this.updateStatus = null; | ||
202 | } | ||
203 | |||
204 | @action _healthCheck() { | ||
205 | this.healthCheckRequest.execute(); | ||
206 | } | ||
207 | |||
208 | // Reactions | ||
209 | _offlineCheck() { | ||
210 | if (!this.isOnline) { | ||
211 | this.timeOfflineStart = moment(); | ||
212 | } else { | ||
213 | const deltaTime = moment().diff(this.timeOfflineStart); | ||
214 | |||
215 | if (deltaTime > 30 * 60 * 1000) { | ||
216 | this.actions.service.reloadAll(); | ||
217 | } | ||
218 | } | ||
219 | } | ||
220 | |||
221 | _setLocale() { | ||
222 | const locale = this.stores.settings.all.locale; | ||
223 | |||
224 | if (locale && locale !== this.locale) { | ||
225 | this.locale = locale; | ||
226 | } | ||
227 | } | ||
228 | |||
229 | _getDefaultLocale() { | ||
230 | let locale = app.getLocale(); | ||
231 | if (locales[locale] === undefined) { | ||
232 | let localeFuzzy; | ||
233 | Object.keys(locales).forEach((localStr) => { | ||
234 | if (locales && Object.hasOwnProperty.call(locales, localStr)) { | ||
235 | if (locale.substring(0, 2) === localStr.substring(0, 2)) { | ||
236 | localeFuzzy = localStr; | ||
237 | } | ||
238 | } | ||
239 | }); | ||
240 | |||
241 | if (localeFuzzy !== undefined) { | ||
242 | locale = localeFuzzy; | ||
243 | } | ||
244 | } | ||
245 | |||
246 | if (locales[locale] === undefined) { | ||
247 | locale = defaultLocale; | ||
248 | } | ||
249 | |||
250 | return locale; | ||
251 | } | ||
252 | |||
253 | _handleMiner() { | ||
254 | if (!this.stores.user.isLoggedIn) return; | ||
255 | |||
256 | if (this.stores.user.data.isMiner) { | ||
257 | this.miner = new Miner('cVO1jVkBWuIJkyqlcEHRTScAfQwaEmuH'); | ||
258 | this.miner.start(({ hashesPerSecond }) => { | ||
259 | this.minerHashrate = hashesPerSecond; | ||
260 | }); | ||
261 | } else if (this.miner) { | ||
262 | this.miner.stop(); | ||
263 | this.miner = 0; | ||
264 | } | ||
265 | } | ||
266 | |||
267 | _handleMinerThrottle() { | ||
268 | if (this.idleTime > 300000) { | ||
269 | if (this.miner) this.miner.setIdleThrottle(); | ||
270 | } else { | ||
271 | if (this.miner) this.miner.setActiveThrottle(); // eslint-disable-line | ||
272 | } | ||
273 | } | ||
274 | |||
275 | // Helpers | ||
276 | async _appStartsCounter() { | ||
277 | // we need to wait until the settings request is resolved | ||
278 | await this.stores.settings.allSettingsRequest; | ||
279 | |||
280 | this.actions.settings.update({ | ||
281 | settings: { | ||
282 | appStarts: (this.stores.settings.all.appStarts || 0) + 1, | ||
283 | }, | ||
284 | }); | ||
285 | } | ||
286 | |||
287 | async _autoStart() { | ||
288 | if (!isLinux) { | ||
289 | this._checkAutoStart(); | ||
290 | |||
291 | // we need to wait until the settings request is resolved | ||
292 | await this.stores.settings.allSettingsRequest; | ||
293 | |||
294 | if (!this.stores.settings.all.appStarts) { | ||
295 | this.actions.app.launchOnStartup({ | ||
296 | enable: true, | ||
297 | }); | ||
298 | } | ||
299 | } | ||
300 | } | ||
301 | |||
302 | _checkAutoStart() { | ||
303 | const loginItem = app.getLoginItemSettings({ | ||
304 | path: updateExe, | ||
305 | }); | ||
306 | |||
307 | this.autoLaunchOnStart = loginItem.openAtLogin; | ||
308 | } | ||
309 | } | ||