diff options
Diffstat (limited to 'src/stores')
-rw-r--r-- | src/stores/AppStore.js | 20 | ||||
-rw-r--r-- | src/stores/FeaturesStore.js | 4 | ||||
-rw-r--r-- | src/stores/GlobalErrorStore.js | 5 | ||||
-rw-r--r-- | src/stores/PaymentStore.js | 3 | ||||
-rw-r--r-- | src/stores/RecipePreviewsStore.js | 10 | ||||
-rw-r--r-- | src/stores/RecipesStore.js | 4 | ||||
-rw-r--r-- | src/stores/RequestStore.js | 11 | ||||
-rw-r--r-- | src/stores/ServicesStore.js | 133 | ||||
-rw-r--r-- | src/stores/SettingsStore.js | 66 | ||||
-rw-r--r-- | src/stores/UserStore.js | 41 |
10 files changed, 212 insertions, 85 deletions
diff --git a/src/stores/AppStore.js b/src/stores/AppStore.js index 0a6309092..6ce79f2e2 100644 --- a/src/stores/AppStore.js +++ b/src/stores/AppStore.js | |||
@@ -1,6 +1,6 @@ | |||
1 | import { remote, ipcRenderer, shell } from 'electron'; | 1 | import { remote, ipcRenderer, shell } from 'electron'; |
2 | import { | 2 | import { |
3 | action, computed, observable, reaction, | 3 | action, computed, observable, |
4 | } from 'mobx'; | 4 | } from 'mobx'; |
5 | import moment from 'moment'; | 5 | import moment from 'moment'; |
6 | import { getDoNotDisturb } from '@meetfranz/electron-notification-state'; | 6 | import { getDoNotDisturb } from '@meetfranz/electron-notification-state'; |
@@ -17,7 +17,6 @@ import Request from './lib/Request'; | |||
17 | import { CHECK_INTERVAL, DEFAULT_APP_SETTINGS } from '../config'; | 17 | import { CHECK_INTERVAL, DEFAULT_APP_SETTINGS } from '../config'; |
18 | import { isMac } from '../environment'; | 18 | import { isMac } from '../environment'; |
19 | import locales from '../i18n/translations'; | 19 | import locales from '../i18n/translations'; |
20 | import { gaEvent, gaPage, statsEvent } from '../lib/analytics'; | ||
21 | import { onVisibilityChange } from '../helpers/visibility-helper'; | 20 | import { onVisibilityChange } from '../helpers/visibility-helper'; |
22 | import { getLocale } from '../helpers/i18n-helpers'; | 21 | import { getLocale } from '../helpers/i18n-helpers'; |
23 | 22 | ||
@@ -25,7 +24,7 @@ import { getServiceIdsFromPartitions, removeServicePartitionDirectory } from '.. | |||
25 | import { isValidExternalURL } from '../helpers/url-helpers'; | 24 | import { isValidExternalURL } from '../helpers/url-helpers'; |
26 | import { sleep } from '../helpers/async-helpers'; | 25 | import { sleep } from '../helpers/async-helpers'; |
27 | 26 | ||
28 | const debug = require('debug')('Franz:AppStore'); | 27 | const debug = require('debug')('Ferdi:AppStore'); |
29 | 28 | ||
30 | const { | 29 | const { |
31 | app, systemPreferences, screen, powerMonitor, | 30 | app, systemPreferences, screen, powerMonitor, |
@@ -35,7 +34,7 @@ const mainWindow = remote.getCurrentWindow(); | |||
35 | 34 | ||
36 | const defaultLocale = DEFAULT_APP_SETTINGS.locale; | 35 | const defaultLocale = DEFAULT_APP_SETTINGS.locale; |
37 | const autoLauncher = new AutoLaunch({ | 36 | const autoLauncher = new AutoLaunch({ |
38 | name: 'Franz', | 37 | name: 'Ferdi', |
39 | }); | 38 | }); |
40 | 39 | ||
41 | const CATALINA_NOTIFICATION_HACK_KEY = '_temp_askedForCatalinaNotificationPermissions'; | 40 | const CATALINA_NOTIFICATION_HACK_KEY = '_temp_askedForCatalinaNotificationPermissions'; |
@@ -59,6 +58,8 @@ export default class AppStore extends Store { | |||
59 | 58 | ||
60 | @observable isOnline = navigator.onLine; | 59 | @observable isOnline = navigator.onLine; |
61 | 60 | ||
61 | @observable authRequestFailed = false; | ||
62 | |||
62 | @observable timeSuspensionStart; | 63 | @observable timeSuspensionStart; |
63 | 64 | ||
64 | @observable timeOfflineStart; | 65 | @observable timeOfflineStart; |
@@ -123,7 +124,7 @@ export default class AppStore extends Store { | |||
123 | 124 | ||
124 | this.isOnline = navigator.onLine; | 125 | this.isOnline = navigator.onLine; |
125 | 126 | ||
126 | // Check if Franz should launch on start | 127 | // Check if Ferdi should launch on start |
127 | // Needs to be delayed a bit | 128 | // Needs to be delayed a bit |
128 | this._autoStart(); | 129 | this._autoStart(); |
129 | 130 | ||
@@ -190,11 +191,6 @@ export default class AppStore extends Store { | |||
190 | debug('Window is visible/focused', isVisible); | 191 | debug('Window is visible/focused', isVisible); |
191 | }); | 192 | }); |
192 | 193 | ||
193 | // analytics autorun | ||
194 | reaction(() => this.stores.router.location.pathname, (pathname) => { | ||
195 | gaPage(pathname); | ||
196 | }); | ||
197 | |||
198 | powerMonitor.on('suspend', () => { | 194 | powerMonitor.on('suspend', () => { |
199 | debug('System suspended starting timer'); | 195 | debug('System suspended starting timer'); |
200 | 196 | ||
@@ -315,8 +311,6 @@ export default class AppStore extends Store { | |||
315 | } catch (err) { | 311 | } catch (err) { |
316 | console.warn(err); | 312 | console.warn(err); |
317 | } | 313 | } |
318 | |||
319 | gaEvent('App', enable ? 'enable autostart' : 'disable autostart'); | ||
320 | } | 314 | } |
321 | 315 | ||
322 | @action _openExternalUrl({ url }) { | 316 | @action _openExternalUrl({ url }) { |
@@ -421,7 +415,7 @@ export default class AppStore extends Store { | |||
421 | } | 415 | } |
422 | 416 | ||
423 | _muteAppHandler() { | 417 | _muteAppHandler() { |
424 | const showMessageBadgesEvenWhenMuted = this.stores.ui.showMessageBadgesEvenWhenMuted; | 418 | const { showMessageBadgesEvenWhenMuted } = this.stores.ui; |
425 | 419 | ||
426 | if (!showMessageBadgesEvenWhenMuted) { | 420 | if (!showMessageBadgesEvenWhenMuted) { |
427 | this.actions.app.setBadge({ unreadDirectMessageCount: 0, unreadIndirectMessageCount: 0 }); | 421 | this.actions.app.setBadge({ unreadDirectMessageCount: 0, unreadIndirectMessageCount: 0 }); |
diff --git a/src/stores/FeaturesStore.js b/src/stores/FeaturesStore.js index adbd401b4..ab5d762c7 100644 --- a/src/stores/FeaturesStore.js +++ b/src/stores/FeaturesStore.js | |||
@@ -13,12 +13,14 @@ import spellchecker from '../features/spellchecker'; | |||
13 | import serviceProxy from '../features/serviceProxy'; | 13 | import serviceProxy from '../features/serviceProxy'; |
14 | import basicAuth from '../features/basicAuth'; | 14 | import basicAuth from '../features/basicAuth'; |
15 | import workspaces from '../features/workspaces'; | 15 | import workspaces from '../features/workspaces'; |
16 | import quickSwitch from '../features/quickSwitch'; | ||
16 | import shareFranz from '../features/shareFranz'; | 17 | import shareFranz from '../features/shareFranz'; |
17 | import announcements from '../features/announcements'; | 18 | import announcements from '../features/announcements'; |
18 | import settingsWS from '../features/settingsWS'; | 19 | import settingsWS from '../features/settingsWS'; |
19 | import serviceLimit from '../features/serviceLimit'; | 20 | import serviceLimit from '../features/serviceLimit'; |
20 | import communityRecipes from '../features/communityRecipes'; | 21 | import communityRecipes from '../features/communityRecipes'; |
21 | import todos from '../features/todos'; | 22 | import todos from '../features/todos'; |
23 | import accentColor from '../features/accentColor'; | ||
22 | import planSelection from '../features/planSelection'; | 24 | import planSelection from '../features/planSelection'; |
23 | import trialStatusBar from '../features/trialStatusBar'; | 25 | import trialStatusBar from '../features/trialStatusBar'; |
24 | 26 | ||
@@ -78,12 +80,14 @@ export default class FeaturesStore extends Store { | |||
78 | serviceProxy(this.stores, this.actions); | 80 | serviceProxy(this.stores, this.actions); |
79 | basicAuth(this.stores, this.actions); | 81 | basicAuth(this.stores, this.actions); |
80 | workspaces(this.stores, this.actions); | 82 | workspaces(this.stores, this.actions); |
83 | quickSwitch(this.stores, this.actions); | ||
81 | shareFranz(this.stores, this.actions); | 84 | shareFranz(this.stores, this.actions); |
82 | announcements(this.stores, this.actions); | 85 | announcements(this.stores, this.actions); |
83 | settingsWS(this.stores, this.actions); | 86 | settingsWS(this.stores, this.actions); |
84 | serviceLimit(this.stores, this.actions); | 87 | serviceLimit(this.stores, this.actions); |
85 | communityRecipes(this.stores, this.actions); | 88 | communityRecipes(this.stores, this.actions); |
86 | todos(this.stores, this.actions); | 89 | todos(this.stores, this.actions); |
90 | accentColor(this.stores, this.actions); | ||
87 | planSelection(this.stores, this.actions); | 91 | planSelection(this.stores, this.actions); |
88 | trialStatusBar(this.stores, this.actions); | 92 | trialStatusBar(this.stores, this.actions); |
89 | } | 93 | } |
diff --git a/src/stores/GlobalErrorStore.js b/src/stores/GlobalErrorStore.js index 7a85c2daa..8bdafb68c 100644 --- a/src/stores/GlobalErrorStore.js +++ b/src/stores/GlobalErrorStore.js | |||
@@ -24,9 +24,12 @@ export default class GlobalErrorStore extends Store { | |||
24 | this.response = {}; | 24 | this.response = {}; |
25 | } | 25 | } |
26 | if (this.error.status === 401) { | 26 | if (this.error.status === 401) { |
27 | this.actions.user.logout({ serverLogout: true }); | 27 | window.ferdi.stores.app.authRequestFailed = true; |
28 | // this.actions.user.logout({ serverLogout: true }); | ||
28 | } | 29 | } |
29 | } | 30 | } |
31 | } else { | ||
32 | window.ferdi.stores.app.authRequestFailed = false; | ||
30 | } | 33 | } |
31 | }); | 34 | }); |
32 | } | 35 | } |
diff --git a/src/stores/PaymentStore.js b/src/stores/PaymentStore.js index eb42ae10e..69e6eb9c3 100644 --- a/src/stores/PaymentStore.js +++ b/src/stores/PaymentStore.js | |||
@@ -4,7 +4,6 @@ import { remote } from 'electron'; | |||
4 | import Store from './lib/Store'; | 4 | import Store from './lib/Store'; |
5 | import CachedRequest from './lib/CachedRequest'; | 5 | import CachedRequest from './lib/CachedRequest'; |
6 | import Request from './lib/Request'; | 6 | import Request from './lib/Request'; |
7 | import { gaEvent } from '../lib/analytics'; | ||
8 | 7 | ||
9 | const { BrowserWindow } = remote; | 8 | const { BrowserWindow } = remote; |
10 | 9 | ||
@@ -30,8 +29,6 @@ export default class PaymentStore extends Store { | |||
30 | @action _createHostedPage({ planId }) { | 29 | @action _createHostedPage({ planId }) { |
31 | const request = this.createHostedPageRequest.execute(planId); | 30 | const request = this.createHostedPageRequest.execute(planId); |
32 | 31 | ||
33 | gaEvent('Payment', 'createHostedPage', planId); | ||
34 | |||
35 | return request; | 32 | return request; |
36 | } | 33 | } |
37 | 34 | ||
diff --git a/src/stores/RecipePreviewsStore.js b/src/stores/RecipePreviewsStore.js index 382820d58..989e1124a 100644 --- a/src/stores/RecipePreviewsStore.js +++ b/src/stores/RecipePreviewsStore.js | |||
@@ -1,11 +1,8 @@ | |||
1 | import { action, computed, observable } from 'mobx'; | 1 | import { action, computed, observable } from 'mobx'; |
2 | import { debounce } from 'lodash'; | ||
3 | import ms from 'ms'; | ||
4 | 2 | ||
5 | import Store from './lib/Store'; | 3 | import Store from './lib/Store'; |
6 | import CachedRequest from './lib/CachedRequest'; | 4 | import CachedRequest from './lib/CachedRequest'; |
7 | import Request from './lib/Request'; | 5 | import Request from './lib/Request'; |
8 | import { gaEvent } from '../lib/analytics'; | ||
9 | 6 | ||
10 | export default class RecipePreviewsStore extends Store { | 7 | export default class RecipePreviewsStore extends Store { |
11 | @observable allRecipePreviewsRequest = new CachedRequest(this.api.recipePreviews, 'all'); | 8 | @observable allRecipePreviewsRequest = new CachedRequest(this.api.recipePreviews, 'all'); |
@@ -41,13 +38,6 @@ export default class RecipePreviewsStore extends Store { | |||
41 | @action _search({ needle }) { | 38 | @action _search({ needle }) { |
42 | if (needle !== '') { | 39 | if (needle !== '') { |
43 | this.searchRecipePreviewsRequest.execute(needle); | 40 | this.searchRecipePreviewsRequest.execute(needle); |
44 | |||
45 | this._analyticsSearch(needle); | ||
46 | } | 41 | } |
47 | } | 42 | } |
48 | |||
49 | // Helper | ||
50 | _analyticsSearch = debounce((needle) => { | ||
51 | gaEvent('Recipe', 'search', needle); | ||
52 | }, ms('3s')); | ||
53 | } | 43 | } |
diff --git a/src/stores/RecipesStore.js b/src/stores/RecipesStore.js index d51192078..8b2bde5df 100644 --- a/src/stores/RecipesStore.js +++ b/src/stores/RecipesStore.js | |||
@@ -5,7 +5,7 @@ import CachedRequest from './lib/CachedRequest'; | |||
5 | import Request from './lib/Request'; | 5 | import Request from './lib/Request'; |
6 | import { matchRoute } from '../helpers/routing-helpers'; | 6 | import { matchRoute } from '../helpers/routing-helpers'; |
7 | 7 | ||
8 | const debug = require('debug')('Franz:RecipeStore'); | 8 | const debug = require('debug')('Ferdi:RecipeStore'); |
9 | 9 | ||
10 | export default class RecipesStore extends Store { | 10 | export default class RecipesStore extends Store { |
11 | @observable allRecipesRequest = new CachedRequest(this.api.recipes, 'all'); | 11 | @observable allRecipesRequest = new CachedRequest(this.api.recipes, 'all'); |
@@ -108,7 +108,7 @@ export default class RecipesStore extends Store { | |||
108 | async _checkIfRecipeIsInstalled() { | 108 | async _checkIfRecipeIsInstalled() { |
109 | const { router } = this.stores; | 109 | const { router } = this.stores; |
110 | 110 | ||
111 | const match = matchRoute('/settings/services/add/:id', router.location.pathname); | 111 | const match = router.location && matchRoute('/settings/services/add/:id', router.location.pathname); |
112 | if (match) { | 112 | if (match) { |
113 | const recipeId = match.id; | 113 | const recipeId = match.id; |
114 | 114 | ||
diff --git a/src/stores/RequestStore.js b/src/stores/RequestStore.js index 9254e3223..a92f4c685 100644 --- a/src/stores/RequestStore.js +++ b/src/stores/RequestStore.js | |||
@@ -1,9 +1,10 @@ | |||
1 | import { ipcRenderer } from 'electron'; | ||
1 | import { action, computed, observable } from 'mobx'; | 2 | import { action, computed, observable } from 'mobx'; |
2 | import ms from 'ms'; | 3 | import ms from 'ms'; |
3 | 4 | ||
4 | import Store from './lib/Store'; | 5 | import Store from './lib/Store'; |
5 | 6 | ||
6 | const debug = require('debug')('Franz:RequestsStore'); | 7 | const debug = require('debug')('Ferdi:RequestsStore'); |
7 | 8 | ||
8 | export default class RequestStore extends Store { | 9 | export default class RequestStore extends Store { |
9 | @observable userInfoRequest; | 10 | @observable userInfoRequest; |
@@ -12,6 +13,8 @@ export default class RequestStore extends Store { | |||
12 | 13 | ||
13 | @observable showRequiredRequestsError = false; | 14 | @observable showRequiredRequestsError = false; |
14 | 15 | ||
16 | @observable localServerPort = 45569; | ||
17 | |||
15 | retries = 0; | 18 | retries = 0; |
16 | 19 | ||
17 | retryDelay = ms('2s'); | 20 | retryDelay = ms('2s'); |
@@ -29,6 +32,12 @@ export default class RequestStore extends Store { | |||
29 | setup() { | 32 | setup() { |
30 | this.userInfoRequest = this.stores.user.getUserInfoRequest; | 33 | this.userInfoRequest = this.stores.user.getUserInfoRequest; |
31 | this.servicesRequest = this.stores.services.allServicesRequest; | 34 | this.servicesRequest = this.stores.services.allServicesRequest; |
35 | |||
36 | ipcRenderer.on('localServerPort', (event, data) => { | ||
37 | if (data.port) { | ||
38 | this.localServerPort = data.port; | ||
39 | } | ||
40 | }); | ||
32 | } | 41 | } |
33 | 42 | ||
34 | @computed get areRequiredRequestsSuccessful() { | 43 | @computed get areRequiredRequestsSuccessful() { |
diff --git a/src/stores/ServicesStore.js b/src/stores/ServicesStore.js index 65e68e4d7..934a8a6e0 100644 --- a/src/stores/ServicesStore.js +++ b/src/stores/ServicesStore.js | |||
@@ -1,23 +1,27 @@ | |||
1 | import { shell, remote } from 'electron'; | ||
1 | import { | 2 | import { |
2 | action, | 3 | action, |
3 | reaction, | 4 | reaction, |
4 | computed, | 5 | computed, |
5 | observable, | 6 | observable, |
6 | } from 'mobx'; | 7 | } from 'mobx'; |
7 | import { debounce, remove } from 'lodash'; | 8 | import { remove } from 'lodash'; |
8 | import ms from 'ms'; | 9 | import ms from 'ms'; |
9 | import { remote } from 'electron'; | 10 | import fs from 'fs-extra'; |
11 | import path from 'path'; | ||
10 | 12 | ||
11 | import Store from './lib/Store'; | 13 | import Store from './lib/Store'; |
12 | import Request from './lib/Request'; | 14 | import Request from './lib/Request'; |
13 | import CachedRequest from './lib/CachedRequest'; | 15 | import CachedRequest from './lib/CachedRequest'; |
14 | import { matchRoute } from '../helpers/routing-helpers'; | 16 | import { matchRoute } from '../helpers/routing-helpers'; |
15 | import { gaEvent, statsEvent } from '../lib/analytics'; | 17 | import { isInTimeframe } from '../helpers/schedule-helpers'; |
18 | import { getRecipeDirectory, getDevRecipeDirectory } from '../helpers/recipe-helpers'; | ||
16 | import { workspaceStore } from '../features/workspaces'; | 19 | import { workspaceStore } from '../features/workspaces'; |
17 | import { serviceLimitStore } from '../features/serviceLimit'; | 20 | import { serviceLimitStore } from '../features/serviceLimit'; |
18 | import { RESTRICTION_TYPES } from '../models/Service'; | 21 | import { RESTRICTION_TYPES } from '../models/Service'; |
22 | import { KEEP_WS_LOADED_USID } from '../config'; | ||
19 | 23 | ||
20 | const debug = require('debug')('Franz:ServiceStore'); | 24 | const debug = require('debug')('Ferdi:ServiceStore'); |
21 | 25 | ||
22 | const { app } = remote; | 26 | const { app } = remote; |
23 | 27 | ||
@@ -36,6 +40,11 @@ export default class ServicesStore extends Store { | |||
36 | 40 | ||
37 | @observable filterNeedle = null; | 41 | @observable filterNeedle = null; |
38 | 42 | ||
43 | // Array of service IDs that have recently been used | ||
44 | // [0] => Most recent, [n] => Least recent | ||
45 | // No service ID should be in the list multiple times, not all service IDs have to be in the list | ||
46 | @observable lastUsedServices = []; | ||
47 | |||
39 | constructor(...args) { | 48 | constructor(...args) { |
40 | super(...args); | 49 | super(...args); |
41 | 50 | ||
@@ -49,6 +58,7 @@ export default class ServicesStore extends Store { | |||
49 | this.actions.service.createFromLegacyService.listen(this._createFromLegacyService.bind(this)); | 58 | this.actions.service.createFromLegacyService.listen(this._createFromLegacyService.bind(this)); |
50 | this.actions.service.updateService.listen(this._updateService.bind(this)); | 59 | this.actions.service.updateService.listen(this._updateService.bind(this)); |
51 | this.actions.service.deleteService.listen(this._deleteService.bind(this)); | 60 | this.actions.service.deleteService.listen(this._deleteService.bind(this)); |
61 | this.actions.service.openDarkmodeCss.listen(this._openDarkmodeCss.bind(this)); | ||
52 | this.actions.service.clearCache.listen(this._clearCache.bind(this)); | 62 | this.actions.service.clearCache.listen(this._clearCache.bind(this)); |
53 | this.actions.service.setWebviewReference.listen(this._setWebviewReference.bind(this)); | 63 | this.actions.service.setWebviewReference.listen(this._setWebviewReference.bind(this)); |
54 | this.actions.service.detachService.listen(this._detachService.bind(this)); | 64 | this.actions.service.detachService.listen(this._detachService.bind(this)); |
@@ -72,6 +82,7 @@ export default class ServicesStore extends Store { | |||
72 | this.actions.service.toggleAudio.listen(this._toggleAudio.bind(this)); | 82 | this.actions.service.toggleAudio.listen(this._toggleAudio.bind(this)); |
73 | this.actions.service.openDevTools.listen(this._openDevTools.bind(this)); | 83 | this.actions.service.openDevTools.listen(this._openDevTools.bind(this)); |
74 | this.actions.service.openDevToolsForActiveService.listen(this._openDevToolsForActiveService.bind(this)); | 84 | this.actions.service.openDevToolsForActiveService.listen(this._openDevToolsForActiveService.bind(this)); |
85 | this.actions.service.setHibernation.listen(this._setHibernation.bind(this)); | ||
75 | 86 | ||
76 | this.registerReactions([ | 87 | this.registerReactions([ |
77 | this._focusServiceReaction.bind(this), | 88 | this._focusServiceReaction.bind(this), |
@@ -98,6 +109,16 @@ export default class ServicesStore extends Store { | |||
98 | () => this.stores.settings.app.spellcheckerLanguage, | 109 | () => this.stores.settings.app.spellcheckerLanguage, |
99 | () => this._shareSettingsWithServiceProcess(), | 110 | () => this._shareSettingsWithServiceProcess(), |
100 | ); | 111 | ); |
112 | |||
113 | reaction( | ||
114 | () => this.stores.settings.app.darkMode, | ||
115 | () => this._shareSettingsWithServiceProcess(), | ||
116 | ); | ||
117 | |||
118 | reaction( | ||
119 | () => this.stores.settings.app.universalDarkMode, | ||
120 | () => this._shareSettingsWithServiceProcess(), | ||
121 | ); | ||
101 | } | 122 | } |
102 | 123 | ||
103 | @computed get all() { | 124 | @computed get all() { |
@@ -128,7 +149,35 @@ export default class ServicesStore extends Store { | |||
128 | const { keepAllWorkspacesLoaded } = this.stores.workspaces.settings; | 149 | const { keepAllWorkspacesLoaded } = this.stores.workspaces.settings; |
129 | const services = this.allServicesRequest.execute().result || []; | 150 | const services = this.allServicesRequest.execute().result || []; |
130 | const filteredServices = showDisabledServices ? services : services.filter(service => service.isEnabled); | 151 | const filteredServices = showDisabledServices ? services : services.filter(service => service.isEnabled); |
131 | return keepAllWorkspacesLoaded ? filteredServices : workspaceStore.filterServicesByActiveWorkspace(filteredServices); | 152 | |
153 | let displayedServices; | ||
154 | if (keepAllWorkspacesLoaded) { | ||
155 | // Keep all enabled services loaded | ||
156 | displayedServices = filteredServices; | ||
157 | } else { | ||
158 | // Keep all services in current workspace loaded | ||
159 | displayedServices = workspaceStore.filterServicesByActiveWorkspace(filteredServices); | ||
160 | |||
161 | // Keep all services active in workspaces that should be kept loaded | ||
162 | for (const workspace of this.stores.workspaces.workspaces) { | ||
163 | // Check if workspace needs to be kept loaded | ||
164 | if (workspace.services.includes(KEEP_WS_LOADED_USID)) { | ||
165 | // Get services for workspace | ||
166 | const serviceIDs = workspace.services.filter(i => i !== KEEP_WS_LOADED_USID); | ||
167 | const wsServices = filteredServices.filter(service => serviceIDs.includes(service.id)); | ||
168 | |||
169 | displayedServices = [ | ||
170 | ...displayedServices, | ||
171 | ...wsServices, | ||
172 | ]; | ||
173 | } | ||
174 | } | ||
175 | |||
176 | // Make sure every service is in the list only once | ||
177 | displayedServices = displayedServices.filter((v, i, a) => a.indexOf(v) === i); | ||
178 | } | ||
179 | |||
180 | return displayedServices; | ||
132 | } | 181 | } |
133 | 182 | ||
134 | @computed get filtered() { | 183 | @computed get filtered() { |
@@ -185,7 +234,6 @@ export default class ServicesStore extends Store { | |||
185 | 234 | ||
186 | if (redirect) { | 235 | if (redirect) { |
187 | this.stores.router.push('/settings/recipes'); | 236 | this.stores.router.push('/settings/recipes'); |
188 | gaEvent('Service', 'create', recipeId); | ||
189 | } | 237 | } |
190 | } | 238 | } |
191 | 239 | ||
@@ -262,7 +310,6 @@ export default class ServicesStore extends Store { | |||
262 | 310 | ||
263 | if (redirect) { | 311 | if (redirect) { |
264 | this.stores.router.push('/settings/services'); | 312 | this.stores.router.push('/settings/services'); |
265 | gaEvent('Service', 'update', service.recipe.id); | ||
266 | } | 313 | } |
267 | } | 314 | } |
268 | 315 | ||
@@ -277,19 +324,35 @@ export default class ServicesStore extends Store { | |||
277 | remove(result, c => c.id === serviceId); | 324 | remove(result, c => c.id === serviceId); |
278 | }); | 325 | }); |
279 | 326 | ||
280 | const service = this.one(serviceId); | ||
281 | |||
282 | await request._promise; | 327 | await request._promise; |
283 | this.actionStatus = request.result.status; | 328 | this.actionStatus = request.result.status; |
329 | } | ||
330 | |||
331 | @action async _openDarkmodeCss({ recipe }) { | ||
332 | // Get directory for recipe | ||
333 | const normalDirectory = getRecipeDirectory(recipe); | ||
334 | const devDirectory = getDevRecipeDirectory(recipe); | ||
335 | let directory; | ||
336 | |||
337 | if (await fs.pathExists(normalDirectory)) { | ||
338 | directory = normalDirectory; | ||
339 | } else if (await fs.pathExists(devDirectory)) { | ||
340 | directory = devDirectory; | ||
341 | } else { | ||
342 | // Recipe cannot be found on drive | ||
343 | return; | ||
344 | } | ||
284 | 345 | ||
285 | gaEvent('Service', 'delete', service.recipe.id); | 346 | // Create and open darkmode.css |
347 | const file = path.join(directory, 'darkmode.css'); | ||
348 | await fs.ensureFile(file); | ||
349 | shell.showItemInFolder(file); | ||
286 | } | 350 | } |
287 | 351 | ||
288 | @action async _clearCache({ serviceId }) { | 352 | @action async _clearCache({ serviceId }) { |
289 | this.clearCacheRequest.reset(); | 353 | this.clearCacheRequest.reset(); |
290 | const request = this.clearCacheRequest.execute(serviceId); | 354 | const request = this.clearCacheRequest.execute(serviceId); |
291 | await request._promise; | 355 | await request._promise; |
292 | gaEvent('Service', 'clear cache'); | ||
293 | } | 356 | } |
294 | 357 | ||
295 | @action _setActive({ serviceId, keepActiveRoute }) { | 358 | @action _setActive({ serviceId, keepActiveRoute }) { |
@@ -301,7 +364,9 @@ export default class ServicesStore extends Store { | |||
301 | }); | 364 | }); |
302 | service.isActive = true; | 365 | service.isActive = true; |
303 | 366 | ||
304 | statsEvent('activate-service', service.recipe.id); | 367 | // Update list of last used services |
368 | this.lastUsedServices = this.lastUsedServices.filter(id => id !== serviceId); | ||
369 | this.lastUsedServices.unshift(serviceId); | ||
305 | 370 | ||
306 | this._focusActiveService(); | 371 | this._focusActiveService(); |
307 | } | 372 | } |
@@ -406,7 +471,19 @@ export default class ServicesStore extends Store { | |||
406 | }, | 471 | }, |
407 | }); | 472 | }); |
408 | } else if (channel === 'notification') { | 473 | } else if (channel === 'notification') { |
409 | const options = args[0].options; | 474 | const { options } = args[0]; |
475 | |||
476 | // Check if we are in scheduled Do-not-Disturb time | ||
477 | const { | ||
478 | scheduledDNDEnabled, | ||
479 | scheduledDNDStart, | ||
480 | scheduledDNDEnd, | ||
481 | } = this.stores.settings.all.app; | ||
482 | |||
483 | if (scheduledDNDEnabled && isInTimeframe(scheduledDNDStart, scheduledDNDEnd)) { | ||
484 | return; | ||
485 | } | ||
486 | |||
410 | if (service.recipe.hasNotificationSound || service.isMuted || this.stores.settings.all.app.isAppMuted) { | 487 | if (service.recipe.hasNotificationSound || service.isMuted || this.stores.settings.all.app.isAppMuted) { |
411 | Object.assign(options, { | 488 | Object.assign(options, { |
412 | silent: true, | 489 | silent: true, |
@@ -414,8 +491,17 @@ export default class ServicesStore extends Store { | |||
414 | } | 491 | } |
415 | 492 | ||
416 | if (service.isNotificationEnabled) { | 493 | if (service.isNotificationEnabled) { |
417 | const title = typeof args[0].title === 'string' ? args[0].title : service.name; | 494 | let title = `Notification from ${service.name}`; |
418 | options.body = typeof options.body === 'string' ? options.body : ''; | 495 | if (!this.stores.settings.all.app.privateNotifications) { |
496 | options.body = typeof options.body === 'string' ? options.body : ''; | ||
497 | title = typeof args[0].title === 'string' ? args[0].title : service.name; | ||
498 | } else { | ||
499 | // Remove message data from notification in private mode | ||
500 | options.body = ''; | ||
501 | options.icon = '/assets/img/notification-badge.gif'; | ||
502 | } | ||
503 | |||
504 | console.log(title, options); | ||
419 | 505 | ||
420 | this.actions.app.notify({ | 506 | this.actions.app.notify({ |
421 | notificationId: args[0].notificationId, | 507 | notificationId: args[0].notificationId, |
@@ -533,7 +619,7 @@ export default class ServicesStore extends Store { | |||
533 | } | 619 | } |
534 | 620 | ||
535 | @action _reorderService({ oldIndex, newIndex }) { | 621 | @action _reorderService({ oldIndex, newIndex }) { |
536 | const showDisabledServices = this.stores.settings.all.app.showDisabledServices; | 622 | const { showDisabledServices } = this.stores.settings.all.app; |
537 | const oldEnabledSortIndex = showDisabledServices ? oldIndex : this.all.indexOf(this.enabled[oldIndex]); | 623 | const oldEnabledSortIndex = showDisabledServices ? oldIndex : this.all.indexOf(this.enabled[oldIndex]); |
538 | const newEnabledSortIndex = showDisabledServices ? newIndex : this.all.indexOf(this.enabled[newIndex]); | 624 | const newEnabledSortIndex = showDisabledServices ? newIndex : this.all.indexOf(this.enabled[newIndex]); |
539 | 625 | ||
@@ -552,8 +638,6 @@ export default class ServicesStore extends Store { | |||
552 | service.order = services[s.id]; | 638 | service.order = services[s.id]; |
553 | }); | 639 | }); |
554 | }); | 640 | }); |
555 | |||
556 | this._reorderAnalytics(); | ||
557 | } | 641 | } |
558 | 642 | ||
559 | @action _toggleNotifications({ serviceId }) { | 643 | @action _toggleNotifications({ serviceId }) { |
@@ -598,6 +682,11 @@ export default class ServicesStore extends Store { | |||
598 | } | 682 | } |
599 | } | 683 | } |
600 | 684 | ||
685 | @action _setHibernation({ serviceId, hibernating }) { | ||
686 | const service = this.one(serviceId); | ||
687 | service.isHibernating = hibernating; | ||
688 | } | ||
689 | |||
601 | // Reactions | 690 | // Reactions |
602 | _focusServiceReaction() { | 691 | _focusServiceReaction() { |
603 | const service = this.active; | 692 | const service = this.active; |
@@ -629,8 +718,8 @@ export default class ServicesStore extends Store { | |||
629 | } | 718 | } |
630 | 719 | ||
631 | _getUnreadMessageCountReaction() { | 720 | _getUnreadMessageCountReaction() { |
632 | const showMessageBadgeWhenMuted = this.stores.settings.all.app.showMessageBadgeWhenMuted; | 721 | const { showMessageBadgeWhenMuted } = this.stores.settings.all.app; |
633 | const showMessageBadgesEvenWhenMuted = this.stores.ui.showMessageBadgesEvenWhenMuted; | 722 | const { showMessageBadgesEvenWhenMuted } = this.stores.ui; |
634 | 723 | ||
635 | const unreadDirectMessageCount = this.allDisplayed | 724 | const unreadDirectMessageCount = this.allDisplayed |
636 | .filter(s => (showMessageBadgeWhenMuted || s.isNotificationEnabled) && showMessageBadgesEvenWhenMuted && s.isBadgeEnabled) | 725 | .filter(s => (showMessageBadgeWhenMuted || s.isNotificationEnabled) && showMessageBadgesEvenWhenMuted && s.isBadgeEnabled) |
@@ -759,10 +848,6 @@ export default class ServicesStore extends Store { | |||
759 | } | 848 | } |
760 | } | 849 | } |
761 | 850 | ||
762 | _reorderAnalytics = debounce(() => { | ||
763 | gaEvent('Service', 'order'); | ||
764 | }, ms('5s')); | ||
765 | |||
766 | _wrapIndex(index, delta, size) { | 851 | _wrapIndex(index, delta, size) { |
767 | return (((index + delta) % size) + size) % size; | 852 | return (((index + delta) % size) + size) % size; |
768 | } | 853 | } |
diff --git a/src/stores/SettingsStore.js b/src/stores/SettingsStore.js index 75bb38fe0..df0fc77e9 100644 --- a/src/stores/SettingsStore.js +++ b/src/stores/SettingsStore.js | |||
@@ -1,17 +1,18 @@ | |||
1 | import { ipcRenderer } from 'electron'; | 1 | import { ipcRenderer, remote } from 'electron'; |
2 | import { | 2 | import { |
3 | action, computed, observable, | 3 | action, computed, observable, reaction, |
4 | } from 'mobx'; | 4 | } from 'mobx'; |
5 | import localStorage from 'mobx-localstorage'; | 5 | import localStorage from 'mobx-localstorage'; |
6 | 6 | ||
7 | import Store from './lib/Store'; | 7 | import Store from './lib/Store'; |
8 | import Request from './lib/Request'; | 8 | import Request from './lib/Request'; |
9 | import { getLocale } from '../helpers/i18n-helpers'; | 9 | import { getLocale } from '../helpers/i18n-helpers'; |
10 | import { API } from '../environment'; | ||
10 | 11 | ||
11 | import { DEFAULT_APP_SETTINGS, FILE_SYSTEM_SETTINGS_TYPES } from '../config'; | 12 | import { DEFAULT_APP_SETTINGS, FILE_SYSTEM_SETTINGS_TYPES, LOCAL_SERVER } from '../config'; |
12 | import { SPELLCHECKER_LOCALES } from '../i18n/languages'; | 13 | import { SPELLCHECKER_LOCALES } from '../i18n/languages'; |
13 | 14 | ||
14 | const debug = require('debug')('Franz:SettingsStore'); | 15 | const debug = require('debug')('Ferdi:SettingsStore'); |
15 | 16 | ||
16 | export default class SettingsStore extends Store { | 17 | export default class SettingsStore extends Store { |
17 | @observable updateAppSettingsRequest = new Request(this.api.local, 'updateAppSettings'); | 18 | @observable updateAppSettingsRequest = new Request(this.api.local, 'updateAppSettings'); |
@@ -43,6 +44,62 @@ export default class SettingsStore extends Store { | |||
43 | 44 | ||
44 | async setup() { | 45 | async setup() { |
45 | await this._migrate(); | 46 | await this._migrate(); |
47 | |||
48 | reaction( | ||
49 | () => this.all.app.autohideMenuBar, | ||
50 | () => remote.getCurrentWindow().setAutoHideMenuBar( | ||
51 | this.all.app.autohideMenuBar, | ||
52 | ), | ||
53 | ); | ||
54 | |||
55 | reaction( | ||
56 | () => this.all.app.server, | ||
57 | (server) => { | ||
58 | if (server === LOCAL_SERVER) { | ||
59 | ipcRenderer.send('startLocalServer'); | ||
60 | } | ||
61 | }, | ||
62 | { | ||
63 | fireImmediately: true, | ||
64 | }, | ||
65 | ); | ||
66 | |||
67 | reaction( | ||
68 | () => this.all.app.locked, | ||
69 | () => { | ||
70 | const { router } = window.ferdi.stores; | ||
71 | |||
72 | if (this.all.app.locked && this.all.app.lockingFeatureEnabled) { | ||
73 | // App just got locked, redirect to unlock screen | ||
74 | router.push('/auth/locked'); | ||
75 | } else if (router.location.pathname.includes('/auth/locked')) { | ||
76 | // App is unlocked but user is still on locked screen | ||
77 | // Redirect to homepage | ||
78 | router.push('/'); | ||
79 | } | ||
80 | }, | ||
81 | ); | ||
82 | |||
83 | // Make sure to lock app on launch if locking feature is enabled | ||
84 | setTimeout(() => { | ||
85 | if (this.all.app.lockingFeatureEnabled) { | ||
86 | // Disable lock first - otherwise the lock might not get activated corrently | ||
87 | this.actions.settings.update({ | ||
88 | type: 'app', | ||
89 | data: { | ||
90 | locked: false, | ||
91 | }, | ||
92 | }); | ||
93 | setTimeout(() => { | ||
94 | this.actions.settings.update({ | ||
95 | type: 'app', | ||
96 | data: { | ||
97 | locked: true, | ||
98 | }, | ||
99 | }); | ||
100 | }, 0); | ||
101 | } | ||
102 | }, 1000); | ||
46 | } | 103 | } |
47 | 104 | ||
48 | @computed get app() { | 105 | @computed get app() { |
@@ -121,6 +178,7 @@ export default class SettingsStore extends Store { | |||
121 | runInBackground: legacySettings.runInBackground, | 178 | runInBackground: legacySettings.runInBackground, |
122 | enableSystemTray: legacySettings.enableSystemTray, | 179 | enableSystemTray: legacySettings.enableSystemTray, |
123 | minimizeToSystemTray: legacySettings.minimizeToSystemTray, | 180 | minimizeToSystemTray: legacySettings.minimizeToSystemTray, |
181 | server: API, | ||
124 | isAppMuted: legacySettings.isAppMuted, | 182 | isAppMuted: legacySettings.isAppMuted, |
125 | enableGPUAcceleration: legacySettings.enableGPUAcceleration, | 183 | enableGPUAcceleration: legacySettings.enableGPUAcceleration, |
126 | showMessageBadgeWhenMuted: legacySettings.showMessageBadgeWhenMuted, | 184 | showMessageBadgeWhenMuted: legacySettings.showMessageBadgeWhenMuted, |
diff --git a/src/stores/UserStore.js b/src/stores/UserStore.js index 297ea1121..d6a2e5fde 100644 --- a/src/stores/UserStore.js +++ b/src/stores/UserStore.js | |||
@@ -8,12 +8,11 @@ import { isDevMode } from '../environment'; | |||
8 | import Store from './lib/Store'; | 8 | import Store from './lib/Store'; |
9 | import Request from './lib/Request'; | 9 | import Request from './lib/Request'; |
10 | import CachedRequest from './lib/CachedRequest'; | 10 | import CachedRequest from './lib/CachedRequest'; |
11 | import { gaEvent } from '../lib/analytics'; | ||
12 | import { sleep } from '../helpers/async-helpers'; | 11 | import { sleep } from '../helpers/async-helpers'; |
13 | import { getPlan } from '../helpers/plan-helpers'; | 12 | import { getPlan } from '../helpers/plan-helpers'; |
14 | import { PLANS } from '../config'; | 13 | import { PLANS } from '../config'; |
15 | 14 | ||
16 | const debug = require('debug')('Franz:UserStore'); | 15 | const debug = require('debug')('Ferdi:UserStore'); |
17 | 16 | ||
18 | // TODO: split stores into UserStore and AuthStore | 17 | // TODO: split stores into UserStore and AuthStore |
19 | export default class UserStore extends Store { | 18 | export default class UserStore extends Store { |
@@ -57,6 +56,8 @@ export default class UserStore extends Store { | |||
57 | 56 | ||
58 | @observable isImportLegacyServicesCompleted = false; | 57 | @observable isImportLegacyServicesCompleted = false; |
59 | 58 | ||
59 | @observable isLoggingOut = false; | ||
60 | |||
60 | @observable id; | 61 | @observable id; |
61 | 62 | ||
62 | @observable authToken = localStorage.getItem('authToken') || null; | 63 | @observable authToken = localStorage.getItem('authToken') || null; |
@@ -96,7 +97,7 @@ export default class UserStore extends Store { | |||
96 | 97 | ||
97 | // Reactions | 98 | // Reactions |
98 | this.registerReactions([ | 99 | this.registerReactions([ |
99 | this._requireAuthenticatedUser, | 100 | // this._requireAuthenticatedUser, |
100 | this._getUserData.bind(this), | 101 | this._getUserData.bind(this), |
101 | this._resetTrialActivationState.bind(this), | 102 | this._resetTrialActivationState.bind(this), |
102 | ]); | 103 | ]); |
@@ -159,7 +160,7 @@ export default class UserStore extends Store { | |||
159 | } | 160 | } |
160 | 161 | ||
161 | @computed get isPremium() { | 162 | @computed get isPremium() { |
162 | return !!this.data.isPremium; | 163 | return true; |
163 | } | 164 | } |
164 | 165 | ||
165 | @computed get isPremiumOverride() { | 166 | @computed get isPremiumOverride() { |
@@ -174,12 +175,13 @@ export default class UserStore extends Store { | |||
174 | } | 175 | } |
175 | 176 | ||
176 | @computed get isPro() { | 177 | @computed get isPro() { |
177 | if (this.isPremiumOverride) return true; | 178 | return true; |
179 | // if (this.isPremiumOverride) return true; | ||
178 | 180 | ||
179 | if (!this.team || (!this.team.plan || this.team.state === 'expired')) return false; | 181 | // if (!this.team || (!this.team.plan || this.team.state === 'expired')) return false; |
180 | const plan = getPlan(this.team.plan); | 182 | // const plan = getPlan(this.team.plan); |
181 | 183 | ||
182 | return plan === PLANS.PRO || plan === PLANS.LEGACY; | 184 | // return plan === PLANS.PRO || plan === PLANS.LEGACY; |
183 | } | 185 | } |
184 | 186 | ||
185 | @computed get legacyServices() { | 187 | @computed get legacyServices() { |
@@ -192,16 +194,12 @@ export default class UserStore extends Store { | |||
192 | this._setUserData(authToken); | 194 | this._setUserData(authToken); |
193 | 195 | ||
194 | this.stores.router.push('/'); | 196 | this.stores.router.push('/'); |
195 | |||
196 | gaEvent('User', 'login'); | ||
197 | } | 197 | } |
198 | 198 | ||
199 | @action _tokenLogin(authToken) { | 199 | @action _tokenLogin(authToken) { |
200 | this._setUserData(authToken); | 200 | this._setUserData(authToken); |
201 | 201 | ||
202 | this.stores.router.push('/'); | 202 | this.stores.router.push('/'); |
203 | |||
204 | gaEvent('User', 'tokenLogin'); | ||
205 | } | 203 | } |
206 | 204 | ||
207 | @action async _signup({ | 205 | @action async _signup({ |
@@ -219,13 +217,11 @@ export default class UserStore extends Store { | |||
219 | currency, | 217 | currency, |
220 | }); | 218 | }); |
221 | 219 | ||
222 | this.hasCompletedSignup = false; | 220 | this.hasCompletedSignup = true; |
223 | 221 | ||
224 | this._setUserData(authToken); | 222 | this._setUserData(authToken); |
225 | 223 | ||
226 | this.stores.router.push(this.PRICING_ROUTE); | 224 | this.stores.router.push('/'); |
227 | |||
228 | gaEvent('User', 'signup'); | ||
229 | } | 225 | } |
230 | 226 | ||
231 | @action async _retrievePassword({ email }) { | 227 | @action async _retrievePassword({ email }) { |
@@ -233,8 +229,6 @@ export default class UserStore extends Store { | |||
233 | 229 | ||
234 | await request._promise; | 230 | await request._promise; |
235 | this.actionStatus = request.result.status || []; | 231 | this.actionStatus = request.result.status || []; |
236 | |||
237 | gaEvent('User', 'retrievePassword'); | ||
238 | } | 232 | } |
239 | 233 | ||
240 | @action async _activateTrial({ planId }) { | 234 | @action async _activateTrial({ planId }) { |
@@ -250,9 +244,6 @@ export default class UserStore extends Store { | |||
250 | 244 | ||
251 | this.stores.features.featuresRequest.invalidate({ immediately: true }); | 245 | this.stores.features.featuresRequest.invalidate({ immediately: true }); |
252 | this.stores.user.getUserInfoRequest.invalidate({ immediately: true }); | 246 | this.stores.user.getUserInfoRequest.invalidate({ immediately: true }); |
253 | |||
254 | |||
255 | gaEvent('User', 'activateTrial'); | ||
256 | } | 247 | } |
257 | 248 | ||
258 | @action async _invite({ invites }) { | 249 | @action async _invite({ invites }) { |
@@ -266,8 +257,6 @@ export default class UserStore extends Store { | |||
266 | if (this.stores.router.location.pathname.includes(this.INVITE_ROUTE)) { | 257 | if (this.stores.router.location.pathname.includes(this.INVITE_ROUTE)) { |
267 | this.stores.router.push('/'); | 258 | this.stores.router.push('/'); |
268 | } | 259 | } |
269 | |||
270 | gaEvent('User', 'inviteUsers'); | ||
271 | } | 260 | } |
272 | 261 | ||
273 | @action async _update({ userData }) { | 262 | @action async _update({ userData }) { |
@@ -277,8 +266,6 @@ export default class UserStore extends Store { | |||
277 | 266 | ||
278 | this.getUserInfoRequest.patch(() => response.data); | 267 | this.getUserInfoRequest.patch(() => response.data); |
279 | this.actionStatus = response.status || []; | 268 | this.actionStatus = response.status || []; |
280 | |||
281 | gaEvent('User', 'update'); | ||
282 | } | 269 | } |
283 | 270 | ||
284 | @action _resetStatus() { | 271 | @action _resetStatus() { |
@@ -301,12 +288,12 @@ export default class UserStore extends Store { | |||
301 | const recipes = services.filter((obj, pos, arr) => arr.map(mapObj => mapObj.recipe.id).indexOf(obj.recipe.id) === pos).map(s => s.recipe.id); | 288 | const recipes = services.filter((obj, pos, arr) => arr.map(mapObj => mapObj.recipe.id).indexOf(obj.recipe.id) === pos).map(s => s.recipe.id); |
302 | 289 | ||
303 | // Install recipes | 290 | // Install recipes |
304 | for (const recipe of recipes) { | 291 | for (const recipe of recipes) { // eslint-disable-line no-unused-vars |
305 | // eslint-disable-next-line | 292 | // eslint-disable-next-line |
306 | await this.stores.recipes._install({ recipeId: recipe }); | 293 | await this.stores.recipes._install({ recipeId: recipe }); |
307 | } | 294 | } |
308 | 295 | ||
309 | for (const service of services) { | 296 | for (const service of services) { // eslint-disable-line no-unused-vars |
310 | this.actions.service.createFromLegacyService({ | 297 | this.actions.service.createFromLegacyService({ |
311 | data: service, | 298 | data: service, |
312 | }); | 299 | }); |