const React = require('react'); const { connect } = require('react-redux'); const Setting = require('lib/models/Setting.js'); const { bridge } = require('electron').remote.require('./bridge'); const { themeStyle } = require('../theme.js'); const pathUtils = require('lib/path-utils.js'); const { _ } = require('lib/locale.js'); const SyncTargetRegistry = require('lib/SyncTargetRegistry'); const shared = require('lib/components/shared/config-shared.js'); class ConfigScreenComponent extends React.Component { constructor() { super(); shared.init(this); this.checkSyncConfig_ = async () => { await shared.checkSyncConfig(this, this.state.settings); }; this.rowStyle_ = { marginBottom: 10, }; } componentWillMount() { this.setState({ settings: this.props.settings }); } keyValueToArray(kv) { let output = []; for (let k in kv) { if (!kv.hasOwnProperty(k)) continue; output.push({ key: k, label: kv[k], }); } return output; } sectionToComponent(key, section, settings) { const theme = themeStyle(this.props.theme); const settingComps = []; for (let i = 0; i < section.metadatas.length; i++) { const md = section.metadatas[i]; const settingComp = this.settingToComponent(md.key, settings[md.key]); settingComps.push(settingComp); } const sectionStyle = { marginBottom: 20, }; const headerStyle = Object.assign({}, theme.headerStyle, { borderBottomWidth: 1, borderBottomColor: theme.dividerColor, borderBottomStyle: 'solid', paddingBottom: '.4em', }); if (section.name === 'general') { sectionStyle.borderTopWidth = 0; } const noteComp = section.name !== 'general' ? null :
{_('Notes and settings are stored in: %s', pathUtils.toSystemSlashes(Setting.value('profileDir'), process.platform))}
; if (section.name === 'sync') { const syncTargetMd = SyncTargetRegistry.idToMetadata(settings['sync.target']); if (syncTargetMd.supportsConfigCheck) { const messages = shared.checkSyncConfigMessages(this); const statusStyle = Object.assign({}, theme.textStyle, { marginTop: 10 }); const statusComp = !messages.length ? null : (
{messages[0]} {messages.length >= 1 ?

{messages[1]}

: null}
); settingComps.push(
{statusComp}
); } } return (

{Setting.sectionNameToLabel(section.name)}

{noteComp}
{settingComps}
); } settingToComponent(key, value) { const theme = themeStyle(this.props.theme); let output = null; const rowStyle = this.rowStyle_; const labelStyle = Object.assign({}, theme.textStyle, { display: 'inline-block', marginRight: 10, color: theme.color, }); const subLabel = Object.assign({}, labelStyle, { opacity: 0.7, marginBottom: Math.round(rowStyle.marginBottom * 0.7), }); const invisibleLabel = Object.assign({}, labelStyle, { opacity: 0, }); const controlStyle = { display: 'inline-block', color: theme.color, backgroundColor: theme.backgroundColor, }; const descriptionStyle = Object.assign({}, theme.textStyle, { color: theme.colorFaded, marginTop: 5, fontStyle: 'italic', maxWidth: '70em', }); const updateSettingValue = (key, value) => { // console.info(key + ' = ' + value); return shared.updateSettingValue(this, key, value); }; // Component key needs to be key+value otherwise it doesn't update when the settings change. const md = Setting.settingMetadata(key); const descriptionText = Setting.keyDescription(key, 'desktop'); const descriptionComp = descriptionText ?
{descriptionText}
: null; if (md.isEnum) { let items = []; const settingOptions = md.options(); let array = this.keyValueToArray(settingOptions); for (let i = 0; i < array.length; i++) { const e = array[i]; items.push( ); } return (
{descriptionComp}
); } else if (md.type === Setting.TYPE_BOOL) { const onCheckboxClick = event => { updateSettingValue(key, !value); }; // Hack: The {key+value.toString()} is needed as otherwise the checkbox doesn't update when the state changes. // There's probably a better way to do this but can't figure it out. return (
{ onCheckboxClick(event); }} /> {descriptionComp}
); } else if (md.type === Setting.TYPE_STRING) { const inputStyle = Object.assign({}, controlStyle, { width: '50%', minWidth: '20em', border: '1px solid', }); const inputType = md.secure === true ? 'password' : 'text'; if (md.subType === 'file_path_and_args') { inputStyle.marginBottom = subLabel.marginBottom; const splitCmd = cmdString => { const path = pathUtils.extractExecutablePath(cmdString); const args = cmdString.substr(path.length + 1); return [pathUtils.unquotePath(path), args]; }; const joinCmd = cmdArray => { if (!cmdArray[0] && !cmdArray[1]) return ''; let cmdString = pathUtils.quotePath(cmdArray[0]); if (!cmdString) cmdString = '""'; if (cmdArray[1]) cmdString += ' ' + cmdArray[1]; return cmdString; }; const onPathChange = event => { const cmd = splitCmd(this.state.settings[key]); cmd[0] = event.target.value; updateSettingValue(key, joinCmd(cmd)); }; const onArgsChange = event => { const cmd = splitCmd(this.state.settings[key]); cmd[1] = event.target.value; updateSettingValue(key, joinCmd(cmd)); }; const browseButtonClick = () => { const paths = bridge().showOpenDialog(); if (!paths || !paths.length) return; const cmd = splitCmd(this.state.settings[key]); cmd[0] = paths[0]; updateSettingValue(key, joinCmd(cmd)); }; const cmd = splitCmd(this.state.settings[key]); return (
Path:
Arguments:
{ onPathChange(event); }} value={cmd[0]} />
{ onArgsChange(event); }} value={cmd[1]} />
{descriptionComp}
); } else { const onTextChange = event => { updateSettingValue(key, event.target.value); }; return (
{ onTextChange(event); }} /> {descriptionComp}
); } } else if (md.type === Setting.TYPE_INT) { const onNumChange = event => { updateSettingValue(key, event.target.value); }; const label = [md.label()]; if (md.unitLabel) label.push('(' + md.unitLabel() + ')'); return (
{ onNumChange(event); }} min={md.minimum} max={md.maximum} step={md.step} /> {descriptionComp}
); } else { console.warn('Type not implemented: ' + key); } return output; } onApplyClick() { shared.saveSettings(this); } onSaveClick() { shared.saveSettings(this); this.props.dispatch({ type: 'NAV_BACK' }); } onCancelClick() { this.props.dispatch({ type: 'NAV_BACK' }); } render() { const theme = themeStyle(this.props.theme); const style = Object.assign( { backgroundColor: theme.backgroundColor, }, this.props.style, { overflow: 'hidden', display: 'flex', flexDirection: 'column', } ); let settings = this.state.settings; const containerStyle = Object.assign({}, theme.containerStyle, { padding: 10, paddingTop: 0 }); const hasChanges = !!this.state.changedSettingKeys.length; const buttonStyle = Object.assign({}, theme.buttonStyle, { display: 'inline-block', marginRight: 10, }); const buttonStyleApprove = Object.assign({}, buttonStyle, { opacity: hasChanges ? 1 : theme.disabledOpacity, }); const settingComps = shared.settingsToComponents2(this, 'desktop', settings); const buttonBarStyle = { display: 'flex', alignItems: 'center', padding: 15, borderBottomWidth: 1, borderBottomStyle: 'solid', borderBottomColor: theme.dividerColor, }; return (
{settingComps}
); } } const mapStateToProps = state => { return { theme: state.settings.theme, settings: state.settings, locale: state.settings.locale, }; }; const ConfigScreen = connect(mapStateToProps)(ConfigScreenComponent); module.exports = { ConfigScreen };