diff --git a/src/actions/service.js b/src/actions/service.js
index 68c62d594..762a24ec3 100644
--- a/src/actions/service.js
+++ b/src/actions/service.js
@@ -91,4 +91,10 @@ export default {
serviceId: PropTypes.string.isRequired,
},
openDevToolsForActiveService: {},
+ hibernate: {
+ serviceId: PropTypes.string.isRequired,
+ },
+ awake: {
+ serviceId: PropTypes.string.isRequired,
+ },
};
diff --git a/src/components/services/content/ServiceView.js b/src/components/services/content/ServiceView.js
index 3b09518c5..600a2f1fc 100644
--- a/src/components/services/content/ServiceView.js
+++ b/src/components/services/content/ServiceView.js
@@ -143,11 +143,13 @@ export default @observer class ServiceView extends Component {
{service.recipe.id === CUSTOM_WEBSITE_ID && (
)}
-
+ {!service.isHibernating && (
+
+ )}
>
)}
>
diff --git a/src/components/settings/services/EditServiceForm.js b/src/components/settings/services/EditServiceForm.js
index 5cde0db8e..1f51430fe 100644
--- a/src/components/settings/services/EditServiceForm.js
+++ b/src/components/settings/services/EditServiceForm.js
@@ -77,6 +77,10 @@ const messages = defineMessages({
id: 'settings.service.form.isMutedInfo',
defaultMessage: '!!!When disabled, all notification sounds and audio playback are muted',
},
+ isHibernationEnabledInfo: {
+ id: 'settings.service.form.isHibernatedEnabledInfo',
+ defaultMessage: '!!!When enabled, a service will be shut down after a period of time to save system resources.',
+ },
headlineNotifications: {
id: 'settings.service.form.headlineNotifications',
defaultMessage: '!!!Notifications',
@@ -333,6 +337,10 @@ export default @observer class EditServiceForm extends Component {
)}
+
+
+ {intl.formatMessage(messages.isHibernationEnabledInfo)}
+
diff --git a/src/containers/settings/EditServiceScreen.js b/src/containers/settings/EditServiceScreen.js
index e4ff03bb3..889ecca6b 100644
--- a/src/containers/settings/EditServiceScreen.js
+++ b/src/containers/settings/EditServiceScreen.js
@@ -33,6 +33,10 @@ const messages = defineMessages({
id: 'settings.service.form.enableService',
defaultMessage: '!!!Enable service',
},
+ enableHibernation: {
+ id: 'settings.service.form.enableHibernation',
+ defaultMessage: '!!!Enable hibernation',
+ },
enableNotification: {
id: 'settings.service.form.enableNotification',
defaultMessage: '!!!Enable Notifications',
@@ -114,8 +118,11 @@ export default @inject('stores', 'actions') @observer class EditServiceScreen ex
const {
stores,
+ router,
} = this.props;
+ const { action } = router.params;
+
let defaultSpellcheckerLanguage = SPELLCHECKER_LOCALES[stores.settings.app.spellcheckerLanguage];
if (stores.settings.app.spellcheckerLanguage === 'automatic') {
@@ -140,6 +147,11 @@ export default @inject('stores', 'actions') @observer class EditServiceScreen ex
value: service.isEnabled,
default: true,
},
+ isHibernationEnabled: {
+ label: intl.formatMessage(messages.enableHibernation),
+ value: action !== 'edit' ? recipe.autoHibernate : service.isHibernationEnabled,
+ default: true,
+ },
isNotificationEnabled: {
label: intl.formatMessage(messages.enableNotification),
value: service.isNotificationEnabled,
diff --git a/src/i18n/locales/en-US.json b/src/i18n/locales/en-US.json
index 4cf444b2c..5584b2732 100644
--- a/src/i18n/locales/en-US.json
+++ b/src/i18n/locales/en-US.json
@@ -298,6 +298,7 @@
"settings.service.form.enableAudio": "Enable audio",
"settings.service.form.enableBadge": "Show unread message badges",
"settings.service.form.enableDarkMode": "Enable Dark Mode",
+ "settings.service.form.enableHibernation": "Enable hibernation",
"settings.service.form.enableNotification": "Enable notifications",
"settings.service.form.enableService": "Enable service",
"settings.service.form.headlineBadges": "Unread message badges",
@@ -308,6 +309,7 @@
"settings.service.form.iconUpload": "Drop your image, or click here",
"settings.service.form.indirectMessageInfo": "You will be notified about all new messages in a channel, not just @username, @channel, @here, ...",
"settings.service.form.indirectMessages": "Show message badge for all new messages",
+ "settings.service.form.isHibernatedEnabledInfo": "When enabled, a service will be shut down after a period of time to save system resources.",
"settings.service.form.isMutedInfo": "When disabled, all notification sounds and audio playback are muted",
"settings.service.form.name": "Name",
"settings.service.form.proxy.headline": "HTTP/HTTPS Proxy Settings",
diff --git a/src/i18n/messages/src/components/settings/services/EditServiceForm.json b/src/i18n/messages/src/components/settings/services/EditServiceForm.json
index e66db807d..e82d27b64 100644
--- a/src/i18n/messages/src/components/settings/services/EditServiceForm.json
+++ b/src/i18n/messages/src/components/settings/services/EditServiceForm.json
@@ -181,16 +181,29 @@
"column": 3
}
},
+ {
+ "id": "settings.service.form.isHibernatedEnabledInfo",
+ "defaultMessage": "!!!When enabled, a service will be shut down after a period of time to save system resources.",
+ "file": "src/components/settings/services/EditServiceForm.js",
+ "start": {
+ "line": 80,
+ "column": 28
+ },
+ "end": {
+ "line": 83,
+ "column": 3
+ }
+ },
{
"id": "settings.service.form.headlineNotifications",
"defaultMessage": "!!!Notifications",
"file": "src/components/settings/services/EditServiceForm.js",
"start": {
- "line": 80,
+ "line": 84,
"column": 25
},
"end": {
- "line": 83,
+ "line": 87,
"column": 3
}
},
@@ -199,11 +212,11 @@
"defaultMessage": "!!!Unread message badges",
"file": "src/components/settings/services/EditServiceForm.js",
"start": {
- "line": 84,
+ "line": 88,
"column": 18
},
"end": {
- "line": 87,
+ "line": 91,
"column": 3
}
},
@@ -212,11 +225,11 @@
"defaultMessage": "!!!General",
"file": "src/components/settings/services/EditServiceForm.js",
"start": {
- "line": 88,
+ "line": 92,
"column": 19
},
"end": {
- "line": 91,
+ "line": 95,
"column": 3
}
},
@@ -225,11 +238,11 @@
"defaultMessage": "!!!Delete",
"file": "src/components/settings/services/EditServiceForm.js",
"start": {
- "line": 92,
+ "line": 96,
"column": 14
},
"end": {
- "line": 95,
+ "line": 99,
"column": 3
}
},
@@ -238,11 +251,11 @@
"defaultMessage": "!!!Drop your image, or click here",
"file": "src/components/settings/services/EditServiceForm.js",
"start": {
- "line": 96,
+ "line": 100,
"column": 14
},
"end": {
- "line": 99,
+ "line": 103,
"column": 3
}
},
@@ -251,11 +264,11 @@
"defaultMessage": "!!!HTTP/HTTPS Proxy Settings",
"file": "src/components/settings/services/EditServiceForm.js",
"start": {
- "line": 100,
+ "line": 104,
"column": 17
},
"end": {
- "line": 103,
+ "line": 107,
"column": 3
}
},
@@ -264,11 +277,11 @@
"defaultMessage": "!!!Please restart Franz after changing proxy Settings.",
"file": "src/components/settings/services/EditServiceForm.js",
"start": {
- "line": 104,
+ "line": 108,
"column": 20
},
"end": {
- "line": 107,
+ "line": 111,
"column": 3
}
},
@@ -277,11 +290,11 @@
"defaultMessage": "!!!Proxy settings will not be synchronized with the Franz servers.",
"file": "src/components/settings/services/EditServiceForm.js",
"start": {
- "line": 108,
+ "line": 112,
"column": 13
},
"end": {
- "line": 111,
+ "line": 115,
"column": 3
}
}
diff --git a/src/i18n/messages/src/containers/settings/EditServiceScreen.json b/src/i18n/messages/src/containers/settings/EditServiceScreen.json
index 42ca42125..2d2a8a8b8 100644
--- a/src/i18n/messages/src/containers/settings/EditServiceScreen.json
+++ b/src/i18n/messages/src/containers/settings/EditServiceScreen.json
@@ -25,16 +25,29 @@
"column": 3
}
},
+ {
+ "id": "settings.service.form.enableHibernation",
+ "defaultMessage": "!!!Enable hibernation",
+ "file": "src/containers/settings/EditServiceScreen.js",
+ "start": {
+ "line": 36,
+ "column": 21
+ },
+ "end": {
+ "line": 39,
+ "column": 3
+ }
+ },
{
"id": "settings.service.form.enableNotification",
"defaultMessage": "!!!Enable Notifications",
"file": "src/containers/settings/EditServiceScreen.js",
"start": {
- "line": 36,
+ "line": 40,
"column": 22
},
"end": {
- "line": 39,
+ "line": 43,
"column": 3
}
},
@@ -43,11 +56,11 @@
"defaultMessage": "!!!Show unread message badges",
"file": "src/containers/settings/EditServiceScreen.js",
"start": {
- "line": 40,
+ "line": 44,
"column": 15
},
"end": {
- "line": 43,
+ "line": 47,
"column": 3
}
},
@@ -56,11 +69,11 @@
"defaultMessage": "!!!Enable audio",
"file": "src/containers/settings/EditServiceScreen.js",
"start": {
- "line": 44,
+ "line": 48,
"column": 15
},
"end": {
- "line": 47,
+ "line": 51,
"column": 3
}
},
@@ -69,11 +82,11 @@
"defaultMessage": "!!!Team",
"file": "src/containers/settings/EditServiceScreen.js",
"start": {
- "line": 48,
+ "line": 52,
"column": 8
},
"end": {
- "line": 51,
+ "line": 55,
"column": 3
}
},
@@ -82,11 +95,11 @@
"defaultMessage": "!!!Custom server",
"file": "src/containers/settings/EditServiceScreen.js",
"start": {
- "line": 52,
+ "line": 56,
"column": 13
},
"end": {
- "line": 55,
+ "line": 59,
"column": 3
}
},
@@ -95,11 +108,11 @@
"defaultMessage": "!!!Show message badge for all new messages",
"file": "src/containers/settings/EditServiceScreen.js",
"start": {
- "line": 56,
+ "line": 60,
"column": 20
},
"end": {
- "line": 59,
+ "line": 63,
"column": 3
}
},
@@ -108,11 +121,11 @@
"defaultMessage": "!!!Custom icon",
"file": "src/containers/settings/EditServiceScreen.js",
"start": {
- "line": 60,
+ "line": 64,
"column": 8
},
"end": {
- "line": 63,
+ "line": 67,
"column": 3
}
},
@@ -121,11 +134,11 @@
"defaultMessage": "!!!Enable Dark Mode",
"file": "src/containers/settings/EditServiceScreen.js",
"start": {
- "line": 64,
+ "line": 68,
"column": 18
},
"end": {
- "line": 67,
+ "line": 71,
"column": 3
}
},
@@ -134,11 +147,11 @@
"defaultMessage": "!!!Use Proxy",
"file": "src/containers/settings/EditServiceScreen.js",
"start": {
- "line": 68,
+ "line": 72,
"column": 15
},
"end": {
- "line": 71,
+ "line": 75,
"column": 3
}
},
@@ -147,11 +160,11 @@
"defaultMessage": "!!!Proxy Host/IP",
"file": "src/containers/settings/EditServiceScreen.js",
"start": {
- "line": 72,
+ "line": 76,
"column": 13
},
"end": {
- "line": 75,
+ "line": 79,
"column": 3
}
},
@@ -160,11 +173,11 @@
"defaultMessage": "!!!Port",
"file": "src/containers/settings/EditServiceScreen.js",
"start": {
- "line": 76,
+ "line": 80,
"column": 13
},
"end": {
- "line": 79,
+ "line": 83,
"column": 3
}
},
@@ -173,11 +186,11 @@
"defaultMessage": "!!!User",
"file": "src/containers/settings/EditServiceScreen.js",
"start": {
- "line": 80,
+ "line": 84,
"column": 13
},
"end": {
- "line": 83,
+ "line": 87,
"column": 3
}
},
@@ -186,11 +199,11 @@
"defaultMessage": "!!!Password",
"file": "src/containers/settings/EditServiceScreen.js",
"start": {
- "line": 84,
+ "line": 88,
"column": 17
},
"end": {
- "line": 87,
+ "line": 91,
"column": 3
}
}
diff --git a/src/models/Recipe.js b/src/models/Recipe.js
index 00c0f699f..bfec1de6d 100644
--- a/src/models/Recipe.js
+++ b/src/models/Recipe.js
@@ -38,6 +38,8 @@ export default class Recipe {
disablewebsecurity = false;
+ autoHibernate = false;
+
constructor(data) {
if (!data) {
throw Error('Recipe config not valid');
@@ -78,6 +80,8 @@ export default class Recipe {
this.disablewebsecurity = data.config.disablewebsecurity || this.disablewebsecurity;
+ this.autoHibernate = data.config.autoHibernate || this.autoHibernate;
+
this.message = data.config.message || this.message;
}
diff --git a/src/models/Service.js b/src/models/Service.js
index e45c39564..3829c29cd 100644
--- a/src/models/Service.js
+++ b/src/models/Service.js
@@ -1,6 +1,7 @@
import { computed, observable, autorun } from 'mobx';
import path from 'path';
import normalizeUrl from 'normalize-url';
+import moment from 'moment';
const debug = require('debug')('Franz:Service');
@@ -70,6 +71,12 @@ export default class Service {
@observable restrictionType = null;
+ @observable isHibernationEnabled = false;
+
+ @observable isHibernating = false;
+
+ @observable lastUsed = Date.now(); // timestamp
+
constructor(data, recipe) {
if (!data) {
console.error('Service config not valid');
@@ -113,6 +120,8 @@ export default class Service {
this.spellcheckerLanguage = data.spellcheckerLanguage !== undefined ? data.spellcheckerLanguage : this.spellcheckerLanguage;
+ this.isHibernationEnabled = data.isHibernationEnabled !== undefined ? data.isHibernationEnabled : this.isHibernationEnabled;
+
this.recipe = recipe;
autorun(() => {
diff --git a/src/stores/ServicesStore.js b/src/stores/ServicesStore.js
index 65e68e4d7..38aaa881d 100644
--- a/src/stores/ServicesStore.js
+++ b/src/stores/ServicesStore.js
@@ -7,6 +7,7 @@ import {
import { debounce, remove } from 'lodash';
import ms from 'ms';
import { remote } from 'electron';
+import moment from 'moment';
import Store from './lib/Store';
import Request from './lib/Request';
@@ -72,6 +73,8 @@ export default class ServicesStore extends Store {
this.actions.service.toggleAudio.listen(this._toggleAudio.bind(this));
this.actions.service.openDevTools.listen(this._openDevTools.bind(this));
this.actions.service.openDevToolsForActiveService.listen(this._openDevToolsForActiveService.bind(this));
+ this.actions.service.hibernate.listen(this._hibernate.bind(this));
+ this.actions.service.awake.listen(this._awake.bind(this));
this.registerReactions([
this._focusServiceReaction.bind(this),
@@ -100,6 +103,42 @@ export default class ServicesStore extends Store {
);
}
+ initialize() {
+ super.initialize();
+
+ // Check services to become hibernated
+ this._hibernationTick();
+ }
+
+ teardown() {
+ super.teardown();
+
+ // Stop checking services for hibernation
+ this._hibernationTick.cancel();
+ }
+
+ /**
+ * Сheck for services to become hibernated.
+ */
+ _hibernationTick = debounce(() => {
+ this._hibernateServices();
+ this._hibernationTick();
+ debug('Hibernation tick');
+ }, ms('1m')); // every 1 min
+
+ /**
+ * Defines which services should be hibernated.
+ */
+ _hibernateServices() {
+ this.all.forEach((service) => {
+ if (!service.isActive && (Date.now() - service.lastUsed > ms('5m'))) {
+ // If service is stale for 5 min, hibernate it.
+ this._hibernate({ serviceId: service.id });
+ }
+ });
+ }
+
+ // Computed props
@computed get all() {
if (this.stores.user.isLoggedIn) {
const services = this.allServicesRequest.execute().result;
@@ -300,6 +339,8 @@ export default class ServicesStore extends Store {
this.all[index].isActive = false;
});
service.isActive = true;
+ this._awake({ serviceId: service.id });
+ service.lastUsed = Date.now();
statsEvent('activate-service', service.recipe.id);
@@ -598,6 +639,24 @@ export default class ServicesStore extends Store {
}
}
+ @action _hibernate({ serviceId }) {
+ const service = this.one(serviceId);
+ if (service.isActive || !service.isHibernationEnabled) {
+ debug('Skipping service hibernation');
+ return;
+ }
+
+ debug(`Hibernate ${service.name}`);
+
+ service.isHibernating = true;
+ }
+
+ @action _awake({ serviceId }) {
+ const service = this.one(serviceId);
+ service.isHibernating = false;
+ service.liveFrom = Date.now();
+ }
+
// Reactions
_focusServiceReaction() {
const service = this.active;