2017-11-12 02:44:26 +02:00
const React = require ( 'react' ) ;
const { connect } = require ( 'react-redux' ) ;
2017-12-14 20:12:14 +02:00
const Setting = require ( 'lib/models/Setting.js' ) ;
2017-11-12 02:44:26 +02:00
const { bridge } = require ( 'electron' ) . remote . require ( './bridge' ) ;
const { themeStyle } = require ( '../theme.js' ) ;
2017-12-08 23:51:59 +02:00
const pathUtils = require ( 'lib/path-utils.js' ) ;
2017-11-12 02:44:26 +02:00
const { _ } = require ( 'lib/locale.js' ) ;
2018-02-06 20:59:36 +02:00
const SyncTargetRegistry = require ( 'lib/SyncTargetRegistry' ) ;
const shared = require ( 'lib/components/shared/config-shared.js' ) ;
2019-09-11 01:53:01 +02:00
const ConfigMenuBar = require ( './ConfigMenuBar.min.js' ) ;
const { EncryptionConfigScreen } = require ( './EncryptionConfigScreen.min' ) ;
const { ClipperConfigScreen } = require ( './ClipperConfigScreen.min' ) ;
2017-11-12 02:44:26 +02:00
class ConfigScreenComponent extends React . Component {
2017-12-08 00:29:02 +02:00
constructor ( ) {
super ( ) ;
2018-02-06 20:59:36 +02:00
shared . init ( this ) ;
2019-09-11 01:53:01 +02:00
this . state . selectedSectionName = 'general' ;
this . state . screenName = '' ;
2018-02-06 20:59:36 +02:00
this . checkSyncConfig _ = async ( ) => {
await shared . checkSyncConfig ( this , this . state . settings ) ;
2019-07-29 14:13:23 +02:00
} ;
2018-02-06 20:59:36 +02:00
this . rowStyle _ = {
marginBottom : 10 ,
} ;
2019-09-11 01:53:01 +02:00
this . configMenuBar _selectionChange = this . configMenuBar _selectionChange . bind ( this ) ;
2017-12-08 00:29:02 +02:00
}
componentWillMount ( ) {
this . setState ( { settings : this . props . settings } ) ;
}
2019-09-11 01:53:01 +02:00
componentDidMount ( ) {
if ( this . props . defaultSection ) {
this . setState ( { selectedSectionName : this . props . defaultSection } , ( ) => {
this . switchSection ( this . props . defaultSection ) ;
} ) ;
}
}
sectionByName ( name ) {
const sections = shared . settingsSections ( { device : 'desktop' , settings : this . state . settings } ) ;
for ( const section of sections ) {
if ( section . name === name ) return section ;
}
2019-09-19 23:51:18 +02:00
throw new Error ( ` Invalid section name: ${ name } ` ) ;
2019-09-11 01:53:01 +02:00
}
screenFromName ( screenName ) {
if ( screenName === 'encryption' ) return < EncryptionConfigScreen theme = { this . props . theme } / > ;
if ( screenName === 'server' ) return < ClipperConfigScreen theme = { this . props . theme } / > ;
2019-09-19 23:51:18 +02:00
throw new Error ( ` Invalid screen name: ${ screenName } ` ) ;
2019-09-11 01:53:01 +02:00
}
switchSection ( name ) {
const section = this . sectionByName ( name ) ;
let screenName = '' ;
if ( section . isScreen ) {
screenName = section . name ;
if ( this . hasChanges ( ) ) {
const ok = confirm ( _ ( 'This will open a new screen. Save your current changes?' ) ) ;
if ( ok ) shared . saveSettings ( this ) ;
}
}
this . setState ( { selectedSectionName : section . name , screenName : screenName } ) ;
}
configMenuBar _selectionChange ( event ) {
this . switchSection ( event . section . name ) ;
}
2017-12-17 13:30:32 +02:00
keyValueToArray ( kv ) {
let output = [ ] ;
for ( let k in kv ) {
if ( ! kv . hasOwnProperty ( k ) ) continue ;
output . push ( {
key : k ,
label : kv [ k ] ,
} ) ;
}
return output ;
}
2019-09-11 01:53:01 +02:00
sectionToComponent ( key , section , settings , selected ) {
2019-01-28 01:15:56 +02:00
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 = {
2019-09-11 01:53:01 +02:00
marginTop : 20 ,
2019-01-28 01:15:56 +02:00
marginBottom : 20 ,
} ;
2019-09-11 01:53:01 +02:00
if ( ! selected ) sectionStyle . display = 'none' ;
2019-02-10 18:29:06 +02:00
2019-01-28 01:15:56 +02:00
if ( section . name === 'general' ) {
sectionStyle . borderTopWidth = 0 ;
}
2019-07-29 14:13:23 +02:00
const noteComp = section . name !== 'general' ? null : < div style = { Object . assign ( { } , theme . textStyle , { marginBottom : 10 } ) } > { _ ( 'Notes and settings are stored in: %s' , pathUtils . toSystemSlashes ( Setting . value ( 'profileDir' ) , process . platform ) ) } < / div > ;
2019-01-28 01:15:56 +02:00
2019-05-08 00:50:12 +02:00
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 : (
< div style = { statusStyle } >
{ messages [ 0 ] }
2019-07-29 14:13:23 +02:00
{ messages . length >= 1 ? < p > { messages [ 1 ] } < / p > : null }
< / div >
) ;
2019-05-08 00:50:12 +02:00
settingComps . push (
< div key = "check_sync_config_button" style = { this . rowStyle _ } >
2019-07-29 14:13:23 +02:00
< button disabled = { this . state . checkSyncConfigResult === 'checking' } style = { theme . buttonStyle } onClick = { this . checkSyncConfig _ } >
{ _ ( 'Check synchronisation configuration' ) }
< / button >
{ statusComp }
< / div >
) ;
2019-05-08 00:50:12 +02:00
}
}
2019-01-28 01:15:56 +02:00
return (
< div key = { key } style = { sectionStyle } >
{ noteComp }
2019-07-29 14:13:23 +02:00
< div > { settingComps } < / div >
2019-01-28 01:15:56 +02:00
< / div >
) ;
}
2017-11-12 02:44:26 +02:00
settingToComponent ( key , value ) {
const theme = themeStyle ( this . props . theme ) ;
let output = null ;
2018-02-06 20:59:36 +02:00
const rowStyle = this . rowStyle _ ;
2017-11-12 02:44:26 +02:00
const labelStyle = Object . assign ( { } , theme . textStyle , {
display : 'inline-block' ,
marginRight : 10 ,
2018-11-08 00:37:13 +02:00
color : theme . color ,
2017-11-12 02:44:26 +02:00
} ) ;
2019-01-30 21:19:06 +02:00
const subLabel = Object . assign ( { } , labelStyle , {
opacity : 0.7 ,
marginBottom : Math . round ( rowStyle . marginBottom * 0.7 ) ,
} ) ;
const invisibleLabel = Object . assign ( { } , labelStyle , {
opacity : 0 ,
} ) ;
2019-09-11 01:53:01 +02:00
const checkboxLabelStyle = Object . assign ( { } , labelStyle , {
marginLeft : 8 ,
} ) ;
2017-11-12 02:44:26 +02:00
const controlStyle = {
display : 'inline-block' ,
2018-11-08 00:37:13 +02:00
color : theme . color ,
backgroundColor : theme . backgroundColor ,
2017-11-12 02:44:26 +02:00
} ;
2018-03-02 20:16:48 +02:00
const descriptionStyle = Object . assign ( { } , theme . textStyle , {
color : theme . colorFaded ,
marginTop : 5 ,
fontStyle : 'italic' ,
2018-06-26 00:54:28 +02:00
maxWidth : '70em' ,
2018-03-02 20:16:48 +02:00
} ) ;
2019-09-11 01:53:01 +02:00
const textInputBaseStyle = Object . assign ( { } , controlStyle , {
border : '1px solid' ,
padding : '4px 6px' ,
borderColor : theme . dividerColor ,
borderRadius : 4 ,
} ) ;
2017-11-12 02:44:26 +02:00
const updateSettingValue = ( key , value ) => {
2019-01-30 21:19:06 +02:00
// console.info(key + ' = ' + value);
2018-02-13 20:26:33 +02:00
return shared . updateSettingValue ( this , key , value ) ;
2019-07-29 14:13:23 +02:00
} ;
2017-11-12 02:44:26 +02:00
2017-11-30 20:36:26 +02:00
// Component key needs to be key+value otherwise it doesn't update when the settings change.
2017-11-12 02:44:26 +02:00
const md = Setting . settingMetadata ( key ) ;
2018-03-02 20:16:48 +02:00
const descriptionText = Setting . keyDescription ( key , 'desktop' ) ;
2019-07-29 14:13:23 +02:00
const descriptionComp = descriptionText ? < div style = { descriptionStyle } > { descriptionText } < / div > : null ;
2018-03-02 20:16:48 +02:00
2017-11-12 02:44:26 +02:00
if ( md . isEnum ) {
let items = [ ] ;
const settingOptions = md . options ( ) ;
2017-12-17 13:30:32 +02:00
let array = this . keyValueToArray ( settingOptions ) ;
for ( let i = 0 ; i < array . length ; i ++ ) {
const e = array [ i ] ;
2019-07-29 14:13:23 +02:00
items . push (
< option value = { e . key . toString ( ) } key = { e . key } >
{ settingOptions [ e . key ] }
< / option >
) ;
2017-11-12 02:44:26 +02:00
}
2019-09-11 01:53:01 +02:00
const selectStyle = Object . assign ( { } , controlStyle , { height : 22 , borderColor : theme . dividerColor } ) ;
2017-11-12 02:44:26 +02:00
return (
2017-12-15 09:31:57 +02:00
< div key = { key } style = { rowStyle } >
2019-07-29 14:13:23 +02:00
< div style = { labelStyle } >
< label > { md . label ( ) } < / label >
< / div >
< select
value = { value }
2019-09-11 01:53:01 +02:00
style = { selectStyle }
2019-07-29 14:13:23 +02:00
onChange = { event => {
updateSettingValue ( key , event . target . value ) ;
} }
>
2017-11-12 02:44:26 +02:00
{ items }
< / select >
2019-07-29 14:13:23 +02:00
{ descriptionComp }
2017-11-12 02:44:26 +02:00
< / div >
) ;
} else if ( md . type === Setting . TYPE _BOOL ) {
2019-09-13 00:16:42 +02:00
const onCheckboxClick = ( ) => {
2019-07-29 14:13:23 +02:00
updateSettingValue ( key , ! value ) ;
} ;
2017-12-08 00:29:02 +02:00
2018-01-23 20:31:49 +02:00
// 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.
2017-11-12 02:44:26 +02:00
return (
2019-07-29 14:13:23 +02:00
< div key = { key + value . toString ( ) } style = { rowStyle } >
2017-11-12 02:44:26 +02:00
< div style = { controlStyle } >
2019-07-29 14:13:23 +02:00
< input
2019-09-19 23:51:18 +02:00
id = { ` setting_checkbox_ ${ key } ` }
2019-07-29 14:13:23 +02:00
type = "checkbox"
checked = { ! ! value }
onChange = { event => {
onCheckboxClick ( event ) ;
} }
/ >
< label
onClick = { event => {
onCheckboxClick ( event ) ;
} }
2019-09-11 01:53:01 +02:00
style = { checkboxLabelStyle }
2019-09-19 23:51:18 +02:00
htmlFor = { ` setting_checkbox_ ${ key } ` }
2019-07-29 14:13:23 +02:00
>
{ md . label ( ) }
< / label >
{ descriptionComp }
2017-11-12 02:44:26 +02:00
< / div >
< / div >
) ;
2017-12-08 00:29:02 +02:00
} else if ( md . type === Setting . TYPE _STRING ) {
2019-09-11 01:53:01 +02:00
const inputStyle = Object . assign ( { } , textInputBaseStyle , {
2018-11-08 00:37:13 +02:00
width : '50%' ,
minWidth : '20em' ,
2019-07-29 14:13:23 +02:00
} ) ;
2018-01-26 00:44:09 +02:00
const inputType = md . secure === true ? 'password' : 'text' ;
2019-01-30 21:19:06 +02:00
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 ] ;
2019-07-29 14:13:23 +02:00
} ;
2019-01-30 21:19:06 +02:00
const joinCmd = cmdArray => {
if ( ! cmdArray [ 0 ] && ! cmdArray [ 1 ] ) return '' ;
let cmdString = pathUtils . quotePath ( cmdArray [ 0 ] ) ;
if ( ! cmdString ) cmdString = '""' ;
2019-09-19 23:51:18 +02:00
if ( cmdArray [ 1 ] ) cmdString += ` ${ cmdArray [ 1 ] } ` ;
2019-01-30 21:19:06 +02:00
return cmdString ;
2019-07-29 14:13:23 +02:00
} ;
2019-01-30 21:19:06 +02:00
const onPathChange = event => {
const cmd = splitCmd ( this . state . settings [ key ] ) ;
cmd [ 0 ] = event . target . value ;
updateSettingValue ( key , joinCmd ( cmd ) ) ;
2019-07-29 14:13:23 +02:00
} ;
2019-01-30 21:19:06 +02:00
const onArgsChange = event => {
const cmd = splitCmd ( this . state . settings [ key ] ) ;
cmd [ 1 ] = event . target . value ;
updateSettingValue ( key , joinCmd ( cmd ) ) ;
2019-07-29 14:13:23 +02:00
} ;
2019-01-30 21:19:06 +02:00
const browseButtonClick = ( ) => {
const paths = bridge ( ) . showOpenDialog ( ) ;
if ( ! paths || ! paths . length ) return ;
const cmd = splitCmd ( this . state . settings [ key ] ) ;
2019-07-29 14:13:23 +02:00
cmd [ 0 ] = paths [ 0 ] ;
2019-01-30 21:19:06 +02:00
updateSettingValue ( key , joinCmd ( cmd ) ) ;
2019-07-29 14:13:23 +02:00
} ;
2019-01-30 21:19:06 +02:00
const cmd = splitCmd ( this . state . settings [ key ] ) ;
return (
< div key = { key } style = { rowStyle } >
2019-07-29 14:13:23 +02:00
< div style = { { display : 'flex' } } >
< div style = { { flex : 0 , whiteSpace : 'nowrap' } } >
< div style = { labelStyle } >
< label > { md . label ( ) } < / label >
< / div >
2019-01-30 21:19:06 +02:00
< / div >
2019-07-29 14:13:23 +02:00
< div style = { { flex : 0 } } >
2019-01-30 21:19:06 +02:00
< div style = { subLabel } > Path : < / div >
< div style = { subLabel } > Arguments : < / div >
< / div >
2019-07-29 14:13:23 +02:00
< div style = { { flex : 1 } } >
< div style = { { display : 'flex' , flexDirection : 'row' , alignItems : 'center' , marginBottom : inputStyle . marginBottom } } >
< input
type = { inputType }
style = { Object . assign ( { } , inputStyle , { marginBottom : 0 } ) }
onChange = { event => {
onPathChange ( event ) ;
} }
value = { cmd [ 0 ] }
/ >
2019-09-11 01:53:01 +02:00
< button onClick = { browseButtonClick } style = { Object . assign ( { } , theme . buttonStyle , { marginLeft : 5 } ) } >
2019-07-29 14:13:23 +02:00
{ _ ( 'Browse...' ) }
< / button >
2019-01-30 21:19:06 +02:00
< / div >
2019-07-29 14:13:23 +02:00
< input
type = { inputType }
style = { inputStyle }
onChange = { event => {
onArgsChange ( event ) ;
} }
value = { cmd [ 1 ] }
/ >
2019-01-30 21:19:06 +02:00
< / div >
< / div >
2019-07-29 14:13:23 +02:00
< div style = { { display : 'flex' } } >
< div style = { { flex : 0 , whiteSpace : 'nowrap' } } >
< div style = { invisibleLabel } >
< label > { md . label ( ) } < / label >
< / div >
2019-01-30 21:19:06 +02:00
< / div >
2019-07-29 14:13:23 +02:00
< div style = { { flex : 1 } } > { descriptionComp } < / div >
2019-01-30 21:19:06 +02:00
< / div >
< / div >
) ;
} else {
2019-07-29 14:13:23 +02:00
const onTextChange = event => {
2019-01-30 21:19:06 +02:00
updateSettingValue ( key , event . target . value ) ;
2019-07-29 14:13:23 +02:00
} ;
2019-01-30 21:19:06 +02:00
return (
< div key = { key } style = { rowStyle } >
2019-07-29 14:13:23 +02:00
< div style = { labelStyle } >
< label > { md . label ( ) } < / label >
< / div >
< input
type = { inputType }
style = { inputStyle }
value = { this . state . settings [ key ] }
onChange = { event => {
onTextChange ( event ) ;
} }
/ >
{ descriptionComp }
2019-01-30 21:19:06 +02:00
< / div >
) ;
}
2018-01-19 14:27:44 +02:00
} else if ( md . type === Setting . TYPE _INT ) {
2019-07-29 14:13:23 +02:00
const onNumChange = event => {
2018-01-23 20:31:49 +02:00
updateSettingValue ( key , event . target . value ) ;
2018-01-19 14:27:44 +02:00
} ;
2019-05-06 22:35:29 +02:00
const label = [ md . label ( ) ] ;
2019-09-19 23:51:18 +02:00
if ( md . unitLabel ) label . push ( ` ( ${ md . unitLabel ( ) } ) ` ) ;
2019-05-06 22:35:29 +02:00
2019-09-11 01:53:01 +02:00
const inputStyle = Object . assign ( { } , textInputBaseStyle ) ;
2018-01-19 14:27:44 +02:00
return (
2018-01-23 20:31:49 +02:00
< div key = { key } style = { rowStyle } >
2019-07-29 14:13:23 +02:00
< div style = { labelStyle } >
< label > { label . join ( ' ' ) } < / label >
< / div >
< input
type = "number"
2019-09-11 01:53:01 +02:00
style = { inputStyle }
2019-07-29 14:13:23 +02:00
value = { this . state . settings [ key ] }
onChange = { event => {
onNumChange ( event ) ;
} }
min = { md . minimum }
max = { md . maximum }
step = { md . step }
/ >
{ descriptionComp }
2018-01-23 20:31:49 +02:00
< / div >
2018-01-19 14:27:44 +02:00
) ;
2017-12-08 00:29:02 +02:00
} else {
2019-09-19 23:51:18 +02:00
console . warn ( ` Type not implemented: ${ key } ` ) ;
2017-11-12 02:44:26 +02:00
}
return output ;
}
2018-06-26 00:54:28 +02:00
onApplyClick ( ) {
shared . saveSettings ( this ) ;
}
2017-12-08 00:29:02 +02:00
onSaveClick ( ) {
2018-02-13 20:26:33 +02:00
shared . saveSettings ( this ) ;
2017-12-08 00:29:02 +02:00
this . props . dispatch ( { type : 'NAV_BACK' } ) ;
}
onCancelClick ( ) {
this . props . dispatch ( { type : 'NAV_BACK' } ) ;
}
2019-09-11 01:53:01 +02:00
hasChanges ( ) {
return ! ! this . state . changedSettingKeys . length ;
}
2017-11-12 02:44:26 +02:00
render ( ) {
const theme = themeStyle ( this . props . theme ) ;
2019-01-28 01:44:16 +02:00
2019-07-29 14:13:23 +02:00
const style = Object . assign (
{
backgroundColor : theme . backgroundColor ,
} ,
this . props . style ,
{
overflow : 'hidden' ,
display : 'flex' ,
flexDirection : 'column' ,
}
) ;
2017-11-12 02:44:26 +02:00
2019-01-28 01:44:16 +02:00
let settings = this . state . settings ;
2017-11-12 02:44:26 +02:00
2019-09-11 01:53:01 +02:00
const containerStyle = Object . assign ( { } , theme . containerStyle , { padding : 10 , paddingTop : 0 , display : 'flex' , flex : 1 } ) ;
2017-11-12 02:44:26 +02:00
2019-09-11 01:53:01 +02:00
const hasChanges = this . hasChanges ( ) ;
2019-01-28 01:44:16 +02:00
2018-11-08 00:37:13 +02:00
const buttonStyle = Object . assign ( { } , theme . buttonStyle , {
2019-01-28 01:44:16 +02:00
display : 'inline-block' ,
2017-12-08 00:29:02 +02:00
marginRight : 10 ,
2018-11-08 00:37:13 +02:00
} ) ;
2017-12-08 00:29:02 +02:00
2019-01-28 01:44:16 +02:00
const buttonStyleApprove = Object . assign ( { } , buttonStyle , {
opacity : hasChanges ? 1 : theme . disabledOpacity ,
} ) ;
2019-09-11 01:53:01 +02:00
const settingComps = shared . settingsToComponents2 ( this , 'desktop' , settings , this . state . selectedSectionName ) ;
2017-11-12 02:44:26 +02:00
2019-01-28 01:44:16 +02:00
const buttonBarStyle = {
display : 'flex' ,
alignItems : 'center' ,
2019-09-11 01:53:01 +02:00
padding : 10 ,
borderTopWidth : 1 ,
borderTopStyle : 'solid' ,
borderTopColor : theme . dividerColor ,
2019-01-28 01:44:16 +02:00
} ;
2019-09-30 00:11:36 +02:00
const screenComp = this . state . screenName ? < div style = { { overflow : 'scroll' , flex : 1 } } > { this . screenFromName ( this . state . screenName ) } < / div > : null ;
2019-09-11 01:53:01 +02:00
if ( screenComp ) containerStyle . display = 'none' ;
const sections = shared . settingsSections ( { device : 'desktop' , settings } ) ;
2017-11-12 02:44:26 +02:00
return (
< div style = { style } >
2019-09-11 01:53:01 +02:00
< ConfigMenuBar
selection = { this . state . selectedSectionName }
onSelectionChange = { this . configMenuBar _selectionChange }
sections = { sections }
theme = { this . props . theme }
/ >
{ screenComp }
< div style = { containerStyle } > { settingComps } < / div >
2019-01-28 01:44:16 +02:00
< div style = { buttonBarStyle } >
2019-07-29 14:13:23 +02:00
< button
onClick = { ( ) => {
this . onCancelClick ( ) ;
} }
style = { buttonStyle }
>
< i style = { theme . buttonIconStyle } className = { 'fa fa-chevron-left' } > < / i >
2019-09-11 01:53:01 +02:00
{ hasChanges && ! screenComp ? _ ( 'Cancel' ) : _ ( 'Back' ) }
2019-07-29 14:13:23 +02:00
< / button >
2019-09-11 01:53:01 +02:00
{ ! screenComp && (
< div >
< button disabled = { ! hasChanges } onClick = { ( ) => { this . onSaveClick ( ) ; } } style = { buttonStyleApprove } > { _ ( 'OK' ) } < / button >
< button disabled = { ! hasChanges } onClick = { ( ) => { this . onApplyClick ( ) ; } } style = { buttonStyleApprove } > { _ ( 'Apply' ) } < / button >
< / div >
) }
2019-02-10 18:29:06 +02:00
< / div >
2017-11-12 02:44:26 +02:00
< / div >
) ;
}
}
2019-07-29 14:13:23 +02:00
const mapStateToProps = state => {
2017-11-12 02:44:26 +02:00
return {
theme : state . settings . theme ,
settings : state . settings ,
locale : state . settings . locale ,
} ;
} ;
const ConfigScreen = connect ( mapStateToProps ) ( ConfigScreenComponent ) ;
2018-11-08 00:37:13 +02:00
module . exports = { ConfigScreen } ;