import { connect } from 'react-redux' ; import { Dispatch } from 'redux' ; import { remote } from 'electron' ; import { accessSync } from 'fs' ; import * as validUrl from 'valid-url' ; import * as fs from 'fs' ; import Settings , { SettingsComponentMethods , SettingsComponentState } from '../components/settings' ; import { AppState } from '../reducers/index' ; import { SETTINGS_IDS } from '../reducers/settingsData' ; import * as SettingsDataActions from '../actions/settingsData' ; import * as SettingsUIActions from '../actions/settingsUI' ; import { polyglot as polyglotConfig } from '../proto/config' ; param { string | boolean } newValue param { SETTINGS_IDS } settingsID returns { ( dispatch : Dispatch < AppState > , getState : ( ) => AppState ) => void } function handleChangeAndError ( newValue , settingsID ) { return ( dispatch , getState ) => { switch ( settingsID ) { case SETTINGS_IDS . ENDPOINT : const endpoint = newValue as string ; const endpointError = ( endpoint === '' ) ? false : ! validateEndpoint ( endpoint ) ; dispatch ( SettingsDataActions . setEndpoint ( endpoint ) ) ; dispatch ( SettingsUIActions . setEndpointError ( endpointError ) ) ; break ; case SETTINGS_IDS . PROTO_DISCOVERY_ROOT : const protoDiscoveryRoot = newValue as string ; const protoDiscoveryRootError = ( protoDiscoveryRoot === '' ) ? false : ! validatePath ( protoDiscoveryRoot ) ; dispatch ( SettingsDataActions . setProtoDiscoveryRoot ( protoDiscoveryRoot ) ) ; dispatch ( SettingsUIActions . setProtoDiscoveryRootError ( protoDiscoveryRootError ) ) ; break ; case SETTINGS_IDS . ADD_PROTOC_INCLUDES : const addProtocIncludes = newValue as string ; const pathArray = addProtocIncludes . split ( ',' ) . map ( elem => elem . trim ( ) ) ; const addProtocIncludesErrors = ( addProtocIncludes === '' ) ? [ ] : validatePaths ( pathArray ) . map ( elem => ! elem ) ; dispatch ( SettingsDataActions . setAddProtocIncludes ( addProtocIncludes ) ) ; dispatch ( SettingsUIActions . setAddProtocIncludesError ( addProtocIncludesErrors ) ) ; break ; case SETTINGS_IDS . DEADLINE_MS : const deadlineMs = newValue as string ; const parsedDeadline = parseInt ( deadlineMs , 10 ) ; if ( ! isNaN ( parsedDeadline ) ) { dispatch ( SettingsDataActions . setDeadlineMs ( parsedDeadline ) ) ; } else { dispatch ( SettingsDataActions . setDeadlineMs ( - 1 ) ) ; } break ; case SETTINGS_IDS . CONFIG_NAME : const configName = newValue as string ; dispatch ( SettingsDataActions . setConfigName ( configName ) ) ; break ; case SETTINGS_IDS . OAUTH_REFRESH_TOKEN_ENDPOINT_URL : const oauthRefreshTokenEndpointUrl = newValue as string ; const oauthRefreshTokenEndpointUrlError = ( oauthRefreshTokenEndpointUrl === '' ) ? false : ! validateUri ( oauthRefreshTokenEndpointUrl ) ; dispatch ( SettingsDataActions . setOauthRefreshTokenEndpointUrl ( oauthRefreshTokenEndpointUrl ) ) ; dispatch ( SettingsUIActions . setOauthRefreshTokenEndpointUrlError ( oauthRefreshTokenEndpointUrlError ) ) ; break ; case SETTINGS_IDS . OAUTH_CLIENT_ID : const oauthClientId = newValue as string ; dispatch ( SettingsDataActions . setOauthClientId ( oauthClientId ) ) ; break ; case SETTINGS_IDS . OAUTH_CLIENT_SECRET : const oauthClientSecret = newValue as string ; dispatch ( SettingsDataActions . setOauthClientSecret ( oauthClientSecret ) ) ; break ; case SETTINGS_IDS . OAUTH_REFRESH_TOKEN_PATH : const oauthRefreshTokenPath = newValue as string ; const oauthRefreshTokenPathError = ( oauthRefreshTokenPath === '' ) ? false : ! validatePath ( oauthRefreshTokenPath ) ; dispatch ( SettingsDataActions . setOauthRefreshTokenPath ( oauthRefreshTokenPath ) ) ; dispatch ( SettingsUIActions . setOauthRefreshTokenPathError ( oauthRefreshTokenPathError ) ) ; break ; case SETTINGS_IDS . OAUTH_ACCESS_TOKEN_PATH : const oauthAccessTokenPath = newValue as string ; const oauthAccessTokenPathError = ( oauthAccessTokenPath === '' ) ? false : ! validatePath ( oauthAccessTokenPath ) ; dispatch ( SettingsDataActions . setOauthAccessTokenPath ( oauthAccessTokenPath ) ) ; dispatch ( SettingsUIActions . setOauthAccessTokenPathError ( oauthAccessTokenPathError ) ) ; break ; case SETTINGS_IDS . USE_TLS : const useTls = newValue as boolean ; dispatch ( SettingsDataActions . setUseTls ( useTls ) ) ; break ; case SETTINGS_IDS . TLS_CA_CERT_PATH : const tlsCaCertPath = newValue as string ; const tlsCaCertPathError = ( tlsCaCertPath === '' ) ? false : ! validatePath ( tlsCaCertPath ) ; dispatch ( SettingsDataActions . setTlsCaCertPath ( tlsCaCertPath ) ) ; dispatch ( SettingsUIActions . setTlsCaCertPathError ( tlsCaCertPathError ) ) ; break ; case SETTINGS_IDS . TLS_CLIENT_CERT_PATH : const tlsClientCertPath = newValue as string ; const tlsClientCertPathError = ( tlsClientCertPath === '' ) ? false : ! validatePath ( tlsClientCertPath ) ; dispatch ( SettingsDataActions . setTlsClientCertPath ( tlsClientCertPath ) ) ; dispatch ( SettingsUIActions . setTlsClientCertPathError ( tlsClientCertPathError ) ) ; break ; case SETTINGS_IDS . TLS_CLIENT_KEY_PATH : const tlsClientKeyPath = newValue as string ; const tlsClientKeyPathError = ( tlsClientKeyPath === '' ) ? false : ! validatePath ( tlsClientKeyPath ) ; dispatch ( SettingsDataActions . setTlsClientKeyPath ( tlsClientKeyPath ) ) ; dispatch ( SettingsUIActions . setTlsClientKeyPathError ( tlsClientKeyPathError ) ) ; break ; case SETTINGS_IDS . TLS_CLIENT_OVERRIDE_AUTHORITY : const tlsClientOverrideAuthority = newValue as string ; dispatch ( SettingsDataActions . setTlsClientOverrideAuthority ( tlsClientOverrideAuthority ) ) ; break ; default : assertUnreachable ( settingsID ) ; console . error ( `Unknown settings state id ${ settingsID } passed to handlePathBlur` ) ; break ; } } ; } param { never } x returns { never } function assertUnreachable ( x ) { throw new Error ( 'Should never reach this.' ) ; } param { string } returns { boolean } function validateUri ( uri ) { const validatedUri = validUrl . isUri ( uri ) ; return validatedUri !== undefined ; } param { string } path returns { boolean } function validatePath ( path ) { return validatePaths ( [ path ] ) [ 0 ] ; } param { string [ ] } paths returns { boolean [ ] } function validatePaths ( paths ) { const validPathList = [ ] ; for ( const path of paths ) { try { accessSync ( path ) ; validPathList . push ( true ) ; } catch ( err ) { console . warn ( path , ' does not exist' ) ; validPathList . push ( false ) ; } } return validPathList ; } param { string } newEndpoint returns { boolean } function validateEndpoint ( newEndpoint ) { return /[^\:]+:[0-9]+/ . test ( newEndpoint ) ; } param { string } settingsId param { string } macMessage param { boolean } multiSelection returns { ( dispatch : Dispatch < AppState > , getState : ( ) => AppState ) => void } function handlePathDoubleClick ( settingsId , macMessage = '' , multiSelection = false ) { return ( dispatch , getState ) => { const customProperties = [ 'openDirectory' , 'openFile' , 'showHiddenFiles' ] ; if ( multiSelection ) { customProperties . push ( 'multiSelections' ) ; } const pathList = remote . dialog . showOpenDialog ( { properties : customProperties , message : macMessage , } as Electron . OpenDialogOptions ) ; if ( pathList === undefined ) { return ; } if ( multiSelection ) { dispatch ( handleChangeAndError ( pathList . join ( ', ' ) , settingsId ) ) ; } else { const path = ( pathList . length >= 1 ) ? pathList [ 0 ] : '' ; dispatch ( handleChangeAndError ( path , settingsId ) ) ; } } ; } param { React . DragEvent < HTMLElement > } event param { SETTINGS_IDS } settingsId param { boolean } multiSelection returns { ( dispatch : Dispatch < AppState > , getState : ( ) => AppState ) => void } function handleFileDrop ( event , settingsId , multiSelection = false ) { return ( dispatch , getState ) => { if ( multiSelection ) { const pathList = Array . from ( event . dataTransfer . files ) . map ( elem => elem . path ) . join ( ', ' ) ; dispatch ( handleChangeAndError ( pathList , settingsId ) ) ; } else { const path = ( event . dataTransfer . files . length >= 1 ) ? event . dataTransfer . files [ 0 ] . path : '' ; dispatch ( handleChangeAndError ( path , settingsId ) ) ; } } ; } returns { ( dispatch : Dispatch < AppState > , getState : ( ) => AppState ) => void } function importConfig ( ) { return ( dispatch , getState ) => { const customProperties = [ 'openFile' , 'showHiddenFiles' ] ; const pathList = remote . dialog . showOpenDialog ( { properties : customProperties , message : 'Open polyglot config file' , } as Electron . OpenDialogOptions ) ; if ( pathList === undefined || pathList . length === 0 ) { return ; } const rawFile = fs . readFileSync ( pathList [ 0 ] ) ; const decodedFile = new TextDecoder ( 'utf-8' ) . decode ( rawFile ) ; const parsedJson = JSON . parse ( decodedFile ) ; const polyglotConfigSet = polyglotConfig . ConfigurationSet . fromObject ( parsedJson ) ; dispatch ( SettingsDataActions . importPolyglotConfigs ( polyglotConfigSet ) ) ; if ( polyglotConfigSet . configurations . length > 0 && polyglotConfigSet . configurations [ 0 ] . name !== undefined ) { dispatch ( handleConfigAutoComplete ( polyglotConfigSet . configurations [ 0 ] . name as string ) ) ; } } ; } returns { ( dispatch : Dispatch < AppState > , getState : ( ) => AppState ) => void } function handleConfigAutoComplete ( suggestion ) { return ( dispatch , getState ) => { dispatch ( SettingsDataActions . setConfigName ( suggestion ) ) ; const selectedConfig = getState ( ) . settingsState . settingsDataState . polyglotConfigs . get ( suggestion ) ; if ( selectedConfig != null ) { dispatch ( SettingsDataActions . setSettingsDataStateFromPolyglotConfig ( selectedConfig ) ) ; } } ; } returns { ( dispatch : Dispatch < AppState > , getState : ( ) => AppState ) => void } function saveConfig ( ) { return ( dispatch , getState ) => { dispatch ( SettingsDataActions . addPolyglotConfigFromCurrentFields ( ) ) ; const pathList = remote . dialog . showSaveDialog ( { message : 'Save polyglot config to file' , } as Electron . OpenDialogOptions ) ; const polyglotConfigSet = polyglotConfig . ConfigurationSet . create ( { configurations : Array . from ( getState ( ) . settingsState . settingsDataState . polyglotConfigs , ( [ key , val ] ) => { return val ; } ) , } ) ; fs . writeFileSync ( pathList , JSON . stringify ( polyglotConfigSet . toJSON ( ) ) ) ; } ; } param { AppState } state returns { SettingsComponentState } param state function mapStateToProps ( state ) { return { settingsDataState : state . settingsState . settingsDataState , settingsUIState : state . settingsState . settingsUIState , } ; } param { Dispatch < AppState > } dispatch returns { SettingsComponentMethods } function mapDispatchToProps ( dispatch ) { return { handleChange : ( newEndpoint , settingsID ) => dispatch ( handleChangeAndError ( newEndpoint , settingsID ) ) , handlePathDoubleClick : ( settingsID , macMessage ? , multiSelection ? ) => dispatch ( handlePathDoubleClick ( settingsID , macMessage , multiSelection ) ) , handleDrop : ( event , settingsID , multiSelection ? ) => dispatch ( handleFileDrop ( event , settingsID , multiSelection ) ) , importConfigFile : ( ) => dispatch ( importConfig ( ) ) , saveConfigFile : ( ) => dispatch ( saveConfig ( ) ) , handleConfigAutoComplete : ( suggestion ) => dispatch ( handleConfigAutoComplete ( suggestion ) ) , } ; } export default ( connect ( mapStateToProps , mapDispatchToProps ) ( Settings ) ) ;