diff options
author | Shin'ya Ueoka <ueokande@i-beam.org> | 2019-05-05 08:03:29 +0900 |
---|---|---|
committer | Shin'ya Ueoka <ueokande@i-beam.org> | 2019-05-06 22:17:18 +0900 |
commit | a0882bbceb7ed71d56bf8557620449fbc3f19749 (patch) | |
tree | f2087d44f21dd68fc3584f62cfb9b62ae58bab2b /src/settings | |
parent | d01db82c0dca352de2d7644c383d388fc3ec0366 (diff) |
Declare setting types
Diffstat (limited to 'src/settings')
-rw-r--r-- | src/settings/actions/index.ts | 16 | ||||
-rw-r--r-- | src/settings/actions/setting.ts | 60 | ||||
-rw-r--r-- | src/settings/components/form/KeymapsForm.tsx | 23 | ||||
-rw-r--r-- | src/settings/components/form/SearchForm.tsx | 30 | ||||
-rw-r--r-- | src/settings/components/index.tsx | 104 | ||||
-rw-r--r-- | src/settings/keymaps.ts | 3 | ||||
-rw-r--r-- | src/settings/reducers/setting.ts | 22 | ||||
-rw-r--r-- | src/settings/storage.ts | 15 |
8 files changed, 163 insertions, 110 deletions
diff --git a/src/settings/actions/index.ts b/src/settings/actions/index.ts index 75c6bb5..b1e996e 100644 --- a/src/settings/actions/index.ts +++ b/src/settings/actions/index.ts @@ -1,3 +1,7 @@ +import { + JSONSettings, FormSettings, SettingSource, +} from '../../shared/SettingData'; + // Settings export const SETTING_SET_SETTINGS = 'setting.set.settings'; export const SETTING_SHOW_ERROR = 'setting.show.error'; @@ -6,25 +10,25 @@ export const SETTING_SWITCH_TO_JSON = 'setting.switch.to.json'; interface SettingSetSettingsAcion { type: typeof SETTING_SET_SETTINGS; - source: string; - json: string; - form: any; + source: SettingSource; + json?: JSONSettings; + form?: FormSettings; } interface SettingShowErrorAction { type: typeof SETTING_SHOW_ERROR; error: string; - json: string; + json: JSONSettings; } interface SettingSwitchToFormAction { type: typeof SETTING_SWITCH_TO_FORM; - form: any; + form: FormSettings, } interface SettingSwitchToJsonAction { type: typeof SETTING_SWITCH_TO_JSON; - json: string; + json: JSONSettings, } export type SettingAction = diff --git a/src/settings/actions/setting.ts b/src/settings/actions/setting.ts index b03cd80..9eb416e 100644 --- a/src/settings/actions/setting.ts +++ b/src/settings/actions/setting.ts @@ -1,35 +1,35 @@ import * as actions from './index'; -import * as validator from '../../shared/settings/validator'; -import * as settingsValues from '../../shared/settings/values'; -import * as settingsStorage from '../../shared/settings/storage'; -import keymaps from '../keymaps'; +import * as storages from '../storage'; +import SettingData, { + JSONSettings, FormSettings, SettingSource, +} from '../../shared/SettingData'; const load = async(): Promise<actions.SettingAction> => { - let settings = await settingsStorage.loadRaw(); - return set(settings); + let data = await storages.load(); + return set(data); }; -const save = async(settings: any): Promise<actions.SettingAction> => { +const save = async(data: SettingData): Promise<actions.SettingAction> => { try { - if (settings.source === 'json') { - let value = JSON.parse(settings.json); - validator.validate(value); + if (data.getSource() === SettingSource.JSON) { + // toSettings exercise validation + data.toSettings(); } } catch (e) { return { type: actions.SETTING_SHOW_ERROR, error: e.toString(), - json: settings.json, + json: data.getJSON(), }; } - await settingsStorage.save(settings); - return set(settings); + await storages.save(data); + return set(data); }; -const switchToForm = (json: string): actions.SettingAction => { +const switchToForm = (json: JSONSettings): actions.SettingAction => { try { - validator.validate(JSON.parse(json)); - let form = settingsValues.formFromJson(json, keymaps.allowedOps); + // toSettings exercise validation + let form = FormSettings.fromSettings(json.toSettings()); return { type: actions.SETTING_SWITCH_TO_FORM, form, @@ -43,21 +43,31 @@ const switchToForm = (json: string): actions.SettingAction => { } }; -const switchToJson = (form: any): actions.SettingAction => { - let json = settingsValues.jsonFromForm(form); +const switchToJson = (form: FormSettings): actions.SettingAction => { + let json = JSONSettings.fromSettings(form.toSettings()); return { type: actions.SETTING_SWITCH_TO_JSON, json, }; }; -const set = (settings: any): actions.SettingAction => { - return { - type: actions.SETTING_SET_SETTINGS, - source: settings.source, - json: settings.json, - form: settings.form, - }; +const set = (data: SettingData): actions.SettingAction => { + let source = data.getSource(); + switch (source) { + case SettingSource.JSON: + return { + type: actions.SETTING_SET_SETTINGS, + source: source, + json: data.getJSON(), + }; + case SettingSource.Form: + return { + type: actions.SETTING_SET_SETTINGS, + source: source, + form: data.getForm(), + }; + } + throw new Error(`unknown source: ${source}`); }; export { load, save, set, switchToForm, switchToJson }; diff --git a/src/settings/components/form/KeymapsForm.tsx b/src/settings/components/form/KeymapsForm.tsx index ab44464..ad4d0e7 100644 --- a/src/settings/components/form/KeymapsForm.tsx +++ b/src/settings/components/form/KeymapsForm.tsx @@ -2,32 +2,30 @@ import './KeymapsForm.scss'; import React from 'react'; import Input from '../ui/Input'; import keymaps from '../../keymaps'; +import { FormKeymaps } from '../../../shared/SettingData'; -type Value = {[key: string]: string}; - -interface Props{ - value: Value; - onChange: (e: Value) => void; +interface Props { + value: FormKeymaps; + onChange: (e: FormKeymaps) => void; onBlur: () => void; } class KeymapsForm extends React.Component<Props> { public static defaultProps: Props = { - value: {}, + value: FormKeymaps.valueOf({}), onChange: () => {}, onBlur: () => {}, } render() { + let values = this.props.value.toJSON(); return <div className='form-keymaps-form'> { keymaps.fields.map((group, index) => { return <div key={index} className='form-keymaps-form-field-group'> { - group.map((field) => { - let name = field[0]; - let label = field[1]; - let value = this.props.value[name] || ''; + group.map(([name, label]) => { + let value = values[name] || ''; return <Input type='text' id={name} name={name} key={name} label={label} value={value} @@ -43,10 +41,7 @@ class KeymapsForm extends React.Component<Props> { } bindValue(name: string, value: string) { - let next = { ...this.props.value }; - next[name] = value; - - this.props.onChange(next); + this.props.onChange(this.props.value.buildWithOverride(name, value)); } } diff --git a/src/settings/components/form/SearchForm.tsx b/src/settings/components/form/SearchForm.tsx index 737e291..67dbeba 100644 --- a/src/settings/components/form/SearchForm.tsx +++ b/src/settings/components/form/SearchForm.tsx @@ -2,31 +2,23 @@ import './SearchForm.scss'; import React from 'react'; import AddButton from '../ui/AddButton'; import DeleteButton from '../ui/DeleteButton'; - -interface Value { - default: string; - engines: string[][]; -} +import { FormSearch } from '../../../shared/SettingData'; interface Props { - value: Value; - onChange: (value: Value) => void; + value: FormSearch; + onChange: (value: FormSearch) => void; onBlur: () => void; } class SearchForm extends React.Component<Props> { public static defaultProps: Props = { - value: { default: '', engines: []}, + value: FormSearch.valueOf({ default: '', engines: []}), onChange: () => {}, onBlur: () => {}, } render() { - let value = this.props.value; - if (!value.engines) { - value.engines = []; - } - + let value = this.props.value.toJSON(); return <div className='form-search-form'> <div className='form-search-form-header'> <div className='column-name'>Name</div> @@ -63,28 +55,28 @@ class SearchForm extends React.Component<Props> { } bindValue(e: any) { - let value = this.props.value; + let value = this.props.value.toJSON(); let name = e.target.name; let index = Number(e.target.getAttribute('data-index')); - let next: Value = { + let next: typeof value = { default: value.default, - engines: value.engines ? value.engines.slice() : [], + engines: value.engines.slice(), }; if (name === 'name') { next.engines[index][0] = e.target.value; - next.default = this.props.value.engines[index][0]; + next.default = value.engines[index][0]; } else if (name === 'url') { next.engines[index][1] = e.target.value; } else if (name === 'default') { - next.default = this.props.value.engines[index][0]; + next.default = value.engines[index][0]; } else if (name === 'add') { next.engines.push(['', '']); } else if (name === 'delete') { next.engines.splice(index, 1); } - this.props.onChange(next); + this.props.onChange(FormSearch.valueOf(next)); if (name === 'delete' || name === 'default') { this.props.onBlur(); } diff --git a/src/settings/components/index.tsx b/src/settings/components/index.tsx index f56e93f..b4a0866 100644 --- a/src/settings/components/index.tsx +++ b/src/settings/components/index.tsx @@ -6,22 +6,26 @@ import SearchForm from './form/SearchForm'; import KeymapsForm from './form/KeymapsForm'; import BlacklistForm from './form/BlacklistForm'; import PropertiesForm from './form/PropertiesForm'; -import * as properties from '../../shared/settings/properties'; import * as settingActions from '../../settings/actions/setting'; +import SettingData, { + JSONSettings, FormKeymaps, FormSearch, FormSettings, +} from '../../shared/SettingData'; +import { State as AppState } from '../reducers/setting'; +import * as settings from '../../shared/Settings'; +import * as PropertyDefs from '../../shared/property-defs'; const DO_YOU_WANT_TO_CONTINUE = 'Some settings in JSON can be lost when migrating. ' + 'Do you want to continue?'; -interface Props { - source: string; - json: string; - form: any; - error: string; - +type StateProps = ReturnType<typeof mapStateToProps>; +interface DispatchProps { + dispatch: (action: any) => void, +} +type Props = StateProps & DispatchProps & { // FIXME store: any; -} +}; class SettingsComponent extends React.Component<Props> { componentDidMount() { @@ -29,12 +33,17 @@ class SettingsComponent extends React.Component<Props> { } renderFormFields(form: any) { + let types = PropertyDefs.defs.reduce( + (o: {[key: string]: string}, def) => { + o[def.name] = def.type; + return o; + }, {}); return <div> <fieldset> <legend>Keybindings</legend> <KeymapsForm value={form.keymaps} - onChange={value => this.bindForm('keymaps', value)} + onChange={this.bindKeymapsForm.bind(this)} onBlur={this.save.bind(this)} /> </fieldset> @@ -42,7 +51,7 @@ class SettingsComponent extends React.Component<Props> { <legend>Search Engines</legend> <SearchForm value={form.search} - onChange={value => this.bindForm('search', value)} + onChange={this.bindSearchForm.bind(this)} onBlur={this.save.bind(this)} /> </fieldset> @@ -50,23 +59,23 @@ class SettingsComponent extends React.Component<Props> { <legend>Blacklist</legend> <BlacklistForm value={form.blacklist} - onChange={value => this.bindForm('blacklist', value)} + onChange={this.bindBlacklistForm.bind(this)} onBlur={this.save.bind(this)} /> </fieldset> <fieldset> <legend>Properties</legend> <PropertiesForm - types={properties.types} + types={types} value={form.properties} - onChange={value => this.bindForm('properties', value)} + onChange={this.bindPropertiesForm.bind(this)} onBlur={this.save.bind(this)} /> </fieldset> </div>; } - renderJsonFields(json: string, error: string) { + renderJsonFields(json: JSONSettings, error: string) { return <div> <Input type='textarea' @@ -76,7 +85,7 @@ class SettingsComponent extends React.Component<Props> { error={error} onValueChange={this.bindJson.bind(this)} onBlur={this.save.bind(this)} - value={json} + value={json.toJSON()} /> </div>; } @@ -87,7 +96,8 @@ class SettingsComponent extends React.Component<Props> { if (this.props.source === 'form') { fields = this.renderFormFields(this.props.form); } else if (this.props.source === 'json') { - fields = this.renderJsonFields(this.props.json, this.props.error); + fields = this.renderJsonFields( + this.props.json as JSONSettings, this.props.error); } return ( <div> @@ -117,45 +127,73 @@ class SettingsComponent extends React.Component<Props> { ); } - bindForm(name: string, value: any) { - let settings = { + bindKeymapsForm(value: FormKeymaps) { + let data = new SettingData({ + source: this.props.source, + form: (this.props.form as FormSettings).buildWithKeymaps(value), + }); + this.props.dispatch(settingActions.set(data)); + } + + bindSearchForm(value: any) { + let data = new SettingData({ + source: this.props.source, + form: (this.props.form as FormSettings).buildWithSearch( + FormSearch.valueOf(value)), + }); + this.props.dispatch(settingActions.set(data)); + } + + bindBlacklistForm(value: any) { + let data = new SettingData({ + source: this.props.source, + form: (this.props.form as FormSettings).buildWithBlacklist( + settings.blacklistValueOf(value)), + }); + this.props.dispatch(settingActions.set(data)); + } + + bindPropertiesForm(value: any) { + let data = new SettingData({ source: this.props.source, - json: this.props.json, - form: { ...this.props.form }, - }; - settings.form[name] = value; - this.props.dispatch(settingActions.set(settings)); + form: (this.props.form as FormSettings).buildWithProperties( + settings.propertiesValueOf(value)), + }); + this.props.dispatch(settingActions.set(data)); } bindJson(_name: string, value: string) { - let settings = { + let data = new SettingData({ source: this.props.source, - json: value, - form: this.props.form, - }; - this.props.dispatch(settingActions.set(settings)); + json: JSONSettings.valueOf(value), + }); + this.props.dispatch(settingActions.set(data)); } bindSource(_name: string, value: string) { let from = this.props.source; if (from === 'form' && value === 'json') { - this.props.dispatch(settingActions.switchToJson(this.props.form)); + this.props.dispatch(settingActions.switchToJson( + this.props.form as FormSettings)); } else if (from === 'json' && value === 'form') { let b = window.confirm(DO_YOU_WANT_TO_CONTINUE); if (!b) { this.forceUpdate(); return; } - this.props.dispatch(settingActions.switchToForm(this.props.json)); + this.props.dispatch( + settingActions.switchToForm(this.props.json as JSONSettings)); } } save() { - let settings = this.props.store.getState(); - this.props.dispatch(settingActions.save(settings)); + let { source, json, form } = this.props.store.getState(); + this.props.dispatch(settingActions.save( + new SettingData({ source, json, form }), + )); } } -const mapStateToProps = (state: any) => state; +const mapStateToProps = (state: AppState) => ({ ...state }); export default connect(mapStateToProps)(SettingsComponent); diff --git a/src/settings/keymaps.ts b/src/settings/keymaps.ts index ccfc74c..38045ad 100644 --- a/src/settings/keymaps.ts +++ b/src/settings/keymaps.ts @@ -66,9 +66,6 @@ const fields = [ ] ]; -const allowedOps = [].concat(...fields.map(group => group.map(e => e[0]))); - export default { fields, - allowedOps, }; diff --git a/src/settings/reducers/setting.ts b/src/settings/reducers/setting.ts index 47c21bf..c4a21c7 100644 --- a/src/settings/reducers/setting.ts +++ b/src/settings/reducers/setting.ts @@ -1,23 +1,25 @@ import * as actions from '../actions'; +import { + JSONSettings, FormSettings, SettingSource, +} from '../../shared/SettingData'; -interface State { - source: string; - json: string; - form: any; +export interface State { + source: SettingSource; + json?: JSONSettings; + form?: FormSettings; error: string; } const defaultState: State = { - source: '', - json: '', - form: null, + source: SettingSource.JSON, + json: JSONSettings.valueOf(''), error: '', }; export default function reducer( state = defaultState, action: actions.SettingAction, -) { +): State { switch (action.type) { case actions.SETTING_SET_SETTINGS: return { ...state, @@ -32,12 +34,12 @@ export default function reducer( case actions.SETTING_SWITCH_TO_FORM: return { ...state, error: '', - source: 'form', + source: SettingSource.Form, form: action.form, }; case actions.SETTING_SWITCH_TO_JSON: return { ...state, error: '', - source: 'json', + source: SettingSource.JSON, json: action.json, }; default: return state; diff --git a/src/settings/storage.ts b/src/settings/storage.ts new file mode 100644 index 0000000..748b9ab --- /dev/null +++ b/src/settings/storage.ts @@ -0,0 +1,15 @@ +import SettingData, { DefaultSettingData } from '../shared/SettingData'; + +export const load = async(): Promise<SettingData> => { + let { settings } = await browser.storage.local.get('settings'); + if (!settings) { + return DefaultSettingData; + } + return SettingData.valueOf(settings); +}; + +export const save = (data: SettingData) => { + return browser.storage.local.set({ + settings: data.toJSON(), + }); +}; |