Add togglable advanced gas controls on send and confirm screens (#6112)
* Extract advanced gas input controls to their own component * Add advanced inline gas toggle to settings * Add optional advanced inline gas to send send screen * Adds optional advanced gas inputs to the confirm screen * Add info modals for advanced gas inputs. * Fix translation of advance gas toggle description. * Lint and unit test fixes for inline-advanced-gas-inputs * Increase margin above advanced options button on send screen * Move methods from constructor to property syntax in advanced-gas-inputs.componentfeature/default_network_editable
parent
c28fa31250
commit
38b91f63a2
@ -0,0 +1,146 @@ |
||||
import React, { Component } from 'react' |
||||
import PropTypes from 'prop-types' |
||||
import classnames from 'classnames' |
||||
import debounce from 'lodash.debounce' |
||||
|
||||
export default class AdvancedTabContent extends Component { |
||||
static contextTypes = { |
||||
t: PropTypes.func, |
||||
} |
||||
|
||||
static propTypes = { |
||||
updateCustomGasPrice: PropTypes.func, |
||||
updateCustomGasLimit: PropTypes.func, |
||||
customGasPrice: PropTypes.number, |
||||
customGasLimit: PropTypes.number, |
||||
insufficientBalance: PropTypes.bool, |
||||
customPriceIsSafe: PropTypes.bool, |
||||
isSpeedUp: PropTypes.bool, |
||||
showGasPriceInfoModal: PropTypes.func, |
||||
showGasLimitInfoModal: PropTypes.func, |
||||
} |
||||
|
||||
debouncedGasLimitReset = debounce((dVal) => { |
||||
if (dVal < 21000) { |
||||
this.props.updateCustomGasLimit(21000) |
||||
} |
||||
}, 1000, { trailing: true }) |
||||
|
||||
onChangeGasLimit = (val) => { |
||||
this.props.updateCustomGasLimit(val) |
||||
this.debouncedGasLimitReset(val) |
||||
} |
||||
|
||||
gasInputError ({ labelKey, insufficientBalance, customPriceIsSafe, isSpeedUp, value }) { |
||||
const { t } = this.context |
||||
let errorText |
||||
let errorType |
||||
let isInError = true |
||||
|
||||
|
||||
if (insufficientBalance) { |
||||
errorText = t('insufficientBalance') |
||||
errorType = 'error' |
||||
} else if (labelKey === 'gasPrice' && isSpeedUp && value === 0) { |
||||
errorText = t('zeroGasPriceOnSpeedUpError') |
||||
errorType = 'error' |
||||
} else if (labelKey === 'gasPrice' && !customPriceIsSafe) { |
||||
errorText = t('gasPriceExtremelyLow') |
||||
errorType = 'warning' |
||||
} else { |
||||
isInError = false |
||||
} |
||||
|
||||
return { |
||||
isInError, |
||||
errorText, |
||||
errorType, |
||||
} |
||||
} |
||||
|
||||
gasInput ({ labelKey, value, onChange, insufficientBalance, showGWEI, customPriceIsSafe, isSpeedUp }) { |
||||
const { |
||||
isInError, |
||||
errorText, |
||||
errorType, |
||||
} = this.gasInputError({ labelKey, insufficientBalance, customPriceIsSafe, isSpeedUp, value }) |
||||
|
||||
return ( |
||||
<div className="advanced-gas-inputs__gas-edit-row__input-wrapper"> |
||||
<input |
||||
className={classnames('advanced-gas-inputs__gas-edit-row__input', { |
||||
'advanced-gas-inputs__gas-edit-row__input--error': isInError && errorType === 'error', |
||||
'advanced-gas-inputs__gas-edit-row__input--warning': isInError && errorType === 'warning', |
||||
})} |
||||
type="number" |
||||
value={value} |
||||
onChange={event => onChange(Number(event.target.value))} |
||||
/> |
||||
<div className={classnames('advanced-gas-inputs__gas-edit-row__input-arrows', { |
||||
'advanced-gas-inputs__gas-edit-row__input--error': isInError && errorType === 'error', |
||||
'advanced-gas-inputs__gas-edit-row__input--warning': isInError && errorType === 'warning', |
||||
})}> |
||||
<div className="advanced-gas-inputs__gas-edit-row__input-arrows__i-wrap" onClick={() => onChange(value + 1)}><i className="fa fa-sm fa-angle-up" /></div> |
||||
<div className="advanced-gas-inputs__gas-edit-row__input-arrows__i-wrap" onClick={() => onChange(value - 1)}><i className="fa fa-sm fa-angle-down" /></div> |
||||
</div> |
||||
{ isInError |
||||
? <div className={`advanced-gas-inputs__gas-edit-row__${errorType}-text`}> |
||||
{ errorText } |
||||
</div> |
||||
: null } |
||||
</div> |
||||
) |
||||
} |
||||
|
||||
infoButton (onClick) { |
||||
return <i className="fa fa-info-circle" onClick={onClick} /> |
||||
} |
||||
|
||||
renderGasEditRow (gasInputArgs) { |
||||
return ( |
||||
<div className="advanced-gas-inputs__gas-edit-row"> |
||||
<div className="advanced-gas-inputs__gas-edit-row__label"> |
||||
{ this.context.t(gasInputArgs.labelKey) } |
||||
{ this.infoButton(() => gasInputArgs.infoOnClick()) } |
||||
</div> |
||||
{ this.gasInput(gasInputArgs) } |
||||
</div> |
||||
) |
||||
} |
||||
|
||||
render () { |
||||
const { |
||||
customGasPrice, |
||||
updateCustomGasPrice, |
||||
customGasLimit, |
||||
insufficientBalance, |
||||
customPriceIsSafe, |
||||
isSpeedUp, |
||||
showGasPriceInfoModal, |
||||
showGasLimitInfoModal, |
||||
} = this.props |
||||
|
||||
return ( |
||||
<div className="advanced-gas-inputs__gas-edit-rows"> |
||||
{ this.renderGasEditRow({ |
||||
labelKey: 'gasPrice', |
||||
value: customGasPrice, |
||||
onChange: updateCustomGasPrice, |
||||
insufficientBalance, |
||||
customPriceIsSafe, |
||||
showGWEI: true, |
||||
isSpeedUp, |
||||
infoOnClick: showGasPriceInfoModal, |
||||
}) } |
||||
{ this.renderGasEditRow({ |
||||
labelKey: 'gasLimit', |
||||
value: customGasLimit, |
||||
onChange: this.onChangeGasLimit, |
||||
insufficientBalance, |
||||
customPriceIsSafe, |
||||
infoOnClick: showGasLimitInfoModal, |
||||
}) } |
||||
</div> |
||||
) |
||||
} |
||||
} |
@ -0,0 +1,12 @@ |
||||
import { connect } from 'react-redux' |
||||
import { showModal } from '../../../actions' |
||||
import AdvancedGasInputs from './advanced-gas-inputs.component' |
||||
|
||||
const mapDispatchToProps = dispatch => { |
||||
return { |
||||
showGasPriceInfoModal: modalName => dispatch(showModal({ name: 'GAS_PRICE_INFO_MODAL' })), |
||||
showGasLimitInfoModal: modalName => dispatch(showModal({ name: 'GAS_LIMIT_INFO_MODAL' })), |
||||
} |
||||
} |
||||
|
||||
export default connect(null, mapDispatchToProps)(AdvancedGasInputs) |
@ -0,0 +1 @@ |
||||
export { default } from './advanced-gas-inputs.container' |
@ -0,0 +1,133 @@ |
||||
.advanced-gas-inputs { |
||||
&__gas-edit-rows { |
||||
display: flex; |
||||
flex-flow: row; |
||||
justify-content: space-between; |
||||
} |
||||
|
||||
&__gas-edit-row { |
||||
display: flex; |
||||
flex-flow: column; |
||||
width: 47.5%; |
||||
|
||||
&__label { |
||||
color: #313B5E; |
||||
font-size: 12px; |
||||
display: flex; |
||||
justify-content: space-between; |
||||
align-items: center; |
||||
|
||||
@media screen and (max-width: 576px) { |
||||
font-size: 10px; |
||||
} |
||||
|
||||
.fa-info-circle { |
||||
color: $silver; |
||||
margin-left: 10px; |
||||
cursor: pointer; |
||||
} |
||||
|
||||
.fa-info-circle:hover { |
||||
color: $mid-gray; |
||||
} |
||||
} |
||||
|
||||
&__error-text { |
||||
font-size: 12px; |
||||
color: red; |
||||
} |
||||
|
||||
&__warning-text { |
||||
font-size: 12px; |
||||
color: orange; |
||||
} |
||||
|
||||
&__input-wrapper { |
||||
position: relative; |
||||
} |
||||
|
||||
&__input { |
||||
border: 1px solid $dusty-gray; |
||||
border-radius: 4px; |
||||
color: $mid-gray; |
||||
font-size: 16px; |
||||
height: 24px; |
||||
width: 100%; |
||||
padding-left: 8px; |
||||
padding-top: 2px; |
||||
margin-top: 7px; |
||||
} |
||||
|
||||
&__input--error { |
||||
border: 1px solid $red; |
||||
} |
||||
|
||||
&__input--warning { |
||||
border: 1px solid $orange; |
||||
} |
||||
|
||||
&__input-arrows { |
||||
position: absolute; |
||||
top: 7px; |
||||
right: 0px; |
||||
width: 17px; |
||||
height: 24px; |
||||
border: 1px solid #dadada; |
||||
border-top-right-radius: 4px; |
||||
display: flex; |
||||
flex-direction: column; |
||||
color: #9b9b9b; |
||||
font-size: .8em; |
||||
border-bottom-right-radius: 4px; |
||||
cursor: pointer; |
||||
|
||||
&__i-wrap { |
||||
width: 100%; |
||||
height: 100%; |
||||
display: flex; |
||||
justify-content: center; |
||||
cursor: pointer; |
||||
} |
||||
|
||||
&__i-wrap:hover { |
||||
background: #4EADE7; |
||||
color: $white; |
||||
} |
||||
|
||||
i:hover { |
||||
background: #4EADE7; |
||||
} |
||||
|
||||
i { |
||||
font-size: 10px; |
||||
} |
||||
} |
||||
|
||||
&__input-arrows--error { |
||||
border: 1px solid $red; |
||||
} |
||||
|
||||
&__input-arrows--warning { |
||||
border: 1px solid $orange; |
||||
} |
||||
|
||||
input[type="number"]::-webkit-inner-spin-button { |
||||
-webkit-appearance: none; |
||||
-moz-appearance: none; |
||||
display: none; |
||||
} |
||||
|
||||
input[type="number"]:hover::-webkit-inner-spin-button { |
||||
-webkit-appearance: none; |
||||
-moz-appearance: none; |
||||
display: none; |
||||
} |
||||
|
||||
&__gwei-symbol { |
||||
position: absolute; |
||||
top: 8px; |
||||
right: 10px; |
||||
color: $dusty-gray; |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue