diff --git a/lib/gui/app/models/local-settings.js b/lib/gui/app/models/local-settings.js index b95e7990..69c59a41 100644 --- a/lib/gui/app/models/local-settings.js +++ b/lib/gui/app/models/local-settings.js @@ -16,20 +16,24 @@ 'use strict' +const electron = require('electron') const Bluebird = require('bluebird') const _ = require('lodash') const fs = require('fs') const path = require('path') const os = require('os') -const Storage = require('./storage') /** - * @summary Local storage settings key + * @summary Userdata directory path + * @description + * Defaults to the following: + * - `%APPDATA%/etcher` on Windows + * - `$XDG_CONFIG_HOME/etcher` or `~/.config/etcher` on Linux + * - `~/Library/Application Support/etcher` on macOS * @constant * @type {String} */ -const LOCAL_STORAGE_SETTINGS_KEY = 'etcher-settings' -const settingsStorage = new Storage(LOCAL_STORAGE_SETTINGS_KEY) +const USER_DATA_DIR = electron.remote.app.getPath('userData') /** * @summary Local settings filename @@ -38,10 +42,19 @@ const settingsStorage = new Storage(LOCAL_STORAGE_SETTINGS_KEY) */ const RCFILE = '.etcher.json' +/** + * @summary Configuration file path + * @type {String} + * @constant + */ +const HOME_CONFIG_PATH = process.platform === 'win32' + ? path.join(USER_DATA_DIR, RCFILE) + : path.join(USER_DATA_DIR, 'config.json') + /** * @summary Read a local .etcherrc file * @function - * @public + * @private * * @param {String} filename - file path * @fulfil {Object} - settings @@ -68,6 +81,33 @@ const readConfigFile = (filename) => { }) } +/** + * @summary Read a local .etcherrc file + * @function + * @private + * + * @param {String} filename - file path + * @returns {Promise} + * + * @example + * writeConfigFile('.etcherrc', { something: 'good' }) + * .then(() => { + * console.log('data written') + * }) + */ +const writeConfigFile = (filename, data) => { + return new Bluebird((resolve, reject) => { + const contents = JSON.stringify(data, null, 2) + fs.writeFile(filename, contents, (error) => { + if (error) { + reject(error) + } else { + resolve() + } + }) + }) +} + /** * @summary Read all local settings * @function @@ -82,22 +122,7 @@ const readConfigFile = (filename) => { * }); */ exports.readAll = () => { - const homeConfigPath = process.platform === 'win32' - ? path.join(os.userInfo().homedir, RCFILE) - : path.join(os.userInfo().homedir, '.config', 'etcher', 'config.json') - const workdirConfigPath = path.join(process.cwd(), RCFILE) - const settings = {} - return Bluebird.try(() => { - _.merge(settings, settingsStorage.getAll()) - }).return(readConfigFile(homeConfigPath)) - .then((homeConfig) => { - _.merge(settings, homeConfig) - }) - .return(readConfigFile(workdirConfigPath)) - .then((workdirConfig) => { - _.merge(settings, workdirConfig) - }) - .return(settings) + return readConfigFile(HOME_CONFIG_PATH) } /** @@ -116,9 +141,7 @@ exports.readAll = () => { * }); */ exports.writeAll = (settings) => { - return Bluebird.try(() => { - settingsStorage.setAll(settings) - }) + return writeConfigFile(HOME_CONFIG_PATH, settings) } /** @@ -137,7 +160,8 @@ exports.writeAll = (settings) => { * }); */ exports.clear = () => { + // TODO: Unlink config file return Bluebird.try(() => { - settingsStorage.clearAll() + // settingsStorage.clearAll() }) } diff --git a/lib/gui/app/models/settings.js b/lib/gui/app/models/settings.js index a215e745..80616c9e 100644 --- a/lib/gui/app/models/settings.js +++ b/lib/gui/app/models/settings.js @@ -23,58 +23,32 @@ const _ = require('lodash') const Bluebird = require('bluebird') const localSettings = require('./local-settings') -const store = require('./store') const errors = require('../../../shared/errors') - -/** - * @summary Set a settings object - * @function - * @private - * - * @description - * Use this function with care, given that it will completely - * override any existing settings in both the redux store, - * and the local user configuration. - * - * This function is prepared to deal with any local configuration - * write issues by rolling back to the previous settings if so. - * - * @param {Object} settings - settings - * @returns {Promise} - * - * @example - * setSettingsObject({ foo: 'bar' }).then(() => { - * console.log('Done!'); - * }); - */ -const setSettingsObject = (settings) => { - const currentSettings = exports.getAll() - - return Bluebird.try(() => { - store.dispatch({ - type: store.Actions.SET_SETTINGS, - data: settings - }) - }).then(() => { - // Revert the application state if writing the data - // to the local machine was not successful - return localSettings.writeAll(settings).catch((error) => { - store.dispatch({ - type: store.Actions.SET_SETTINGS, - data: currentSettings - }) - - throw error - }) - }) -} +const release = require('../../../shared/release') +const packageJSON = require('../../../../package.json') /** * @summary Default settings * @constant * @type {Object} */ -const DEFAULT_SETTINGS = store.Defaults.get('settings').toJS() +const DEFAULT_SETTINGS = { + unsafeMode: false, + errorReporting: true, + unmountOnSuccess: true, + validateWriteOnSuccess: true, + updatesEnabled: packageJSON.updates.enabled && !_.includes([ 'rpm', 'deb' ], packageJSON.packageType), + includeUnstableUpdateChannel: !release.isStableRelease(packageJSON.version), + lastSleptUpdateNotifier: null, + lastSleptUpdateNotifierVersion: null, + desktopNotifications: true +} + +/** + * @summary Settings state + * @type {Object} + */ +const settings = _.assign({}, DEFAULT_SETTINGS) /** * @summary Reset settings to their default values @@ -89,7 +63,8 @@ const DEFAULT_SETTINGS = store.Defaults.get('settings').toJS() * }); */ exports.reset = () => { - return setSettingsObject(DEFAULT_SETTINGS) + // TODO: Remove default settings from config file (?) + return exports.assign(DEFAULT_SETTINGS) } /** @@ -107,14 +82,14 @@ exports.reset = () => { * console.log('Done!'); * }); */ -exports.assign = (settings) => { - if (_.isNil(settings)) { +exports.assign = (data) => { + if (_.isNil(data)) { return Bluebird.reject(errors.createError({ title: 'Missing settings' })) } - return setSettingsObject(_.assign(exports.getAll(), settings)) + return localSettings.writeAll(_.assign(exports.getAll(), data)) } /** @@ -177,7 +152,7 @@ exports.set = (key, value) => { * const value = settings.get('unmountOnSuccess'); */ exports.get = (key) => { - return _.get(exports.getAll(), [ key ]) + return _.cloneDeep(_.get(settings, [ key ])) } /** @@ -192,5 +167,5 @@ exports.get = (key) => { * console.log(allSettings.unmountOnSuccess); */ exports.getAll = () => { - return store.getState().get('settings').toJS() + return _.cloneDeep(settings) } diff --git a/lib/gui/app/models/store.js b/lib/gui/app/models/store.js index b78fb4a8..96fbd8d2 100644 --- a/lib/gui/app/models/store.js +++ b/lib/gui/app/models/store.js @@ -23,10 +23,8 @@ const uuidV4 = require('uuid/v4') const constraints = require('../../../shared/drive-constraints') const supportedFormats = require('../../../shared/supported-formats') const errors = require('../../../shared/errors') -const release = require('../../../shared/release') const fileExtensions = require('../../../shared/file-extensions') const utils = require('../../../shared/utils') -const packageJSON = require('../../../../package.json') /** * @summary Verify and throw if any state fields are nil @@ -95,17 +93,6 @@ const DEFAULT_STATE = Immutable.fromJS({ percentage: 0, speed: 0, totalSpeed: 0 - }, - settings: { - unsafeMode: false, - errorReporting: true, - unmountOnSuccess: true, - validateWriteOnSuccess: true, - updatesEnabled: packageJSON.updates.enabled && !_.includes([ 'rpm', 'deb' ], packageJSON.packageType), - includeUnstableUpdateChannel: !release.isStableRelease(packageJSON.version), - lastSleptUpdateNotifier: null, - lastSleptUpdateNotifierVersion: null, - desktopNotifications: true } }) @@ -123,8 +110,7 @@ const ACTIONS = _.fromPairs(_.map([ 'SELECT_DRIVE', 'SELECT_IMAGE', 'DESELECT_DRIVE', - 'DESELECT_IMAGE', - 'SET_SETTINGS' + 'DESELECT_IMAGE' ], (message) => { return [ message, message ] })) @@ -512,34 +498,6 @@ const storeReducer = (state = DEFAULT_STATE, action) => { return state.deleteIn([ 'selection', 'image' ]) } - case ACTIONS.SET_SETTINGS: { - // Type: action.data : SettingsObject - - if (!action.data) { - throw errors.createError({ - title: 'Missing settings' - }) - } - - if (!_.isPlainObject(action.data)) { - throw errors.createError({ - title: `Invalid settings: ${action.data}` - }) - } - - const invalidPair = _.find(_.toPairs(action.data), (pair) => { - return _.isObject(_.last(pair)) - }) - - if (!_.isNil(invalidPair)) { - throw errors.createError({ - title: `Invalid setting value: ${_.last(invalidPair)} for ${_.first(invalidPair)}` - }) - } - - return state.setIn([ 'settings' ], Immutable.fromJS(action.data)) - } - default: { return state }