@ -1,10 +1,10 @@
import React , { useState , useReducer , useEffect , useCallback } from 'react' // eslint-disable-line
import React , { useState , useReducer , useEffect , useCallback } from 'react' // eslint-disable-line
import { CopyToClipboard } from '@remix-ui/clipboard' // eslint-disable-line
import { CopyToClipboard } from '@remix-ui/clipboard' // eslint-disable-line
import { enablePersonalModeText , ethereunVMText , labels , generateContractMetadataText , matomoAnalytics , textDark , textSecondary , warnText , wordWrapText , swarmSettingsTitle , ipfsSettingsText } from './constants'
import { enablePersonalModeText , ethereunVMText , labels , generateContractMetadataText , matomoAnalytics , textDark , textSecondary , warnText , wordWrapText , swarmSettingsTitle , ipfsSettingsText , useAutoCompleteText , useShowGasInEditorText , displayErrorsText } from './constants'
import './remix-ui-settings.css'
import './remix-ui-settings.css'
import { ethereumVM , generateContractMetadat , personal , textWrapEventAction , useMatomoAnalytics , saveTokenToast , removeTokenToast , saveSwarmSettingsToast , saveIpfsSettingsToast } from './settingsAction'
import { ethereumVM , generateContractMetadat , personal , textWrapEventAction , useMatomoAnalytics , saveTokenToast , removeTokenToast , saveSwarmSettingsToast , saveIpfsSettingsToast , useAutoCompletion , useShowGasInEditor , useDisplayErrors } from './settingsAction'
import { initialState , toastInitialState , toastReducer , settingReducer } from './settingsReducer'
import { initialState , toastInitialState , toastReducer , settingReducer } from './settingsReducer'
import { Toaster } from '@remix-ui/toaster' // eslint-disable-line
import { Toaster } from '@remix-ui/toaster' // eslint-disable-line
import { RemixUiThemeModule , ThemeModule } from '@remix-ui/theme-module'
import { RemixUiThemeModule , ThemeModule } from '@remix-ui/theme-module'
@ -14,16 +14,16 @@ import { GithubSettings } from './github-settings'
export interface RemixUiSettingsProps {
export interface RemixUiSettingsProps {
config : any ,
config : any ,
editor : any ,
editor : any ,
_deps : any ,
_deps : any ,
useMatomoAnalytics : boolean
useMatomoAnalytics : boolean
themeModule : ThemeModule
themeModule : ThemeModule
}
}
export const RemixUiSettings = ( props : RemixUiSettingsProps ) = > {
export const RemixUiSettings = ( props : RemixUiSettingsProps ) = > {
const [ , dispatch ] = useReducer ( settingReducer , initialState )
const [ , dispatch ] = useReducer ( settingReducer , initialState )
const [ state , dispatchToast ] = useReducer ( toastReducer , toastInitialState )
const [ state , dispatchToast ] = useReducer ( toastReducer , toastInitialState )
const [ tokenValue , setTokenValue ] = useState ( { } )
const [ tokenValue , setTokenValue ] = useState ( { } )
const [ themeName , ] = useState ( '' )
const [ themeName , ] = useState ( '' )
const [ privateBeeAddress , setPrivateBeeAddress ] = useState ( '' )
const [ privateBeeAddress , setPrivateBeeAddress ] = useState ( '' )
const [ postageStampId , setPostageStampId ] = useState ( '' )
const [ postageStampId , setPostageStampId ] = useState ( '' )
const [ resetState , refresh ] = useState ( 0 )
const [ resetState , refresh ] = useState ( 0 )
@ -33,13 +33,21 @@ export const RemixUiSettings = (props: RemixUiSettingsProps) => {
const [ ipfsProjectId , setipfsProjectId ] = useState ( '' )
const [ ipfsProjectId , setipfsProjectId ] = useState ( '' )
const [ ipfsProjectSecret , setipfsProjectSecret ] = useState ( '' )
const [ ipfsProjectSecret , setipfsProjectSecret ] = useState ( '' )
const initValue = ( ) = > {
const initValue = ( ) = > {
const metadataConfig = props . config . get ( 'settings/generate-contract-metadata' )
const metadataConfig = props . config . get ( 'settings/generate-contract-metadata' )
if ( metadataConfig === undefined || metadataConfig === null ) generateContractMetadat ( props . config , true , dispatch )
if ( metadataConfig === undefined || metadataConfig === null ) generateContractMetadat ( props . config , true , dispatch )
const javascriptVM = props . config . get ( 'settings/always-use-vm' )
const javascriptVM = props . config . get ( 'settings/always-use-vm' )
if ( javascriptVM === null || javascriptVM === undefined ) ethereumVM ( props . config , true , dispatch )
if ( javascriptVM === null || javascriptVM === undefined ) ethereumVM ( props . config , true , dispatch )
const useAutoComplete = props . config . get ( 'settings/use-auto-complete' )
if ( useAutoComplete === null || useAutoComplete === undefined ) useAutoCompletion ( props . config , true , dispatch )
const displayErrors = props . config . get ( 'settings/display-errors' )
if ( displayErrors === null || displayErrors === undefined ) useDisplayErrors ( props . config , true , dispatch )
const useShowGas = props . config . get ( 'settings/show-gas' )
if ( useShowGas === null || useShowGas === undefined ) useShowGasInEditor ( props . config , true , dispatch )
}
}
useEffect ( ( ) = > initValue ( ) , [ resetState , props . config ] )
useEffect ( ( ) = > initValue ( ) , [ resetState , props . config ] )
useEffect ( ( ) = > initValue ( ) , [ ] )
useEffect ( ( ) = > initValue ( ) , [ ] )
@ -88,7 +96,6 @@ export const RemixUiSettings = (props: RemixUiSettingsProps) => {
setipfsProjectSecret ( configipfsProjectSecret )
setipfsProjectSecret ( configipfsProjectSecret )
}
}
} , [ themeName , state . message ] )
} , [ themeName , state . message ] )
useEffect ( ( ) = > {
useEffect ( ( ) = > {
@ -115,6 +122,18 @@ export const RemixUiSettings = (props: RemixUiSettingsProps) => {
useMatomoAnalytics ( props . config , event . target . checked , dispatch )
useMatomoAnalytics ( props . config , event . target . checked , dispatch )
}
}
const onchangeUseAutoComplete = event = > {
useAutoCompletion ( props . config , event . target . checked , dispatch )
}
const onchangeShowGasInEditor = event = > {
useShowGasInEditor ( props . config , event . target . checked , dispatch )
}
const onchangeDisplayErrors = event = > {
useDisplayErrors ( props . config , event . target . checked , dispatch )
}
const getTextClass = ( key ) = > {
const getTextClass = ( key ) = > {
if ( props . config . get ( key ) ) {
if ( props . config . get ( key ) ) {
return textDark
return textDark
@ -129,51 +148,71 @@ export const RemixUiSettings = (props: RemixUiSettingsProps) => {
const isEditorWrapChecked = props . config . get ( 'settings/text-wrap' ) || false
const isEditorWrapChecked = props . config . get ( 'settings/text-wrap' ) || false
const isPersonalChecked = props . config . get ( 'settings/personal-mode' ) || false
const isPersonalChecked = props . config . get ( 'settings/personal-mode' ) || false
const isMatomoChecked = props . config . get ( 'settings/matomo-analytics' ) || false
const isMatomoChecked = props . config . get ( 'settings/matomo-analytics' ) || false
const isAutoCompleteChecked = props . config . get ( 'settings/auto-completion' ) === null ? true : props . config . get ( 'settings/auto-completion' )
const isShowGasInEditorChecked = props . config . get ( 'settings/show-gas' ) === null ? true : props . config . get ( 'settings/show-gas' )
const displayErrorsChecked = props . config . get ( 'settings/display-errors' ) === null ? true : props . config . get ( 'settings/display-errors' )
return (
return (
< div className = "$border-top" >
< div className = "$border-top" >
< div title = "Reset to Default settings." className = 'd-flex justify-content-end pr-4' >
< div title = "Reset to Default settings." className = 'd-flex justify-content-end pr-4' >
< button className = "btn btn-sm btn-secondary ml-2" onClick = { ( ) = > {
< button className = "btn btn-sm btn-secondary ml-2" onClick = { ( ) = > {
try {
if ( ( window as any ) . remixFileSystem . name === 'indexedDB' ) {
props . config . clear ( )
try {
try {
if ( ( window as any ) . remixFileSystem . name === 'indexedDB' ) {
localStorage . clear ( ) // remove the whole storage
props . config . clear ( )
try {
localStorage . clear ( ) // remove the whole storage
} catch ( e ) {
console . log ( e )
}
} else {
props . config . clear ( ) // remove only the remix settings
}
refresh ( resetState + 1 )
} catch ( e ) {
} catch ( e ) {
console . log ( e )
console . log ( e )
}
}
} } > Reset to Default settings < / button >
} else {
props . config . clear ( ) // remove only the remix settings
}
refresh ( resetState + 1 )
} catch ( e ) {
console . log ( e )
}
} } > Reset to Default settings < / button >
< / div >
< / div >
< div className = "card-body pt-3 pb-2" >
< div className = "card-body pt-3 pb-2" >
< h6 className = "card-title" > General settings < / h6 >
< h6 className = "card-title" > General settings < / h6 >
< div className = "mt-2 custom-control custom-checkbox mb-1" >
< div className = "mt-2 custom-control custom-checkbox mb-1" >
< input onChange = { onchangeGenerateContractMetadata } id = "generatecontractmetadata" data - id = "settingsTabGenerateContractMetadata" type = "checkbox" className = "custom-control-input" name = "contractMetadata" checked = { isMetadataChecked } / >
< input onChange = { onchangeGenerateContractMetadata } id = "generatecontractmetadata" data - id = "settingsTabGenerateContractMetadata" type = "checkbox" className = "custom-control-input" name = "contractMetadata" checked = { isMetadataChecked } / >
< label className = { ` form-check-label custom-control-label align-middle ${ getTextClass ( 'settings/generate-contract-metadata' ) } ` } data - id = "settingsTabGenerateContractMetadataLabel" htmlFor = "generatecontractmetadata" > { generateContractMetadataText } < / label >
< label className = { ` form-check-label custom-control-label align-middle ${ getTextClass ( 'settings/generate-contract-metadata' ) } ` } data - id = "settingsTabGenerateContractMetadataLabel" htmlFor = "generatecontractmetadata" > { generateContractMetadataText } < / label >
< / div >
< / div >
< div className = "fmt-2 custom-control custom-checkbox mb-1" >
< div className = "fmt-2 custom-control custom-checkbox mb-1" >
< input onChange = { onchangeOption } className = "custom-control-input" id = "alwaysUseVM" data - id = "settingsTabAlwaysUseVM" type = "checkbox" name = "ethereumVM" checked = { isEthereumVMChecked } / >
< input onChange = { onchangeOption } className = "custom-control-input" id = "alwaysUseVM" data - id = "settingsTabAlwaysUseVM" type = "checkbox" name = "ethereumVM" checked = { isEthereumVMChecked } / >
< label className = { ` form-check-label custom-control-label align-middle ${ getTextClass ( 'settings/always-use-vm' ) } ` } htmlFor = "alwaysUseVM" > { ethereunVMText } < / label >
< label className = { ` form-check-label custom-control-label align-middle ${ getTextClass ( 'settings/always-use-vm' ) } ` } htmlFor = "alwaysUseVM" > { ethereunVMText } < / label >
< / div >
< / div >
< div className = "mt-2 custom-control custom-checkbox mb-1" >
< div className = "mt-2 custom-control custom-checkbox mb-1" >
< input id = "editorWrap" className = "custom-control-input" type = "checkbox" onChange = { textWrapEvent } checked = { isEditorWrapChecked } / >
< input id = "editorWrap" className = "custom-control-input" type = "checkbox" onChange = { textWrapEvent } checked = { isEditorWrapChecked } / >
< label className = { ` form-check-label custom-control-label align-middle ${ getTextClass ( 'settings/text-wrap' ) } ` } htmlFor = "editorWrap" > { wordWrapText } < / label >
< label className = { ` form-check-label custom-control-label align-middle ${ getTextClass ( 'settings/text-wrap' ) } ` } htmlFor = "editorWrap" > { wordWrapText } < / label >
< / div >
< / div >
< div className = 'custom-control custom-checkbox mb-1' >
< input onChange = { onchangeUseAutoComplete } id = "settingsUseAutoComplete" type = "checkbox" className = "custom-control-input" checked = { isAutoCompleteChecked } / >
< label className = { ` form-check-label custom-control-label align-middle ${ getTextClass ( 'settings/use-auto-complete' ) } ` } htmlFor = "settingsUseAutoComplete" >
< span > { useAutoCompleteText } < / span >
< / label >
< / div >
< div className = 'custom-control custom-checkbox mb-1' >
< input onChange = { onchangeShowGasInEditor } id = "settingsUseShowGas" type = "checkbox" className = "custom-control-input" checked = { isShowGasInEditorChecked } / >
< label className = { ` form-check-label custom-control-label align-middle ${ getTextClass ( 'settings/use-auto-complete' ) } ` } htmlFor = "settingsUseShowGas" >
< span > { useShowGasInEditorText } < / span >
< / label >
< / div >
< div className = 'custom-control custom-checkbox mb-1' >
< input onChange = { onchangeDisplayErrors } id = "settingsDisplayErrors" type = "checkbox" className = "custom-control-input" checked = { displayErrorsChecked } / >
< label className = { ` form-check-label custom-control-label align-middle ${ getTextClass ( 'settings/use-auto-complete' ) } ` } htmlFor = "settingsDisplayErrors" >
< span > { displayErrorsText } < / span >
< / label >
< / div >
< div className = "custom-control custom-checkbox mb-1" >
< div className = "custom-control custom-checkbox mb-1" >
< input onChange = { onchangePersonal } id = "personal" type = "checkbox" className = "custom-control-input" checked = { isPersonalChecked } / >
< input onChange = { onchangePersonal } id = "personal" type = "checkbox" className = "custom-control-input" checked = { isPersonalChecked } / >
< label className = { ` form-check-label custom-control-label align-middle ${ getTextClass ( 'settings/personal-mode' ) } ` } htmlFor = "personal" >
< label className = { ` form-check-label custom-control-label align-middle ${ getTextClass ( 'settings/personal-mode' ) } ` } htmlFor = "personal" >
< i className = "fas fa-exclamation-triangle text-warning" aria - hidden = "true" > < / i > < span > < / span >
< i className = "fas fa-exclamation-triangle text-warning" aria - hidden = "true" > < / i > < span > < / span >
< span > < / span > { enablePersonalModeText } { warnText }
< span > < / span > { enablePersonalModeText } { warnText }
< / label >
< / label >
< / div >
< / div >
< div className = "custom-control custom-checkbox mb-1" >
< div className = "custom-control custom-checkbox mb-1" >
< input onChange = { onchangeMatomoAnalytics } id = "settingsMatomoAnalytics" type = "checkbox" className = "custom-control-input" checked = { isMatomoChecked } / >
< input onChange = { onchangeMatomoAnalytics } id = "settingsMatomoAnalytics" type = "checkbox" className = "custom-control-input" checked = { isMatomoChecked } / >
< label className = { ` form-check-label custom-control-label align-middle ${ getTextClass ( 'settings/matomo-analytics' ) } ` } htmlFor = "settingsMatomoAnalytics" >
< label className = { ` form-check-label custom-control-label align-middle ${ getTextClass ( 'settings/matomo-analytics' ) } ` } htmlFor = "settingsMatomoAnalytics" >
< span > { matomoAnalytics } < / span >
< span > { matomoAnalytics } < / span >
< a href = "https://medium.com/p/66ef69e14931/" target = "_blank" > Analytics in Remix IDE < / a > < span > & < / span > < a target = "_blank" href = "https://matomo.org/free-software" > Matomo < / a >
< a href = "https://medium.com/p/66ef69e14931/" target = "_blank" > Analytics in Remix IDE < / a > < span > & < / span > < a target = "_blank" href = "https://matomo.org/free-software" > Matomo < / a >
@ -191,7 +230,7 @@ export const RemixUiSettings = (props: RemixUiSettingsProps) => {
const removeToken = ( type : string ) = > {
const removeToken = ( type : string ) = > {
setTokenValue ( prevState = > {
setTokenValue ( prevState = > {
return { . . . prevState , [ type ] : '' }
return { . . . prevState , [ type ] : '' }
} )
} )
removeTokenToast ( props . config , dispatchToast , labels [ type ] . key )
removeTokenToast ( props . config , dispatchToast , labels [ type ] . key )
}
}
@ -199,7 +238,7 @@ export const RemixUiSettings = (props: RemixUiSettingsProps) => {
const handleSaveTokenState = useCallback (
const handleSaveTokenState = useCallback (
( event , type ) = > {
( event , type ) = > {
setTokenValue ( prevState = > {
setTokenValue ( prevState = > {
return { . . . prevState , [ type ] : event . target . value }
return { . . . prevState , [ type ] : event . target . value }
} )
} )
} ,
} ,
[ tokenValue ]
[ tokenValue ]
@ -208,13 +247,13 @@ export const RemixUiSettings = (props: RemixUiSettingsProps) => {
const token = ( type : string ) = > (
const token = ( type : string ) = > (
< div className = "border-top" >
< div className = "border-top" >
< div className = "card-body pt-3 pb-2" >
< div className = "card-body pt-3 pb-2" >
< h6 className = "card-title" > { labels [ type ] . title } < / h6 >
< h6 className = "card-title" > { labels [ type ] . title } < / h6 >
< p className = "mb-1" > { labels [ type ] . message1 } < / p >
< p className = "mb-1" > { labels [ type ] . message1 } < / p >
< p className = "" > { labels [ type ] . message2 } < / p >
< p className = "" > { labels [ type ] . message2 } < / p >
< p className = "mb-1" > < a className = "text-primary" target = "_blank" href = { labels [ type ] . link } > { labels [ type ] . link } < / a > < / p >
< p className = "mb-1" > < a className = "text-primary" target = "_blank" href = { labels [ type ] . link } > { labels [ type ] . link } < / a > < / p >
< div className = "" > < label > TOKEN : < / label >
< div className = "" > < label > TOKEN : < / label >
< div className = "text-secondary mb-0 h6" >
< div className = "text-secondary mb-0 h6" >
< input id = "gistaccesstoken" data - id = "settingsTabGistAccessToken" type = "password" className = "form-control" onChange = { ( e ) = > handleSaveTokenState ( e , type ) } value = { tokenValue [ type ] } / >
< input id = "gistaccesstoken" data - id = "settingsTabGistAccessToken" type = "password" className = "form-control" onChange = { ( e ) = > handleSaveTokenState ( e , type ) } value = { tokenValue [ type ] } / >
< div className = "d-flex justify-content-end pt-2" >
< div className = "d-flex justify-content-end pt-2" >
< CopyToClipboard content = { tokenValue [ type ] } data - id = 'copyToClipboardCopyIcon' / >
< CopyToClipboard content = { tokenValue [ type ] } data - id = 'copyToClipboardCopyIcon' / >
< input className = "btn btn-sm btn-primary ml-2" id = "savegisttoken" data - id = "settingsTabSaveGistToken" onClick = { ( ) = > saveToken ( type ) } value = "Save" type = "button" disabled = { tokenValue === '' } > < / input >
< input className = "btn btn-sm btn-primary ml-2" id = "savegisttoken" data - id = "settingsTabSaveGistToken" onClick = { ( ) = > saveToken ( type ) } value = "Save" type = "button" disabled = { tokenValue === '' } > < / input >
@ -247,15 +286,15 @@ export const RemixUiSettings = (props: RemixUiSettingsProps) => {
const swarmSettings = ( ) = > (
const swarmSettings = ( ) = > (
< div className = "border-top" >
< div className = "border-top" >
< div className = "card-body pt-3 pb-2" >
< div className = "card-body pt-3 pb-2" >
< h6 className = "card-title" > { swarmSettingsTitle } < / h6 >
< h6 className = "card-title" > { swarmSettingsTitle } < / h6 >
< div className = "pt-2 mb-1" > < label > PRIVATE BEE ADDRESS : < / label >
< div className = "pt-2 mb-1" > < label > PRIVATE BEE ADDRESS : < / label >
< div className = "text-secondary mb-0 h6" >
< div className = "text-secondary mb-0 h6" >
< input id = "swarmprivatebeeaddress" data - id = "settingsPrivateBeeAddress" className = "form-control" onChange = { handleSavePrivateBeeAddress } value = { privateBeeAddress } / >
< input id = "swarmprivatebeeaddress" data - id = "settingsPrivateBeeAddress" className = "form-control" onChange = { handleSavePrivateBeeAddress } value = { privateBeeAddress } / >
< / div >
< / div >
< / div >
< / div >
< div className = "" > < label > POSTAGE STAMP ID : < / label >
< div className = "" > < label > POSTAGE STAMP ID : < / label >
< div className = "text-secondary mb-0 h6" >
< div className = "text-secondary mb-0 h6" >
< input id = "swarmpostagestamp" data - id = "settingsPostageStampId" className = "form-control" onChange = { handleSavePostageStampId } value = { postageStampId } / >
< input id = "swarmpostagestamp" data - id = "settingsPostageStampId" className = "form-control" onChange = { handleSavePostageStampId } value = { postageStampId } / >
< div className = "d-flex justify-content-end pt-2" >
< div className = "d-flex justify-content-end pt-2" >
< / div >
< / div >
< / div >
< / div >
@ -310,38 +349,38 @@ export const RemixUiSettings = (props: RemixUiSettingsProps) => {
const ipfsSettings = ( ) = > (
const ipfsSettings = ( ) = > (
< div className = "border-top" >
< div className = "border-top" >
< div className = "card-body pt-3 pb-2" >
< div className = "card-body pt-3 pb-2" >
< h6 className = "card-title" > { ipfsSettingsText } < / h6 >
< h6 className = "card-title" > { ipfsSettingsText } < / h6 >
< div className = "pt-2 mb-1" > < label > IPFS HOST : < / label >
< div className = "pt-2 mb-1" > < label > IPFS HOST : < / label >
< div className = "text-secondary mb-0 h6" >
< div className = "text-secondary mb-0 h6" >
< input placeholder = 'e.g. ipfs.infura.io' id = "settingsIpfsUrl" data - id = "settingsIpfsUrl" className = "form-control" onChange = { handleSaveIpfsUrl } value = { ipfsUrl } / >
< input placeholder = 'e.g. ipfs.infura.io' id = "settingsIpfsUrl" data - id = "settingsIpfsUrl" className = "form-control" onChange = { handleSaveIpfsUrl } value = { ipfsUrl } / >
< / div >
< / div >
< / div >
< / div >
< div className = "" > < label > IPFS PROTOCOL : < / label >
< div className = "" > < label > IPFS PROTOCOL : < / label >
< div className = "text-secondary mb-0 h6" >
< div className = "text-secondary mb-0 h6" >
< input placeholder = 'e.g. https' id = "settingsIpfsProtocol" data - id = "settingsIpfsProtocol" className = "form-control" onChange = { handleSaveIpfsProtocol } value = { ipfsProtocol } / >
< input placeholder = 'e.g. https' id = "settingsIpfsProtocol" data - id = "settingsIpfsProtocol" className = "form-control" onChange = { handleSa veIpfsProtocol } value = { ipfsProtocol } / >
< / div >
< / div >
< / div >
< / div >
< div className = "" > < label > IPFS PORT : < / label >
< div className = "" > < label > IPFS PORT : < / label >
< div className = "text-secondary mb-0 h6" >
< div className = "text-secondary mb-0 h6" >
< input placeholder = 'e.g. 5001' id = "settingsIpfsPort" data - id = "settingsIpfsPort" className = "form-control" onChange = { handleSaveIpfsPort } value = { ipfsPort } / >
< input placeholder = 'e.g. 5001' id = "settingsIpfsPort" data - id = "settingsIpfsPort" className = "form-control" onChange = { handleSa veIpfsPort } value = { ipfsPort } / >
< / div >
< / div >
< / div >
< / div >
< div className = "" > < label > IPFS PROJECT ID [ INFURA ] : < / label >
< div className = "" > < label > IPFS PROJECT ID [ INFURA ] : < / label >
< div className = "text-secondary mb-0 h6" >
< div className = "text-secondary mb-0 h6" >
< input id = "settingsIpfsProjectId" data - id = "settingsIpfsProjectId" className = "form-control" onChange = { handleSaveIpfsProjectId } value = { ipfsProjectId } / >
< input id = "settingsIpfsProjectId" data - id = "settingsIpfsProjectId" className = "form-control" onChange = { handleSa veIpfsProjectId } value = { ipfsProjectId } / >
< / div >
< / div >
< / div >
< / div >
< div className = "" > < label > IPFS PROJECT SECRET [ INFURA ] : < / label >
< div className = "" > < label > IPFS PROJECT SECRET [ INFURA ] : < / label >
< div className = "text-secondary mb-0 h6" >
< div className = "text-secondary mb-0 h6" >
< input id = "settingsIpfsProjectSecret" data - id = "settingsIpfsProjectSecret" className = "form-control" type = "password" onChange = { handleSaveIpfsSecret } value = { ipfsProjectSecret } / >
< input id = "settingsIpfsProjectSecret" data - id = "settingsIpfsProjectSecret" className = "form-control" type = "password" onChange = { handleSaveIpfsSecret } value = { ipfsProjectSecret } / >
< / div >
< / div >
< div className = "d-flex justify-content-end pt-2" >
< input className = "btn btn-sm btn-primary ml-2" id = "saveIpfssettings" data - id = "settingsTabSaveIpfsSettings" onClick = { ( ) = > saveIpfsSettings ( ) } value = "Save" type = "button" > < / input >
< / div >
< / div >
< / div >
< / div >
< div className = "d-flex justify-content-end pt-2" >
< / div > )
< input className = "btn btn-sm btn-primary ml-2" id = "saveIpfssettings" data - id = "settingsTabSaveIpfsSettings" onClick = { ( ) = > saveIpfsSettings ( ) } value = "Save" type = "button" > < / input >
< / div >
< / div >
< / div > )
return (
return (