Persist settings in ${XDG_CONFIG_HOME}/flipper/settings

Summary:
Moves the settings state from electron local storage into a json file in the users configured config location.
Unless modified by the user, this will usually be `~/.config/flipper/settings.json`

Settings will now persist across re-installs, and can now be easily inspected and backed up.

Reviewed By: passy

Differential Revision: D17712687

fbshipit-source-id: 1e778063e41d0a1a86145817b9797bf0458121da
This commit is contained in:
John Knox
2019-10-07 08:49:05 -07:00
committed by Facebook Github Bot
parent 7775e82851
commit 85c0ec0d13
4 changed files with 36 additions and 26 deletions

View File

@@ -163,6 +163,6 @@ class SignInSheet extends Component<Props, State> {
}
export default connect<StateFromProps, DispatchFromProps, OwnProps, Store>(
({settingsState}) => ({settings: settingsState.settings}),
({settingsState}) => ({settings: settingsState}),
{updateSettings},
)(SignInSheet);

View File

@@ -27,11 +27,14 @@ import plugins, {
Action as PluginsAction,
} from './plugins';
import settings, {
State as SettingsState,
Settings as SettingsState,
Action as SettingsAction,
} from './settings';
import user, {State as UserState, Action as UserAction} from './user';
import JsonFileStorage from '../utils/jsonFileReduxPersistStorage';
import os from 'os';
import {resolve} from 'path';
import xdg from 'xdg-basedir';
import {persistReducer, PersistPartial} from 'redux-persist';
import {Store as ReduxStore, MiddlewareAPI as ReduxMiddlewareAPI} from 'redux';
@@ -61,6 +64,14 @@ export type State = {
export type Store = ReduxStore<State, Actions>;
export type MiddlewareAPI = ReduxMiddlewareAPI<Dispatch<Actions>, State>;
const settingsStorage = new JsonFileStorage(
resolve(
...(xdg.config ? [xdg.config] : [os.homedir(), '.config']),
'flipper',
'settings.json',
),
);
export default combineReducers<State, Actions>({
application: persistReducer<ApplicationState, Actions>(
{
@@ -101,7 +112,7 @@ export default combineReducers<State, Actions>({
user,
),
settingsState: persistReducer(
{key: 'settings', storage, whitelist: ['settings']},
{key: 'settings', storage: settingsStorage},
settings,
),
});

View File

@@ -11,10 +11,6 @@ export type Settings = {
androidHome: string;
};
export type State = {
settings: Settings;
};
export type Action =
| {type: 'INIT'}
| {
@@ -22,21 +18,16 @@ export type Action =
payload: Settings;
};
const initialState: State = {
settings: {
androidHome: '/opt/android_sdk',
},
const initialState: Settings = {
androidHome: '/opt/android_sdk',
};
export default function reducer(
state: State = initialState,
state: Settings = initialState,
action: Actions,
): State {
): Settings {
if (action.type === 'UPDATE_SETTINGS') {
return {
...state,
settings: action.payload,
};
return action.payload;
}
return state;
}

View File

@@ -5,7 +5,9 @@
* @format
*/
import {promises} from 'fs';
import {promises, exists} from 'fs';
import path from 'path';
import {promisify} from 'util';
/**
* Redux-persist storage engine for storing state in a human readable JSON file.
@@ -28,14 +30,12 @@ export default class JsonFileStorage {
.then(buffer => buffer.toString())
.then(this.deserializeValue)
.catch(e => {
console.error(
console.warn(
`Failed to read settings file: "${
this.filepath
}". ${e}. Replacing file with default settings.`,
);
return promises
.writeFile(this.filepath, JSON.stringify({}))
.then(() => ({}));
return this.writeContents(JSON.stringify({})).then(() => ({}));
});
}
@@ -51,7 +51,7 @@ export default class JsonFileStorage {
setItem(_key: string, value: any, callback?: (_: any) => any): Promise<any> {
const originalValue = this.parseFile();
const writePromise = originalValue.then(_ =>
promises.writeFile(this.filepath, this.serializeValue(value)),
this.writeContents(this.serializeValue(value)),
);
return Promise.all([originalValue, writePromise]).then(([o, _]) => {
@@ -61,8 +61,7 @@ export default class JsonFileStorage {
}
removeItem(_key: string, callback?: () => any): Promise<void> {
return promises
.writeFile(this.filepath, JSON.stringify({}))
return this.writeContents(JSON.stringify({}))
.then(_ => callback && callback())
.then(() => {});
}
@@ -86,4 +85,13 @@ export default class JsonFileStorage {
}, {});
return JSON.stringify(reconstructedObject);
}
writeContents(content: string): Promise<void> {
const dir = path.dirname(this.filepath);
return promisify(exists)(dir)
.then(dirExists =>
dirExists ? Promise.resolve() : promises.mkdir(dir, {recursive: true}),
)
.then(() => promises.writeFile(this.filepath, content));
}
}