From f720d3012e6bb55332c919d06114037239e77e07 Mon Sep 17 00:00:00 2001 From: Stefan Malzner Date: Mon, 26 Mar 2018 12:23:41 +0200 Subject: fix(Windows): Fix shortcuts for closing, minimizing and quitting the app --- src/lib/Menu.js | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/lib/Menu.js b/src/lib/Menu.js index 5a05e47b3..e7a525e36 100644 --- a/src/lib/Menu.js +++ b/src/lib/Menu.js @@ -431,14 +431,14 @@ const _titleBarTemplateFactory = intl => [ submenu: [ { label: intl.formatMessage(menuItems.minimize), - accelerator: 'Alt+M', + accelerator: 'Ctrl+M', click(menuItem, browserWindow) { browserWindow.minimize(); }, }, { label: intl.formatMessage(menuItems.close), - accelerator: 'Alt+W', + accelerator: 'Ctrl+W', click(menuItem, browserWindow) { browserWindow.close(); }, @@ -643,10 +643,7 @@ export default class FranzMenu { }, { label: intl.formatMessage(menuItems.quit), - accelerator: 'Alt+F4', - click: () => { - app.quit(); - }, + role: 'quit', }, ]; -- cgit v1.2.3-70-g09d2 From 1bbf9662e20f6c70250102584dcffa420b4e6d33 Mon Sep 17 00:00:00 2001 From: Stefan Malzner Date: Mon, 26 Mar 2018 14:23:15 +0200 Subject: fix(Windows): Fix shortcut to toggle fullscreen --- src/lib/Menu.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/lib/Menu.js b/src/lib/Menu.js index e7a525e36..884ad4786 100644 --- a/src/lib/Menu.js +++ b/src/lib/Menu.js @@ -415,7 +415,7 @@ const _titleBarTemplateFactory = intl => [ label: app.mainWindow.isFullScreen() // label doesn't work, gets overridden by Electron ? intl.formatMessage(menuItems.exitFullScreen) : intl.formatMessage(menuItems.enterFullScreen), - accelerator: `${ctrlKey}+F`, + accelerator: 'F11', click(menuItem, browserWindow) { browserWindow.setFullScreen(!browserWindow.isFullScreen()); }, -- cgit v1.2.3-70-g09d2 From 4d6bb5f10f8e2322ea6f81372ffe7cb3711c5ad5 Mon Sep 17 00:00:00 2001 From: Stefan Malzner Date: Mon, 26 Mar 2018 14:23:42 +0200 Subject: http is dead, long live https --- src/lib/Menu.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/lib/Menu.js b/src/lib/Menu.js index 884ad4786..19b8d5b15 100644 --- a/src/lib/Menu.js +++ b/src/lib/Menu.js @@ -450,7 +450,7 @@ const _titleBarTemplateFactory = intl => [ submenu: [ { label: intl.formatMessage(menuItems.learnMore), - click() { shell.openExternal('http://meetfranz.com'); }, + click() { shell.openExternal('https://meetfranz.com'); }, }, { label: intl.formatMessage(menuItems.changelog), @@ -461,7 +461,7 @@ const _titleBarTemplateFactory = intl => [ }, { label: intl.formatMessage(menuItems.support), - click() { shell.openExternal('http://meetfranz.com/support'); }, + click() { shell.openExternal('https://meetfranz.com/support'); }, }, { type: 'separator', -- cgit v1.2.3-70-g09d2 From 63d4281300ef86cbec6869bad3cbbb976c219c7d Mon Sep 17 00:00:00 2001 From: Stefan Malzner Date: Tue, 27 Mar 2018 15:19:44 +0200 Subject: Move "locale" to user data --- src/containers/settings/EditSettingsScreen.js | 10 +--------- src/models/User.js | 2 ++ src/stores/AppStore.js | 6 +++++- src/stores/UserStore.js | 20 ++++++++++++++++++++ 4 files changed, 28 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/containers/settings/EditSettingsScreen.js b/src/containers/settings/EditSettingsScreen.js index 1fa7ce8bc..e67c2964b 100644 --- a/src/containers/settings/EditSettingsScreen.js +++ b/src/containers/settings/EditSettingsScreen.js @@ -91,15 +91,13 @@ export default class EditSettingsScreen extends Component { showDisabledServices: settingsData.showDisabledServices, showMessageBadgeWhenMuted: settingsData.showMessageBadgeWhenMuted, enableSpellchecking: settingsData.enableSpellchecking, - // spellcheckingLanguage: settingsData.spellcheckingLanguage, - locale: settingsData.locale, - beta: settingsData.beta, }, }); user.update({ userData: { beta: settingsData.beta, + locale: settingsData.locale, }, }); } @@ -169,12 +167,6 @@ export default class EditSettingsScreen extends Component { value: settings.all.enableSpellchecking, default: DEFAULT_APP_SETTINGS.enableSpellchecking, }, - // spellcheckingLanguage: { - // label: intl.formatMessage(messages.spellcheckingLanguage), - // value: settings.all.spellcheckingLanguage, - // options: spellcheckerLocales, - // default: DEFAULT_APP_SETTINGS.spellcheckingLanguage, - // }, locale: { label: intl.formatMessage(messages.language), value: app.locale, diff --git a/src/models/User.js b/src/models/User.js index 2e5df4795..3e4aa187d 100644 --- a/src/models/User.js +++ b/src/models/User.js @@ -15,6 +15,7 @@ export default class User { @observable donor = {}; @observable isDonor = false; @observable isMiner = false; + @observable locale = false; constructor(data) { if (!data.id) { @@ -33,5 +34,6 @@ export default class User { this.isDonor = data.isDonor || this.isDonor; this.isSubscriptionOwner = data.isSubscriptionOwner || this.isSubscriptionOwner; this.isMiner = data.isMiner || this.isMiner; + this.locale = data.locale || this.locale; } } diff --git a/src/stores/AppStore.js b/src/stores/AppStore.js index 4ac8325d4..94ed308f3 100644 --- a/src/stores/AppStore.js +++ b/src/stores/AppStore.js @@ -15,6 +15,8 @@ import { gaEvent } from '../lib/analytics'; import { getServiceIdsFromPartitions, removeServicePartitionDirectory } from '../helpers/service-helpers.js'; +const debug = require('debug')('AppStore'); + const { app } = remote; const defaultLocale = DEFAULT_APP_SETTINGS.locale; @@ -279,13 +281,15 @@ export default class AppStore extends Store { } _setLocale() { - const locale = this.stores.settings.all.locale; + const { locale } = this.stores.user.data; if (locale && Object.prototype.hasOwnProperty.call(locales, locale) && locale !== this.locale) { this.locale = locale; } else if (!locale) { this.locale = this._getDefaultLocale(); } + + debug(`Set locale to "${this.locale}"`); } _getDefaultLocale() { diff --git a/src/stores/UserStore.js b/src/stores/UserStore.js index 7dbbd955b..c151f6d59 100644 --- a/src/stores/UserStore.js +++ b/src/stores/UserStore.js @@ -9,6 +9,8 @@ import Request from './lib/Request'; import CachedRequest from './lib/CachedRequest'; import { gaEvent } from '../lib/analytics'; +const debug = require('debug')('UserStore'); + // TODO: split stores into UserStore and AuthStore export default class UserStore extends Store { BASE_ROUTE = '/auth'; @@ -69,6 +71,11 @@ export default class UserStore extends Store { ]); } + setup() { + // Data migration + this._migrateUserLocale(); + } + // Routes get loginRoute() { return this.LOGIN_ROUTE; @@ -292,4 +299,17 @@ export default class UserStore extends Store { this.id = null; } } + + async _migrateUserLocale() { + await this.getUserInfoRequest._promise; + + if (!this.data.locale) { + debug('Migrate "locale" to user data'); + this.actions.user.update({ + userData: { + locale: this.stores.app.locale, + }, + }); + } + } } -- cgit v1.2.3-70-g09d2 From 8aab8699e02ed9ec736bb6dfab0edd3fe9156c8d Mon Sep 17 00:00:00 2001 From: Stefan Malzner Date: Tue, 27 Mar 2018 21:25:56 +0200 Subject: Split settings into multiple stores; app specific settings are now stored in config file --- src/actions/settings.js | 4 ++- src/api/LocalApi.js | 8 +++++ src/api/server/LocalApi.js | 19 +++++++++++- src/config.js | 8 ++++- src/containers/layout/AppLayoutContainer.js | 6 ++-- src/containers/settings/EditSettingsScreen.js | 32 +++++++------------- src/electron/Settings.js | 34 ++++++++++++++++++--- src/electron/ipc-api/settings.js | 8 +++++ src/models/Settings.js | 43 +++++++++++++++++++-------- src/stores/AppStore.js | 17 ++++++----- src/stores/ServicesStore.js | 15 +++++----- src/stores/SettingsStore.js | 41 ++++++++++++++++--------- src/stores/UIStore.js | 2 +- 13 files changed, 164 insertions(+), 73 deletions(-) (limited to 'src') diff --git a/src/actions/settings.js b/src/actions/settings.js index 3d53cd674..fd29b798b 100644 --- a/src/actions/settings.js +++ b/src/actions/settings.js @@ -2,9 +2,11 @@ import PropTypes from 'prop-types'; export default { update: { - settings: PropTypes.object.isRequired, + type: PropTypes.string.isRequired, + data: PropTypes.object.isRequired, }, remove: { + type: PropTypes.string.isRequired, key: PropTypes.string.isRequired, }, }; diff --git a/src/api/LocalApi.js b/src/api/LocalApi.js index 59d7d8fa2..741917104 100644 --- a/src/api/LocalApi.js +++ b/src/api/LocalApi.js @@ -4,6 +4,14 @@ export default class LocalApi { this.local = local; } + getAppSettings() { + return this.local.getAppSettings(); + } + + updateAppSettings(data) { + return this.local.updateAppSettings(data); + } + getAppCacheSize() { return this.local.getAppCacheSize(); } diff --git a/src/api/server/LocalApi.js b/src/api/server/LocalApi.js index 4d2497c61..78deb7aa5 100644 --- a/src/api/server/LocalApi.js +++ b/src/api/server/LocalApi.js @@ -1,4 +1,4 @@ -import { remote } from 'electron'; +import { ipcRenderer, remote } from 'electron'; import du from 'du'; import { getServicePartitionsDirectory } from '../../helpers/service-helpers.js'; @@ -8,6 +8,23 @@ const debug = require('debug')('LocalApi'); const { session } = remote; export default class LocalApi { + // Settings + getAppSettings() { + return new Promise((resolve) => { + ipcRenderer.once('appSettings', (event, data) => { + debug('LocalApi::getAppSettings resolves', data); + resolve(data); + }); + + ipcRenderer.send('getAppSettings'); + }); + } + + async updateAppSettings(data) { + debug('LocalApi::updateAppSettings resolves', data); + ipcRenderer.send('updateAppSettings', data); + } + // Services async getAppCacheSize() { const partitionsDir = getServicePartitionsDirectory(); diff --git a/src/config.js b/src/config.js index e66594c59..366231f79 100644 --- a/src/config.js +++ b/src/config.js @@ -1,3 +1,8 @@ +import electron from 'electron'; +import path from 'path'; + +const app = process.type === 'renderer' ? electron.remote.app : electron.app; + export const CHECK_INTERVAL = 1000 * 3600; // How often should we perform checks export const LOCAL_API = 'http://localhost:3000'; export const DEV_API = 'https://dev.franzinfra.com'; @@ -13,7 +18,6 @@ export const DEFAULT_APP_SETTINGS = { showDisabledServices: true, showMessageBadgeWhenMuted: true, enableSpellchecking: true, - // spellcheckingLanguage: 'auto', locale: '', fallbackLocale: 'en-US', beta: false, @@ -22,3 +26,5 @@ export const DEFAULT_APP_SETTINGS = { export const FRANZ_SERVICE_REQUEST = 'http://bit.ly/franz-service-request'; export const FRANZ_TRANSLATION = 'http://bit.ly/franz-translate'; + +export const SETTINGS_PATH = path.join(app.getPath('userData'), 'config', 'settings.json'); diff --git a/src/containers/layout/AppLayoutContainer.js b/src/containers/layout/AppLayoutContainer.js index 075bd5e34..0931738fd 100644 --- a/src/containers/layout/AppLayoutContainer.js +++ b/src/containers/layout/AppLayoutContainer.js @@ -77,7 +77,7 @@ export default class AppLayoutContainer extends Component { ); @@ -99,7 +99,7 @@ export default class AppLayoutContainer extends Component { setWebviewReference={setWebviewReference} openWindow={openWindow} reload={reload} - isAppMuted={settings.all.isAppMuted} + isAppMuted={settings.all.app.isAppMuted} update={updateService} /> ); diff --git a/src/containers/settings/EditSettingsScreen.js b/src/containers/settings/EditSettingsScreen.js index e67c2964b..1bd147099 100644 --- a/src/containers/settings/EditSettingsScreen.js +++ b/src/containers/settings/EditSettingsScreen.js @@ -55,10 +55,6 @@ const messages = defineMessages({ id: 'settings.app.form.spellcheckingLanguage', defaultMessage: '!!!Language for spell checking', }, - // spellcheckingAutomaticDetection: { - // id: 'settings.app.form.spellcheckingAutomaticDetection', - // defaultMessage: '!!!Detect language automatically', - // }, beta: { id: 'settings.app.form.beta', defaultMessage: '!!!Include beta versions', @@ -84,13 +80,16 @@ export default class EditSettingsScreen extends Component { }); settings.update({ - settings: { + type: 'app', + data: { runInBackground: settingsData.runInBackground, enableSystemTray: settingsData.enableSystemTray, minimizeToSystemTray: settingsData.minimizeToSystemTray, showDisabledServices: settingsData.showDisabledServices, showMessageBadgeWhenMuted: settingsData.showMessageBadgeWhenMuted, enableSpellchecking: settingsData.enableSpellchecking, + beta: settingsData.beta, // we need this info in the main process as well + locale: settingsData.locale, // we need this info in the main process as well }, }); @@ -114,17 +113,6 @@ export default class EditSettingsScreen extends Component { }); }); - // const spellcheckerLocales = [{ - // value: 'auto', - // label: intl.formatMessage(messages.spellcheckingAutomaticDetection), - // }]; - // Object.keys(SPELLCHECKER_LOCALES).forEach((key) => { - // spellcheckerLocales.push({ - // value: key, - // label: SPELLCHECKER_LOCALES[key], - // }); - // }); - const config = { fields: { autoLaunchOnStart: { @@ -139,32 +127,32 @@ export default class EditSettingsScreen extends Component { }, runInBackground: { label: intl.formatMessage(messages.runInBackground), - value: settings.all.runInBackground, + value: settings.all.app.runInBackground, default: DEFAULT_APP_SETTINGS.runInBackground, }, enableSystemTray: { label: intl.formatMessage(messages.enableSystemTray), - value: settings.all.enableSystemTray, + value: settings.all.app.enableSystemTray, default: DEFAULT_APP_SETTINGS.enableSystemTray, }, minimizeToSystemTray: { label: intl.formatMessage(messages.minimizeToSystemTray), - value: settings.all.minimizeToSystemTray, + value: settings.all.app.minimizeToSystemTray, default: DEFAULT_APP_SETTINGS.minimizeToSystemTray, }, showDisabledServices: { label: intl.formatMessage(messages.showDisabledServices), - value: settings.all.showDisabledServices, + value: settings.all.app.showDisabledServices, default: DEFAULT_APP_SETTINGS.showDisabledServices, }, showMessageBadgeWhenMuted: { label: intl.formatMessage(messages.showMessageBadgeWhenMuted), - value: settings.all.showMessageBadgeWhenMuted, + value: settings.all.app.showMessageBadgeWhenMuted, default: DEFAULT_APP_SETTINGS.showMessageBadgeWhenMuted, }, enableSpellchecking: { label: intl.formatMessage(messages.enableSpellchecking), - value: settings.all.enableSpellchecking, + value: settings.all.app.enableSpellchecking, default: DEFAULT_APP_SETTINGS.enableSpellchecking, }, locale: { diff --git a/src/electron/Settings.js b/src/electron/Settings.js index 824b4c20c..e24aefdbb 100644 --- a/src/electron/Settings.js +++ b/src/electron/Settings.js @@ -1,27 +1,53 @@ -import { observable } from 'mobx'; +import { observable, toJS } from 'mobx'; +import { pathExistsSync, outputJsonSync, readJsonSync } from 'fs-extra'; -import { DEFAULT_APP_SETTINGS } from '../config'; +import { SETTINGS_PATH, DEFAULT_APP_SETTINGS } from '../config'; + +const debug = require('debug')('Settings'); export default class Settings { @observable store = { - autoLaunchOnStart: DEFAULT_APP_SETTINGS.autoLaunchOnStart, autoLaunchInBackground: DEFAULT_APP_SETTINGS.autoLaunchInBackground, runInBackground: DEFAULT_APP_SETTINGS.runInBackground, enableSystemTray: DEFAULT_APP_SETTINGS.enableSystemTray, minimizeToSystemTray: DEFAULT_APP_SETTINGS.minimizeToSystemTray, locale: DEFAULT_APP_SETTINGS.locale, beta: DEFAULT_APP_SETTINGS.beta, + isAppMuted: DEFAULT_APP_SETTINGS.isAppMuted, + showMessageBadgeWhenMuted: DEFAULT_APP_SETTINGS.showMessageBadgeWhenMuted, + showDisabledServices: DEFAULT_APP_SETTINGS.showDisabledServices, + enableSpellchecking: DEFAULT_APP_SETTINGS.enableSpellchecking, }; + constructor() { + if (!pathExistsSync(SETTINGS_PATH)) { + this._writeFile(); + } else { + this._hydrate(); + } + } + set(settings) { this.store = Object.assign(this.store, settings); + + this._writeFile(); } - all() { + get all() { return this.store; } get(key) { return this.store[key]; } + + _hydrate() { + this.store = readJsonSync(SETTINGS_PATH); + debug('Hydrate store', toJS(this.store)); + } + + _writeFile() { + outputJsonSync(SETTINGS_PATH, this.store); + debug('Write settings file', toJS(this.store)); + } } diff --git a/src/electron/ipc-api/settings.js b/src/electron/ipc-api/settings.js index 995b28fbd..00bdc0113 100644 --- a/src/electron/ipc-api/settings.js +++ b/src/electron/ipc-api/settings.js @@ -4,4 +4,12 @@ export default (params) => { ipcMain.on('settings', (event, args) => { params.settings.set(args); }); + + ipcMain.on('getAppSettings', () => { + params.mainWindow.webContents.send('appSettings', params.settings.all); + }); + + ipcMain.on('updateAppSettings', (event, args) => { + params.settings.set(args); + }); }; diff --git a/src/models/Settings.js b/src/models/Settings.js index e39b63087..f58c05b38 100644 --- a/src/models/Settings.js +++ b/src/models/Settings.js @@ -2,19 +2,38 @@ import { observable, extendObservable } from 'mobx'; import { DEFAULT_APP_SETTINGS } from '../config'; export default class Settings { - @observable autoLaunchInBackground = DEFAULT_APP_SETTINGS.autoLaunchInBackground; - @observable runInBackground = DEFAULT_APP_SETTINGS.runInBackground; - @observable enableSystemTray = DEFAULT_APP_SETTINGS.enableSystemTray; - @observable minimizeToSystemTray = DEFAULT_APP_SETTINGS.minimizeToSystemTray; - @observable showDisabledServices = DEFAULT_APP_SETTINGS.showDisabledServices; - @observable showMessageBadgeWhenMuted = DEFAULT_APP_SETTINGS.showMessageBadgeWhenMuted; - @observable enableSpellchecking = DEFAULT_APP_SETTINGS.enableSpellchecking; - @observable locale = DEFAULT_APP_SETTINGS.locale; - @observable beta = DEFAULT_APP_SETTINGS.beta; - @observable isAppMuted = DEFAULT_APP_SETTINGS.isAppMuted; + @observable app = { + autoLaunchInBackground: DEFAULT_APP_SETTINGS.autoLaunchInBackground, + runInBackground: DEFAULT_APP_SETTINGS.runInBackground, + enableSystemTray: DEFAULT_APP_SETTINGS.enableSystemTray, + minimizeToSystemTray: DEFAULT_APP_SETTINGS.minimizeToSystemTray, + isAppMuted: DEFAULT_APP_SETTINGS.isAppMuted, + showMessageBadgeWhenMuted: DEFAULT_APP_SETTINGS.showMessageBadgeWhenMuted, + showDisabledServices: DEFAULT_APP_SETTINGS.showDisabledServices, + enableSpellchecking: DEFAULT_APP_SETTINGS.enableSpellchecking, + locale: DEFAULT_APP_SETTINGS.locale, + beta: DEFAULT_APP_SETTINGS.beta, - constructor(data) { - Object.assign(this, data); + } + + @observable service = { + activeService: DEFAULT_APP_SETTINGS.autoLaunchInBackground, + } + + @observable group = { + collapsed: [], + disabled: [], + } + + @observable stats = { + appStarts: 0, + } + + constructor({ app, service, group, stats }) { + Object.assign(this.app, app); + Object.assign(this.service, service); + Object.assign(this.group, group); + Object.assign(this.stats, stats); } update(data) { diff --git a/src/stores/AppStore.js b/src/stores/AppStore.js index 94ed308f3..3c6c24b59 100644 --- a/src/stores/AppStore.js +++ b/src/stores/AppStore.js @@ -159,7 +159,7 @@ export default class AppStore extends Store { // Actions @action _notify({ title, options, notificationId, serviceId = null }) { - if (this.stores.settings.all.isAppMuted) return; + if (this.stores.settings.all.app.isAppMuted) return; const notification = new window.Notification(title, options); notification.onclick = (e) => { @@ -240,14 +240,15 @@ export default class AppStore extends Store { this.isSystemMuteOverridden = overrideSystemMute; this.actions.settings.update({ - settings: { + type: 'app', + data: { isAppMuted: isMuted, }, }); } @action _toggleMuteApp() { - this._muteApp({ isMuted: !this.stores.settings.all.isAppMuted }); + this._muteApp({ isMuted: !this.stores.settings.all.app.isAppMuted }); } @action async _clearAllCache() { @@ -331,8 +332,9 @@ export default class AppStore extends Store { // Helpers _appStartsCounter() { this.actions.settings.update({ - settings: { - appStarts: (this.stores.settings.all.appStarts || 0) + 1, + type: 'stats', + data: { + appStarts: (this.stores.settings.all.stats.appStarts || 0) + 1, }, }); } @@ -340,7 +342,8 @@ export default class AppStore extends Store { async _autoStart() { this.autoLaunchOnStart = await this._checkAutoStart(); - if (this.stores.settings.all.appStarts === 1) { + if (this.stores.settings.all.stats.appStarts === 1) { + debug('Set app to launch on start'); this.actions.app.launchOnStartup({ enable: true, }); @@ -353,7 +356,7 @@ export default class AppStore extends Store { _systemDND() { const dnd = getDoNotDisturb(); - if (dnd !== this.stores.settings.all.isAppMuted && !this.isSystemMuteOverridden) { + if (dnd !== this.stores.settings.all.app.isAppMuted && !this.isSystemMuteOverridden) { this.actions.app.muteApp({ isMuted: dnd, overrideSystemMute: false, diff --git a/src/stores/ServicesStore.js b/src/stores/ServicesStore.js index f7d92b1ff..b96bc506b 100644 --- a/src/stores/ServicesStore.js +++ b/src/stores/ServicesStore.js @@ -86,13 +86,13 @@ export default class ServicesStore extends Store { } @computed get allDisplayed() { - return this.stores.settings.all.showDisabledServices ? this.all : this.enabled; + return this.stores.settings.all.service.showDisabledServices ? this.all : this.enabled; } // This is just used to avoid unnecessary rerendering of resource-heavy webviews @computed get allDisplayedUnordered() { const services = this.allServicesRequest.execute().result || []; - return this.stores.settings.all.showDisabledServices ? services : services.filter(service => service.isEnabled); + return this.stores.settings.all.service.showDisabledServices ? services : services.filter(service => service.isEnabled); } @computed get filtered() { @@ -334,7 +334,7 @@ export default class ServicesStore extends Store { }); } else if (channel === 'notification') { const options = args[0].options; - if (service.recipe.hasNotificationSound || service.isMuted || this.stores.settings.all.isAppMuted) { + if (service.recipe.hasNotificationSound || service.isMuted || this.stores.settings.all.app.isAppMuted) { Object.assign(options, { silent: true, }); @@ -434,7 +434,7 @@ export default class ServicesStore extends Store { } @action _reorder({ oldIndex, newIndex }) { - const showDisabledServices = this.stores.settings.all.showDisabledServices; + const showDisabledServices = this.stores.settings.all.service.showDisabledServices; const oldEnabledSortIndex = showDisabledServices ? oldIndex : this.all.indexOf(this.enabled[oldIndex]); const newEnabledSortIndex = showDisabledServices ? newIndex : this.all.indexOf(this.enabled[newIndex]); @@ -512,7 +512,8 @@ export default class ServicesStore extends Store { if (service) { this.actions.settings.update({ - settings: { + type: 'service', + data: { activeService: service.id, }, }); @@ -520,7 +521,7 @@ export default class ServicesStore extends Store { } _mapActiveServiceToServiceModelReaction() { - const { activeService } = this.stores.settings.all; + const { activeService } = this.stores.settings.all.service; if (this.allDisplayed.length) { this.allDisplayed.map(service => Object.assign(service, { isActive: activeService ? activeService === service.id : this.allDisplayed[0].id === service.id, @@ -529,7 +530,7 @@ export default class ServicesStore extends Store { } _getUnreadMessageCountReaction() { - const showMessageBadgeWhenMuted = this.stores.settings.all.showMessageBadgeWhenMuted; + const showMessageBadgeWhenMuted = this.stores.settings.all.app.showMessageBadgeWhenMuted; const showMessageBadgesEvenWhenMuted = this.stores.ui.showMessageBadgesEvenWhenMuted; const unreadDirectMessageCount = this.allDisplayed diff --git a/src/stores/SettingsStore.js b/src/stores/SettingsStore.js index b7d803398..b3f5d3eaf 100644 --- a/src/stores/SettingsStore.js +++ b/src/stores/SettingsStore.js @@ -1,12 +1,18 @@ import { ipcRenderer } from 'electron'; -import { action, computed } from 'mobx'; +import { action, computed, observable } from 'mobx'; import localStorage from 'mobx-localstorage'; import Store from './lib/Store'; -import { gaEvent } from '../lib/analytics'; import SettingsModel from '../models/Settings'; +import Request from './lib/Request'; +import CachedRequest from './lib/CachedRequest'; + +const debug = require('debug')('SettingsStore'); export default class SettingsStore extends Store { + @observable appSettingsRequest = new CachedRequest(this.api.local, 'getAppSettings'); + @observable updateAppSettingsRequest = new Request(this.api.local, 'updateAppSettings'); + constructor(...args) { super(...args); @@ -15,22 +21,29 @@ export default class SettingsStore extends Store { this.actions.settings.remove.listen(this._remove.bind(this)); } - setup() { - this._shareSettingsWithMainProcess(); - } - @computed get all() { - return new SettingsModel(localStorage.getItem('app') || {}); + return new SettingsModel({ + app: this.appSettingsRequest.execute().result || {}, + service: localStorage.getItem('service') || {}, + group: localStorage.getItem('group') || {}, + stats: localStorage.getItem('stats') || {}, + }); } - @action async _update({ settings }) { + @action async _update({ type, data }) { + debug('Update settings', type, data, this.all); const appSettings = this.all; - localStorage.setItem('app', Object.assign(appSettings, settings)); - - // We need a little hack to wait until everything is patched - setTimeout(() => this._shareSettingsWithMainProcess(), 0); - - gaEvent('Settings', 'update'); + if (type !== 'app') { + localStorage.setItem(type, Object.assign(appSettings[type], data)); + } else { + debug('Store app settings on file system', type, data); + this.updateAppSettingsRequest.execute(data); + + this.appSettingsRequest.patch((result) => { + if (!result) return; + Object.assign(result, data); + }); + } } @action async _remove({ key }) { diff --git a/src/stores/UIStore.js b/src/stores/UIStore.js index 5e9cc9ba7..b391bdcae 100644 --- a/src/stores/UIStore.js +++ b/src/stores/UIStore.js @@ -17,7 +17,7 @@ export default class UIStore extends Store { @computed get showMessageBadgesEvenWhenMuted() { const settings = this.stores.settings.all; - return (settings.isAppMuted && settings.showMessageBadgeWhenMuted) || !settings.isAppMuted; + return (settings.app.isAppMuted && settings.app.showMessageBadgeWhenMuted) || !settings.isAppMuted; } // Actions -- cgit v1.2.3-70-g09d2 From ca66ab1a3b53ec677e0eba69e9feea3ad777c3bf Mon Sep 17 00:00:00 2001 From: Stefan Malzner Date: Wed, 28 Mar 2018 10:03:34 +0200 Subject: Add settings migration --- src/models/Settings.js | 5 +++- src/stores/SettingsStore.js | 65 ++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 62 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/models/Settings.js b/src/models/Settings.js index f58c05b38..6b41b5d95 100644 --- a/src/models/Settings.js +++ b/src/models/Settings.js @@ -29,11 +29,14 @@ export default class Settings { appStarts: 0, } - constructor({ app, service, group, stats }) { + @observable migration = {} + + constructor({ app, service, group, stats, migration }) { Object.assign(this.app, app); Object.assign(this.service, service); Object.assign(this.group, group); Object.assign(this.stats, stats); + Object.assign(this.migration, migration); } update(data) { diff --git a/src/stores/SettingsStore.js b/src/stores/SettingsStore.js index b3f5d3eaf..075cb6482 100644 --- a/src/stores/SettingsStore.js +++ b/src/stores/SettingsStore.js @@ -21,22 +21,27 @@ export default class SettingsStore extends Store { this.actions.settings.remove.listen(this._remove.bind(this)); } + setup() { + this._migrate(); + } + @computed get all() { return new SettingsModel({ app: this.appSettingsRequest.execute().result || {}, service: localStorage.getItem('service') || {}, group: localStorage.getItem('group') || {}, stats: localStorage.getItem('stats') || {}, + migration: localStorage.getItem('migration') || {}, }); } @action async _update({ type, data }) { - debug('Update settings', type, data, this.all); const appSettings = this.all; if (type !== 'app') { + debug('Update settings', type, data, this.all); localStorage.setItem(type, Object.assign(appSettings[type], data)); } else { - debug('Store app settings on file system', type, data); + debug('Update settings on file system', type, data); this.updateAppSettingsRequest.execute(data); this.appSettingsRequest.patch((result) => { @@ -46,18 +51,64 @@ export default class SettingsStore extends Store { } } - @action async _remove({ key }) { - const appSettings = this.all; + @action async _remove({ type, key }) { + if (type === 'app') return; // app keys can't be deleted + + const appSettings = this.all[type]; if (Object.hasOwnProperty.call(appSettings, key)) { delete appSettings[key]; - localStorage.setItem('app', appSettings); - } - this._shareSettingsWithMainProcess(); + this.actions.settings.update({ + type, + data: appSettings, + }); + } } // Reactions _shareSettingsWithMainProcess() { ipcRenderer.send('settings', this.all); } + + // Helper + _migrate() { + const legacySettings = localStorage.getItem('app'); + + if (!this.all.migration['5.0.0-beta.17-settings']) { + this.actions.settings.update({ + type: 'app', + data: { + autoLaunchInBackground: legacySettings.autoLaunchInBackground, + runInBackground: legacySettings.runInBackground, + enableSystemTray: legacySettings.enableSystemTray, + minimizeToSystemTray: legacySettings.minimizeToSystemTray, + isAppMuted: legacySettings.isAppMuted, + enableGPUAcceleration: legacySettings.enableGPUAcceleration, + showMessageBadgeWhenMuted: legacySettings.showMessageBadgeWhenMuted, + showDisabledServices: legacySettings.showDisabledServices, + enableSpellchecking: legacySettings.enableSpellchecking, + locale: legacySettings.locale, + beta: legacySettings.beta, + }, + }); + + this.actions.settings.update({ + type: 'service', + data: { + activeService: legacySettings.activeService, + }, + }); + + this.actions.settings.update({ + type: 'migration', + data: { + '5.0.0-beta.17-settings': true, + }, + }); + + localStorage.removeItem('app'); + + debug('Migrated settings to split stores'); + } + } } -- cgit v1.2.3-70-g09d2 From b3cfa16fb079ddbc339316fc09324cb7ebe7abf0 Mon Sep 17 00:00:00 2001 From: Stefan Malzner Date: Wed, 28 Mar 2018 10:05:00 +0200 Subject: Add beta and locale settings to app config as well --- src/stores/UserStore.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/stores/UserStore.js b/src/stores/UserStore.js index c151f6d59..574616925 100644 --- a/src/stores/UserStore.js +++ b/src/stores/UserStore.js @@ -263,8 +263,10 @@ export default class UserStore extends Store { // We need to set the beta flag for the SettingsStore this.actions.settings.update({ - settings: { + type: 'app', + data: { beta: data.beta, + locale: data.locale, }, }); } -- cgit v1.2.3-70-g09d2 From 04145fb473d929c6b80fcb9a66c635a6af7382a8 Mon Sep 17 00:00:00 2001 From: Stefan Malzner Date: Wed, 28 Mar 2018 10:07:31 +0200 Subject: Fix issue with endless data fetching loop when user logs out --- src/stores/AppStore.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/stores/AppStore.js b/src/stores/AppStore.js index 3c6c24b59..ea1e71bdc 100644 --- a/src/stores/AppStore.js +++ b/src/stores/AppStore.js @@ -282,7 +282,11 @@ export default class AppStore extends Store { } _setLocale() { - const { locale } = this.stores.user.data; + let locale; + if (this.stores.user.isLoggedIn) { + locale = this.stores.user.data.locale; + } + if (locale && Object.prototype.hasOwnProperty.call(locales, locale) && locale !== this.locale) { this.locale = locale; -- cgit v1.2.3-70-g09d2 From 2de13b5161c82e5857edb13d4a971a31b7628f0b Mon Sep 17 00:00:00 2001 From: Stefan Malzner Date: Wed, 28 Mar 2018 10:11:12 +0200 Subject: Set correct keys for new settings store --- src/stores/ServicesStore.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/stores/ServicesStore.js b/src/stores/ServicesStore.js index b96bc506b..6dfc114f5 100644 --- a/src/stores/ServicesStore.js +++ b/src/stores/ServicesStore.js @@ -86,13 +86,13 @@ export default class ServicesStore extends Store { } @computed get allDisplayed() { - return this.stores.settings.all.service.showDisabledServices ? this.all : this.enabled; + return this.stores.settings.all.app.showDisabledServices ? this.all : this.enabled; } // This is just used to avoid unnecessary rerendering of resource-heavy webviews @computed get allDisplayedUnordered() { const services = this.allServicesRequest.execute().result || []; - return this.stores.settings.all.service.showDisabledServices ? services : services.filter(service => service.isEnabled); + return this.stores.settings.all.app.showDisabledServices ? services : services.filter(service => service.isEnabled); } @computed get filtered() { @@ -434,7 +434,7 @@ export default class ServicesStore extends Store { } @action _reorder({ oldIndex, newIndex }) { - const showDisabledServices = this.stores.settings.all.service.showDisabledServices; + const showDisabledServices = this.stores.settings.all.app.showDisabledServices; const oldEnabledSortIndex = showDisabledServices ? oldIndex : this.all.indexOf(this.enabled[oldIndex]); const newEnabledSortIndex = showDisabledServices ? newIndex : this.all.indexOf(this.enabled[newIndex]); @@ -554,7 +554,10 @@ export default class ServicesStore extends Store { _logoutReaction() { if (!this.stores.user.isLoggedIn) { - this.actions.settings.remove({ key: 'activeService' }); + this.actions.settings.remove({ + type: 'service', + key: 'activeService', + }); this.allServicesRequest.invalidate().reset(); } } @@ -562,7 +565,7 @@ export default class ServicesStore extends Store { _shareSettingsWithServiceProcess() { this.actions.service.sendIPCMessageToAllServices({ channel: 'settings-update', - args: this.stores.settings.all, + args: this.stores.settings.all.app, }); } -- cgit v1.2.3-70-g09d2 From 5398846e58d87f5ef54ccc19f4ccb318166ef621 Mon Sep 17 00:00:00 2001 From: Stefan Malzner Date: Wed, 28 Mar 2018 10:12:03 +0200 Subject: Replace share settings with webviews autorun with single key reaction --- src/stores/ServicesStore.js | 14 +++++++++----- src/webview/plugin.js | 6 +++++- 2 files changed, 14 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/stores/ServicesStore.js b/src/stores/ServicesStore.js index 6dfc114f5..ccb85421a 100644 --- a/src/stores/ServicesStore.js +++ b/src/stores/ServicesStore.js @@ -1,8 +1,5 @@ -// import { remote } from 'electron'; -import { action, computed, observable } from 'mobx'; +import { action, reaction, computed, observable } from 'mobx'; import { debounce, remove } from 'lodash'; -// import path from 'path'; -// import fs from 'fs-extra'; import Store from './lib/Store'; import Request from './lib/Request'; @@ -63,13 +60,20 @@ export default class ServicesStore extends Store { this._mapActiveServiceToServiceModelReaction.bind(this), this._saveActiveService.bind(this), this._logoutReaction.bind(this), - this._shareSettingsWithServiceProcess.bind(this), ]); // Just bind this this._initializeServiceRecipeInWebview.bind(this); } + setup() { + // Single key reactions + reaction( + () => this.stores.settings.all.app.enableSpellchecking, + () => this._shareSettingsWithServiceProcess(), + ); + } + @computed get all() { if (this.stores.user.isLoggedIn) { const services = this.allServicesRequest.execute().result; diff --git a/src/webview/plugin.js b/src/webview/plugin.js index 52b19b3fd..c6530fef6 100644 --- a/src/webview/plugin.js +++ b/src/webview/plugin.js @@ -8,6 +8,8 @@ import RecipeWebview from './lib/RecipeWebview'; import Spellchecker from './spellchecker'; import './notifications'; +const debug = require('debug')('Plugin'); + ipcRenderer.on('initializeRecipe', (e, data) => { const modulePath = path.join(data.recipe.path, 'webview.js'); // Delete module from cache @@ -15,8 +17,9 @@ ipcRenderer.on('initializeRecipe', (e, data) => { try { // eslint-disable-next-line require(modulePath)(new RecipeWebview(), data); + debug('Initialize Recipe'); } catch (err) { - console.error(err); + debug('Recipe initialization failed', err); } }); @@ -31,6 +34,7 @@ new ContextMenuListener((info) => { // eslint-disable-line ipcRenderer.on('settings-update', (e, data) => { spellchecker.toggleSpellchecker(data.enableSpellchecking); + debug('Settings update received', data); }); // initSpellche -- cgit v1.2.3-70-g09d2 From de06f275bc0ba6ddbe7627895efc3f8827bb6880 Mon Sep 17 00:00:00 2001 From: Stefan Malzner Date: Wed, 28 Mar 2018 10:44:43 +0200 Subject: Don't repeat yourself --- src/electron/Settings.js | 13 +------------ src/models/Settings.js | 14 +------------- 2 files changed, 2 insertions(+), 25 deletions(-) (limited to 'src') diff --git a/src/electron/Settings.js b/src/electron/Settings.js index e24aefdbb..b3138e948 100644 --- a/src/electron/Settings.js +++ b/src/electron/Settings.js @@ -6,18 +6,7 @@ import { SETTINGS_PATH, DEFAULT_APP_SETTINGS } from '../config'; const debug = require('debug')('Settings'); export default class Settings { - @observable store = { - autoLaunchInBackground: DEFAULT_APP_SETTINGS.autoLaunchInBackground, - runInBackground: DEFAULT_APP_SETTINGS.runInBackground, - enableSystemTray: DEFAULT_APP_SETTINGS.enableSystemTray, - minimizeToSystemTray: DEFAULT_APP_SETTINGS.minimizeToSystemTray, - locale: DEFAULT_APP_SETTINGS.locale, - beta: DEFAULT_APP_SETTINGS.beta, - isAppMuted: DEFAULT_APP_SETTINGS.isAppMuted, - showMessageBadgeWhenMuted: DEFAULT_APP_SETTINGS.showMessageBadgeWhenMuted, - showDisabledServices: DEFAULT_APP_SETTINGS.showDisabledServices, - enableSpellchecking: DEFAULT_APP_SETTINGS.enableSpellchecking, - }; + @observable store = DEFAULT_APP_SETTINGS; constructor() { if (!pathExistsSync(SETTINGS_PATH)) { diff --git a/src/models/Settings.js b/src/models/Settings.js index 6b41b5d95..942232d12 100644 --- a/src/models/Settings.js +++ b/src/models/Settings.js @@ -2,19 +2,7 @@ import { observable, extendObservable } from 'mobx'; import { DEFAULT_APP_SETTINGS } from '../config'; export default class Settings { - @observable app = { - autoLaunchInBackground: DEFAULT_APP_SETTINGS.autoLaunchInBackground, - runInBackground: DEFAULT_APP_SETTINGS.runInBackground, - enableSystemTray: DEFAULT_APP_SETTINGS.enableSystemTray, - minimizeToSystemTray: DEFAULT_APP_SETTINGS.minimizeToSystemTray, - isAppMuted: DEFAULT_APP_SETTINGS.isAppMuted, - showMessageBadgeWhenMuted: DEFAULT_APP_SETTINGS.showMessageBadgeWhenMuted, - showDisabledServices: DEFAULT_APP_SETTINGS.showDisabledServices, - enableSpellchecking: DEFAULT_APP_SETTINGS.enableSpellchecking, - locale: DEFAULT_APP_SETTINGS.locale, - beta: DEFAULT_APP_SETTINGS.beta, - - } + @observable app = DEFAULT_APP_SETTINGS @observable service = { activeService: DEFAULT_APP_SETTINGS.autoLaunchInBackground, -- cgit v1.2.3-70-g09d2 From d411dc029aa79a86b8b5f710a54cea80afb7b5f3 Mon Sep 17 00:00:00 2001 From: Stefan Malzner Date: Wed, 28 Mar 2018 10:45:14 +0200 Subject: mini cleanup --- src/config.js | 1 - src/models/Settings.js | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'src') diff --git a/src/config.js b/src/config.js index 366231f79..8f6b85e1e 100644 --- a/src/config.js +++ b/src/config.js @@ -10,7 +10,6 @@ export const LIVE_API = 'https://api.franzinfra.com'; export const GA_ID = 'UA-74126766-6'; export const DEFAULT_APP_SETTINGS = { - autoLaunchOnStart: true, autoLaunchInBackground: false, runInBackground: true, enableSystemTray: true, diff --git a/src/models/Settings.js b/src/models/Settings.js index 942232d12..0e4c59057 100644 --- a/src/models/Settings.js +++ b/src/models/Settings.js @@ -5,7 +5,7 @@ export default class Settings { @observable app = DEFAULT_APP_SETTINGS @observable service = { - activeService: DEFAULT_APP_SETTINGS.autoLaunchInBackground, + activeService: '', } @observable group = { -- cgit v1.2.3-70-g09d2 From a01ec7fe971ef863caae4c4a70df2d015f1dd62e Mon Sep 17 00:00:00 2001 From: Stefan Malzner Date: Wed, 28 Mar 2018 11:06:02 +0200 Subject: minor cleanup --- src/electron/ipc-api/settings.js | 4 ---- src/stores/SettingsStore.js | 5 ----- 2 files changed, 9 deletions(-) (limited to 'src') diff --git a/src/electron/ipc-api/settings.js b/src/electron/ipc-api/settings.js index 00bdc0113..3eab68a91 100644 --- a/src/electron/ipc-api/settings.js +++ b/src/electron/ipc-api/settings.js @@ -1,10 +1,6 @@ import { ipcMain } from 'electron'; export default (params) => { - ipcMain.on('settings', (event, args) => { - params.settings.set(args); - }); - ipcMain.on('getAppSettings', () => { params.mainWindow.webContents.send('appSettings', params.settings.all); }); diff --git a/src/stores/SettingsStore.js b/src/stores/SettingsStore.js index 075cb6482..fe0ce2be9 100644 --- a/src/stores/SettingsStore.js +++ b/src/stores/SettingsStore.js @@ -65,11 +65,6 @@ export default class SettingsStore extends Store { } } - // Reactions - _shareSettingsWithMainProcess() { - ipcRenderer.send('settings', this.all); - } - // Helper _migrate() { const legacySettings = localStorage.getItem('app'); -- cgit v1.2.3-70-g09d2 From 655a6ed192bb1942b641f073b8f0db10c8692374 Mon Sep 17 00:00:00 2001 From: Stefan Date: Wed, 28 Mar 2018 14:55:51 +0200 Subject: fix(Windows): Hide title bar when in fullscreen --- src/components/layout/AppLayout.js | 4 +++- src/containers/layout/AppLayoutContainer.js | 1 + src/stores/AppStore.js | 10 ++++++++-- 3 files changed, 12 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/components/layout/AppLayout.js b/src/components/layout/AppLayout.js index 66aef1730..746775a7f 100644 --- a/src/components/layout/AppLayout.js +++ b/src/components/layout/AppLayout.js @@ -43,6 +43,7 @@ const messages = defineMessages({ @observer export default class AppLayout extends Component { static propTypes = { + isFullScreen: PropTypes.bool.isRequired, sidebar: PropTypes.element.isRequired, services: PropTypes.element.isRequired, children: PropTypes.element, @@ -69,6 +70,7 @@ export default class AppLayout extends Component { render() { const { + isFullScreen, sidebar, services, children, @@ -90,7 +92,7 @@ export default class AppLayout extends Component { return (
- {isWindows && } + {isWindows && !isFullScreen && }
{sidebar}
diff --git a/src/containers/layout/AppLayoutContainer.js b/src/containers/layout/AppLayoutContainer.js index 075bd5e34..222ffdc1a 100644 --- a/src/containers/layout/AppLayoutContainer.js +++ b/src/containers/layout/AppLayoutContainer.js @@ -106,6 +106,7 @@ export default class AppLayoutContainer extends Component { return ( { this.isOnline = true; }); window.addEventListener('offline', () => { this.isOnline = false; }); + mainWindow.on('enter-full-screen', () => { this.isFullScreen = true; }); + mainWindow.on('leave-full-screen', () => { this.isFullScreen = false; }); + + this.isOnline = navigator.onLine; // Check if Franz should launch on start @@ -170,8 +178,6 @@ export default class AppStore extends Store { this.actions.service.setActive({ serviceId }); - const mainWindow = remote.getCurrentWindow(); - if (isWindows) { mainWindow.restore(); } else if (isLinux) { -- cgit v1.2.3-70-g09d2 From 2364d595fe3d7f1c2824a0a6f33d37e13c81f698 Mon Sep 17 00:00:00 2001 From: Stefan Date: Wed, 28 Mar 2018 14:56:12 +0200 Subject: minor style fix --- src/styles/title-bar.scss | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src') diff --git a/src/styles/title-bar.scss b/src/styles/title-bar.scss index 492245e2f..5316f35b3 100644 --- a/src/styles/title-bar.scss +++ b/src/styles/title-bar.scss @@ -32,8 +32,7 @@ margin: 4px; border-radius: $theme-border-radius-small; } - &.selected { - // background: $theme-brand-primary; + &.selected, &.selected:focus { background: none; .menu-item { -- cgit v1.2.3-70-g09d2 From 199f6763c946a044a28077cf8aa131f29af83868 Mon Sep 17 00:00:00 2001 From: Stefan Malzner Date: Wed, 28 Mar 2018 15:12:14 +0200 Subject: feature(App): Add option to disable GPU acceleration --- src/components/settings/settings/EditSettingsForm.js | 6 ++++++ src/config.js | 1 + src/containers/settings/EditSettingsScreen.js | 10 ++++++++++ src/i18n/locales/en-US.json | 1 + src/index.js | 8 ++++++++ 5 files changed, 26 insertions(+) (limited to 'src') diff --git a/src/components/settings/settings/EditSettingsForm.js b/src/components/settings/settings/EditSettingsForm.js index 72aa5a8af..97f535594 100644 --- a/src/components/settings/settings/EditSettingsForm.js +++ b/src/components/settings/settings/EditSettingsForm.js @@ -76,6 +76,10 @@ const messages = defineMessages({ id: 'settings.app.currentVersion', defaultMessage: '!!!Current version:', }, + enableGPUAccelerationInfo: { + id: 'settings.app.restartRequired', + defaultMessage: '!!!Changes require restart', + }, }); @observer @@ -172,6 +176,8 @@ export default class EditSettingsForm extends Component { {/* Advanced */}

{intl.formatMessage(messages.headlineAdvanced)}

+ +

{intl.formatMessage(messages.enableGPUAccelerationInfo)}

{/*