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-54-g00ecf