diff options
Diffstat (limited to 'src/stores/AppStore.js')
-rw-r--r-- | src/stores/AppStore.js | 84 |
1 files changed, 47 insertions, 37 deletions
diff --git a/src/stores/AppStore.js b/src/stores/AppStore.js index dd4642d70..72c4b4d0b 100644 --- a/src/stores/AppStore.js +++ b/src/stores/AppStore.js | |||
@@ -1,21 +1,25 @@ | |||
1 | import { remote, ipcRenderer, shell } from 'electron'; | 1 | import { remote, ipcRenderer, shell } from 'electron'; |
2 | import { action, computed, observable } from 'mobx'; | 2 | import { |
3 | action, computed, observable, reaction, | ||
4 | } from 'mobx'; | ||
3 | import moment from 'moment'; | 5 | import moment from 'moment'; |
4 | import key from 'keymaster'; | ||
5 | import { getDoNotDisturb } from '@meetfranz/electron-notification-state'; | 6 | import { getDoNotDisturb } from '@meetfranz/electron-notification-state'; |
6 | import AutoLaunch from 'auto-launch'; | 7 | import AutoLaunch from 'auto-launch'; |
7 | import prettyBytes from 'pretty-bytes'; | 8 | import prettyBytes from 'pretty-bytes'; |
9 | import ms from 'ms'; | ||
10 | import { URL } from 'url'; | ||
8 | 11 | ||
9 | import Store from './lib/Store'; | 12 | import Store from './lib/Store'; |
10 | import Request from './lib/Request'; | 13 | import Request from './lib/Request'; |
11 | import { CHECK_INTERVAL, DEFAULT_APP_SETTINGS } from '../config'; | 14 | import { CHECK_INTERVAL, DEFAULT_APP_SETTINGS } from '../config'; |
12 | import { isMac, isLinux, isWindows } from '../environment'; | 15 | import { isMac } from '../environment'; |
13 | import locales from '../i18n/translations'; | 16 | import locales from '../i18n/translations'; |
14 | import { gaEvent } from '../lib/analytics'; | 17 | import { gaEvent, gaPage, statsEvent } from '../lib/analytics'; |
15 | import { onVisibilityChange } from '../helpers/visibility-helper'; | 18 | import { onVisibilityChange } from '../helpers/visibility-helper'; |
16 | import { getLocale } from '../helpers/i18n-helpers'; | 19 | import { getLocale } from '../helpers/i18n-helpers'; |
17 | 20 | ||
18 | import { getServiceIdsFromPartitions, removeServicePartitionDirectory } from '../helpers/service-helpers.js'; | 21 | import { getServiceIdsFromPartitions, removeServicePartitionDirectory } from '../helpers/service-helpers.js'; |
22 | import { isValidExternalURL } from '../helpers/url-helpers'; | ||
19 | 23 | ||
20 | const debug = require('debug')('Franz:AppStore'); | 24 | const debug = require('debug')('Franz:AppStore'); |
21 | 25 | ||
@@ -63,6 +67,8 @@ export default class AppStore extends Store { | |||
63 | 67 | ||
64 | @observable isFocused = true; | 68 | @observable isFocused = true; |
65 | 69 | ||
70 | @observable nextAppReleaseVersion = null; | ||
71 | |||
66 | dictionaries = []; | 72 | dictionaries = []; |
67 | 73 | ||
68 | constructor(...args) { | 74 | constructor(...args) { |
@@ -110,16 +116,16 @@ export default class AppStore extends Store { | |||
110 | // Check if system is muted | 116 | // Check if system is muted |
111 | // There are no events to subscribe so we need to poll everey 5s | 117 | // There are no events to subscribe so we need to poll everey 5s |
112 | this._systemDND(); | 118 | this._systemDND(); |
113 | setInterval(() => this._systemDND(), 5000); | 119 | setInterval(() => this._systemDND(), ms('5s')); |
114 | 120 | ||
115 | // Check for updates once every 4 hours | 121 | // Check for updates once every 4 hours |
116 | setInterval(() => this._checkForUpdates(), CHECK_INTERVAL); | 122 | setInterval(() => this._checkForUpdates(), CHECK_INTERVAL); |
117 | // Check for an update in 30s (need a delay to prevent Squirrel Installer lock file issues) | 123 | // Check for an update in 30s (need a delay to prevent Squirrel Installer lock file issues) |
118 | setTimeout(() => this._checkForUpdates(), 30000); | 124 | setTimeout(() => this._checkForUpdates(), ms('30s')); |
119 | ipcRenderer.on('autoUpdate', (event, data) => { | 125 | ipcRenderer.on('autoUpdate', (event, data) => { |
120 | if (data.available) { | 126 | if (data.available) { |
121 | this.updateStatus = this.updateStatusTypes.AVAILABLE; | 127 | this.updateStatus = this.updateStatusTypes.AVAILABLE; |
122 | 128 | this.nextAppReleaseVersion = data.version; | |
123 | if (isMac) { | 129 | if (isMac) { |
124 | app.dock.bounce(); | 130 | app.dock.bounce(); |
125 | } | 131 | } |
@@ -143,32 +149,14 @@ export default class AppStore extends Store { | |||
143 | 149 | ||
144 | // Handle deep linking (franz://) | 150 | // Handle deep linking (franz://) |
145 | ipcRenderer.on('navigateFromDeepLink', (event, data) => { | 151 | ipcRenderer.on('navigateFromDeepLink', (event, data) => { |
146 | const { url } = data; | 152 | debug('Navigate from deep link', data); |
153 | let { url } = data; | ||
147 | if (!url) return; | 154 | if (!url) return; |
148 | 155 | ||
149 | this.stores.router.push(data.url); | 156 | url = url.replace(/\/$/, ''); |
150 | }); | ||
151 | |||
152 | // Set active the next service | ||
153 | key( | ||
154 | '⌘+pagedown, ctrl+pagedown, ⌘+alt+right, ctrl+tab', () => { | ||
155 | this.actions.service.setActiveNext(); | ||
156 | }, | ||
157 | ); | ||
158 | 157 | ||
159 | // Set active the prev service | 158 | this.stores.router.push(url); |
160 | key( | 159 | }); |
161 | '⌘+pageup, ctrl+pageup, ⌘+alt+left, ctrl+shift+tab', () => { | ||
162 | this.actions.service.setActivePrev(); | ||
163 | }, | ||
164 | ); | ||
165 | |||
166 | // Global Mute | ||
167 | key( | ||
168 | '⌘+shift+m ctrl+shift+m', () => { | ||
169 | this.actions.app.toggleMuteApp(); | ||
170 | }, | ||
171 | ); | ||
172 | 160 | ||
173 | this.locale = this._getDefaultLocale(); | 161 | this.locale = this._getDefaultLocale(); |
174 | 162 | ||
@@ -181,6 +169,13 @@ export default class AppStore extends Store { | |||
181 | 169 | ||
182 | debug('Window is visible/focused', isVisible); | 170 | debug('Window is visible/focused', isVisible); |
183 | }); | 171 | }); |
172 | |||
173 | // analytics autorun | ||
174 | reaction(() => this.stores.router.location.pathname, (pathname) => { | ||
175 | gaPage(pathname); | ||
176 | }); | ||
177 | |||
178 | statsEvent('app-start'); | ||
184 | } | 179 | } |
185 | 180 | ||
186 | @computed get cacheSize() { | 181 | @computed get cacheSize() { |
@@ -193,7 +188,15 @@ export default class AppStore extends Store { | |||
193 | }) { | 188 | }) { |
194 | if (this.stores.settings.all.app.isAppMuted) return; | 189 | if (this.stores.settings.all.app.isAppMuted) return; |
195 | 190 | ||
191 | // TODO: is there a simple way to use blobs for notifications without storing them on disk? | ||
192 | if (options.icon.startsWith('blob:')) { | ||
193 | delete options.icon; | ||
194 | } | ||
195 | |||
196 | const notification = new window.Notification(title, options); | 196 | const notification = new window.Notification(title, options); |
197 | |||
198 | debug('New notification', title, options); | ||
199 | |||
197 | notification.onclick = (e) => { | 200 | notification.onclick = (e) => { |
198 | if (serviceId) { | 201 | if (serviceId) { |
199 | this.actions.service.sendIPCMessage({ | 202 | this.actions.service.sendIPCMessage({ |
@@ -203,12 +206,13 @@ export default class AppStore extends Store { | |||
203 | }); | 206 | }); |
204 | 207 | ||
205 | this.actions.service.setActive({ serviceId }); | 208 | this.actions.service.setActive({ serviceId }); |
206 | 209 | mainWindow.show(); | |
207 | if (isWindows) { | 210 | if (app.mainWindow.isMinimized()) { |
208 | mainWindow.restore(); | 211 | mainWindow.restore(); |
209 | } else if (isLinux) { | ||
210 | mainWindow.show(); | ||
211 | } | 212 | } |
213 | mainWindow.focus(); | ||
214 | |||
215 | debug('Notification click handler'); | ||
212 | } | 216 | } |
213 | }; | 217 | }; |
214 | } | 218 | } |
@@ -244,7 +248,14 @@ export default class AppStore extends Store { | |||
244 | } | 248 | } |
245 | 249 | ||
246 | @action _openExternalUrl({ url }) { | 250 | @action _openExternalUrl({ url }) { |
247 | shell.openExternal(url); | 251 | const parsedUrl = new URL(url); |
252 | debug('open external url', parsedUrl); | ||
253 | |||
254 | if (isValidExternalURL(url)) { | ||
255 | shell.openExternal(url); | ||
256 | } | ||
257 | |||
258 | gaEvent('External URL', 'open', parsedUrl.host); | ||
248 | } | 259 | } |
249 | 260 | ||
250 | @action _checkForUpdates() { | 261 | @action _checkForUpdates() { |
@@ -268,7 +279,6 @@ export default class AppStore extends Store { | |||
268 | 279 | ||
269 | @action _muteApp({ isMuted, overrideSystemMute = true }) { | 280 | @action _muteApp({ isMuted, overrideSystemMute = true }) { |
270 | this.isSystemMuteOverridden = overrideSystemMute; | 281 | this.isSystemMuteOverridden = overrideSystemMute; |
271 | |||
272 | this.actions.settings.update({ | 282 | this.actions.settings.update({ |
273 | type: 'app', | 283 | type: 'app', |
274 | data: { | 284 | data: { |
@@ -305,7 +315,7 @@ export default class AppStore extends Store { | |||
305 | } else { | 315 | } else { |
306 | const deltaTime = moment().diff(this.timeOfflineStart); | 316 | const deltaTime = moment().diff(this.timeOfflineStart); |
307 | 317 | ||
308 | if (deltaTime > 30 * 60 * 1000) { | 318 | if (deltaTime > ms('30m')) { |
309 | this.actions.service.reloadAll(); | 319 | this.actions.service.reloadAll(); |
310 | } | 320 | } |
311 | } | 321 | } |