aboutsummaryrefslogtreecommitdiffstats
path: root/src/stores
diff options
context:
space:
mode:
Diffstat (limited to 'src/stores')
-rw-r--r--src/stores/AppStore.js60
-rw-r--r--src/stores/ServicesStore.js53
-rw-r--r--src/stores/SettingsStore.js18
-rw-r--r--src/stores/UIStore.js8
-rw-r--r--src/stores/UserStore.js6
5 files changed, 116 insertions, 29 deletions
diff --git a/src/stores/AppStore.js b/src/stores/AppStore.js
index 14bdab094..5a6c12ee1 100644
--- a/src/stores/AppStore.js
+++ b/src/stores/AppStore.js
@@ -45,7 +45,7 @@ export default class AppStore extends Store {
45 miner = null; 45 miner = null;
46 @observable minerHashrate = 0.0; 46 @observable minerHashrate = 0.0;
47 47
48 @observable isSystemMuted = false; 48 @observable isSystemMuteOverridden = false;
49 49
50 constructor(...args) { 50 constructor(...args) {
51 super(...args); 51 super(...args);
@@ -67,6 +67,7 @@ export default class AppStore extends Store {
67 this._setLocale.bind(this), 67 this._setLocale.bind(this),
68 this._handleMiner.bind(this), 68 this._handleMiner.bind(this),
69 this._handleMinerThrottle.bind(this), 69 this._handleMinerThrottle.bind(this),
70 this._muteAppHandler.bind(this),
70 ]); 71 ]);
71 } 72 }
72 73
@@ -115,6 +116,14 @@ export default class AppStore extends Store {
115 } 116 }
116 }); 117 });
117 118
119 // Handle deep linking (franz://)
120 ipcRenderer.on('navigateFromDeepLink', (event, data) => {
121 const { url } = data;
122 if (!url) return;
123
124 this.stores.router.push(data.url);
125 });
126
118 // Check system idle time every minute 127 // Check system idle time every minute
119 setInterval(() => { 128 setInterval(() => {
120 this.idleTime = idleTimer.getIdleTime(); 129 this.idleTime = idleTimer.getIdleTime();
@@ -127,16 +136,22 @@ export default class AppStore extends Store {
127 136
128 // Set active the next service 137 // Set active the next service
129 key( 138 key(
130 '⌘+pagedown, ctrl+pagedown, ⌘+tab, ctrl+tab', () => { 139 '⌘+pagedown, ctrl+pagedown, ⌘+alt+right, ctrl+tab', () => {
131 this.actions.service.setActiveNext(); 140 this.actions.service.setActiveNext();
132 }); 141 });
133 142
134 // Set active the prev service 143 // Set active the prev service
135 key( 144 key(
136 '⌘+pageup, ctrl+pageup, ⌘+shift+tab, ctrl+shift+tab', () => { 145 '⌘+pageup, ctrl+pageup, ⌘+alt+left, ctrl+shift+tab', () => {
137 this.actions.service.setActivePrev(); 146 this.actions.service.setActivePrev();
138 }); 147 });
139 148
149 // Global Mute
150 key(
151 '⌘+shift+m ctrl+shift+m', () => {
152 this.actions.app.toggleMuteApp();
153 });
154
140 this.locale = this._getDefaultLocale(); 155 this.locale = this._getDefaultLocale();
141 156
142 this._healthCheck(); 157 this._healthCheck();
@@ -144,6 +159,8 @@ export default class AppStore extends Store {
144 159
145 // Actions 160 // Actions
146 @action _notify({ title, options, notificationId, serviceId = null }) { 161 @action _notify({ title, options, notificationId, serviceId = null }) {
162 if (this.stores.settings.all.isAppMuted) return;
163
147 const notification = new window.Notification(title, options); 164 const notification = new window.Notification(title, options);
148 notification.onclick = (e) => { 165 notification.onclick = (e) => {
149 if (serviceId) { 166 if (serviceId) {
@@ -154,6 +171,11 @@ export default class AppStore extends Store {
154 }); 171 });
155 172
156 this.actions.service.setActive({ serviceId }); 173 this.actions.service.setActive({ serviceId });
174
175 if (!isMac) {
176 const mainWindow = remote.getCurrentWindow();
177 mainWindow.restore();
178 }
157 } 179 }
158 }; 180 };
159 } 181 }
@@ -211,16 +233,18 @@ export default class AppStore extends Store {
211 this.healthCheckRequest.execute(); 233 this.healthCheckRequest.execute();
212 } 234 }
213 235
214 @action _muteApp({ isMuted }) { 236 @action _muteApp({ isMuted, overrideSystemMute = true }) {
237 this.isSystemMuteOverriden = overrideSystemMute;
238
215 this.actions.settings.update({ 239 this.actions.settings.update({
216 settings: { 240 settings: {
217 isMuted, 241 isAppMuted: isMuted,
218 }, 242 },
219 }); 243 });
220 } 244 }
221 245
222 @action _toggleMuteApp() { 246 @action _toggleMuteApp() {
223 this._muteApp({ isMuted: !this.stores.settings.all.isMuted }); 247 this._muteApp({ isMuted: !this.stores.settings.all.isAppMuted });
224 } 248 }
225 249
226 // Reactions 250 // Reactions
@@ -239,8 +263,10 @@ export default class AppStore extends Store {
239 _setLocale() { 263 _setLocale() {
240 const locale = this.stores.settings.all.locale; 264 const locale = this.stores.settings.all.locale;
241 265
242 if (locale && locale !== this.locale) { 266 if (locale && Object.prototype.hasOwnProperty.call(locales, locale) && locale !== this.locale) {
243 this.locale = locale; 267 this.locale = locale;
268 } else if (!locale) {
269 this.locale = this._getDefaultLocale();
244 } 270 }
245 } 271 }
246 272
@@ -265,6 +291,10 @@ export default class AppStore extends Store {
265 locale = defaultLocale; 291 locale = defaultLocale;
266 } 292 }
267 293
294 if (!locale) {
295 locale = DEFAULT_APP_SETTINGS.fallbackLocale;
296 }
297
268 return locale; 298 return locale;
269 } 299 }
270 300
@@ -290,6 +320,14 @@ export default class AppStore extends Store {
290 } 320 }
291 } 321 }
292 322
323 _muteAppHandler() {
324 const showMessageBadgesEvenWhenMuted = this.stores.ui.showMessageBadgesEvenWhenMuted;
325
326 if (!showMessageBadgesEvenWhenMuted) {
327 this.actions.app.setBadge({ unreadDirectMessageCount: 0, unreadIndirectMessageCount: 0 });
328 }
329 }
330
293 // Helpers 331 // Helpers
294 async _appStartsCounter() { 332 async _appStartsCounter() {
295 // we need to wait until the settings request is resolved 333 // we need to wait until the settings request is resolved
@@ -320,6 +358,12 @@ export default class AppStore extends Store {
320 } 358 }
321 359
322 _systemDND() { 360 _systemDND() {
323 this.isSystemMuted = getDoNotDisturb(); 361 const dnd = getDoNotDisturb();
362 if (dnd === this.stores.settings.all.isAppMuted || !this.isSystemMuteOverriden) {
363 this.actions.app.muteApp({
364 isMuted: dnd,
365 overrideSystemMute: false,
366 });
367 }
324 } 368 }
325} 369}
diff --git a/src/stores/ServicesStore.js b/src/stores/ServicesStore.js
index 269cd1526..66f37af26 100644
--- a/src/stores/ServicesStore.js
+++ b/src/stores/ServicesStore.js
@@ -37,6 +37,7 @@ export default class ServicesStore extends Store {
37 this.actions.service.toggleService.listen(this._toggleService.bind(this)); 37 this.actions.service.toggleService.listen(this._toggleService.bind(this));
38 this.actions.service.handleIPCMessage.listen(this._handleIPCMessage.bind(this)); 38 this.actions.service.handleIPCMessage.listen(this._handleIPCMessage.bind(this));
39 this.actions.service.sendIPCMessage.listen(this._sendIPCMessage.bind(this)); 39 this.actions.service.sendIPCMessage.listen(this._sendIPCMessage.bind(this));
40 this.actions.service.sendIPCMessageToAllServices.listen(this._sendIPCMessageToAllServices.bind(this));
40 this.actions.service.setUnreadMessageCount.listen(this._setUnreadMessageCount.bind(this)); 41 this.actions.service.setUnreadMessageCount.listen(this._setUnreadMessageCount.bind(this));
41 this.actions.service.openWindow.listen(this._openWindow.bind(this)); 42 this.actions.service.openWindow.listen(this._openWindow.bind(this));
42 this.actions.service.filter.listen(this._filter.bind(this)); 43 this.actions.service.filter.listen(this._filter.bind(this));
@@ -58,6 +59,7 @@ export default class ServicesStore extends Store {
58 this._mapActiveServiceToServiceModelReaction.bind(this), 59 this._mapActiveServiceToServiceModelReaction.bind(this),
59 this._saveActiveService.bind(this), 60 this._saveActiveService.bind(this),
60 this._logoutReaction.bind(this), 61 this._logoutReaction.bind(this),
62 this._shareSettingsWithServiceProcess.bind(this),
61 ]); 63 ]);
62 64
63 // Just bind this 65 // Just bind this
@@ -284,6 +286,7 @@ export default class ServicesStore extends Store {
284 if (channel === 'hello') { 286 if (channel === 'hello') {
285 this._initRecipePolling(service.id); 287 this._initRecipePolling(service.id);
286 this._initializeServiceRecipeInWebview(serviceId); 288 this._initializeServiceRecipeInWebview(serviceId);
289 this._shareSettingsWithServiceProcess();
287 } else if (channel === 'messages') { 290 } else if (channel === 'messages') {
288 this.actions.service.setUnreadMessageCount({ 291 this.actions.service.setUnreadMessageCount({
289 serviceId, 292 serviceId,
@@ -294,7 +297,7 @@ export default class ServicesStore extends Store {
294 }); 297 });
295 } else if (channel === 'notification') { 298 } else if (channel === 'notification') {
296 const options = args[0].options; 299 const options = args[0].options;
297 if (service.recipe.hasNotificationSound || service.isMuted) { 300 if (service.recipe.hasNotificationSound || service.isMuted || this.stores.settings.all.isAppMuted) {
298 Object.assign(options, { 301 Object.assign(options, {
299 silent: true, 302 silent: true,
300 }); 303 });
@@ -330,7 +333,17 @@ export default class ServicesStore extends Store {
330 @action _sendIPCMessage({ serviceId, channel, args }) { 333 @action _sendIPCMessage({ serviceId, channel, args }) {
331 const service = this.one(serviceId); 334 const service = this.one(serviceId);
332 335
333 service.webview.send(channel, args); 336 if (service.webview) {
337 service.webview.send(channel, args);
338 }
339 }
340
341 @action _sendIPCMessageToAllServices({ channel, args }) {
342 this.all.forEach(s => this.actions.service.sendIPCMessage({
343 serviceId: s.id,
344 channel,
345 args,
346 }));
334 } 347 }
335 348
336 @action _openWindow({ event }) { 349 @action _openWindow({ event }) {
@@ -457,8 +470,10 @@ export default class ServicesStore extends Store {
457 const service = this.active; 470 const service = this.active;
458 471
459 if (service) { 472 if (service) {
460 this.stores.settings.updateSettingsRequest.execute({ 473 this.actions.settings.update({
461 activeService: service.id, 474 settings: {
475 activeService: service.id,
476 },
462 }); 477 });
463 } 478 }
464 } 479 }
@@ -473,19 +488,26 @@ export default class ServicesStore extends Store {
473 } 488 }
474 489
475 _getUnreadMessageCountReaction() { 490 _getUnreadMessageCountReaction() {
476 const unreadDirectMessageCount = this.enabled 491 const showMessageBadgeWhenMuted = this.stores.settings.all.showMessageBadgeWhenMuted;
492 const showMessageBadgesEvenWhenMuted = this.stores.ui.showMessageBadgesEvenWhenMuted;
493
494 const unreadDirectMessageCount = this.allDisplayed
495 .filter(s => (showMessageBadgeWhenMuted || s.isNotificationEnabled) && showMessageBadgesEvenWhenMuted && s.isBadgeEnabled)
477 .map(s => s.unreadDirectMessageCount) 496 .map(s => s.unreadDirectMessageCount)
478 .reduce((a, b) => a + b, 0); 497 .reduce((a, b) => a + b, 0);
479 498
480 const unreadIndirectMessageCount = this.enabled 499 const unreadIndirectMessageCount = this.allDisplayed
481 .filter(s => s.isIndirectMessageBadgeEnabled) 500 .filter(s => (showMessageBadgeWhenMuted || s.isIndirectMessageBadgeEnabled) && showMessageBadgesEvenWhenMuted && s.isBadgeEnabled)
482 .map(s => s.unreadIndirectMessageCount) 501 .map(s => s.unreadIndirectMessageCount)
483 .reduce((a, b) => a + b, 0); 502 .reduce((a, b) => a + b, 0);
484 503
485 this.actions.app.setBadge({ 504 // We can't just block this earlier, otherwise the mobx reaction won't be aware of the vars to watch in some cases
486 unreadDirectMessageCount, 505 if (showMessageBadgesEvenWhenMuted) {
487 unreadIndirectMessageCount, 506 this.actions.app.setBadge({
488 }); 507 unreadDirectMessageCount,
508 unreadIndirectMessageCount,
509 });
510 }
489 } 511 }
490 512
491 _logoutReaction() { 513 _logoutReaction() {
@@ -495,6 +517,13 @@ export default class ServicesStore extends Store {
495 } 517 }
496 } 518 }
497 519
520 _shareSettingsWithServiceProcess() {
521 this.actions.service.sendIPCMessageToAllServices({
522 channel: 'settings-update',
523 args: this.stores.settings.all,
524 });
525 }
526
498 _cleanUpTeamIdAndCustomUrl(recipeId, data) { 527 _cleanUpTeamIdAndCustomUrl(recipeId, data) {
499 const serviceData = data; 528 const serviceData = data;
500 const recipe = this.stores.recipes.one(recipeId); 529 const recipe = this.stores.recipes.one(recipeId);
@@ -527,6 +556,8 @@ export default class ServicesStore extends Store {
527 556
528 if (service) { 557 if (service) {
529 const loop = () => { 558 const loop = () => {
559 if (!service.webview) return;
560
530 service.webview.send('poll'); 561 service.webview.send('poll');
531 562
532 setTimeout(loop, delay); 563 setTimeout(loop, delay);
diff --git a/src/stores/SettingsStore.js b/src/stores/SettingsStore.js
index 331df5c15..da99a720f 100644
--- a/src/stores/SettingsStore.js
+++ b/src/stores/SettingsStore.js
@@ -1,11 +1,11 @@
1import { ipcRenderer } from 'electron'; 1import { ipcRenderer } from 'electron';
2import { action, computed, observable } from 'mobx'; 2import { action, computed, observable, extendObservable } from 'mobx';
3 3
4import Store from './lib/Store'; 4import Store from './lib/Store';
5import Request from './lib/Request'; 5import Request from './lib/Request';
6import CachedRequest from './lib/CachedRequest'; 6import CachedRequest from './lib/CachedRequest';
7import { gaEvent } from '../lib/analytics'; 7import { gaEvent } from '../lib/analytics';
8import { DEFAULT_APP_SETTINGS } from '../config'; 8import SettingsModel from '../models/Settings';
9 9
10export default class SettingsStore extends Store { 10export default class SettingsStore extends Store {
11 @observable allSettingsRequest = new CachedRequest(this.api.local, 'getSettings'); 11 @observable allSettingsRequest = new CachedRequest(this.api.local, 'getSettings');
@@ -18,10 +18,6 @@ export default class SettingsStore extends Store {
18 // Register action handlers 18 // Register action handlers
19 this.actions.settings.update.listen(this._update.bind(this)); 19 this.actions.settings.update.listen(this._update.bind(this));
20 this.actions.settings.remove.listen(this._remove.bind(this)); 20 this.actions.settings.remove.listen(this._remove.bind(this));
21
22 // this.registerReactions([
23 // this._shareSettingsWithMainProcess.bind(this),
24 // ]);
25 } 21 }
26 22
27 setup() { 23 setup() {
@@ -30,14 +26,18 @@ export default class SettingsStore extends Store {
30 } 26 }
31 27
32 @computed get all() { 28 @computed get all() {
33 return observable(Object.assign(DEFAULT_APP_SETTINGS, this.allSettingsRequest.result)); 29 return new SettingsModel(this.allSettingsRequest.result);
34 } 30 }
35 31
36 @action async _update({ settings }) { 32 @action async _update({ settings }) {
37 await this.updateSettingsRequest.execute(settings)._promise; 33 await this.updateSettingsRequest.execute(settings)._promise;
38 await this.allSettingsRequest.invalidate({ immediately: true }); 34 await this.allSettingsRequest.patch((result) => {
35 if (!result) return;
36 extendObservable(result, settings);
37 });
39 38
40 this._shareSettingsWithMainProcess(); 39 // We need a little hack to wait until everything is patched
40 setTimeout(() => this._shareSettingsWithMainProcess(), 0);
41 41
42 gaEvent('Settings', 'update'); 42 gaEvent('Settings', 'update');
43 } 43 }
diff --git a/src/stores/UIStore.js b/src/stores/UIStore.js
index cb45b88b5..5e9cc9ba7 100644
--- a/src/stores/UIStore.js
+++ b/src/stores/UIStore.js
@@ -1,4 +1,4 @@
1import { action, observable } from 'mobx'; 1import { action, observable, computed } from 'mobx';
2 2
3import Store from './lib/Store'; 3import Store from './lib/Store';
4 4
@@ -14,6 +14,12 @@ export default class UIStore extends Store {
14 this.actions.ui.toggleServiceUpdatedInfoBar.listen(this._toggleServiceUpdatedInfoBar.bind(this)); 14 this.actions.ui.toggleServiceUpdatedInfoBar.listen(this._toggleServiceUpdatedInfoBar.bind(this));
15 } 15 }
16 16
17 @computed get showMessageBadgesEvenWhenMuted() {
18 const settings = this.stores.settings.all;
19
20 return (settings.isAppMuted && settings.showMessageBadgeWhenMuted) || !settings.isAppMuted;
21 }
22
17 // Actions 23 // Actions
18 @action _openSettings({ path = '/settings' }) { 24 @action _openSettings({ path = '/settings' }) {
19 const settingsPath = path !== '/settings' ? `/settings/${path}` : path; 25 const settingsPath = path !== '/settings' ? `/settings/${path}` : path;
diff --git a/src/stores/UserStore.js b/src/stores/UserStore.js
index 1cb2ecac3..09000dcdb 100644
--- a/src/stores/UserStore.js
+++ b/src/stores/UserStore.js
@@ -26,6 +26,7 @@ export default class UserStore extends Store {
26 @observable getUserInfoRequest = new CachedRequest(this.api.user, 'getInfo'); 26 @observable getUserInfoRequest = new CachedRequest(this.api.user, 'getInfo');
27 @observable updateUserInfoRequest = new Request(this.api.user, 'updateInfo'); 27 @observable updateUserInfoRequest = new Request(this.api.user, 'updateInfo');
28 @observable getLegacyServicesRequest = new CachedRequest(this.api.user, 'getLegacyServices'); 28 @observable getLegacyServicesRequest = new CachedRequest(this.api.user, 'getLegacyServices');
29 @observable deleteAccountRequest = new CachedRequest(this.api.user, 'delete');
29 30
30 @observable isImportLegacyServicesExecuting = false; 31 @observable isImportLegacyServicesExecuting = false;
31 @observable isImportLegacyServicesCompleted = false; 32 @observable isImportLegacyServicesCompleted = false;
@@ -57,6 +58,7 @@ export default class UserStore extends Store {
57 this.actions.user.update.listen(this._update.bind(this)); 58 this.actions.user.update.listen(this._update.bind(this));
58 this.actions.user.resetStatus.listen(this._resetStatus.bind(this)); 59 this.actions.user.resetStatus.listen(this._resetStatus.bind(this));
59 this.actions.user.importLegacyServices.listen(this._importLegacyServices.bind(this)); 60 this.actions.user.importLegacyServices.listen(this._importLegacyServices.bind(this));
61 this.actions.user.delete.listen(this._delete.bind(this));
60 62
61 // Reactions 63 // Reactions
62 this.registerReactions([ 64 this.registerReactions([
@@ -212,6 +214,10 @@ export default class UserStore extends Store {
212 this.isImportLegacyServicesCompleted = true; 214 this.isImportLegacyServicesCompleted = true;
213 } 215 }
214 216
217 @action async _delete() {
218 this.deleteAccountRequest.execute();
219 }
220
215 // This is a mobx autorun which forces the user to login if not authenticated 221 // This is a mobx autorun which forces the user to login if not authenticated
216 _requireAuthenticatedUser = () => { 222 _requireAuthenticatedUser = () => {
217 if (this.isTokenExpired) { 223 if (this.isTokenExpired) {