refactor: Remove use of localStorage for local settings

Change-Type: minor
This commit is contained in:
Jonas Hermsmeier 2018-05-23 17:07:04 +02:00
parent 447efc7096
commit 2a6670a404
No known key found for this signature in database
GPG Key ID: 1B870F801A0CEE9F
3 changed files with 76 additions and 119 deletions

View File

@ -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()
})
}

View File

@ -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)
}

View File

@ -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
}