diff options
Diffstat (limited to 'src/stores/ServicesStore.js')
-rw-r--r-- | src/stores/ServicesStore.js | 131 |
1 files changed, 115 insertions, 16 deletions
diff --git a/src/stores/ServicesStore.js b/src/stores/ServicesStore.js index 448260638..21ed0a234 100644 --- a/src/stores/ServicesStore.js +++ b/src/stores/ServicesStore.js | |||
@@ -20,6 +20,8 @@ import { workspaceStore } from '../features/workspaces'; | |||
20 | import { serviceLimitStore } from '../features/serviceLimit'; | 20 | import { serviceLimitStore } from '../features/serviceLimit'; |
21 | import { RESTRICTION_TYPES } from '../models/Service'; | 21 | import { RESTRICTION_TYPES } from '../models/Service'; |
22 | import { KEEP_WS_LOADED_USID } from '../config'; | 22 | import { KEEP_WS_LOADED_USID } from '../config'; |
23 | import { TODOS_RECIPE_ID } from '../features/todos'; | ||
24 | import { SPELLCHECKER_LOCALES } from '../i18n/languages'; | ||
23 | 25 | ||
24 | const debug = require('debug')('Ferdi:ServiceStore'); | 26 | const debug = require('debug')('Ferdi:ServiceStore'); |
25 | 27 | ||
@@ -82,7 +84,9 @@ export default class ServicesStore extends Store { | |||
82 | this.actions.service.toggleAudio.listen(this._toggleAudio.bind(this)); | 84 | this.actions.service.toggleAudio.listen(this._toggleAudio.bind(this)); |
83 | this.actions.service.openDevTools.listen(this._openDevTools.bind(this)); | 85 | this.actions.service.openDevTools.listen(this._openDevTools.bind(this)); |
84 | this.actions.service.openDevToolsForActiveService.listen(this._openDevToolsForActiveService.bind(this)); | 86 | this.actions.service.openDevToolsForActiveService.listen(this._openDevToolsForActiveService.bind(this)); |
85 | this.actions.service.setHibernation.listen(this._setHibernation.bind(this)); | 87 | this.actions.service.hibernate.listen(this._hibernate.bind(this)); |
88 | this.actions.service.awake.listen(this._awake.bind(this)); | ||
89 | this.actions.service.resetLastPollTimer.listen(this._resetLastPollTimer.bind(this)); | ||
86 | this.actions.service.shareSettingsWithServiceProcess.listen(this._shareSettingsWithServiceProcess.bind(this)); | 90 | this.actions.service.shareSettingsWithServiceProcess.listen(this._shareSettingsWithServiceProcess.bind(this)); |
87 | 91 | ||
88 | this.registerReactions([ | 92 | this.registerReactions([ |
@@ -93,6 +97,7 @@ export default class ServicesStore extends Store { | |||
93 | this._logoutReaction.bind(this), | 97 | this._logoutReaction.bind(this), |
94 | this._handleMuteSettings.bind(this), | 98 | this._handleMuteSettings.bind(this), |
95 | this._restrictServiceAccess.bind(this), | 99 | this._restrictServiceAccess.bind(this), |
100 | this._checkForActiveService.bind(this), | ||
96 | ]); | 101 | ]); |
97 | 102 | ||
98 | // Just bind this | 103 | // Just bind this |
@@ -155,16 +160,24 @@ export default class ServicesStore extends Store { | |||
155 | */ | 160 | */ |
156 | _serviceMaintenance() { | 161 | _serviceMaintenance() { |
157 | this.all.forEach((service) => { | 162 | this.all.forEach((service) => { |
158 | if (service.lastPoll && (service.lastPoll) - service.lastPollAnswer > ms('30s')) { | 163 | // Defines which services should be hibernated. |
159 | // If service did not reply for more than 30s try to reload. | 164 | if (!service.isActive && (Date.now() - service.lastUsed > ms('5m'))) { |
165 | // If service is stale for 5 min, hibernate it. | ||
166 | this._hibernate({ serviceId: service.id }); | ||
167 | } | ||
168 | |||
169 | if (service.lastPoll && (service.lastPoll - service.lastPollAnswer > ms('1m'))) { | ||
170 | // If service did not reply for more than 1m try to reload. | ||
160 | if (!service.isActive) { | 171 | if (!service.isActive) { |
161 | if (this.stores.app.isOnline && service.lostRecipeReloadAttempt < 3) { | 172 | if (this.stores.app.isOnline && service.lostRecipeReloadAttempt < 3) { |
162 | service.webview.reload(); | 173 | debug(`Reloading service: ${service.name} (${service.id}). Attempt: ${service.lostRecipeReloadAttempt}`); |
174 | // service.webview.reload(); | ||
163 | service.lostRecipeReloadAttempt += 1; | 175 | service.lostRecipeReloadAttempt += 1; |
164 | 176 | ||
165 | service.lostRecipeConnection = false; | 177 | service.lostRecipeConnection = false; |
166 | } | 178 | } |
167 | } else { | 179 | } else { |
180 | debug(`Service lost connection: ${service.name} (${service.id}).`); | ||
168 | service.lostRecipeConnection = true; | 181 | service.lostRecipeConnection = true; |
169 | } | 182 | } |
170 | } else { | 183 | } else { |
@@ -256,6 +269,14 @@ export default class ServicesStore extends Store { | |||
256 | return null; | 269 | return null; |
257 | } | 270 | } |
258 | 271 | ||
272 | @computed get isTodosServiceAdded() { | ||
273 | return this.allDisplayed.find(service => service.recipe.id === TODOS_RECIPE_ID && service.isEnabled) || null; | ||
274 | } | ||
275 | |||
276 | @computed get isTodosServiceActive() { | ||
277 | return this.active && this.active.recipe.id === TODOS_RECIPE_ID; | ||
278 | } | ||
279 | |||
259 | one(id) { | 280 | one(id) { |
260 | return this.all.find(service => service.id === id); | 281 | return this.all.find(service => service.id === id); |
261 | } | 282 | } |
@@ -265,10 +286,34 @@ export default class ServicesStore extends Store { | |||
265 | } | 286 | } |
266 | 287 | ||
267 | // Actions | 288 | // Actions |
268 | @action async _createService({ recipeId, serviceData, redirect = true }) { | 289 | async _createService({ |
290 | recipeId, serviceData, redirect = true, skipCleanup = false, | ||
291 | }) { | ||
269 | if (serviceLimitStore.userHasReachedServiceLimit) return; | 292 | if (serviceLimitStore.userHasReachedServiceLimit) return; |
270 | 293 | ||
271 | const data = this._cleanUpTeamIdAndCustomUrl(recipeId, serviceData); | 294 | if (!this.stores.recipes.isInstalled(recipeId)) { |
295 | debug(`Recipe "${recipeId}" is not installed, installing recipe`); | ||
296 | await this.stores.recipes._install({ recipeId }); | ||
297 | debug(`Recipe "${recipeId}" installed`); | ||
298 | } | ||
299 | |||
300 | // set default values for serviceData | ||
301 | Object.assign({ | ||
302 | isEnabled: true, | ||
303 | isHibernationEnabled: false, | ||
304 | isNotificationEnabled: true, | ||
305 | isBadgeEnabled: true, | ||
306 | isMuted: false, | ||
307 | customIcon: false, | ||
308 | isDarkModeEnabled: false, | ||
309 | spellcheckerLanguage: SPELLCHECKER_LOCALES[this.stores.settings.app.spellcheckerLanguage], | ||
310 | }, serviceData); | ||
311 | |||
312 | let data = serviceData; | ||
313 | |||
314 | if (!skipCleanup) { | ||
315 | data = this._cleanUpTeamIdAndCustomUrl(recipeId, serviceData); | ||
316 | } | ||
272 | 317 | ||
273 | const response = await this.createServiceRequest.execute(recipeId, data)._promise; | 318 | const response = await this.createServiceRequest.execute(recipeId, data)._promise; |
274 | 319 | ||
@@ -427,8 +472,13 @@ export default class ServicesStore extends Store { | |||
427 | this.all[index].isActive = false; | 472 | this.all[index].isActive = false; |
428 | }); | 473 | }); |
429 | service.isActive = true; | 474 | service.isActive = true; |
475 | this._awake({ serviceId: service.id }); | ||
430 | service.lastUsed = Date.now(); | 476 | service.lastUsed = Date.now(); |
431 | 477 | ||
478 | if (this.active.recipe.id === TODOS_RECIPE_ID && !this.stores.todos.settings.isFeatureEnabledByUser) { | ||
479 | this.actions.todos.toggleTodosFeatureVisibility(); | ||
480 | } | ||
481 | |||
432 | // Update list of last used services | 482 | // Update list of last used services |
433 | this.lastUsedServices = this.lastUsedServices.filter(id => id !== serviceId); | 483 | this.lastUsedServices = this.lastUsedServices.filter(id => id !== serviceId); |
434 | this.lastUsedServices.unshift(serviceId); | 484 | this.lastUsedServices.unshift(serviceId); |
@@ -617,10 +667,10 @@ export default class ServicesStore extends Store { | |||
617 | @action _sendIPCMessage({ serviceId, channel, args }) { | 667 | @action _sendIPCMessage({ serviceId, channel, args }) { |
618 | const service = this.one(serviceId); | 668 | const service = this.one(serviceId); |
619 | 669 | ||
620 | if (service.webview) { | 670 | // Make sure the args are clean, otherwise ElectronJS can't transmit them |
621 | // Make sure the args are clean, otherwise ElectronJS can't transmit them | 671 | const cleanArgs = JSON.parse(JSON.stringify(args)); |
622 | const cleanArgs = JSON.parse(JSON.stringify(args)); | ||
623 | 672 | ||
673 | if (service.webview) { | ||
624 | service.webview.send(channel, cleanArgs); | 674 | service.webview.send(channel, cleanArgs); |
625 | } | 675 | } |
626 | } | 676 | } |
@@ -659,8 +709,11 @@ export default class ServicesStore extends Store { | |||
659 | service.resetMessageCount(); | 709 | service.resetMessageCount(); |
660 | service.lostRecipeConnection = false; | 710 | service.lostRecipeConnection = false; |
661 | 711 | ||
662 | // service.webview.loadURL(service.url); | 712 | if (service.recipe.id === TODOS_RECIPE_ID) { |
663 | service.webview.reload(); | 713 | return this.actions.todos.reload(); |
714 | } | ||
715 | |||
716 | return service.webview.loadURL(service.url); | ||
664 | } | 717 | } |
665 | 718 | ||
666 | @action _reloadActive() { | 719 | @action _reloadActive() { |
@@ -743,23 +796,57 @@ export default class ServicesStore extends Store { | |||
743 | 796 | ||
744 | @action _openDevTools({ serviceId }) { | 797 | @action _openDevTools({ serviceId }) { |
745 | const service = this.one(serviceId); | 798 | const service = this.one(serviceId); |
746 | 799 | if (service.recipe.id === TODOS_RECIPE_ID) { | |
747 | service.webview.openDevTools(); | 800 | this.actions.todos.openDevTools(); |
801 | } else { | ||
802 | service.webview.openDevTools(); | ||
803 | } | ||
748 | } | 804 | } |
749 | 805 | ||
750 | @action _openDevToolsForActiveService() { | 806 | @action _openDevToolsForActiveService() { |
751 | const service = this.active; | 807 | const service = this.active; |
752 | 808 | ||
753 | if (service) { | 809 | if (service) { |
754 | service.webview.openDevTools(); | 810 | this._openDevTools({ serviceId: service.id }); |
755 | } else { | 811 | } else { |
756 | debug('No service is active'); | 812 | debug('No service is active'); |
757 | } | 813 | } |
758 | } | 814 | } |
759 | 815 | ||
760 | @action _setHibernation({ serviceId, hibernating }) { | 816 | @action _hibernate({ serviceId }) { |
817 | const service = this.one(serviceId); | ||
818 | if (service.isActive || !service.isHibernationEnabled) { | ||
819 | debug('Skipping service hibernation'); | ||
820 | return; | ||
821 | } | ||
822 | |||
823 | debug(`Hibernate ${service.name}`); | ||
824 | |||
825 | service.isHibernating = true; | ||
826 | } | ||
827 | |||
828 | @action _awake({ serviceId }) { | ||
761 | const service = this.one(serviceId); | 829 | const service = this.one(serviceId); |
762 | service.isHibernating = hibernating; | 830 | service.isHibernating = false; |
831 | service.liveFrom = Date.now(); | ||
832 | } | ||
833 | |||
834 | @action _resetLastPollTimer({ serviceId = null }) { | ||
835 | debug(`Reset last poll timer for ${serviceId ? `service: "${serviceId}"` : 'all services'}`); | ||
836 | |||
837 | const resetTimer = (service) => { | ||
838 | service.lastPollAnswer = Date.now(); | ||
839 | service.lastPoll = Date.now(); | ||
840 | }; | ||
841 | |||
842 | if (!serviceId) { | ||
843 | this.allDisplayed.forEach(service => resetTimer(service)); | ||
844 | } else { | ||
845 | const service = this.one(serviceId); | ||
846 | if (service) { | ||
847 | resetTimer(service); | ||
848 | } | ||
849 | } | ||
763 | } | 850 | } |
764 | 851 | ||
765 | // Reactions | 852 | // Reactions |
@@ -893,6 +980,18 @@ export default class ServicesStore extends Store { | |||
893 | }); | 980 | }); |
894 | } | 981 | } |
895 | 982 | ||
983 | _checkForActiveService() { | ||
984 | if (!this.stores.router.location || this.stores.router.location.pathname.includes('auth/signup')) { | ||
985 | return; | ||
986 | } | ||
987 | |||
988 | if (this.allDisplayed.findIndex(service => service.isActive) === -1 && this.allDisplayed.length !== 0) { | ||
989 | debug('No active service found, setting active service to index 0'); | ||
990 | |||
991 | this._setActive({ serviceId: this.allDisplayed[0].id }); | ||
992 | } | ||
993 | } | ||
994 | |||
896 | // Helper | 995 | // Helper |
897 | _initializeServiceRecipeInWebview(serviceId) { | 996 | _initializeServiceRecipeInWebview(serviceId) { |
898 | const service = this.one(serviceId); | 997 | const service = this.one(serviceId); |