Clean setting
This commit is contained in:
parent
96649fef63
commit
df890379ca
5 changed files with 144 additions and 143 deletions
|
@ -1,4 +1,7 @@
|
||||||
export default {
|
export default {
|
||||||
// Settings
|
// Settings
|
||||||
SETTING_SET_SETTINGS: 'setting.set.settings',
|
SETTING_SET_SETTINGS: 'setting.set.settings',
|
||||||
|
SETTING_SHOW_ERROR: 'setting.show.error',
|
||||||
|
SETTING_SWITCH_TO_FORM: 'setting.switch.to.form',
|
||||||
|
SETTING_SWITCH_TO_JSON: 'setting.switch.to.json',
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import actions from 'settings/actions';
|
import actions from 'settings/actions';
|
||||||
import messages from 'shared/messages';
|
import messages from 'shared/messages';
|
||||||
import DefaultSettings from 'shared/settings/default';
|
import * as validator from 'shared/settings/validator';
|
||||||
import * as settingsStorage from 'shared/settings/storage';
|
import KeymapsForm from '../components/form/keymaps-form';
|
||||||
import * as settingsValues from 'shared/settings/values';
|
import * as settingsValues from 'shared/settings/values';
|
||||||
|
import * as settingsStorage from 'shared/settings/storage';
|
||||||
|
|
||||||
const load = async() => {
|
const load = async() => {
|
||||||
let settings = await settingsStorage.loadRaw();
|
let settings = await settingsStorage.loadRaw();
|
||||||
|
@ -10,6 +11,18 @@ const load = async() => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const save = async(settings) => {
|
const save = async(settings) => {
|
||||||
|
try {
|
||||||
|
if (settings.source === 'json') {
|
||||||
|
let value = JSON.parse(settings.json);
|
||||||
|
validator.validate(value);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
return {
|
||||||
|
type: actions.SETTING_SHOW_ERROR,
|
||||||
|
error: e.toString(),
|
||||||
|
json: settings.json,
|
||||||
|
};
|
||||||
|
}
|
||||||
await settingsStorage.save(settings);
|
await settingsStorage.save(settings);
|
||||||
await browser.runtime.sendMessage({
|
await browser.runtime.sendMessage({
|
||||||
type: messages.SETTINGS_RELOAD
|
type: messages.SETTINGS_RELOAD
|
||||||
|
@ -17,21 +30,39 @@ const save = async(settings) => {
|
||||||
return set(settings);
|
return set(settings);
|
||||||
};
|
};
|
||||||
|
|
||||||
const set = (settings) => {
|
const switchToForm = (json) => {
|
||||||
let value = JSON.parse(DefaultSettings.json);
|
try {
|
||||||
if (settings.source === 'json') {
|
validator.validate(JSON.parse(json));
|
||||||
value = settingsValues.valueFromJson(settings.json);
|
// AllowdOps filters operations, this is dirty dependency
|
||||||
} else if (settings.source === 'form') {
|
let form = settingsValues.formFromJson(json, KeymapsForm.AllowdOps);
|
||||||
value = settingsValues.valueFromForm(settings.form);
|
return {
|
||||||
|
type: actions.SETTING_SWITCH_TO_FORM,
|
||||||
|
form,
|
||||||
|
};
|
||||||
|
} catch (e) {
|
||||||
|
return {
|
||||||
|
type: actions.SETTING_SHOW_ERROR,
|
||||||
|
error: e.toString(),
|
||||||
|
json,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const switchToJson = (form) => {
|
||||||
|
let json = settingsValues.jsonFromForm(form);
|
||||||
|
return {
|
||||||
|
type: actions.SETTING_SWITCH_TO_JSON,
|
||||||
|
json,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const set = (settings) => {
|
||||||
return {
|
return {
|
||||||
type: actions.SETTING_SET_SETTINGS,
|
type: actions.SETTING_SET_SETTINGS,
|
||||||
source: settings.source,
|
source: settings.source,
|
||||||
json: settings.json,
|
json: settings.json,
|
||||||
form: settings.form,
|
form: settings.form,
|
||||||
value,
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export { load, save };
|
export { load, save, switchToForm, switchToJson };
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import './site.scss';
|
import './site.scss';
|
||||||
import { h, Component } from 'preact';
|
import { h, Component } from 'preact';
|
||||||
|
import { connect } from 'preact-redux';
|
||||||
import Input from './ui/input';
|
import Input from './ui/input';
|
||||||
import SearchForm from './form/search-form';
|
import SearchForm from './form/search-form';
|
||||||
import KeymapsForm from './form/keymaps-form';
|
import KeymapsForm from './form/keymaps-form';
|
||||||
|
@ -7,63 +8,36 @@ import BlacklistForm from './form/blacklist-form';
|
||||||
import PropertiesForm from './form/properties-form';
|
import PropertiesForm from './form/properties-form';
|
||||||
import * as properties from 'shared/settings/properties';
|
import * as properties from 'shared/settings/properties';
|
||||||
import * as settingActions from 'settings/actions/setting';
|
import * as settingActions from 'settings/actions/setting';
|
||||||
import * as validator from 'shared/settings/validator';
|
|
||||||
import * as settingsValues from 'shared/settings/values';
|
|
||||||
|
|
||||||
const DO_YOU_WANT_TO_CONTINUE =
|
const DO_YOU_WANT_TO_CONTINUE =
|
||||||
'Some settings in JSON can be lost when migrating. ' +
|
'Some settings in JSON can be lost when migrating. ' +
|
||||||
'Do you want to continue?';
|
'Do you want to continue?';
|
||||||
|
|
||||||
class SettingsComponent extends Component {
|
class SettingsComponent extends Component {
|
||||||
constructor(props, context) {
|
|
||||||
super(props, context);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
settings: {
|
|
||||||
json: '',
|
|
||||||
},
|
|
||||||
errors: {
|
|
||||||
json: '',
|
|
||||||
}
|
|
||||||
};
|
|
||||||
this.context.store.subscribe(this.stateChanged.bind(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.context.store.dispatch(settingActions.load());
|
this.props.dispatch(settingActions.load());
|
||||||
}
|
}
|
||||||
|
|
||||||
stateChanged() {
|
renderFormFields(form) {
|
||||||
let settings = this.context.store.getState();
|
|
||||||
this.setState({
|
|
||||||
settings: {
|
|
||||||
source: settings.source,
|
|
||||||
json: settings.json,
|
|
||||||
form: settings.form,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
renderFormFields() {
|
|
||||||
return <div>
|
return <div>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>Keybindings</legend>
|
<legend>Keybindings</legend>
|
||||||
<KeymapsForm
|
<KeymapsForm
|
||||||
value={this.state.settings.form.keymaps}
|
value={form.keymaps}
|
||||||
onChange={value => this.bindForm('keymaps', value)}
|
onChange={value => this.bindForm('keymaps', value)}
|
||||||
/>
|
/>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>Search Engines</legend>
|
<legend>Search Engines</legend>
|
||||||
<SearchForm
|
<SearchForm
|
||||||
value={this.state.settings.form.search}
|
value={form.search}
|
||||||
onChange={value => this.bindForm('search', value)}
|
onChange={value => this.bindForm('search', value)}
|
||||||
/>
|
/>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>Blacklist</legend>
|
<legend>Blacklist</legend>
|
||||||
<BlacklistForm
|
<BlacklistForm
|
||||||
value={this.state.settings.form.blacklist}
|
value={form.blacklist}
|
||||||
onChange={value => this.bindForm('blacklist', value)}
|
onChange={value => this.bindForm('blacklist', value)}
|
||||||
/>
|
/>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
@ -71,33 +45,33 @@ class SettingsComponent extends Component {
|
||||||
<legend>Properties</legend>
|
<legend>Properties</legend>
|
||||||
<PropertiesForm
|
<PropertiesForm
|
||||||
types={properties.types}
|
types={properties.types}
|
||||||
value={this.state.settings.form.properties}
|
value={form.properties}
|
||||||
onChange={value => this.bindForm('properties', value)}
|
onChange={value => this.bindForm('properties', value)}
|
||||||
/>
|
/>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderJsonFields() {
|
renderJsonFields(json, error) {
|
||||||
return <div>
|
return <div>
|
||||||
<Input
|
<Input
|
||||||
type='textarea'
|
type='textarea'
|
||||||
name='json'
|
name='json'
|
||||||
label='Plain JSON'
|
label='Plain JSON'
|
||||||
spellCheck='false'
|
spellCheck='false'
|
||||||
error={this.state.errors.json}
|
error={error}
|
||||||
onChange={this.bindValue.bind(this)}
|
onChange={this.bindJson.bind(this)}
|
||||||
value={this.state.settings.json}
|
value={json}
|
||||||
/>
|
/>
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let fields = null;
|
let fields = null;
|
||||||
if (this.state.settings.source === 'form') {
|
if (this.props.source === 'form') {
|
||||||
fields = this.renderFormFields();
|
fields = this.renderFormFields(this.props.form);
|
||||||
} else if (this.state.settings.source === 'json') {
|
} else if (this.props.source === 'json') {
|
||||||
fields = this.renderJsonFields();
|
fields = this.renderJsonFields(this.props.json, this.props.error);
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
@ -108,7 +82,7 @@ class SettingsComponent extends Component {
|
||||||
id='setting-source-form'
|
id='setting-source-form'
|
||||||
name='source'
|
name='source'
|
||||||
label='Use form'
|
label='Use form'
|
||||||
checked={this.state.settings.source === 'form'}
|
checked={this.props.source === 'form'}
|
||||||
value='form'
|
value='form'
|
||||||
onChange={this.bindSource.bind(this)} />
|
onChange={this.bindSource.bind(this)} />
|
||||||
|
|
||||||
|
@ -116,7 +90,7 @@ class SettingsComponent extends Component {
|
||||||
type='radio'
|
type='radio'
|
||||||
name='source'
|
name='source'
|
||||||
label='Use plain JSON'
|
label='Use plain JSON'
|
||||||
checked={this.state.settings.source === 'json'}
|
checked={this.props.source === 'json'}
|
||||||
value='json'
|
value='json'
|
||||||
onChange={this.bindSource.bind(this)} />
|
onChange={this.bindSource.bind(this)} />
|
||||||
|
|
||||||
|
@ -126,98 +100,44 @@ class SettingsComponent extends Component {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
validate(target) {
|
|
||||||
if (target.name === 'json') {
|
|
||||||
let settings = JSON.parse(target.value);
|
|
||||||
validator.validate(settings);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
validateValue(e) {
|
|
||||||
let next = { ...this.state };
|
|
||||||
|
|
||||||
next.errors.json = '';
|
|
||||||
try {
|
|
||||||
this.validate(e.target);
|
|
||||||
} catch (err) {
|
|
||||||
next.errors.json = err.message;
|
|
||||||
}
|
|
||||||
next.settings[e.target.name] = e.target.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
bindForm(name, value) {
|
bindForm(name, value) {
|
||||||
let next = { ...this.state,
|
let settings = {
|
||||||
settings: { ...this.state.settings,
|
source: this.props.source,
|
||||||
form: { ...this.state.settings.form }}};
|
json: this.props.json,
|
||||||
next.settings.form[name] = value;
|
form: { ...this.props.form },
|
||||||
this.setState(next);
|
};
|
||||||
this.context.store.dispatch(settingActions.save(next.settings));
|
settings.form[name] = value;
|
||||||
|
this.props.dispatch(settingActions.save(settings));
|
||||||
}
|
}
|
||||||
|
|
||||||
bindValue(e) {
|
bindJson(e) {
|
||||||
let next = { ...this.state };
|
let settings = {
|
||||||
let error = false;
|
source: this.props.source,
|
||||||
|
json: e.target.value,
|
||||||
next.errors.json = '';
|
form: this.props.form,
|
||||||
try {
|
};
|
||||||
this.validate(e.target);
|
this.props.dispatch(settingActions.save(settings));
|
||||||
} catch (err) {
|
|
||||||
next.errors.json = err.message;
|
|
||||||
error = true;
|
|
||||||
}
|
|
||||||
next.settings[e.target.name] = e.target.value;
|
|
||||||
|
|
||||||
this.setState(this.state);
|
|
||||||
if (!error) {
|
|
||||||
this.context.store.dispatch(settingActions.save(next.settings));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
migrateToForm() {
|
|
||||||
let b = window.confirm(DO_YOU_WANT_TO_CONTINUE);
|
|
||||||
if (!b) {
|
|
||||||
this.setState(this.state);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
validator.validate(JSON.parse(this.state.settings.json));
|
|
||||||
} catch (err) {
|
|
||||||
this.setState(this.state);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let form = settingsValues.formFromJson(
|
|
||||||
this.state.settings.json, KeymapsForm.AllowdOps);
|
|
||||||
let next = { ...this.state };
|
|
||||||
next.settings.form = form;
|
|
||||||
next.settings.source = 'form';
|
|
||||||
next.errors.json = '';
|
|
||||||
|
|
||||||
this.setState(next);
|
|
||||||
this.context.store.dispatch(settingActions.save(next.settings));
|
|
||||||
}
|
|
||||||
|
|
||||||
migrateToJson() {
|
|
||||||
let json = settingsValues.jsonFromForm(this.state.settings.form);
|
|
||||||
let next = { ...this.state };
|
|
||||||
next.settings.json = json;
|
|
||||||
next.settings.source = 'json';
|
|
||||||
next.errors.json = '';
|
|
||||||
|
|
||||||
this.setState(next);
|
|
||||||
this.context.store.dispatch(settingActions.save(next.settings));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bindSource(e) {
|
bindSource(e) {
|
||||||
let from = this.state.settings.source;
|
let from = this.props.source;
|
||||||
let to = e.target.value;
|
let to = e.target.value;
|
||||||
|
|
||||||
if (from === 'form' && to === 'json') {
|
if (from === 'form' && to === 'json') {
|
||||||
this.migrateToJson();
|
this.props.dispatch(settingActions.switchToJson(this.props.form));
|
||||||
} else if (from === 'json' && to === 'form') {
|
} else if (from === 'json' && to === 'form') {
|
||||||
this.migrateToForm();
|
let b = window.confirm(DO_YOU_WANT_TO_CONTINUE);
|
||||||
|
if (!b) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.props.dispatch(settingActions.switchToForm(this.props.json));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let settings = this.context.store.getState();
|
||||||
|
this.props.dispatch(settingActions.save(settings));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default SettingsComponent;
|
const mapStateToProps = state => state;
|
||||||
|
|
||||||
|
export default connect(mapStateToProps)(SettingsComponent);
|
||||||
|
|
|
@ -4,20 +4,33 @@ const defaultState = {
|
||||||
source: '',
|
source: '',
|
||||||
json: '',
|
json: '',
|
||||||
form: null,
|
form: null,
|
||||||
value: {}
|
error: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function reducer(state = defaultState, action = {}) {
|
export default function reducer(state = defaultState, action = {}) {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case actions.SETTING_SET_SETTINGS:
|
case actions.SETTING_SET_SETTINGS:
|
||||||
return {
|
return { ...state,
|
||||||
source: action.source,
|
source: action.source,
|
||||||
json: action.json,
|
json: action.json,
|
||||||
form: action.form,
|
form: action.form,
|
||||||
value: action.value,
|
errors: '',
|
||||||
};
|
error: '', };
|
||||||
|
case actions.SETTING_SHOW_ERROR:
|
||||||
|
return { ...state,
|
||||||
|
error: action.text,
|
||||||
|
json: action.json, };
|
||||||
|
case actions.SETTING_SWITCH_TO_FORM:
|
||||||
|
return { ...state,
|
||||||
|
error: '',
|
||||||
|
source: 'form',
|
||||||
|
form: action.form, };
|
||||||
|
case actions.SETTING_SWITCH_TO_JSON:
|
||||||
|
return { ...state,
|
||||||
|
error: '',
|
||||||
|
source: 'json',
|
||||||
|
json: action.json, };
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,21 +1,55 @@
|
||||||
import actions from 'settings/actions';
|
import actions from 'settings/actions';
|
||||||
import settingReducer from 'settings/reducers/setting';
|
import settingReducer from 'settings/reducers/setting';
|
||||||
|
|
||||||
describe("setting reducer", () => {
|
describe("settings setting reducer", () => {
|
||||||
it('return the initial state', () => {
|
it('return the initial state', () => {
|
||||||
let state = settingReducer(undefined, {});
|
let state = settingReducer(undefined, {});
|
||||||
expect(state).to.have.deep.property('json', '');
|
expect(state).to.have.deep.property('json', '');
|
||||||
expect(state).to.have.deep.property('value', {});
|
expect(state).to.have.deep.property('form', null);
|
||||||
|
expect(state).to.have.deep.property('error', '');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('return next state for SETTING_SET_SETTINGS', () => {
|
it('return next state for SETTING_SET_SETTINGS', () => {
|
||||||
let action = {
|
let action = {
|
||||||
type: actions.SETTING_SET_SETTINGS,
|
type: actions.SETTING_SET_SETTINGS,
|
||||||
|
source: 'json',
|
||||||
json: '{ "key": "value" }',
|
json: '{ "key": "value" }',
|
||||||
value: { key: 123 },
|
form: {},
|
||||||
};
|
};
|
||||||
let state = settingReducer(undefined, action);
|
let state = settingReducer(undefined, action);
|
||||||
|
expect(state).to.have.deep.property('source', 'json');
|
||||||
expect(state).to.have.deep.property('json', '{ "key": "value" }');
|
expect(state).to.have.deep.property('json', '{ "key": "value" }');
|
||||||
expect(state).to.have.deep.property('value', { key: 123 });
|
expect(state).to.have.deep.property('form', {});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('return next state for SETTING_SHOW_ERROR', () => {
|
||||||
|
let action = {
|
||||||
|
type: actions.SETTING_SHOW_ERROR,
|
||||||
|
text: 'bad value',
|
||||||
|
json: '{}',
|
||||||
|
};
|
||||||
|
let state = settingReducer(undefined, action);
|
||||||
|
expect(state).to.have.deep.property('error', 'bad value');
|
||||||
|
expect(state).to.have.deep.property('json', '{}');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('return next state for SETTING_SWITCH_TO_FORM', () => {
|
||||||
|
let action = {
|
||||||
|
type: actions.SETTING_SWITCH_TO_FORM,
|
||||||
|
form: {},
|
||||||
|
};
|
||||||
|
let state = settingReducer(undefined, action);
|
||||||
|
expect(state).to.have.deep.property('form', {});
|
||||||
|
expect(state).to.have.deep.property('source', 'form');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('return next state for SETTING_SWITCH_TO_JSON', () => {
|
||||||
|
let action = {
|
||||||
|
type: actions.SETTING_SWITCH_TO_JSON,
|
||||||
|
json: '{}',
|
||||||
|
};
|
||||||
|
let state = settingReducer(undefined, action);
|
||||||
|
expect(state).to.have.deep.property('json', '{}');
|
||||||
|
expect(state).to.have.deep.property('source', 'json');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Reference in a new issue