diff options
author | Vijay A <avijayr@protonmail.com> | 2021-08-13 00:45:01 +0530 |
---|---|---|
committer | Vijay A <avijayr@protonmail.com> | 2021-08-13 00:45:01 +0530 |
commit | 2d27d5e66649d4f5baf127a53ee5ae524eae3a59 (patch) | |
tree | c592ea219ac8cd987fc367f57b54034c450ab2ab /src/stores | |
parent | Ferdi v5.6.0 (diff) | |
parent | 5.6.1-nightly.24 [skip ci] (diff) | |
download | ferdium-app-2d27d5e66649d4f5baf127a53ee5ae524eae3a59.tar.gz ferdium-app-2d27d5e66649d4f5baf127a53ee5ae524eae3a59.tar.zst ferdium-app-2d27d5e66649d4f5baf127a53ee5ae524eae3a59.zip |
chore: merge from nightly branch into release branch in prep for next beta
Diffstat (limited to 'src/stores')
-rw-r--r-- | src/stores/AppStore.js | 105 | ||||
-rw-r--r-- | src/stores/FeaturesStore.js | 18 | ||||
-rw-r--r-- | src/stores/NewsStore.js | 2 | ||||
-rw-r--r-- | src/stores/PaymentStore.js | 68 | ||||
-rw-r--r-- | src/stores/RecipePreviewsStore.js | 8 | ||||
-rw-r--r-- | src/stores/RecipesStore.js | 17 | ||||
-rw-r--r-- | src/stores/ServicesStore.js | 452 | ||||
-rw-r--r-- | src/stores/SettingsStore.js | 109 | ||||
-rw-r--r-- | src/stores/UIStore.js | 2 | ||||
-rw-r--r-- | src/stores/UserStore.js | 69 | ||||
-rw-r--r-- | src/stores/index.js | 6 | ||||
-rw-r--r-- | src/stores/lib/CachedRequest.js | 4 | ||||
-rw-r--r-- | src/stores/lib/Reaction.js | 4 | ||||
-rw-r--r-- | src/stores/lib/Request.js | 2 | ||||
-rw-r--r-- | src/stores/lib/Store.js | 6 |
15 files changed, 409 insertions, 463 deletions
diff --git a/src/stores/AppStore.js b/src/stores/AppStore.js index bbb5e6305..1d706f1ef 100644 --- a/src/stores/AppStore.js +++ b/src/stores/AppStore.js | |||
@@ -1,26 +1,38 @@ | |||
1 | import { ipcRenderer, shell } from 'electron'; | 1 | import { ipcRenderer, shell } from 'electron'; |
2 | import { | 2 | import { |
3 | app, screen, powerMonitor, nativeTheme, getCurrentWindow, process as remoteProcess, | 3 | app, |
4 | screen, | ||
5 | powerMonitor, | ||
6 | nativeTheme, | ||
7 | getCurrentWindow, | ||
8 | process as remoteProcess, | ||
4 | } from '@electron/remote'; | 9 | } from '@electron/remote'; |
5 | import { action, computed, observable } from 'mobx'; | 10 | import { action, computed, observable } from 'mobx'; |
6 | import moment from 'moment'; | 11 | import moment from 'moment'; |
7 | import AutoLaunch from 'auto-launch'; | 12 | import AutoLaunch from 'auto-launch'; |
8 | import ms from 'ms'; | 13 | import ms from 'ms'; |
9 | import { URL } from 'url'; | 14 | import { URL } from 'url'; |
10 | import path from 'path'; | ||
11 | import { readJsonSync } from 'fs-extra'; | 15 | import { readJsonSync } from 'fs-extra'; |
12 | 16 | ||
13 | import Store from './lib/Store'; | 17 | import Store from './lib/Store'; |
14 | import Request from './lib/Request'; | 18 | import Request from './lib/Request'; |
15 | import { CHECK_INTERVAL } from '../config'; | 19 | import { CHECK_INTERVAL } from '../config'; |
16 | import { | 20 | import { |
17 | DEFAULT_APP_SETTINGS, isMac, ferdiVersion, electronVersion, osRelease, | 21 | DEFAULT_APP_SETTINGS, |
22 | isMac, | ||
23 | ferdiVersion, | ||
24 | electronVersion, | ||
25 | osRelease, | ||
26 | userDataPath, | ||
18 | } from '../environment'; | 27 | } from '../environment'; |
19 | import locales from '../i18n/translations'; | 28 | import locales from '../i18n/translations'; |
20 | import { onVisibilityChange } from '../helpers/visibility-helper'; | 29 | import { onVisibilityChange } from '../helpers/visibility-helper'; |
21 | import { getLocale } from '../helpers/i18n-helpers'; | 30 | import { getLocale } from '../helpers/i18n-helpers'; |
22 | 31 | ||
23 | import { getServiceIdsFromPartitions, removeServicePartitionDirectory } from '../helpers/service-helpers.js'; | 32 | import { |
33 | getServiceIdsFromPartitions, | ||
34 | removeServicePartitionDirectory, | ||
35 | } from '../helpers/service-helpers'; | ||
24 | import { isValidExternalURL } from '../helpers/url-helpers'; | 36 | import { isValidExternalURL } from '../helpers/url-helpers'; |
25 | import { sleep } from '../helpers/async-helpers'; | 37 | import { sleep } from '../helpers/async-helpers'; |
26 | 38 | ||
@@ -49,7 +61,10 @@ export default class AppStore extends Store { | |||
49 | 61 | ||
50 | @observable healthCheckRequest = new Request(this.api.app, 'health'); | 62 | @observable healthCheckRequest = new Request(this.api.app, 'health'); |
51 | 63 | ||
52 | @observable getAppCacheSizeRequest = new Request(this.api.local, 'getAppCacheSize'); | 64 | @observable getAppCacheSizeRequest = new Request( |
65 | this.api.local, | ||
66 | 'getAppCacheSize', | ||
67 | ); | ||
53 | 68 | ||
54 | @observable clearAppCacheRequest = new Request(this.api.local, 'clearCache'); | 69 | @observable clearAppCacheRequest = new Request(this.api.local, 'clearCache'); |
55 | 70 | ||
@@ -93,7 +108,9 @@ export default class AppStore extends Store { | |||
93 | this.actions.app.openExternalUrl.listen(this._openExternalUrl.bind(this)); | 108 | this.actions.app.openExternalUrl.listen(this._openExternalUrl.bind(this)); |
94 | this.actions.app.checkForUpdates.listen(this._checkForUpdates.bind(this)); | 109 | this.actions.app.checkForUpdates.listen(this._checkForUpdates.bind(this)); |
95 | this.actions.app.installUpdate.listen(this._installUpdate.bind(this)); | 110 | this.actions.app.installUpdate.listen(this._installUpdate.bind(this)); |
96 | this.actions.app.resetUpdateStatus.listen(this._resetUpdateStatus.bind(this)); | 111 | this.actions.app.resetUpdateStatus.listen( |
112 | this._resetUpdateStatus.bind(this), | ||
113 | ); | ||
97 | this.actions.app.healthCheck.listen(this._healthCheck.bind(this)); | 114 | this.actions.app.healthCheck.listen(this._healthCheck.bind(this)); |
98 | this.actions.app.muteApp.listen(this._muteApp.bind(this)); | 115 | this.actions.app.muteApp.listen(this._muteApp.bind(this)); |
99 | this.actions.app.toggleMuteApp.listen(this._toggleMuteApp.bind(this)); | 116 | this.actions.app.toggleMuteApp.listen(this._toggleMuteApp.bind(this)); |
@@ -183,9 +200,7 @@ export default class AppStore extends Store { | |||
183 | // Handle deep linking (ferdi://) | 200 | // Handle deep linking (ferdi://) |
184 | ipcRenderer.on('navigateFromDeepLink', (event, data) => { | 201 | ipcRenderer.on('navigateFromDeepLink', (event, data) => { |
185 | debug('Navigate from deep link', data); | 202 | debug('Navigate from deep link', data); |
186 | let { | 203 | let { url } = data; |
187 | url, | ||
188 | } = data; | ||
189 | if (!url) return; | 204 | if (!url) return; |
190 | 205 | ||
191 | url = url.replace(/\/$/, ''); | 206 | url = url.replace(/\/$/, ''); |
@@ -221,7 +236,10 @@ export default class AppStore extends Store { | |||
221 | debug('System resumed, last suspended on', this.timeSuspensionStart); | 236 | debug('System resumed, last suspended on', this.timeSuspensionStart); |
222 | this.actions.service.resetLastPollTimer(); | 237 | this.actions.service.resetLastPollTimer(); |
223 | 238 | ||
224 | if (this.timeSuspensionStart.add(10, 'm').isBefore(moment()) && this.stores.settings.app.get('reloadAfterResume')) { | 239 | if ( |
240 | this.timeSuspensionStart.add(10, 'm').isBefore(moment()) | ||
241 | && this.stores.settings.app.get('reloadAfterResume') | ||
242 | ) { | ||
225 | debug('Reloading services, user info and features'); | 243 | debug('Reloading services, user info and features'); |
226 | 244 | ||
227 | setInterval(() => { | 245 | setInterval(() => { |
@@ -266,15 +284,15 @@ export default class AppStore extends Store { | |||
266 | ferdi: { | 284 | ferdi: { |
267 | version: ferdiVersion, | 285 | version: ferdiVersion, |
268 | electron: electronVersion, | 286 | electron: electronVersion, |
269 | installedRecipes: this.stores.recipes.all.map(recipe => ({ | 287 | installedRecipes: this.stores.recipes.all.map((recipe) => ({ |
270 | id: recipe.id, | 288 | id: recipe.id, |
271 | version: recipe.version, | 289 | version: recipe.version, |
272 | })), | 290 | })), |
273 | devRecipes: this.stores.recipePreviews.dev.map(recipe => ({ | 291 | devRecipes: this.stores.recipePreviews.dev.map((recipe) => ({ |
274 | id: recipe.id, | 292 | id: recipe.id, |
275 | version: recipe.version, | 293 | version: recipe.version, |
276 | })), | 294 | })), |
277 | services: this.stores.services.all.map(service => ({ | 295 | services: this.stores.services.all.map((service) => ({ |
278 | id: service.id, | 296 | id: service.id, |
279 | recipe: service.recipe.id, | 297 | recipe: service.recipe.id, |
280 | isAttached: service.isAttached, | 298 | isAttached: service.isAttached, |
@@ -285,11 +303,11 @@ export default class AppStore extends Store { | |||
285 | isDarkModeEnabled: service.isDarkModeEnabled, | 303 | isDarkModeEnabled: service.isDarkModeEnabled, |
286 | })), | 304 | })), |
287 | messages: this.stores.globalError.messages, | 305 | messages: this.stores.globalError.messages, |
288 | workspaces: this.stores.workspaces.workspaces.map(workspace => ({ | 306 | workspaces: this.stores.workspaces.workspaces.map((workspace) => ({ |
289 | id: workspace.id, | 307 | id: workspace.id, |
290 | services: workspace.services, | 308 | services: workspace.services, |
291 | })), | 309 | })), |
292 | windowSettings: readJsonSync(path.join(app.getPath('userData'), 'window-state.json')), | 310 | windowSettings: readJsonSync(userDataPath('window-state.json')), |
293 | settings, | 311 | settings, |
294 | features: this.stores.features.features, | 312 | features: this.stores.features.features, |
295 | user: this.stores.user.data.id, | 313 | user: this.stores.user.data.id, |
@@ -299,10 +317,7 @@ export default class AppStore extends Store { | |||
299 | 317 | ||
300 | // Actions | 318 | // Actions |
301 | @action _notify({ | 319 | @action _notify({ |
302 | title, | 320 | title, options, notificationId, serviceId = null, |
303 | options, | ||
304 | notificationId, | ||
305 | serviceId = null, | ||
306 | }) { | 321 | }) { |
307 | if (this.stores.settings.all.app.isAppMuted) return; | 322 | if (this.stores.settings.all.app.isAppMuted) return; |
308 | 323 | ||
@@ -339,15 +354,15 @@ export default class AppStore extends Store { | |||
339 | }; | 354 | }; |
340 | } | 355 | } |
341 | 356 | ||
342 | @action _setBadge({ | 357 | @action _setBadge({ unreadDirectMessageCount, unreadIndirectMessageCount }) { |
343 | unreadDirectMessageCount, | ||
344 | unreadIndirectMessageCount, | ||
345 | }) { | ||
346 | let indicator = unreadDirectMessageCount; | 358 | let indicator = unreadDirectMessageCount; |
347 | 359 | ||
348 | if (indicator === 0 && unreadIndirectMessageCount !== 0) { | 360 | if (indicator === 0 && unreadIndirectMessageCount !== 0) { |
349 | indicator = '•'; | 361 | indicator = '•'; |
350 | } else if (unreadDirectMessageCount === 0 && unreadIndirectMessageCount === 0) { | 362 | } else if ( |
363 | unreadDirectMessageCount === 0 | ||
364 | && unreadIndirectMessageCount === 0 | ||
365 | ) { | ||
351 | indicator = 0; | 366 | indicator = 0; |
352 | } else { | 367 | } else { |
353 | indicator = parseInt(indicator, 10); | 368 | indicator = parseInt(indicator, 10); |
@@ -358,9 +373,7 @@ export default class AppStore extends Store { | |||
358 | }); | 373 | }); |
359 | } | 374 | } |
360 | 375 | ||
361 | @action _launchOnStartup({ | 376 | @action _launchOnStartup({ enable }) { |
362 | enable, | ||
363 | }) { | ||
364 | this.autoLaunchOnStart = enable; | 377 | this.autoLaunchOnStart = enable; |
365 | 378 | ||
366 | try { | 379 | try { |
@@ -376,9 +389,7 @@ export default class AppStore extends Store { | |||
376 | } | 389 | } |
377 | } | 390 | } |
378 | 391 | ||
379 | @action _openExternalUrl({ | 392 | @action _openExternalUrl({ url }) { |
380 | url, | ||
381 | }) { | ||
382 | const parsedUrl = new URL(url); | 393 | const parsedUrl = new URL(url); |
383 | debug('open external url', parsedUrl); | 394 | debug('open external url', parsedUrl); |
384 | 395 | ||
@@ -414,10 +425,7 @@ export default class AppStore extends Store { | |||
414 | this.healthCheckRequest.execute(); | 425 | this.healthCheckRequest.execute(); |
415 | } | 426 | } |
416 | 427 | ||
417 | @action _muteApp({ | 428 | @action _muteApp({ isMuted, overrideSystemMute = true }) { |
418 | isMuted, | ||
419 | overrideSystemMute = true, | ||
420 | }) { | ||
421 | this.isSystemMuteOverridden = overrideSystemMute; | 429 | this.isSystemMuteOverridden = overrideSystemMute; |
422 | this.actions.settings.update({ | 430 | this.actions.settings.update({ |
423 | type: 'app', | 431 | type: 'app', |
@@ -437,16 +445,24 @@ export default class AppStore extends Store { | |||
437 | this.isClearingAllCache = true; | 445 | this.isClearingAllCache = true; |
438 | const clearAppCache = this.clearAppCacheRequest.execute(); | 446 | const clearAppCache = this.clearAppCacheRequest.execute(); |
439 | const allServiceIds = await getServiceIdsFromPartitions(); | 447 | const allServiceIds = await getServiceIdsFromPartitions(); |
440 | const allOrphanedServiceIds = allServiceIds.filter(id => !this.stores.services.all.find(s => id.replace('service-', '') === s.id)); | 448 | const allOrphanedServiceIds = allServiceIds.filter( |
449 | (id) => !this.stores.services.all.find( | ||
450 | (s) => id.replace('service-', '') === s.id, | ||
451 | ), | ||
452 | ); | ||
441 | 453 | ||
442 | try { | 454 | try { |
443 | await Promise.all(allOrphanedServiceIds.map(id => removeServicePartitionDirectory(id))); | 455 | await Promise.all( |
456 | allOrphanedServiceIds.map((id) => removeServicePartitionDirectory(id)), | ||
457 | ); | ||
444 | } catch (ex) { | 458 | } catch (ex) { |
445 | console.log('Error while deleting service partition directory - ', ex); | 459 | console.log('Error while deleting service partition directory - ', ex); |
446 | } | 460 | } |
447 | await Promise.all(this.stores.services.all.map(s => this.actions.service.clearCache({ | 461 | await Promise.all( |
448 | serviceId: s.id, | 462 | this.stores.services.all.map((s) => this.actions.service.clearCache({ |
449 | }))); | 463 | serviceId: s.id, |
464 | })), | ||
465 | ); | ||
450 | 466 | ||
451 | await clearAppCache._promise; | 467 | await clearAppCache._promise; |
452 | 468 | ||
@@ -476,7 +492,11 @@ export default class AppStore extends Store { | |||
476 | locale = this.stores.user.data.locale; | 492 | locale = this.stores.user.data.locale; |
477 | } | 493 | } |
478 | 494 | ||
479 | if (locale && Object.prototype.hasOwnProperty.call(locales, locale) && locale !== this.locale) { | 495 | if ( |
496 | locale | ||
497 | && Object.prototype.hasOwnProperty.call(locales, locale) | ||
498 | && locale !== this.locale | ||
499 | ) { | ||
480 | this.locale = locale; | 500 | this.locale = locale; |
481 | } else if (!locale) { | 501 | } else if (!locale) { |
482 | this.locale = this._getDefaultLocale(); | 502 | this.locale = this._getDefaultLocale(); |
@@ -553,7 +573,10 @@ export default class AppStore extends Store { | |||
553 | const dnd = await ipcRenderer.invoke('get-dnd'); | 573 | const dnd = await ipcRenderer.invoke('get-dnd'); |
554 | debug('Do not disturb mode is', dnd); | 574 | debug('Do not disturb mode is', dnd); |
555 | // ipcRenderer.on('autoUpdate', (event, data) => { | 575 | // ipcRenderer.on('autoUpdate', (event, data) => { |
556 | if (dnd !== this.stores.settings.all.app.isAppMuted && !this.isSystemMuteOverridden) { | 576 | if ( |
577 | dnd !== this.stores.settings.all.app.isAppMuted | ||
578 | && !this.isSystemMuteOverridden | ||
579 | ) { | ||
557 | this.actions.app.muteApp({ | 580 | this.actions.app.muteApp({ |
558 | isMuted: dnd, | 581 | isMuted: dnd, |
559 | overrideSystemMute: false, | 582 | overrideSystemMute: false, |
diff --git a/src/stores/FeaturesStore.js b/src/stores/FeaturesStore.js index 2fee9bdda..ac623c258 100644 --- a/src/stores/FeaturesStore.js +++ b/src/stores/FeaturesStore.js | |||
@@ -1,15 +1,12 @@ | |||
1 | import { | 1 | import { |
2 | computed, | 2 | computed, |
3 | observable, | 3 | observable, |
4 | reaction, | ||
5 | runInAction, | 4 | runInAction, |
6 | } from 'mobx'; | 5 | } from 'mobx'; |
7 | 6 | ||
8 | import Store from './lib/Store'; | 7 | import Store from './lib/Store'; |
9 | import CachedRequest from './lib/CachedRequest'; | 8 | import CachedRequest from './lib/CachedRequest'; |
10 | 9 | ||
11 | import delayApp from '../features/delayApp'; | ||
12 | import spellchecker from '../features/spellchecker'; | ||
13 | import serviceProxy from '../features/serviceProxy'; | 10 | import serviceProxy from '../features/serviceProxy'; |
14 | import basicAuth from '../features/basicAuth'; | 11 | import basicAuth from '../features/basicAuth'; |
15 | import workspaces from '../features/workspaces'; | 12 | import workspaces from '../features/workspaces'; |
@@ -19,12 +16,9 @@ import publishDebugInfo from '../features/publishDebugInfo'; | |||
19 | import shareFranz from '../features/shareFranz'; | 16 | import shareFranz from '../features/shareFranz'; |
20 | import announcements from '../features/announcements'; | 17 | import announcements from '../features/announcements'; |
21 | import settingsWS from '../features/settingsWS'; | 18 | import settingsWS from '../features/settingsWS'; |
22 | import serviceLimit from '../features/serviceLimit'; | ||
23 | import communityRecipes from '../features/communityRecipes'; | 19 | import communityRecipes from '../features/communityRecipes'; |
24 | import todos from '../features/todos'; | 20 | import todos from '../features/todos'; |
25 | import appearance from '../features/appearance'; | 21 | import appearance from '../features/appearance'; |
26 | import planSelection from '../features/planSelection'; | ||
27 | import trialStatusBar from '../features/trialStatusBar'; | ||
28 | 22 | ||
29 | import { DEFAULT_FEATURES_CONFIG } from '../config'; | 23 | import { DEFAULT_FEATURES_CONFIG } from '../config'; |
30 | 24 | ||
@@ -43,13 +37,6 @@ export default class FeaturesStore extends Store { | |||
43 | 37 | ||
44 | await this.featuresRequest._promise; | 38 | await this.featuresRequest._promise; |
45 | setTimeout(this._setupFeatures.bind(this), 1); | 39 | setTimeout(this._setupFeatures.bind(this), 1); |
46 | |||
47 | // single key reaction | ||
48 | reaction(() => this.stores.user.data.isPremium, () => { | ||
49 | if (this.stores.user.isLoggedIn) { | ||
50 | this.featuresRequest.invalidate({ immediately: true }); | ||
51 | } | ||
52 | }); | ||
53 | } | 40 | } |
54 | 41 | ||
55 | @computed get anonymousFeatures() { | 42 | @computed get anonymousFeatures() { |
@@ -80,8 +67,6 @@ export default class FeaturesStore extends Store { | |||
80 | } | 67 | } |
81 | 68 | ||
82 | _setupFeatures() { | 69 | _setupFeatures() { |
83 | delayApp(this.stores, this.actions); | ||
84 | spellchecker(this.stores, this.actions); | ||
85 | serviceProxy(this.stores, this.actions); | 70 | serviceProxy(this.stores, this.actions); |
86 | basicAuth(this.stores, this.actions); | 71 | basicAuth(this.stores, this.actions); |
87 | workspaces(this.stores, this.actions); | 72 | workspaces(this.stores, this.actions); |
@@ -91,11 +76,8 @@ export default class FeaturesStore extends Store { | |||
91 | shareFranz(this.stores, this.actions); | 76 | shareFranz(this.stores, this.actions); |
92 | announcements(this.stores, this.actions); | 77 | announcements(this.stores, this.actions); |
93 | settingsWS(this.stores, this.actions); | 78 | settingsWS(this.stores, this.actions); |
94 | serviceLimit(this.stores, this.actions); | ||
95 | communityRecipes(this.stores, this.actions); | 79 | communityRecipes(this.stores, this.actions); |
96 | todos(this.stores, this.actions); | 80 | todos(this.stores, this.actions); |
97 | appearance(this.stores, this.actions); | 81 | appearance(this.stores, this.actions); |
98 | planSelection(this.stores, this.actions); | ||
99 | trialStatusBar(this.stores, this.actions); | ||
100 | } | 82 | } |
101 | } | 83 | } |
diff --git a/src/stores/NewsStore.js b/src/stores/NewsStore.js index 86e092592..66a17cb29 100644 --- a/src/stores/NewsStore.js +++ b/src/stores/NewsStore.js | |||
@@ -38,7 +38,7 @@ export default class NewsStore extends Store { | |||
38 | 38 | ||
39 | this.latestNewsRequest.invalidate().patch((result) => { | 39 | this.latestNewsRequest.invalidate().patch((result) => { |
40 | // TODO: check if we can use mobx.array remove | 40 | // TODO: check if we can use mobx.array remove |
41 | remove(result, n => n.id === newsId); | 41 | remove(result, (n) => n.id === newsId); |
42 | }); | 42 | }); |
43 | } | 43 | } |
44 | 44 | ||
diff --git a/src/stores/PaymentStore.js b/src/stores/PaymentStore.js deleted file mode 100644 index 05bb5b3d0..000000000 --- a/src/stores/PaymentStore.js +++ /dev/null | |||
@@ -1,68 +0,0 @@ | |||
1 | import { action, observable, computed } from 'mobx'; | ||
2 | import { BrowserWindow, getCurrentWindow } from '@electron/remote'; | ||
3 | |||
4 | import Store from './lib/Store'; | ||
5 | import CachedRequest from './lib/CachedRequest'; | ||
6 | import Request from './lib/Request'; | ||
7 | |||
8 | export default class PaymentStore extends Store { | ||
9 | @observable plansRequest = new CachedRequest(this.api.payment, 'plans'); | ||
10 | |||
11 | @observable createHostedPageRequest = new Request(this.api.payment, 'getHostedPage'); | ||
12 | |||
13 | constructor(...args) { | ||
14 | super(...args); | ||
15 | |||
16 | this.actions.payment.createHostedPage.listen(this._createHostedPage.bind(this)); | ||
17 | this.actions.payment.upgradeAccount.listen(this._upgradeAccount.bind(this)); | ||
18 | } | ||
19 | |||
20 | @computed get plan() { | ||
21 | if (this.plansRequest.isError) { | ||
22 | return {}; | ||
23 | } | ||
24 | return this.plansRequest.execute().result || {}; | ||
25 | } | ||
26 | |||
27 | @action _createHostedPage({ planId }) { | ||
28 | const request = this.createHostedPageRequest.execute(planId); | ||
29 | |||
30 | return request; | ||
31 | } | ||
32 | |||
33 | @action _upgradeAccount({ planId, onCloseWindow = () => null }) { | ||
34 | let hostedPageURL = this.stores.features.features.subscribeURL; | ||
35 | |||
36 | const parsedUrl = new URL(hostedPageURL); | ||
37 | const params = new URLSearchParams(parsedUrl.search.slice(1)); | ||
38 | |||
39 | params.set('plan', planId); | ||
40 | |||
41 | hostedPageURL = this.stores.user.getAuthURL(`${parsedUrl.origin}${parsedUrl.pathname}?${params.toString()}`); | ||
42 | |||
43 | const win = new BrowserWindow({ | ||
44 | parent: getCurrentWindow(), | ||
45 | modal: true, | ||
46 | title: '🔒 Upgrade Your Franz Account', | ||
47 | width: 800, | ||
48 | height: window.innerHeight - 100, | ||
49 | maxWidth: 800, | ||
50 | minWidth: 600, | ||
51 | autoHideMenuBar: true, | ||
52 | webPreferences: { | ||
53 | nodeIntegration: true, | ||
54 | webviewTag: true, | ||
55 | enableRemoteModule: true, | ||
56 | contextIsolation: false, | ||
57 | }, | ||
58 | }); | ||
59 | win.loadURL(`file://${__dirname}/../index.html#/payment/${encodeURIComponent(hostedPageURL)}`); | ||
60 | |||
61 | win.on('closed', () => { | ||
62 | this.stores.user.getUserInfoRequest.invalidate({ immediately: true }); | ||
63 | this.stores.features.featuresRequest.invalidate({ immediately: true }); | ||
64 | |||
65 | onCloseWindow(); | ||
66 | }); | ||
67 | } | ||
68 | } | ||
diff --git a/src/stores/RecipePreviewsStore.js b/src/stores/RecipePreviewsStore.js index 989e1124a..f4e39306c 100644 --- a/src/stores/RecipePreviewsStore.js +++ b/src/stores/RecipePreviewsStore.js | |||
@@ -7,8 +7,6 @@ import Request from './lib/Request'; | |||
7 | export default class RecipePreviewsStore extends Store { | 7 | export default class RecipePreviewsStore extends Store { |
8 | @observable allRecipePreviewsRequest = new CachedRequest(this.api.recipePreviews, 'all'); | 8 | @observable allRecipePreviewsRequest = new CachedRequest(this.api.recipePreviews, 'all'); |
9 | 9 | ||
10 | @observable featuredRecipePreviewsRequest = new CachedRequest(this.api.recipePreviews, 'featured'); | ||
11 | |||
12 | @observable searchRecipePreviewsRequest = new Request(this.api.recipePreviews, 'search'); | 10 | @observable searchRecipePreviewsRequest = new Request(this.api.recipePreviews, 'search'); |
13 | 11 | ||
14 | constructor(...args) { | 12 | constructor(...args) { |
@@ -22,16 +20,12 @@ export default class RecipePreviewsStore extends Store { | |||
22 | return this.allRecipePreviewsRequest.execute().result || []; | 20 | return this.allRecipePreviewsRequest.execute().result || []; |
23 | } | 21 | } |
24 | 22 | ||
25 | @computed get featured() { | ||
26 | return this.featuredRecipePreviewsRequest.execute().result || []; | ||
27 | } | ||
28 | |||
29 | @computed get searchResults() { | 23 | @computed get searchResults() { |
30 | return this.searchRecipePreviewsRequest.result || []; | 24 | return this.searchRecipePreviewsRequest.result || []; |
31 | } | 25 | } |
32 | 26 | ||
33 | @computed get dev() { | 27 | @computed get dev() { |
34 | return this.stores.recipes.all.filter(r => r.local); | 28 | return this.stores.recipes.all.filter((r) => r.local); |
35 | } | 29 | } |
36 | 30 | ||
37 | // Actions | 31 | // Actions |
diff --git a/src/stores/RecipesStore.js b/src/stores/RecipesStore.js index b49fb72d9..d2acebb75 100644 --- a/src/stores/RecipesStore.js +++ b/src/stores/RecipesStore.js | |||
@@ -1,13 +1,12 @@ | |||
1 | import { action, computed, observable } from 'mobx'; | 1 | import { action, computed, observable } from 'mobx'; |
2 | import fs from 'fs-extra'; | 2 | import { readJSONSync } from 'fs-extra'; |
3 | import path from 'path'; | ||
4 | import semver from 'semver'; | 3 | import semver from 'semver'; |
5 | 4 | ||
6 | import Store from './lib/Store'; | 5 | import Store from './lib/Store'; |
7 | import CachedRequest from './lib/CachedRequest'; | 6 | import CachedRequest from './lib/CachedRequest'; |
8 | import Request from './lib/Request'; | 7 | import Request from './lib/Request'; |
9 | import { matchRoute } from '../helpers/routing-helpers'; | 8 | import { matchRoute } from '../helpers/routing-helpers'; |
10 | import { RECIPES_PATH } from '../environment'; | 9 | import { asarRecipesPath } from '../environment'; |
11 | 10 | ||
12 | const debug = require('debug')('Ferdi:RecipeStore'); | 11 | const debug = require('debug')('Ferdi:RecipeStore'); |
13 | 12 | ||
@@ -54,11 +53,11 @@ export default class RecipesStore extends Store { | |||
54 | } | 53 | } |
55 | 54 | ||
56 | @computed get recipeIdForServices() { | 55 | @computed get recipeIdForServices() { |
57 | return this.stores.services.all.map(s => s.recipe.id); | 56 | return this.stores.services.all.map((s) => s.recipe.id); |
58 | } | 57 | } |
59 | 58 | ||
60 | one(id) { | 59 | one(id) { |
61 | return this.all.find(recipe => recipe.id === id); | 60 | return this.all.find((recipe) => recipe.id === id); |
62 | } | 61 | } |
63 | 62 | ||
64 | isInstalled(id) { | 63 | isInstalled(id) { |
@@ -78,7 +77,7 @@ export default class RecipesStore extends Store { | |||
78 | const recipes = {}; | 77 | const recipes = {}; |
79 | 78 | ||
80 | // Hackfix, reference this.all to fetch services | 79 | // Hackfix, reference this.all to fetch services |
81 | debug(`Check Recipe updates for ${this.all.map(recipe => recipe.id)}`); | 80 | debug(`Check Recipe updates for ${this.all.map((recipe) => recipe.id)}`); |
82 | 81 | ||
83 | recipeIds.forEach((r) => { | 82 | recipeIds.forEach((r) => { |
84 | const recipe = this.one(r); | 83 | const recipe = this.one(r); |
@@ -90,15 +89,15 @@ export default class RecipesStore extends Store { | |||
90 | const remoteUpdates = await this.getRecipeUpdatesRequest.execute(recipes)._promise; | 89 | const remoteUpdates = await this.getRecipeUpdatesRequest.execute(recipes)._promise; |
91 | 90 | ||
92 | // Check for local updates | 91 | // Check for local updates |
93 | const allJsonFile = path.join(RECIPES_PATH, 'all.json'); | 92 | const allJsonFile = asarRecipesPath('all.json'); |
94 | const allJson = await fs.readJSON(allJsonFile); | 93 | const allJson = readJSONSync(allJsonFile); |
95 | const localUpdates = []; | 94 | const localUpdates = []; |
96 | 95 | ||
97 | Object.keys(recipes).forEach((recipe) => { | 96 | Object.keys(recipes).forEach((recipe) => { |
98 | const version = recipes[recipe]; | 97 | const version = recipes[recipe]; |
99 | 98 | ||
100 | // Find recipe in local recipe repository | 99 | // Find recipe in local recipe repository |
101 | const localRecipe = allJson.find(r => r.id === recipe); | 100 | const localRecipe = allJson.find((r) => r.id === recipe); |
102 | 101 | ||
103 | if (localRecipe && semver.lt(version, localRecipe.version)) { | 102 | if (localRecipe && semver.lt(version, localRecipe.version)) { |
104 | localUpdates.push(recipe); | 103 | localUpdates.push(recipe); |
diff --git a/src/stores/ServicesStore.js b/src/stores/ServicesStore.js index 9b69cb7c6..4ccb995ae 100644 --- a/src/stores/ServicesStore.js +++ b/src/stores/ServicesStore.js | |||
@@ -1,25 +1,21 @@ | |||
1 | import { shell } from 'electron'; | 1 | import { shell } from 'electron'; |
2 | import { | 2 | import { action, reaction, computed, observable } from 'mobx'; |
3 | action, | ||
4 | reaction, | ||
5 | computed, | ||
6 | observable, | ||
7 | } from 'mobx'; | ||
8 | import { debounce, remove } from 'lodash'; | 3 | import { debounce, remove } from 'lodash'; |
9 | import ms from 'ms'; | 4 | import ms from 'ms'; |
10 | import { app } from '@electron/remote'; | 5 | import { app } from '@electron/remote'; |
11 | import fs from 'fs-extra'; | 6 | import { ensureFileSync, pathExistsSync, writeFileSync } from 'fs-extra'; |
12 | import path from 'path'; | 7 | import { join } from 'path'; |
13 | 8 | ||
14 | import Store from './lib/Store'; | 9 | import Store from './lib/Store'; |
15 | import Request from './lib/Request'; | 10 | import Request from './lib/Request'; |
16 | import CachedRequest from './lib/CachedRequest'; | 11 | import CachedRequest from './lib/CachedRequest'; |
17 | import { matchRoute } from '../helpers/routing-helpers'; | 12 | import { matchRoute } from '../helpers/routing-helpers'; |
18 | import { isInTimeframe } from '../helpers/schedule-helpers'; | 13 | import { isInTimeframe } from '../helpers/schedule-helpers'; |
19 | import { getRecipeDirectory, getDevRecipeDirectory } from '../helpers/recipe-helpers'; | 14 | import { |
15 | getRecipeDirectory, | ||
16 | getDevRecipeDirectory, | ||
17 | } from '../helpers/recipe-helpers'; | ||
20 | import { workspaceStore } from '../features/workspaces'; | 18 | import { workspaceStore } from '../features/workspaces'; |
21 | import { serviceLimitStore } from '../features/serviceLimit'; | ||
22 | import { RESTRICTION_TYPES } from '../models/Service'; | ||
23 | import { KEEP_WS_LOADED_USID } from '../config'; | 19 | import { KEEP_WS_LOADED_USID } from '../config'; |
24 | import { SPELLCHECKER_LOCALES } from '../i18n/languages'; | 20 | import { SPELLCHECKER_LOCALES } from '../i18n/languages'; |
25 | 21 | ||
@@ -32,7 +28,10 @@ export default class ServicesStore extends Store { | |||
32 | 28 | ||
33 | @observable updateServiceRequest = new Request(this.api.services, 'update'); | 29 | @observable updateServiceRequest = new Request(this.api.services, 'update'); |
34 | 30 | ||
35 | @observable reorderServicesRequest = new Request(this.api.services, 'reorder'); | 31 | @observable reorderServicesRequest = new Request( |
32 | this.api.services, | ||
33 | 'reorder', | ||
34 | ); | ||
36 | 35 | ||
37 | @observable deleteServiceRequest = new Request(this.api.services, 'delete'); | 36 | @observable deleteServiceRequest = new Request(this.api.services, 'delete'); |
38 | 37 | ||
@@ -53,22 +52,36 @@ export default class ServicesStore extends Store { | |||
53 | this.actions.service.blurActive.listen(this._blurActive.bind(this)); | 52 | this.actions.service.blurActive.listen(this._blurActive.bind(this)); |
54 | this.actions.service.setActiveNext.listen(this._setActiveNext.bind(this)); | 53 | this.actions.service.setActiveNext.listen(this._setActiveNext.bind(this)); |
55 | this.actions.service.setActivePrev.listen(this._setActivePrev.bind(this)); | 54 | this.actions.service.setActivePrev.listen(this._setActivePrev.bind(this)); |
56 | this.actions.service.showAddServiceInterface.listen(this._showAddServiceInterface.bind(this)); | 55 | this.actions.service.showAddServiceInterface.listen( |
56 | this._showAddServiceInterface.bind(this), | ||
57 | ); | ||
57 | this.actions.service.createService.listen(this._createService.bind(this)); | 58 | this.actions.service.createService.listen(this._createService.bind(this)); |
58 | this.actions.service.createFromLegacyService.listen(this._createFromLegacyService.bind(this)); | 59 | this.actions.service.createFromLegacyService.listen( |
60 | this._createFromLegacyService.bind(this), | ||
61 | ); | ||
59 | this.actions.service.updateService.listen(this._updateService.bind(this)); | 62 | this.actions.service.updateService.listen(this._updateService.bind(this)); |
60 | this.actions.service.deleteService.listen(this._deleteService.bind(this)); | 63 | this.actions.service.deleteService.listen(this._deleteService.bind(this)); |
61 | this.actions.service.openRecipeFile.listen(this._openRecipeFile.bind(this)); | 64 | this.actions.service.openRecipeFile.listen(this._openRecipeFile.bind(this)); |
62 | this.actions.service.clearCache.listen(this._clearCache.bind(this)); | 65 | this.actions.service.clearCache.listen(this._clearCache.bind(this)); |
63 | this.actions.service.setWebviewReference.listen(this._setWebviewReference.bind(this)); | 66 | this.actions.service.setWebviewReference.listen( |
67 | this._setWebviewReference.bind(this), | ||
68 | ); | ||
64 | this.actions.service.detachService.listen(this._detachService.bind(this)); | 69 | this.actions.service.detachService.listen(this._detachService.bind(this)); |
65 | this.actions.service.focusService.listen(this._focusService.bind(this)); | 70 | this.actions.service.focusService.listen(this._focusService.bind(this)); |
66 | this.actions.service.focusActiveService.listen(this._focusActiveService.bind(this)); | 71 | this.actions.service.focusActiveService.listen( |
72 | this._focusActiveService.bind(this), | ||
73 | ); | ||
67 | this.actions.service.toggleService.listen(this._toggleService.bind(this)); | 74 | this.actions.service.toggleService.listen(this._toggleService.bind(this)); |
68 | this.actions.service.handleIPCMessage.listen(this._handleIPCMessage.bind(this)); | 75 | this.actions.service.handleIPCMessage.listen( |
76 | this._handleIPCMessage.bind(this), | ||
77 | ); | ||
69 | this.actions.service.sendIPCMessage.listen(this._sendIPCMessage.bind(this)); | 78 | this.actions.service.sendIPCMessage.listen(this._sendIPCMessage.bind(this)); |
70 | this.actions.service.sendIPCMessageToAllServices.listen(this._sendIPCMessageToAllServices.bind(this)); | 79 | this.actions.service.sendIPCMessageToAllServices.listen( |
71 | this.actions.service.setUnreadMessageCount.listen(this._setUnreadMessageCount.bind(this)); | 80 | this._sendIPCMessageToAllServices.bind(this), |
81 | ); | ||
82 | this.actions.service.setUnreadMessageCount.listen( | ||
83 | this._setUnreadMessageCount.bind(this), | ||
84 | ); | ||
72 | this.actions.service.openWindow.listen(this._openWindow.bind(this)); | 85 | this.actions.service.openWindow.listen(this._openWindow.bind(this)); |
73 | this.actions.service.filter.listen(this._filter.bind(this)); | 86 | this.actions.service.filter.listen(this._filter.bind(this)); |
74 | this.actions.service.resetFilter.listen(this._resetFilter.bind(this)); | 87 | this.actions.service.resetFilter.listen(this._resetFilter.bind(this)); |
@@ -76,16 +89,27 @@ export default class ServicesStore extends Store { | |||
76 | this.actions.service.reload.listen(this._reload.bind(this)); | 89 | this.actions.service.reload.listen(this._reload.bind(this)); |
77 | this.actions.service.reloadActive.listen(this._reloadActive.bind(this)); | 90 | this.actions.service.reloadActive.listen(this._reloadActive.bind(this)); |
78 | this.actions.service.reloadAll.listen(this._reloadAll.bind(this)); | 91 | this.actions.service.reloadAll.listen(this._reloadAll.bind(this)); |
79 | this.actions.service.reloadUpdatedServices.listen(this._reloadUpdatedServices.bind(this)); | 92 | this.actions.service.reloadUpdatedServices.listen( |
93 | this._reloadUpdatedServices.bind(this), | ||
94 | ); | ||
80 | this.actions.service.reorder.listen(this._reorder.bind(this)); | 95 | this.actions.service.reorder.listen(this._reorder.bind(this)); |
81 | this.actions.service.toggleNotifications.listen(this._toggleNotifications.bind(this)); | 96 | this.actions.service.toggleNotifications.listen( |
97 | this._toggleNotifications.bind(this), | ||
98 | ); | ||
82 | this.actions.service.toggleAudio.listen(this._toggleAudio.bind(this)); | 99 | this.actions.service.toggleAudio.listen(this._toggleAudio.bind(this)); |
100 | this.actions.service.toggleDarkMode.listen(this._toggleDarkMode.bind(this)); | ||
83 | this.actions.service.openDevTools.listen(this._openDevTools.bind(this)); | 101 | this.actions.service.openDevTools.listen(this._openDevTools.bind(this)); |
84 | this.actions.service.openDevToolsForActiveService.listen(this._openDevToolsForActiveService.bind(this)); | 102 | this.actions.service.openDevToolsForActiveService.listen( |
103 | this._openDevToolsForActiveService.bind(this), | ||
104 | ); | ||
85 | this.actions.service.hibernate.listen(this._hibernate.bind(this)); | 105 | this.actions.service.hibernate.listen(this._hibernate.bind(this)); |
86 | this.actions.service.awake.listen(this._awake.bind(this)); | 106 | this.actions.service.awake.listen(this._awake.bind(this)); |
87 | this.actions.service.resetLastPollTimer.listen(this._resetLastPollTimer.bind(this)); | 107 | this.actions.service.resetLastPollTimer.listen( |
88 | this.actions.service.shareSettingsWithServiceProcess.listen(this._shareSettingsWithServiceProcess.bind(this)); | 108 | this._resetLastPollTimer.bind(this), |
109 | ); | ||
110 | this.actions.service.shareSettingsWithServiceProcess.listen( | ||
111 | this._shareSettingsWithServiceProcess.bind(this), | ||
112 | ); | ||
89 | 113 | ||
90 | this.registerReactions([ | 114 | this.registerReactions([ |
91 | this._focusServiceReaction.bind(this), | 115 | this._focusServiceReaction.bind(this), |
@@ -94,7 +118,6 @@ export default class ServicesStore extends Store { | |||
94 | this._saveActiveService.bind(this), | 118 | this._saveActiveService.bind(this), |
95 | this._logoutReaction.bind(this), | 119 | this._logoutReaction.bind(this), |
96 | this._handleMuteSettings.bind(this), | 120 | this._handleMuteSettings.bind(this), |
97 | this._restrictServiceAccess.bind(this), | ||
98 | this._checkForActiveService.bind(this), | 121 | this._checkForActiveService.bind(this), |
99 | ]); | 122 | ]); |
100 | 123 | ||
@@ -167,18 +190,42 @@ export default class ServicesStore extends Store { | |||
167 | * Run various maintenance tasks on services | 190 | * Run various maintenance tasks on services |
168 | */ | 191 | */ |
169 | _serviceMaintenance() { | 192 | _serviceMaintenance() { |
170 | this.all.forEach((service) => { | 193 | this.all.forEach(service => { |
171 | // Defines which services should be hibernated. | 194 | // Defines which services should be hibernated or woken up |
172 | if (!service.isActive && (Date.now() - service.lastUsed > ms('5m'))) { | 195 | if (!service.isActive) { |
173 | // If service is stale for 5 min, hibernate it. | 196 | if ( |
174 | this._hibernate({ serviceId: service.id }); | 197 | !service.lastHibernated && |
198 | Date.now() - service.lastUsed > | ||
199 | ms(`${this.stores.settings.all.app.hibernationStrategy}s`) | ||
200 | ) { | ||
201 | // If service is stale, hibernate it. | ||
202 | this._hibernate({ serviceId: service.id }); | ||
203 | } | ||
204 | |||
205 | if ( | ||
206 | service.lastHibernated && | ||
207 | Number(this.stores.settings.all.app.wakeUpStrategy) > 0 | ||
208 | ) { | ||
209 | // If service is in hibernation and the wakeup time has elapsed, wake it. | ||
210 | if ( | ||
211 | Date.now() - service.lastHibernated > | ||
212 | ms(`${this.stores.settings.all.app.wakeUpStrategy}s`) | ||
213 | ) { | ||
214 | this._awake({ serviceId: service.id }); | ||
215 | } | ||
216 | } | ||
175 | } | 217 | } |
176 | 218 | ||
177 | if (service.lastPoll && (service.lastPoll - service.lastPollAnswer > ms('1m'))) { | 219 | if ( |
220 | service.lastPoll && | ||
221 | service.lastPoll - service.lastPollAnswer > ms('1m') | ||
222 | ) { | ||
178 | // If service did not reply for more than 1m try to reload. | 223 | // If service did not reply for more than 1m try to reload. |
179 | if (!service.isActive) { | 224 | if (!service.isActive) { |
180 | if (this.stores.app.isOnline && service.lostRecipeReloadAttempt < 3) { | 225 | if (this.stores.app.isOnline && service.lostRecipeReloadAttempt < 3) { |
181 | debug(`Reloading service: ${service.name} (${service.id}). Attempt: ${service.lostRecipeReloadAttempt}`); | 226 | debug( |
227 | `Reloading service: ${service.name} (${service.id}). Attempt: ${service.lostRecipeReloadAttempt}`, | ||
228 | ); | ||
182 | // service.webview.reload(); | 229 | // service.webview.reload(); |
183 | service.lostRecipeReloadAttempt += 1; | 230 | service.lostRecipeReloadAttempt += 1; |
184 | 231 | ||
@@ -200,10 +247,16 @@ export default class ServicesStore extends Store { | |||
200 | if (this.stores.user.isLoggedIn) { | 247 | if (this.stores.user.isLoggedIn) { |
201 | const services = this.allServicesRequest.execute().result; | 248 | const services = this.allServicesRequest.execute().result; |
202 | if (services) { | 249 | if (services) { |
203 | return observable(services.slice().slice().sort((a, b) => a.order - b.order).map((s, index) => { | 250 | return observable( |
204 | s.index = index; | 251 | services |
205 | return s; | 252 | .slice() |
206 | })); | 253 | .slice() |
254 | .sort((a, b) => a.order - b.order) | ||
255 | .map((s, index) => { | ||
256 | s.index = index; | ||
257 | return s; | ||
258 | }), | ||
259 | ); | ||
207 | } | 260 | } |
208 | } | 261 | } |
209 | return []; | 262 | return []; |
@@ -214,7 +267,9 @@ export default class ServicesStore extends Store { | |||
214 | } | 267 | } |
215 | 268 | ||
216 | @computed get allDisplayed() { | 269 | @computed get allDisplayed() { |
217 | const services = this.stores.settings.all.app.showDisabledServices ? this.all : this.enabled; | 270 | const services = this.stores.settings.all.app.showDisabledServices |
271 | ? this.all | ||
272 | : this.enabled; | ||
218 | return workspaceStore.filterServicesByActiveWorkspace(services); | 273 | return workspaceStore.filterServicesByActiveWorkspace(services); |
219 | } | 274 | } |
220 | 275 | ||
@@ -223,7 +278,9 @@ export default class ServicesStore extends Store { | |||
223 | const { showDisabledServices } = this.stores.settings.all.app; | 278 | const { showDisabledServices } = this.stores.settings.all.app; |
224 | const { keepAllWorkspacesLoaded } = this.stores.workspaces.settings; | 279 | const { keepAllWorkspacesLoaded } = this.stores.workspaces.settings; |
225 | const services = this.allServicesRequest.execute().result || []; | 280 | const services = this.allServicesRequest.execute().result || []; |
226 | const filteredServices = showDisabledServices ? services : services.filter(service => service.isEnabled); | 281 | const filteredServices = showDisabledServices |
282 | ? services | ||
283 | : services.filter(service => service.isEnabled); | ||
227 | 284 | ||
228 | let displayedServices; | 285 | let displayedServices; |
229 | if (keepAllWorkspacesLoaded) { | 286 | if (keepAllWorkspacesLoaded) { |
@@ -231,32 +288,38 @@ export default class ServicesStore extends Store { | |||
231 | displayedServices = filteredServices; | 288 | displayedServices = filteredServices; |
232 | } else { | 289 | } else { |
233 | // Keep all services in current workspace loaded | 290 | // Keep all services in current workspace loaded |
234 | displayedServices = workspaceStore.filterServicesByActiveWorkspace(filteredServices); | 291 | displayedServices = |
292 | workspaceStore.filterServicesByActiveWorkspace(filteredServices); | ||
235 | 293 | ||
236 | // Keep all services active in workspaces that should be kept loaded | 294 | // Keep all services active in workspaces that should be kept loaded |
237 | for (const workspace of this.stores.workspaces.workspaces) { | 295 | for (const workspace of this.stores.workspaces.workspaces) { |
238 | // Check if workspace needs to be kept loaded | 296 | // Check if workspace needs to be kept loaded |
239 | if (workspace.services.includes(KEEP_WS_LOADED_USID)) { | 297 | if (workspace.services.includes(KEEP_WS_LOADED_USID)) { |
240 | // Get services for workspace | 298 | // Get services for workspace |
241 | const serviceIDs = workspace.services.filter(i => i !== KEEP_WS_LOADED_USID); | 299 | const serviceIDs = workspace.services.filter( |
242 | const wsServices = filteredServices.filter(service => serviceIDs.includes(service.id)); | 300 | i => i !== KEEP_WS_LOADED_USID, |
243 | 301 | ); | |
244 | displayedServices = [ | 302 | const wsServices = filteredServices.filter(service => |
245 | ...displayedServices, | 303 | serviceIDs.includes(service.id), |
246 | ...wsServices, | 304 | ); |
247 | ]; | 305 | |
306 | displayedServices = [...displayedServices, ...wsServices]; | ||
248 | } | 307 | } |
249 | } | 308 | } |
250 | 309 | ||
251 | // Make sure every service is in the list only once | 310 | // Make sure every service is in the list only once |
252 | displayedServices = displayedServices.filter((v, i, a) => a.indexOf(v) === i); | 311 | displayedServices = displayedServices.filter( |
312 | (v, i, a) => a.indexOf(v) === i, | ||
313 | ); | ||
253 | } | 314 | } |
254 | 315 | ||
255 | return displayedServices; | 316 | return displayedServices; |
256 | } | 317 | } |
257 | 318 | ||
258 | @computed get filtered() { | 319 | @computed get filtered() { |
259 | return this.all.filter(service => service.name.toLowerCase().includes(this.filterNeedle.toLowerCase())); | 320 | return this.all.filter(service => |
321 | service.name.toLowerCase().includes(this.filterNeedle.toLowerCase()), | ||
322 | ); | ||
260 | } | 323 | } |
261 | 324 | ||
262 | @computed get active() { | 325 | @computed get active() { |
@@ -264,7 +327,10 @@ export default class ServicesStore extends Store { | |||
264 | } | 327 | } |
265 | 328 | ||
266 | @computed get activeSettings() { | 329 | @computed get activeSettings() { |
267 | const match = matchRoute('/settings/services/edit/:id', this.stores.router.location.pathname); | 330 | const match = matchRoute( |
331 | '/settings/services/edit/:id', | ||
332 | this.stores.router.location.pathname, | ||
333 | ); | ||
268 | if (match) { | 334 | if (match) { |
269 | const activeService = this.one(match.id); | 335 | const activeService = this.one(match.id); |
270 | if (activeService) { | 336 | if (activeService) { |
@@ -278,7 +344,11 @@ export default class ServicesStore extends Store { | |||
278 | } | 344 | } |
279 | 345 | ||
280 | @computed get isTodosServiceAdded() { | 346 | @computed get isTodosServiceAdded() { |
281 | return this.allDisplayed.find(service => service.isTodosService && service.isEnabled) || false; | 347 | return ( |
348 | this.allDisplayed.find( | ||
349 | service => service.isTodosService && service.isEnabled, | ||
350 | ) || false | ||
351 | ); | ||
282 | } | 352 | } |
283 | 353 | ||
284 | @computed get isTodosServiceActive() { | 354 | @computed get isTodosServiceActive() { |
@@ -295,10 +365,11 @@ export default class ServicesStore extends Store { | |||
295 | 365 | ||
296 | // Actions | 366 | // Actions |
297 | async _createService({ | 367 | async _createService({ |
298 | recipeId, serviceData, redirect = true, skipCleanup = false, | 368 | recipeId, |
369 | serviceData, | ||
370 | redirect = true, | ||
371 | skipCleanup = false, | ||
299 | }) { | 372 | }) { |
300 | if (serviceLimitStore.userHasReachedServiceLimit) return; | ||
301 | |||
302 | if (!this.stores.recipes.isInstalled(recipeId)) { | 373 | if (!this.stores.recipes.isInstalled(recipeId)) { |
303 | debug(`Recipe "${recipeId}" is not installed, installing recipe`); | 374 | debug(`Recipe "${recipeId}" is not installed, installing recipe`); |
304 | await this.stores.recipes._install({ recipeId }); | 375 | await this.stores.recipes._install({ recipeId }); |
@@ -307,17 +378,21 @@ export default class ServicesStore extends Store { | |||
307 | 378 | ||
308 | // set default values for serviceData | 379 | // set default values for serviceData |
309 | // eslint-disable-next-line prefer-object-spread | 380 | // eslint-disable-next-line prefer-object-spread |
310 | Object.assign({ | 381 | Object.assign( |
311 | isEnabled: true, | 382 | { |
312 | isHibernationEnabled: false, | 383 | isEnabled: true, |
313 | isNotificationEnabled: true, | 384 | isHibernationEnabled: false, |
314 | isBadgeEnabled: true, | 385 | isNotificationEnabled: true, |
315 | isMuted: false, | 386 | isBadgeEnabled: true, |
316 | customIcon: false, | 387 | isMuted: false, |
317 | isDarkModeEnabled: false, | 388 | customIcon: false, |
318 | spellcheckerLanguage: SPELLCHECKER_LOCALES[this.stores.settings.app.spellcheckerLanguage], | 389 | isDarkModeEnabled: false, |
319 | userAgentPref: '', | 390 | spellcheckerLanguage: |
320 | }, serviceData); | 391 | SPELLCHECKER_LOCALES[this.stores.settings.app.spellcheckerLanguage], |
392 | userAgentPref: '', | ||
393 | }, | ||
394 | serviceData, | ||
395 | ); | ||
321 | 396 | ||
322 | let data = serviceData; | 397 | let data = serviceData; |
323 | 398 | ||
@@ -325,9 +400,10 @@ export default class ServicesStore extends Store { | |||
325 | data = this._cleanUpTeamIdAndCustomUrl(recipeId, serviceData); | 400 | data = this._cleanUpTeamIdAndCustomUrl(recipeId, serviceData); |
326 | } | 401 | } |
327 | 402 | ||
328 | const response = await this.createServiceRequest.execute(recipeId, data)._promise; | 403 | const response = await this.createServiceRequest.execute(recipeId, data) |
404 | ._promise; | ||
329 | 405 | ||
330 | this.allServicesRequest.patch((result) => { | 406 | this.allServicesRequest.patch(result => { |
331 | if (!result) return; | 407 | if (!result) return; |
332 | result.push(response.data); | 408 | result.push(response.data); |
333 | }); | 409 | }); |
@@ -371,7 +447,10 @@ export default class ServicesStore extends Store { | |||
371 | 447 | ||
372 | @action async _updateService({ serviceId, serviceData, redirect = true }) { | 448 | @action async _updateService({ serviceId, serviceData, redirect = true }) { |
373 | const service = this.one(serviceId); | 449 | const service = this.one(serviceId); |
374 | const data = this._cleanUpTeamIdAndCustomUrl(service.recipe.id, serviceData); | 450 | const data = this._cleanUpTeamIdAndCustomUrl( |
451 | service.recipe.id, | ||
452 | serviceData, | ||
453 | ); | ||
375 | const request = this.updateServiceRequest.execute(serviceId, data); | 454 | const request = this.updateServiceRequest.execute(serviceId, data); |
376 | 455 | ||
377 | const newData = serviceData; | 456 | const newData = serviceData; |
@@ -382,7 +461,7 @@ export default class ServicesStore extends Store { | |||
382 | newData.hasCustomUploadedIcon = true; | 461 | newData.hasCustomUploadedIcon = true; |
383 | } | 462 | } |
384 | 463 | ||
385 | this.allServicesRequest.patch((result) => { | 464 | this.allServicesRequest.patch(result => { |
386 | if (!result) return; | 465 | if (!result) return; |
387 | 466 | ||
388 | // patch custom icon deletion | 467 | // patch custom icon deletion |
@@ -396,7 +475,10 @@ export default class ServicesStore extends Store { | |||
396 | newData.iconUrl = data.customIconUrl; | 475 | newData.iconUrl = data.customIconUrl; |
397 | } | 476 | } |
398 | 477 | ||
399 | Object.assign(result.find(c => c.id === serviceId), newData); | 478 | Object.assign( |
479 | result.find(c => c.id === serviceId), | ||
480 | newData, | ||
481 | ); | ||
400 | }); | 482 | }); |
401 | 483 | ||
402 | await request._promise; | 484 | await request._promise; |
@@ -429,7 +511,7 @@ export default class ServicesStore extends Store { | |||
429 | this.stores.router.push(redirect); | 511 | this.stores.router.push(redirect); |
430 | } | 512 | } |
431 | 513 | ||
432 | this.allServicesRequest.patch((result) => { | 514 | this.allServicesRequest.patch(result => { |
433 | remove(result, c => c.id === serviceId); | 515 | remove(result, c => c.id === serviceId); |
434 | }); | 516 | }); |
435 | 517 | ||
@@ -443,9 +525,9 @@ export default class ServicesStore extends Store { | |||
443 | const devDirectory = getDevRecipeDirectory(recipe); | 525 | const devDirectory = getDevRecipeDirectory(recipe); |
444 | let directory; | 526 | let directory; |
445 | 527 | ||
446 | if (await fs.pathExists(normalDirectory)) { | 528 | if (pathExistsSync(normalDirectory)) { |
447 | directory = normalDirectory; | 529 | directory = normalDirectory; |
448 | } else if (await fs.pathExists(devDirectory)) { | 530 | } else if (pathExistsSync(devDirectory)) { |
449 | directory = devDirectory; | 531 | directory = devDirectory; |
450 | } else { | 532 | } else { |
451 | // Recipe cannot be found on drive | 533 | // Recipe cannot be found on drive |
@@ -453,17 +535,19 @@ export default class ServicesStore extends Store { | |||
453 | } | 535 | } |
454 | 536 | ||
455 | // Create and open file | 537 | // Create and open file |
456 | const filePath = path.join(directory, file); | 538 | const filePath = join(directory, file); |
457 | if (file === 'user.js') { | 539 | if (file === 'user.js') { |
458 | if (!await fs.exists(filePath)) { | 540 | if (!pathExistsSync(filePath)) { |
459 | await fs.writeFile(filePath, `module.exports = (config, Ferdi) => { | 541 | writeFileSync( |
542 | filePath, | ||
543 | `module.exports = (config, Ferdi) => { | ||
460 | // Write your scripts here | 544 | // Write your scripts here |
461 | console.log("Hello, World!", config); | 545 | console.log("Hello, World!", config); |
462 | } | 546 | }; |
463 | `); | 547 | `); |
464 | } | 548 | } |
465 | } else { | 549 | } else { |
466 | await fs.ensureFile(filePath); | 550 | ensureFileSync(filePath); |
467 | } | 551 | } |
468 | shell.showItemInFolder(filePath); | 552 | shell.showItemInFolder(filePath); |
469 | } | 553 | } |
@@ -474,23 +558,27 @@ export default class ServicesStore extends Store { | |||
474 | await request._promise; | 558 | await request._promise; |
475 | } | 559 | } |
476 | 560 | ||
477 | @action _setActive({ serviceId, keepActiveRoute }) { | 561 | @action _setActive({ serviceId, keepActiveRoute = null }) { |
478 | if (!keepActiveRoute) this.stores.router.push('/'); | 562 | if (!keepActiveRoute) this.stores.router.push('/'); |
479 | const service = this.one(serviceId); | 563 | const service = this.one(serviceId); |
480 | 564 | ||
481 | this.all.forEach((s, index) => { | 565 | this.all.forEach(s => { |
482 | this.all[index].isActive = false; | 566 | s.isActive = false; |
483 | }); | 567 | }); |
484 | service.isActive = true; | 568 | service.isActive = true; |
485 | this._awake({ serviceId: service.id }); | 569 | this._awake({ serviceId: service.id }); |
486 | service.lastUsed = Date.now(); | ||
487 | 570 | ||
488 | if (this.isTodosServiceActive && !this.stores.todos.settings.isFeatureEnabledByUser) { | 571 | if ( |
572 | this.isTodosServiceActive && | ||
573 | !this.stores.todos.settings.isFeatureEnabledByUser | ||
574 | ) { | ||
489 | this.actions.todos.toggleTodosFeatureVisibility(); | 575 | this.actions.todos.toggleTodosFeatureVisibility(); |
490 | } | 576 | } |
491 | 577 | ||
492 | // Update list of last used services | 578 | // Update list of last used services |
493 | this.lastUsedServices = this.lastUsedServices.filter(id => id !== serviceId); | 579 | this.lastUsedServices = this.lastUsedServices.filter( |
580 | id => id !== serviceId, | ||
581 | ); | ||
494 | this.lastUsedServices.unshift(serviceId); | 582 | this.lastUsedServices.unshift(serviceId); |
495 | 583 | ||
496 | this._focusActiveService(); | 584 | this._focusActiveService(); |
@@ -502,7 +590,11 @@ export default class ServicesStore extends Store { | |||
502 | } | 590 | } |
503 | 591 | ||
504 | @action _setActiveNext() { | 592 | @action _setActiveNext() { |
505 | const nextIndex = this._wrapIndex(this.allDisplayed.findIndex(service => service.isActive), 1, this.allDisplayed.length); | 593 | const nextIndex = this._wrapIndex( |
594 | this.allDisplayed.findIndex(service => service.isActive), | ||
595 | 1, | ||
596 | this.allDisplayed.length, | ||
597 | ); | ||
506 | 598 | ||
507 | // TODO: simplify this; | 599 | // TODO: simplify this; |
508 | this.all.forEach((s, index) => { | 600 | this.all.forEach((s, index) => { |
@@ -512,7 +604,11 @@ export default class ServicesStore extends Store { | |||
512 | } | 604 | } |
513 | 605 | ||
514 | @action _setActivePrev() { | 606 | @action _setActivePrev() { |
515 | const prevIndex = this._wrapIndex(this.allDisplayed.findIndex(service => service.isActive), -1, this.allDisplayed.length); | 607 | const prevIndex = this._wrapIndex( |
608 | this.allDisplayed.findIndex(service => service.isActive), | ||
609 | -1, | ||
610 | this.allDisplayed.length, | ||
611 | ); | ||
516 | 612 | ||
517 | // TODO: simplify this; | 613 | // TODO: simplify this; |
518 | this.all.forEach((s, index) => { | 614 | this.all.forEach((s, index) => { |
@@ -603,17 +699,21 @@ export default class ServicesStore extends Store { | |||
603 | const { options } = args[0]; | 699 | const { options } = args[0]; |
604 | 700 | ||
605 | // Check if we are in scheduled Do-not-Disturb time | 701 | // Check if we are in scheduled Do-not-Disturb time |
606 | const { | 702 | const { scheduledDNDEnabled, scheduledDNDStart, scheduledDNDEnd } = |
607 | scheduledDNDEnabled, | 703 | this.stores.settings.all.app; |
608 | scheduledDNDStart, | ||
609 | scheduledDNDEnd, | ||
610 | } = this.stores.settings.all.app; | ||
611 | 704 | ||
612 | if (scheduledDNDEnabled && isInTimeframe(scheduledDNDStart, scheduledDNDEnd)) { | 705 | if ( |
706 | scheduledDNDEnabled && | ||
707 | isInTimeframe(scheduledDNDStart, scheduledDNDEnd) | ||
708 | ) { | ||
613 | return; | 709 | return; |
614 | } | 710 | } |
615 | 711 | ||
616 | if (service.recipe.hasNotificationSound || service.isMuted || this.stores.settings.all.app.isAppMuted) { | 712 | if ( |
713 | service.recipe.hasNotificationSound || | ||
714 | service.isMuted || | ||
715 | this.stores.settings.all.app.isAppMuted | ||
716 | ) { | ||
617 | Object.assign(options, { | 717 | Object.assign(options, { |
618 | silent: true, | 718 | silent: true, |
619 | }); | 719 | }); |
@@ -623,7 +723,8 @@ export default class ServicesStore extends Store { | |||
623 | let title = `Notification from ${service.name}`; | 723 | let title = `Notification from ${service.name}`; |
624 | if (!this.stores.settings.all.app.privateNotifications) { | 724 | if (!this.stores.settings.all.app.privateNotifications) { |
625 | options.body = typeof options.body === 'string' ? options.body : ''; | 725 | options.body = typeof options.body === 'string' ? options.body : ''; |
626 | title = typeof args[0].title === 'string' ? args[0].title : service.name; | 726 | title = |
727 | typeof args[0].title === 'string' ? args[0].title : service.name; | ||
627 | } else { | 728 | } else { |
628 | // Remove message data from notification in private mode | 729 | // Remove message data from notification in private mode |
629 | options.body = ''; | 730 | options.body = ''; |
@@ -686,11 +787,13 @@ export default class ServicesStore extends Store { | |||
686 | } | 787 | } |
687 | 788 | ||
688 | @action _sendIPCMessageToAllServices({ channel, args }) { | 789 | @action _sendIPCMessageToAllServices({ channel, args }) { |
689 | this.all.forEach(s => this.actions.service.sendIPCMessage({ | 790 | this.all.forEach(s => |
690 | serviceId: s.id, | 791 | this.actions.service.sendIPCMessage({ |
691 | channel, | 792 | serviceId: s.id, |
692 | args, | 793 | channel, |
693 | })); | 794 | args, |
795 | }), | ||
796 | ); | ||
694 | } | 797 | } |
695 | 798 | ||
696 | @action _openWindow({ event }) { | 799 | @action _openWindow({ event }) { |
@@ -737,9 +840,11 @@ export default class ServicesStore extends Store { | |||
737 | } | 840 | } |
738 | 841 | ||
739 | @action _reloadAll() { | 842 | @action _reloadAll() { |
740 | this.enabled.forEach(s => this._reload({ | 843 | this.enabled.forEach(s => |
741 | serviceId: s.id, | 844 | this._reload({ |
742 | })); | 845 | serviceId: s.id, |
846 | }), | ||
847 | ); | ||
743 | } | 848 | } |
744 | 849 | ||
745 | @action _reloadUpdatedServices() { | 850 | @action _reloadUpdatedServices() { |
@@ -758,10 +863,18 @@ export default class ServicesStore extends Store { | |||
758 | 863 | ||
759 | @action _reorderService({ oldIndex, newIndex }) { | 864 | @action _reorderService({ oldIndex, newIndex }) { |
760 | const { showDisabledServices } = this.stores.settings.all.app; | 865 | const { showDisabledServices } = this.stores.settings.all.app; |
761 | const oldEnabledSortIndex = showDisabledServices ? oldIndex : this.all.indexOf(this.enabled[oldIndex]); | 866 | const oldEnabledSortIndex = showDisabledServices |
762 | const newEnabledSortIndex = showDisabledServices ? newIndex : this.all.indexOf(this.enabled[newIndex]); | 867 | ? oldIndex |
763 | 868 | : this.all.indexOf(this.enabled[oldIndex]); | |
764 | this.all.splice(newEnabledSortIndex, 0, this.all.splice(oldEnabledSortIndex, 1)[0]); | 869 | const newEnabledSortIndex = showDisabledServices |
870 | ? newIndex | ||
871 | : this.all.indexOf(this.enabled[newIndex]); | ||
872 | |||
873 | this.all.splice( | ||
874 | newEnabledSortIndex, | ||
875 | 0, | ||
876 | this.all.splice(oldEnabledSortIndex, 1)[0], | ||
877 | ); | ||
765 | 878 | ||
766 | const services = {}; | 879 | const services = {}; |
767 | this.all.forEach((s, index) => { | 880 | this.all.forEach((s, index) => { |
@@ -769,8 +882,8 @@ export default class ServicesStore extends Store { | |||
769 | }); | 882 | }); |
770 | 883 | ||
771 | this.reorderServicesRequest.execute(services); | 884 | this.reorderServicesRequest.execute(services); |
772 | this.allServicesRequest.patch((data) => { | 885 | this.allServicesRequest.patch(data => { |
773 | data.forEach((s) => { | 886 | data.forEach(s => { |
774 | const service = s; | 887 | const service = s; |
775 | 888 | ||
776 | service.order = services[s.id]; | 889 | service.order = services[s.id]; |
@@ -804,6 +917,18 @@ export default class ServicesStore extends Store { | |||
804 | }); | 917 | }); |
805 | } | 918 | } |
806 | 919 | ||
920 | @action _toggleDarkMode({ serviceId }) { | ||
921 | const service = this.one(serviceId); | ||
922 | |||
923 | this.actions.service.updateService({ | ||
924 | serviceId, | ||
925 | serviceData: { | ||
926 | isDarkModeEnabled: !service.isDarkModeEnabled, | ||
927 | }, | ||
928 | redirect: false, | ||
929 | }); | ||
930 | } | ||
931 | |||
807 | @action _openDevTools({ serviceId }) { | 932 | @action _openDevTools({ serviceId }) { |
808 | const service = this.one(serviceId); | 933 | const service = this.one(serviceId); |
809 | if (service.isTodosService) { | 934 | if (service.isTodosService) { |
@@ -825,26 +950,36 @@ export default class ServicesStore extends Store { | |||
825 | 950 | ||
826 | @action _hibernate({ serviceId }) { | 951 | @action _hibernate({ serviceId }) { |
827 | const service = this.one(serviceId); | 952 | const service = this.one(serviceId); |
828 | if (service.isActive || !service.isHibernationEnabled) { | 953 | if (!service.canHibernate) { |
829 | debug('Skipping service hibernation'); | 954 | return; |
955 | } | ||
956 | if (service.isActive) { | ||
957 | debug(`Skipping service hibernation for ${service.name}`); | ||
830 | return; | 958 | return; |
831 | } | 959 | } |
832 | 960 | ||
833 | debug(`Hibernate ${service.name}`); | 961 | debug(`Hibernate ${service.name}`); |
834 | 962 | ||
835 | service.isHibernating = true; | 963 | service.isHibernationRequested = true; |
964 | service.lastHibernated = Date.now(); | ||
836 | } | 965 | } |
837 | 966 | ||
838 | @action _awake({ serviceId }) { | 967 | @action _awake({ serviceId }) { |
839 | const service = this.one(serviceId); | 968 | const service = this.one(serviceId); |
840 | service.isHibernating = false; | 969 | debug(`Waking up from service hibernation for ${service.name}`); |
841 | service.liveFrom = Date.now(); | 970 | service.isHibernationRequested = false; |
971 | service.lastUsed = Date.now(); | ||
972 | service.lastHibernated = null; | ||
842 | } | 973 | } |
843 | 974 | ||
844 | @action _resetLastPollTimer({ serviceId = null }) { | 975 | @action _resetLastPollTimer({ serviceId = null }) { |
845 | debug(`Reset last poll timer for ${serviceId ? `service: "${serviceId}"` : 'all services'}`); | 976 | debug( |
977 | `Reset last poll timer for ${ | ||
978 | serviceId ? `service: "${serviceId}"` : 'all services' | ||
979 | }`, | ||
980 | ); | ||
846 | 981 | ||
847 | const resetTimer = (service) => { | 982 | const resetTimer = service => { |
848 | service.lastPollAnswer = Date.now(); | 983 | service.lastPollAnswer = Date.now(); |
849 | service.lastPoll = Date.now(); | 984 | service.lastPoll = Date.now(); |
850 | }; | 985 | }; |
@@ -884,9 +1019,13 @@ export default class ServicesStore extends Store { | |||
884 | _mapActiveServiceToServiceModelReaction() { | 1019 | _mapActiveServiceToServiceModelReaction() { |
885 | const { activeService } = this.stores.settings.all.service; | 1020 | const { activeService } = this.stores.settings.all.service; |
886 | if (this.allDisplayed.length) { | 1021 | if (this.allDisplayed.length) { |
887 | this.allDisplayed.map(service => Object.assign(service, { | 1022 | this.allDisplayed.map(service => |
888 | isActive: activeService ? activeService === service.id : this.allDisplayed[0].id === service.id, | 1023 | Object.assign(service, { |
889 | })); | 1024 | isActive: activeService |
1025 | ? activeService === service.id | ||
1026 | : this.allDisplayed[0].id === service.id, | ||
1027 | }), | ||
1028 | ); | ||
890 | } | 1029 | } |
891 | } | 1030 | } |
892 | 1031 | ||
@@ -895,12 +1034,23 @@ export default class ServicesStore extends Store { | |||
895 | const { showMessageBadgesEvenWhenMuted } = this.stores.ui; | 1034 | const { showMessageBadgesEvenWhenMuted } = this.stores.ui; |
896 | 1035 | ||
897 | const unreadDirectMessageCount = this.allDisplayed | 1036 | const unreadDirectMessageCount = this.allDisplayed |
898 | .filter(s => (showMessageBadgeWhenMuted || s.isNotificationEnabled) && showMessageBadgesEvenWhenMuted && s.isBadgeEnabled) | 1037 | .filter( |
1038 | s => | ||
1039 | (showMessageBadgeWhenMuted || s.isNotificationEnabled) && | ||
1040 | showMessageBadgesEvenWhenMuted && | ||
1041 | s.isBadgeEnabled, | ||
1042 | ) | ||
899 | .map(s => s.unreadDirectMessageCount) | 1043 | .map(s => s.unreadDirectMessageCount) |
900 | .reduce((a, b) => a + b, 0); | 1044 | .reduce((a, b) => a + b, 0); |
901 | 1045 | ||
902 | const unreadIndirectMessageCount = this.allDisplayed | 1046 | const unreadIndirectMessageCount = this.allDisplayed |
903 | .filter(s => (showMessageBadgeWhenMuted && showMessageBadgesEvenWhenMuted) && (s.isBadgeEnabled && s.isIndirectMessageBadgeEnabled)) | 1047 | .filter( |
1048 | s => | ||
1049 | showMessageBadgeWhenMuted && | ||
1050 | showMessageBadgesEvenWhenMuted && | ||
1051 | s.isBadgeEnabled && | ||
1052 | s.isIndirectMessageBadgeEnabled, | ||
1053 | ) | ||
904 | .map(s => s.unreadIndirectMessageCount) | 1054 | .map(s => s.unreadIndirectMessageCount) |
905 | .reduce((a, b) => a + b, 0); | 1055 | .reduce((a, b) => a + b, 0); |
906 | 1056 | ||
@@ -927,7 +1077,7 @@ export default class ServicesStore extends Store { | |||
927 | const { enabled } = this; | 1077 | const { enabled } = this; |
928 | const { isAppMuted } = this.stores.settings.app; | 1078 | const { isAppMuted } = this.stores.settings.app; |
929 | 1079 | ||
930 | enabled.forEach((service) => { | 1080 | enabled.forEach(service => { |
931 | const { isAttached } = service; | 1081 | const { isAttached } = service; |
932 | const isMuted = isAppMuted || service.isMuted; | 1082 | const isMuted = isAppMuted || service.isMuted; |
933 | 1083 | ||
@@ -954,48 +1104,30 @@ export default class ServicesStore extends Store { | |||
954 | 1104 | ||
955 | if (!recipe) return; | 1105 | if (!recipe) return; |
956 | 1106 | ||
957 | if (recipe.hasTeamId && recipe.hasCustomUrl && data.team && data.customUrl) { | 1107 | if ( |
1108 | recipe.hasTeamId && | ||
1109 | recipe.hasCustomUrl && | ||
1110 | data.team && | ||
1111 | data.customUrl | ||
1112 | ) { | ||
958 | delete serviceData.team; | 1113 | delete serviceData.team; |
959 | } | 1114 | } |
960 | 1115 | ||
961 | return serviceData; | 1116 | return serviceData; |
962 | } | 1117 | } |
963 | 1118 | ||
964 | _restrictServiceAccess() { | ||
965 | const { features } = this.stores.features; | ||
966 | const { userHasReachedServiceLimit, serviceLimit } = this.stores.serviceLimit; | ||
967 | |||
968 | this.all.map((service, index) => { | ||
969 | if (userHasReachedServiceLimit) { | ||
970 | service.isServiceAccessRestricted = index >= serviceLimit; | ||
971 | |||
972 | if (service.isServiceAccessRestricted) { | ||
973 | service.restrictionType = RESTRICTION_TYPES.SERVICE_LIMIT; | ||
974 | |||
975 | debug('Restricting access to server due to service limit'); | ||
976 | } | ||
977 | } | ||
978 | |||
979 | if (service.isUsingCustomUrl) { | ||
980 | service.isServiceAccessRestricted = !features.isCustomUrlIncludedInCurrentPlan; | ||
981 | |||
982 | if (service.isServiceAccessRestricted) { | ||
983 | service.restrictionType = RESTRICTION_TYPES.CUSTOM_URL; | ||
984 | |||
985 | debug('Restricting access to server due to custom url'); | ||
986 | } | ||
987 | } | ||
988 | |||
989 | return service; | ||
990 | }); | ||
991 | } | ||
992 | |||
993 | _checkForActiveService() { | 1119 | _checkForActiveService() { |
994 | if (!this.stores.router.location || this.stores.router.location.pathname.includes('auth/signup')) { | 1120 | if ( |
1121 | !this.stores.router.location || | ||
1122 | this.stores.router.location.pathname.includes('auth/signup') | ||
1123 | ) { | ||
995 | return; | 1124 | return; |
996 | } | 1125 | } |
997 | 1126 | ||
998 | if (this.allDisplayed.findIndex(service => service.isActive) === -1 && this.allDisplayed.length !== 0) { | 1127 | if ( |
1128 | this.allDisplayed.findIndex(service => service.isActive) === -1 && | ||
1129 | this.allDisplayed.length !== 0 | ||
1130 | ) { | ||
999 | debug('No active service found, setting active service to index 0'); | 1131 | debug('No active service found, setting active service to index 0'); |
1000 | 1132 | ||
1001 | this._setActive({ serviceId: this.allDisplayed[0].id }); | 1133 | this._setActive({ serviceId: this.allDisplayed[0].id }); |
@@ -1008,13 +1140,19 @@ export default class ServicesStore extends Store { | |||
1008 | 1140 | ||
1009 | if (service.webview) { | 1141 | if (service.webview) { |
1010 | // We need to completely clone the object, otherwise Electron won't be able to send the object via IPC | 1142 | // We need to completely clone the object, otherwise Electron won't be able to send the object via IPC |
1011 | const shareWithWebview = JSON.parse(JSON.stringify(service.shareWithWebview)); | 1143 | const shareWithWebview = JSON.parse( |
1144 | JSON.stringify(service.shareWithWebview), | ||
1145 | ); | ||
1012 | 1146 | ||
1013 | debug('Initialize recipe', service.recipe.id, service.name); | 1147 | debug('Initialize recipe', service.recipe.id, service.name); |
1014 | service.webview.send('initialize-recipe', { | 1148 | service.webview.send( |
1015 | ...shareWithWebview, | 1149 | 'initialize-recipe', |
1016 | franzVersion: app.getVersion(), | 1150 | { |
1017 | }, service.recipe); | 1151 | ...shareWithWebview, |
1152 | franzVersion: app.getVersion(), | ||
1153 | }, | ||
1154 | service.recipe, | ||
1155 | ); | ||
1018 | } | 1156 | } |
1019 | } | 1157 | } |
1020 | 1158 | ||
diff --git a/src/stores/SettingsStore.js b/src/stores/SettingsStore.js index 292242552..cd627c2b8 100644 --- a/src/stores/SettingsStore.js +++ b/src/stores/SettingsStore.js | |||
@@ -164,11 +164,24 @@ export default class SettingsStore extends Store { | |||
164 | } | 164 | } |
165 | } | 165 | } |
166 | 166 | ||
167 | _ensureMigrationAndMarkDone(migrationName, callback) { | ||
168 | if (!this.all.migration[migrationName]) { | ||
169 | callback(); | ||
170 | |||
171 | const data = {}; | ||
172 | data[migrationName] = true; | ||
173 | this.actions.settings.update({ | ||
174 | type: 'migration', | ||
175 | data, | ||
176 | }); | ||
177 | } | ||
178 | } | ||
179 | |||
167 | // Helper | 180 | // Helper |
168 | async _migrate() { | 181 | async _migrate() { |
169 | const legacySettings = localStorage.getItem('app') || {}; | 182 | const legacySettings = localStorage.getItem('app') || {}; |
170 | 183 | ||
171 | if (!this.all.migration['5.0.0-beta.17-settings']) { | 184 | this._ensureMigrationAndMarkDone('5.0.0-beta.17-settings', () => { |
172 | this.actions.settings.update({ | 185 | this.actions.settings.update({ |
173 | type: 'app', | 186 | type: 'app', |
174 | data: { | 187 | data: { |
@@ -193,19 +206,12 @@ export default class SettingsStore extends Store { | |||
193 | }, | 206 | }, |
194 | }); | 207 | }); |
195 | 208 | ||
196 | this.actions.settings.update({ | ||
197 | type: 'migration', | ||
198 | data: { | ||
199 | '5.0.0-beta.17-settings': true, | ||
200 | }, | ||
201 | }); | ||
202 | |||
203 | localStorage.removeItem('app'); | 209 | localStorage.removeItem('app'); |
204 | 210 | ||
205 | debug('Migrated settings to split stores'); | 211 | debug('Migrated settings to split stores'); |
206 | } | 212 | }); |
207 | 213 | ||
208 | if (!this.all.migration['5.0.0-beta.19-settings']) { | 214 | this._ensureMigrationAndMarkDone('5.0.0-beta.19-settings', () => { |
209 | const spellcheckerLanguage = getLocale({ | 215 | const spellcheckerLanguage = getLocale({ |
210 | locale: this.stores.settings.app.locale, | 216 | locale: this.stores.settings.app.locale, |
211 | locales: SPELLCHECKER_LOCALES, | 217 | locales: SPELLCHECKER_LOCALES, |
@@ -219,16 +225,9 @@ export default class SettingsStore extends Store { | |||
219 | spellcheckerLanguage, | 225 | spellcheckerLanguage, |
220 | }, | 226 | }, |
221 | }); | 227 | }); |
228 | }); | ||
222 | 229 | ||
223 | this.actions.settings.update({ | 230 | this._ensureMigrationAndMarkDone('5.4.4-beta.2-settings', () => { |
224 | type: 'migration', | ||
225 | data: { | ||
226 | '5.0.0-beta.19-settings': true, | ||
227 | }, | ||
228 | }); | ||
229 | } | ||
230 | |||
231 | if (!this.all.migration['5.4.4-beta.2-settings']) { | ||
232 | const { | 231 | const { |
233 | showServiceNavigationBar, | 232 | showServiceNavigationBar, |
234 | } = this.all.app; | 233 | } = this.all.app; |
@@ -239,53 +238,22 @@ export default class SettingsStore extends Store { | |||
239 | navigationBarBehaviour: showServiceNavigationBar ? 'custom' : 'never', | 238 | navigationBarBehaviour: showServiceNavigationBar ? 'custom' : 'never', |
240 | }, | 239 | }, |
241 | }); | 240 | }); |
241 | }); | ||
242 | 242 | ||
243 | this.actions.settings.update({ | 243 | this._ensureMigrationAndMarkDone('5.4.4-beta.4-settings', () => { |
244 | type: 'migration', | ||
245 | data: { | ||
246 | '5.4.4-beta.2-settings': true, | ||
247 | }, | ||
248 | }); | ||
249 | } | ||
250 | |||
251 | if (!this.all.migration['5.4.4-beta.4-settings']) { | ||
252 | this.actions.settings.update({ | 244 | this.actions.settings.update({ |
253 | type: 'app', | 245 | type: 'app', |
254 | data: { | 246 | data: { |
255 | todoServer: 'isUsingCustomTodoService', | 247 | todoServer: 'isUsingCustomTodoService', |
256 | customTodoServer: legacySettings.todoServer, | 248 | customTodoServer: legacySettings.todoServer, |
257 | }, | ||
258 | }); | ||
259 | |||
260 | this.actions.settings.update({ | ||
261 | type: 'migration', | ||
262 | data: { | ||
263 | '5.4.4-beta.4-settings': true, | ||
264 | }, | ||
265 | }); | ||
266 | |||
267 | debug('Migrated old todo setting to new custom todo setting'); | ||
268 | } | ||
269 | |||
270 | if (!this.all.migration['5.4.4-beta.4-settings']) { | ||
271 | this.actions.settings.update({ | ||
272 | type: 'app', | ||
273 | data: { | ||
274 | automaticUpdates: !(legacySettings.noUpdates), | 249 | automaticUpdates: !(legacySettings.noUpdates), |
275 | }, | 250 | }, |
276 | }); | 251 | }); |
277 | 252 | ||
278 | this.actions.settings.update({ | 253 | debug('Migrated old todo setting to new custom todo setting'); |
279 | type: 'migration', | 254 | }); |
280 | data: { | ||
281 | '5.4.4-beta.4-settings': true, | ||
282 | }, | ||
283 | }); | ||
284 | |||
285 | debug('Migrated updates settings'); | ||
286 | } | ||
287 | 255 | ||
288 | if (!this.all.migration['password-hashing']) { | 256 | this._ensureMigrationAndMarkDone('password-hashing', () => { |
289 | if (this.stores.settings.app.lockedPassword !== '') { | 257 | if (this.stores.settings.app.lockedPassword !== '') { |
290 | this.actions.settings.update({ | 258 | this.actions.settings.update({ |
291 | type: 'app', | 259 | type: 'app', |
@@ -295,46 +263,25 @@ export default class SettingsStore extends Store { | |||
295 | }); | 263 | }); |
296 | } | 264 | } |
297 | 265 | ||
298 | this.actions.settings.update({ | ||
299 | type: 'migration', | ||
300 | data: { | ||
301 | 'password-hashing': true, | ||
302 | }, | ||
303 | }); | ||
304 | |||
305 | debug('Migrated updates settings'); | 266 | debug('Migrated updates settings'); |
306 | } | 267 | }); |
307 | 268 | ||
308 | if (!this.all.migration['5.6.0-beta.6-settings']) { | 269 | this._ensureMigrationAndMarkDone('5.6.0-beta.6-settings', () => { |
309 | this.actions.settings.update({ | 270 | this.actions.settings.update({ |
310 | type: 'app', | 271 | type: 'app', |
311 | data: { | 272 | data: { |
312 | searchEngine: SEARCH_ENGINE_DDG, | 273 | searchEngine: SEARCH_ENGINE_DDG, |
313 | }, | 274 | }, |
314 | }); | 275 | }); |
276 | }); | ||
315 | 277 | ||
316 | this.actions.settings.update({ | 278 | this._ensureMigrationAndMarkDone('user-agent-settings', () => { |
317 | type: 'migration', | ||
318 | data: { | ||
319 | '5.6.0-beta.6-settings': true, | ||
320 | }, | ||
321 | }); | ||
322 | } | ||
323 | |||
324 | if (!this.all.migration['user-agent-settings']) { | ||
325 | this.actions.settings.update({ | 279 | this.actions.settings.update({ |
326 | type: 'app', | 280 | type: 'app', |
327 | data: { | 281 | data: { |
328 | userAgentPref: DEFAULT_APP_SETTINGS.userAgentPref, | 282 | userAgentPref: DEFAULT_APP_SETTINGS.userAgentPref, |
329 | }, | 283 | }, |
330 | }); | 284 | }); |
331 | 285 | }); | |
332 | this.actions.settings.update({ | ||
333 | type: 'migration', | ||
334 | data: { | ||
335 | 'user-agent-settings': true, | ||
336 | }, | ||
337 | }); | ||
338 | } | ||
339 | } | 286 | } |
340 | } | 287 | } |
diff --git a/src/stores/UIStore.js b/src/stores/UIStore.js index 0ca61046a..adcd776c1 100644 --- a/src/stores/UIStore.js +++ b/src/stores/UIStore.js | |||
@@ -71,7 +71,7 @@ export default class UIStore extends Store { | |||
71 | 71 | ||
72 | @computed get theme() { | 72 | @computed get theme() { |
73 | const themeId = (this.isDarkThemeActive || this.stores.settings.app.darkMode) ? 'dark' : 'default'; | 73 | const themeId = (this.isDarkThemeActive || this.stores.settings.app.darkMode) ? 'dark' : 'default'; |
74 | const accentColor = this.stores.settings.app.accentColor; | 74 | const { accentColor } = this.stores.settings.app; |
75 | return theme(themeId, accentColor); | 75 | return theme(themeId, accentColor); |
76 | } | 76 | } |
77 | 77 | ||
diff --git a/src/stores/UserStore.js b/src/stores/UserStore.js index 7947e5a27..2e009893a 100644 --- a/src/stores/UserStore.js +++ b/src/stores/UserStore.js | |||
@@ -2,16 +2,13 @@ import { observable, computed, action } from 'mobx'; | |||
2 | import moment from 'moment'; | 2 | import moment from 'moment'; |
3 | import jwt from 'jsonwebtoken'; | 3 | import jwt from 'jsonwebtoken'; |
4 | import localStorage from 'mobx-localstorage'; | 4 | import localStorage from 'mobx-localstorage'; |
5 | import ms from 'ms'; | ||
6 | import { session } from '@electron/remote'; | 5 | import { session } from '@electron/remote'; |
7 | 6 | ||
8 | import { isDevMode } from '../environment'; | 7 | import { isDevMode } from '../environment'; |
9 | import Store from './lib/Store'; | 8 | import Store from './lib/Store'; |
10 | import Request from './lib/Request'; | 9 | import Request from './lib/Request'; |
11 | import CachedRequest from './lib/CachedRequest'; | 10 | import CachedRequest from './lib/CachedRequest'; |
12 | import { sleep } from '../helpers/async-helpers'; | 11 | import { TODOS_PARTITION_ID } from '../config'; |
13 | import { getPlan } from '../helpers/plan-helpers'; | ||
14 | import { PLANS, TODOS_PARTITION_ID } from '../config'; | ||
15 | 12 | ||
16 | const debug = require('debug')('Ferdi:UserStore'); | 13 | const debug = require('debug')('Ferdi:UserStore'); |
17 | 14 | ||
@@ -27,8 +24,6 @@ export default class UserStore extends Store { | |||
27 | 24 | ||
28 | SIGNUP_ROUTE = `${this.BASE_ROUTE}/signup`; | 25 | SIGNUP_ROUTE = `${this.BASE_ROUTE}/signup`; |
29 | 26 | ||
30 | PRICING_ROUTE = `${this.BASE_ROUTE}/signup/pricing`; | ||
31 | |||
32 | SETUP_ROUTE = `${this.BASE_ROUTE}/signup/setup`; | 27 | SETUP_ROUTE = `${this.BASE_ROUTE}/signup/setup`; |
33 | 28 | ||
34 | IMPORT_ROUTE = `${this.BASE_ROUTE}/signup/import`; | 29 | IMPORT_ROUTE = `${this.BASE_ROUTE}/signup/import`; |
@@ -45,8 +40,6 @@ export default class UserStore extends Store { | |||
45 | 40 | ||
46 | @observable passwordRequest = new Request(this.api.user, 'password'); | 41 | @observable passwordRequest = new Request(this.api.user, 'password'); |
47 | 42 | ||
48 | @observable activateTrialRequest = new Request(this.api.user, 'activateTrial'); | ||
49 | |||
50 | @observable inviteRequest = new Request(this.api.user, 'invite'); | 43 | @observable inviteRequest = new Request(this.api.user, 'invite'); |
51 | 44 | ||
52 | @observable getUserInfoRequest = new CachedRequest(this.api.user, 'getInfo'); | 45 | @observable getUserInfoRequest = new CachedRequest(this.api.user, 'getInfo'); |
@@ -71,8 +64,6 @@ export default class UserStore extends Store { | |||
71 | 64 | ||
72 | @observable hasCompletedSignup = false; | 65 | @observable hasCompletedSignup = false; |
73 | 66 | ||
74 | @observable hasActivatedTrial = false; | ||
75 | |||
76 | @observable userData = {}; | 67 | @observable userData = {}; |
77 | 68 | ||
78 | @observable actionStatus = []; | 69 | @observable actionStatus = []; |
@@ -93,7 +84,6 @@ export default class UserStore extends Store { | |||
93 | this.actions.user.retrievePassword.listen(this._retrievePassword.bind(this)); | 84 | this.actions.user.retrievePassword.listen(this._retrievePassword.bind(this)); |
94 | this.actions.user.logout.listen(this._logout.bind(this)); | 85 | this.actions.user.logout.listen(this._logout.bind(this)); |
95 | this.actions.user.signup.listen(this._signup.bind(this)); | 86 | this.actions.user.signup.listen(this._signup.bind(this)); |
96 | this.actions.user.activateTrial.listen(this._activateTrial.bind(this)); | ||
97 | this.actions.user.invite.listen(this._invite.bind(this)); | 87 | this.actions.user.invite.listen(this._invite.bind(this)); |
98 | this.actions.user.update.listen(this._update.bind(this)); | 88 | this.actions.user.update.listen(this._update.bind(this)); |
99 | this.actions.user.resetStatus.listen(this._resetStatus.bind(this)); | 89 | this.actions.user.resetStatus.listen(this._resetStatus.bind(this)); |
@@ -104,7 +94,6 @@ export default class UserStore extends Store { | |||
104 | this.registerReactions([ | 94 | this.registerReactions([ |
105 | this._requireAuthenticatedUser.bind(this), | 95 | this._requireAuthenticatedUser.bind(this), |
106 | this._getUserData.bind(this), | 96 | this._getUserData.bind(this), |
107 | this._resetTrialActivationState.bind(this), | ||
108 | ]); | 97 | ]); |
109 | } | 98 | } |
110 | 99 | ||
@@ -126,10 +115,6 @@ export default class UserStore extends Store { | |||
126 | return this.SIGNUP_ROUTE; | 115 | return this.SIGNUP_ROUTE; |
127 | } | 116 | } |
128 | 117 | ||
129 | get pricingRoute() { | ||
130 | return this.PRICING_ROUTE; | ||
131 | } | ||
132 | |||
133 | get setupRoute() { | 118 | get setupRoute() { |
134 | return this.SETUP_ROUTE; | 119 | return this.SETUP_ROUTE; |
135 | } | 120 | } |
@@ -172,31 +157,6 @@ export default class UserStore extends Store { | |||
172 | return this.data.team || null; | 157 | return this.data.team || null; |
173 | } | 158 | } |
174 | 159 | ||
175 | @computed get isPremium() { | ||
176 | return true; | ||
177 | } | ||
178 | |||
179 | @computed get isPremiumOverride() { | ||
180 | return ((!this.team || !this.team.plan) && this.isPremium) || (this.team && this.team.state === 'expired' && this.isPremium); | ||
181 | } | ||
182 | |||
183 | @computed get isPersonal() { | ||
184 | if (!this.team || !this.team.plan) return false; | ||
185 | const plan = getPlan(this.team.plan); | ||
186 | |||
187 | return plan === PLANS.PERSONAL; | ||
188 | } | ||
189 | |||
190 | @computed get isPro() { | ||
191 | return true; | ||
192 | // if (this.isPremiumOverride) return true; | ||
193 | |||
194 | // if (!this.team || (!this.team.plan || this.team.state === 'expired')) return false; | ||
195 | // const plan = getPlan(this.team.plan); | ||
196 | |||
197 | // return plan === PLANS.PRO || plan === PLANS.LEGACY; | ||
198 | } | ||
199 | |||
200 | @computed get legacyServices() { | 160 | @computed get legacyServices() { |
201 | return this.getLegacyServicesRequest.execute() || {}; | 161 | return this.getLegacyServicesRequest.execute() || {}; |
202 | } | 162 | } |
@@ -244,23 +204,8 @@ export default class UserStore extends Store { | |||
244 | this.actionStatus = request.result.status || []; | 204 | this.actionStatus = request.result.status || []; |
245 | } | 205 | } |
246 | 206 | ||
247 | @action async _activateTrial({ planId }) { | ||
248 | debug('activate trial', planId); | ||
249 | |||
250 | this.activateTrialRequest.execute({ | ||
251 | plan: planId, | ||
252 | }); | ||
253 | |||
254 | await this.activateTrialRequest._promise; | ||
255 | |||
256 | this.hasActivatedTrial = true; | ||
257 | |||
258 | this.stores.features.featuresRequest.invalidate({ immediately: true }); | ||
259 | this.stores.user.getUserInfoRequest.invalidate({ immediately: true }); | ||
260 | } | ||
261 | |||
262 | @action async _invite({ invites }) { | 207 | @action async _invite({ invites }) { |
263 | const data = invites.filter(invite => invite.email !== ''); | 208 | const data = invites.filter((invite) => invite.email !== ''); |
264 | 209 | ||
265 | const response = await this.inviteRequest.execute(data)._promise; | 210 | const response = await this.inviteRequest.execute(data)._promise; |
266 | 211 | ||
@@ -305,7 +250,7 @@ export default class UserStore extends Store { | |||
305 | this.isImportLegacyServicesExecuting = true; | 250 | this.isImportLegacyServicesExecuting = true; |
306 | 251 | ||
307 | // Reduces recipe duplicates | 252 | // Reduces recipe duplicates |
308 | const recipes = services.filter((obj, pos, arr) => arr.map(mapObj => mapObj.recipe.id).indexOf(obj.recipe.id) === pos).map(s => s.recipe.id); | 253 | const recipes = services.filter((obj, pos, arr) => arr.map((mapObj) => mapObj.recipe.id).indexOf(obj.recipe.id) === pos).map((s) => s.recipe.id); |
309 | 254 | ||
310 | // Install recipes | 255 | // Install recipes |
311 | for (const recipe of recipes) { // eslint-disable-line no-unused-vars | 256 | for (const recipe of recipes) { // eslint-disable-line no-unused-vars |
@@ -386,14 +331,6 @@ export default class UserStore extends Store { | |||
386 | } | 331 | } |
387 | } | 332 | } |
388 | 333 | ||
389 | async _resetTrialActivationState() { | ||
390 | if (this.hasActivatedTrial) { | ||
391 | await sleep(ms('12s')); | ||
392 | |||
393 | this.hasActivatedTrial = false; | ||
394 | } | ||
395 | } | ||
396 | |||
397 | // Helpers | 334 | // Helpers |
398 | _parseToken(authToken) { | 335 | _parseToken(authToken) { |
399 | try { | 336 | try { |
diff --git a/src/stores/index.js b/src/stores/index.js index 4eeef7982..b6e481e8a 100644 --- a/src/stores/index.js +++ b/src/stores/index.js | |||
@@ -6,16 +6,13 @@ import ServicesStore from './ServicesStore'; | |||
6 | import RecipesStore from './RecipesStore'; | 6 | import RecipesStore from './RecipesStore'; |
7 | import RecipePreviewsStore from './RecipePreviewsStore'; | 7 | import RecipePreviewsStore from './RecipePreviewsStore'; |
8 | import UIStore from './UIStore'; | 8 | import UIStore from './UIStore'; |
9 | import PaymentStore from './PaymentStore'; | ||
10 | import NewsStore from './NewsStore'; | 9 | import NewsStore from './NewsStore'; |
11 | import RequestStore from './RequestStore'; | 10 | import RequestStore from './RequestStore'; |
12 | import GlobalErrorStore from './GlobalErrorStore'; | 11 | import GlobalErrorStore from './GlobalErrorStore'; |
13 | import { workspaceStore } from '../features/workspaces'; | 12 | import { workspaceStore } from '../features/workspaces'; |
14 | import { announcementsStore } from '../features/announcements'; | 13 | import { announcementsStore } from '../features/announcements'; |
15 | import { serviceLimitStore } from '../features/serviceLimit'; | ||
16 | import { communityRecipesStore } from '../features/communityRecipes'; | 14 | import { communityRecipesStore } from '../features/communityRecipes'; |
17 | import { todosStore } from '../features/todos'; | 15 | import { todosStore } from '../features/todos'; |
18 | import { planSelectionStore } from '../features/planSelection'; | ||
19 | 16 | ||
20 | export default (api, actions, router) => { | 17 | export default (api, actions, router) => { |
21 | const stores = {}; | 18 | const stores = {}; |
@@ -29,16 +26,13 @@ export default (api, actions, router) => { | |||
29 | recipes: new RecipesStore(stores, api, actions), | 26 | recipes: new RecipesStore(stores, api, actions), |
30 | recipePreviews: new RecipePreviewsStore(stores, api, actions), | 27 | recipePreviews: new RecipePreviewsStore(stores, api, actions), |
31 | ui: new UIStore(stores, api, actions), | 28 | ui: new UIStore(stores, api, actions), |
32 | payment: new PaymentStore(stores, api, actions), | ||
33 | news: new NewsStore(stores, api, actions), | 29 | news: new NewsStore(stores, api, actions), |
34 | requests: new RequestStore(stores, api, actions), | 30 | requests: new RequestStore(stores, api, actions), |
35 | globalError: new GlobalErrorStore(stores, api, actions), | 31 | globalError: new GlobalErrorStore(stores, api, actions), |
36 | workspaces: workspaceStore, | 32 | workspaces: workspaceStore, |
37 | announcements: announcementsStore, | 33 | announcements: announcementsStore, |
38 | serviceLimit: serviceLimitStore, | ||
39 | communityRecipes: communityRecipesStore, | 34 | communityRecipes: communityRecipesStore, |
40 | todos: todosStore, | 35 | todos: todosStore, |
41 | planSelection: planSelectionStore, | ||
42 | }); | 36 | }); |
43 | // Initialize all stores | 37 | // Initialize all stores |
44 | Object.keys(stores).forEach((name) => { | 38 | Object.keys(stores).forEach((name) => { |
diff --git a/src/stores/lib/CachedRequest.js b/src/stores/lib/CachedRequest.js index 31c7ce241..94f615144 100644 --- a/src/stores/lib/CachedRequest.js +++ b/src/stores/lib/CachedRequest.js | |||
@@ -92,7 +92,7 @@ export default class CachedRequest extends Request { | |||
92 | } | 92 | } |
93 | 93 | ||
94 | removeCacheForCallWith(...args) { | 94 | removeCacheForCallWith(...args) { |
95 | remove(this._apiCalls, c => isEqual(c.args, args)); | 95 | remove(this._apiCalls, (c) => isEqual(c.args, args)); |
96 | } | 96 | } |
97 | 97 | ||
98 | _addApiCall(args) { | 98 | _addApiCall(args) { |
@@ -102,6 +102,6 @@ export default class CachedRequest extends Request { | |||
102 | } | 102 | } |
103 | 103 | ||
104 | _findApiCall(args) { | 104 | _findApiCall(args) { |
105 | return this._apiCalls.find(c => isEqual(c.args, args)); | 105 | return this._apiCalls.find((c) => isEqual(c.args, args)); |
106 | } | 106 | } |
107 | } | 107 | } |
diff --git a/src/stores/lib/Reaction.js b/src/stores/lib/Reaction.js index f8009b7f6..7e1bc685e 100644 --- a/src/stores/lib/Reaction.js +++ b/src/stores/lib/Reaction.js | |||
@@ -26,6 +26,6 @@ export default class Reaction { | |||
26 | } | 26 | } |
27 | } | 27 | } |
28 | 28 | ||
29 | export const createReactions = reactions => ( | 29 | export const createReactions = (reactions) => ( |
30 | reactions.map(r => new Reaction(r)) | 30 | reactions.map((r) => new Reaction(r)) |
31 | ); | 31 | ); |
diff --git a/src/stores/lib/Request.js b/src/stores/lib/Request.js index cfc857c2e..32ffe4367 100644 --- a/src/stores/lib/Request.js +++ b/src/stores/lib/Request.js | |||
@@ -107,7 +107,7 @@ export default class Request { | |||
107 | } | 107 | } |
108 | 108 | ||
109 | _triggerHooks() { | 109 | _triggerHooks() { |
110 | Request._hooks.forEach(hook => hook(this)); | 110 | Request._hooks.forEach((hook) => hook(this)); |
111 | } | 111 | } |
112 | 112 | ||
113 | reset = () => { | 113 | reset = () => { |
diff --git a/src/stores/lib/Store.js b/src/stores/lib/Store.js index 8d2fb4066..b03a7e725 100644 --- a/src/stores/lib/Store.js +++ b/src/stores/lib/Store.js | |||
@@ -28,18 +28,18 @@ export default class Store { | |||
28 | } | 28 | } |
29 | 29 | ||
30 | registerReactions(reactions) { | 30 | registerReactions(reactions) { |
31 | reactions.forEach(reaction => this._reactions.push(new Reaction(reaction))); | 31 | reactions.forEach((reaction) => this._reactions.push(new Reaction(reaction))); |
32 | } | 32 | } |
33 | 33 | ||
34 | setup() {} | 34 | setup() {} |
35 | 35 | ||
36 | initialize() { | 36 | initialize() { |
37 | this.setup(); | 37 | this.setup(); |
38 | this._reactions.forEach(reaction => reaction.start()); | 38 | this._reactions.forEach((reaction) => reaction.start()); |
39 | } | 39 | } |
40 | 40 | ||
41 | teardown() { | 41 | teardown() { |
42 | this._reactions.forEach(reaction => reaction.stop()); | 42 | this._reactions.forEach((reaction) => reaction.stop()); |
43 | } | 43 | } |
44 | 44 | ||
45 | resetStatus() { | 45 | resetStatus() { |