1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-12-23 09:52:26 +01:00

Final gas customization fixes

This commit is contained in:
Dan Miller 2018-11-27 14:00:41 -03:30
parent 75d7545437
commit d8e41a6aa5
18 changed files with 198 additions and 104 deletions

View File

@ -413,7 +413,7 @@ describe('Transaction Controller', function () {
gasPrice: '0xa', gasPrice: '0xa',
} }
txController.txStateManager._saveTxList([ txController.txStateManager._saveTxList([
{ id: 1, status: 'submitted', metamaskNetworkId: currentNetworkId, txParams, history: [] }, { id: 1, status: 'submitted', metamaskNetworkId: currentNetworkId, txParams, history: [{}] },
]) ])
expectedTxParams = Object.assign({}, txParams, { gasPrice: '0xb'}) expectedTxParams = Object.assign({}, txParams, { gasPrice: '0xb'})

View File

@ -3,6 +3,7 @@ import PropTypes from 'prop-types'
import classnames from 'classnames' import classnames from 'classnames'
import Loading from '../../../loading-screen' import Loading from '../../../loading-screen'
import GasPriceChart from '../../gas-price-chart' import GasPriceChart from '../../gas-price-chart'
import debounce from 'lodash.debounce'
export default class AdvancedTabContent extends Component { export default class AdvancedTabContent extends Component {
static contextTypes = { static contextTypes = {
@ -22,7 +23,21 @@ export default class AdvancedTabContent extends Component {
insufficientBalance: PropTypes.bool, insufficientBalance: PropTypes.bool,
} }
gasInput (value, onChange, min, insufficientBalance, precision, showGWEI) { constructor (props) {
super(props)
this.debouncedGasLimitReset = debounce((dVal) => {
if (dVal < 21000) {
props.updateCustomGasLimit(21000)
}
}, 1000, { trailing: true })
this.onChangeGasLimit = (val) => {
props.updateCustomGasLimit(val)
this.debouncedGasLimitReset(val)
}
}
gasInput (value, onChange, min, insufficientBalance, showGWEI) {
return ( return (
<div className="advanced-tab__gas-edit-row__input-wrapper"> <div className="advanced-tab__gas-edit-row__input-wrapper">
<input <input
@ -32,14 +47,13 @@ export default class AdvancedTabContent extends Component {
type="number" type="number"
value={value} value={value}
min={min} min={min}
precision={precision}
onChange={event => onChange(Number(event.target.value))} onChange={event => onChange(Number(event.target.value))}
/> />
<div className={classnames('advanced-tab__gas-edit-row__input-arrows', { <div className={classnames('advanced-tab__gas-edit-row__input-arrows', {
'advanced-tab__gas-edit-row__input-arrows--error': insufficientBalance, 'advanced-tab__gas-edit-row__input-arrows--error': insufficientBalance,
})}> })}>
<div className="advanced-tab__gas-edit-row__input-arrows__i-wrap"><i className="fa fa-sm fa-angle-up" onClick={() => onChange(value + 1)} /></div> <div className="advanced-tab__gas-edit-row__input-arrows__i-wrap" onClick={() => onChange(value + 1)}><i className="fa fa-sm fa-angle-up" /></div>
<div className="advanced-tab__gas-edit-row__input-arrows__i-wrap"><i className="fa fa-sm fa-angle-down" onClick={() => onChange(value - 1)} /></div> <div className="advanced-tab__gas-edit-row__input-arrows__i-wrap" onClick={() => onChange(value - 1)}><i className="fa fa-sm fa-angle-down" /></div>
</div> </div>
{insufficientBalance && <div className="advanced-tab__gas-edit-row__insufficient-balance"> {insufficientBalance && <div className="advanced-tab__gas-edit-row__insufficient-balance">
Insufficient Balance Insufficient Balance
@ -84,8 +98,8 @@ export default class AdvancedTabContent extends Component {
renderGasEditRows (customGasPrice, updateCustomGasPrice, customGasLimit, updateCustomGasLimit, insufficientBalance) { renderGasEditRows (customGasPrice, updateCustomGasPrice, customGasLimit, updateCustomGasLimit, insufficientBalance) {
return ( return (
<div className="advanced-tab__gas-edit-rows"> <div className="advanced-tab__gas-edit-rows">
{ this.renderGasEditRow('gasPrice', customGasPrice, updateCustomGasPrice, customGasPrice, insufficientBalance, 9, true) } { this.renderGasEditRow('gasPrice', customGasPrice, updateCustomGasPrice, customGasPrice, insufficientBalance, true) }
{ this.renderGasEditRow('gasLimit', customGasLimit, updateCustomGasLimit, customGasLimit, insufficientBalance, 0) } { this.renderGasEditRow('gasLimit', customGasLimit, this.onChangeGasLimit, customGasLimit, insufficientBalance) }
</div> </div>
) )
} }

View File

@ -148,6 +148,7 @@
height: 100%; height: 100%;
display: flex; display: flex;
justify-content: center; justify-content: center;
cursor: pointer;
} }
&__i-wrap:hover { &__i-wrap:hover {

View File

@ -155,8 +155,11 @@ describe('AdvancedTabContent Component', function () {
describe('renderGasEditRows()', () => { describe('renderGasEditRows()', () => {
let gasEditRows let gasEditRows
let tempOnChangeGasLimit
beforeEach(() => { beforeEach(() => {
tempOnChangeGasLimit = wrapper.instance().onChangeGasLimit
wrapper.instance().onChangeGasLimit = () => 'mockOnChangeGasLimit'
AdvancedTabContent.prototype.renderGasEditRow.resetHistory() AdvancedTabContent.prototype.renderGasEditRow.resetHistory()
gasEditRows = shallow(wrapper.instance().renderGasEditRows( gasEditRows = shallow(wrapper.instance().renderGasEditRows(
'mockGasPrice', 'mockGasPrice',
@ -167,6 +170,10 @@ describe('AdvancedTabContent Component', function () {
)) ))
}) })
afterEach(() => {
wrapper.instance().onChangeGasLimit = tempOnChangeGasLimit
})
it('should render the gas-edit-rows root node', () => { it('should render the gas-edit-rows root node', () => {
assert(gasEditRows.hasClass('advanced-tab__gas-edit-rows')) assert(gasEditRows.hasClass('advanced-tab__gas-edit-rows'))
}) })
@ -182,10 +189,10 @@ describe('AdvancedTabContent Component', function () {
const renderGasEditRowSpyArgs = AdvancedTabContent.prototype.renderGasEditRow.args const renderGasEditRowSpyArgs = AdvancedTabContent.prototype.renderGasEditRow.args
assert.equal(renderGasEditRowSpyArgs.length, 2) assert.equal(renderGasEditRowSpyArgs.length, 2)
assert.deepEqual(renderGasEditRowSpyArgs[0].map(String), [ assert.deepEqual(renderGasEditRowSpyArgs[0].map(String), [
'gasPrice', 'mockGasPrice', () => 'mockUpdateCustomGasPriceReturn', 'mockGasPrice', false, 9, true, 'gasPrice', 'mockGasPrice', () => 'mockUpdateCustomGasPriceReturn', 'mockGasPrice', false, true,
].map(String)) ].map(String))
assert.deepEqual(renderGasEditRowSpyArgs[1].map(String), [ assert.deepEqual(renderGasEditRowSpyArgs[1].map(String), [
'gasLimit', 'mockGasLimit', () => 'mockUpdateCustomGasLimitReturn', 'mockGasLimit', false, 0, 'gasLimit', 'mockGasLimit', () => 'mockOnChangeGasLimit', 'mockGasLimit', false,
].map(String)) ].map(String))
}) })
}) })
@ -234,7 +241,6 @@ describe('AdvancedTabContent Component', function () {
const inputProps = gasInput.find('input').props() const inputProps = gasInput.find('input').props()
assert.equal(inputProps.min, 0) assert.equal(inputProps.min, 0)
assert.equal(inputProps.value, 321) assert.equal(inputProps.value, 321)
assert.equal(inputProps.precision, 8)
}) })
it('should call the passed onChange method with the value of the input onChange event', () => { it('should call the passed onChange method with the value of the input onChange event', () => {
@ -257,9 +263,9 @@ describe('AdvancedTabContent Component', function () {
8, 8,
false false
)) ))
const upArrow = gasInput.find('.fa-angle-up') const upArrow = gasInput.find('.advanced-tab__gas-edit-row__input-arrows__i-wrap').at(0)
assert.equal(upArrow.props().onClick(), 329) assert.equal(upArrow.props().onClick(), 329)
const downArrow = gasInput.find('.fa-angle-down') const downArrow = gasInput.find('.advanced-tab__gas-edit-row__input-arrows__i-wrap').at(1)
assert.equal(downArrow.props().onClick(), 327) assert.equal(downArrow.props().onClick(), 327)
}) })
}) })

View File

@ -89,7 +89,7 @@ export default class GasModalPageContainer extends Component {
renderInfoRows (newTotalFiat, newTotalEth, sendAmount, transactionFee) { renderInfoRows (newTotalFiat, newTotalEth, sendAmount, transactionFee) {
return ( return (
<div> <div className="gas-modal-content__info-row-wrapper">
<div className="gas-modal-content__info-row"> <div className="gas-modal-content__info-row">
<div className="gas-modal-content__info-row__send-info"> <div className="gas-modal-content__info-row__send-info">
<span className="gas-modal-content__info-row__send-info__label">{this.context.t('sendAmount')}</span> <span className="gas-modal-content__info-row__send-info__label">{this.context.t('sendAmount')}</span>
@ -167,7 +167,6 @@ export default class GasModalPageContainer extends Component {
onClose={() => cancelAndClose()} onClose={() => cancelAndClose()}
onSubmit={() => { onSubmit={() => {
onSubmit(customModalGasLimitInHex, customModalGasPriceInHex) onSubmit(customModalGasLimitInHex, customModalGasPriceInHex)
cancelAndClose()
}} }}
submitText={this.context.t('save')} submitText={this.context.t('save')}
headerCloseText={'Close'} headerCloseText={'Close'}

View File

@ -142,6 +142,7 @@ const mapDispatchToProps = dispatch => {
dispatch(resetCustomData()) dispatch(resetCustomData())
dispatch(hideModal()) dispatch(hideModal())
}, },
hideModal: () => dispatch(hideModal()),
updateCustomGasPrice, updateCustomGasPrice,
convertThenUpdateCustomGasPrice: newPrice => updateCustomGasPrice(decGWEIToHexWEI(newPrice)), convertThenUpdateCustomGasPrice: newPrice => updateCustomGasPrice(decGWEIToHexWEI(newPrice)),
convertThenUpdateCustomGasLimit: newLimit => dispatch(setCustomGasLimit(addHexPrefix(newLimit.toString(16)))), convertThenUpdateCustomGasLimit: newLimit => dispatch(setCustomGasLimit(addHexPrefix(newLimit.toString(16)))),
@ -150,6 +151,8 @@ const mapDispatchToProps = dispatch => {
dispatch(setGasPrice(newPrice)) dispatch(setGasPrice(newPrice))
}, },
updateConfirmTxGasAndCalculate: (gasLimit, gasPrice) => { updateConfirmTxGasAndCalculate: (gasLimit, gasPrice) => {
updateCustomGasPrice(gasPrice)
dispatch(setCustomGasLimit(addHexPrefix(gasLimit.toString(16))))
return dispatch(updateGasAndCalculate({ gasLimit, gasPrice })) return dispatch(updateGasAndCalculate({ gasLimit, gasPrice }))
}, },
createSpeedUpTransaction: (txId, gasPrice) => { createSpeedUpTransaction: (txId, gasPrice) => {
@ -172,6 +175,8 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => {
updateConfirmTxGasAndCalculate: dispatchUpdateConfirmTxGasAndCalculate, updateConfirmTxGasAndCalculate: dispatchUpdateConfirmTxGasAndCalculate,
createSpeedUpTransaction: dispatchCreateSpeedUpTransaction, createSpeedUpTransaction: dispatchCreateSpeedUpTransaction,
hideSidebar: dispatchHideSidebar, hideSidebar: dispatchHideSidebar,
cancelAndClose: dispatchCancelAndClose,
hideModal: dispatchHideModal,
...otherDispatchProps ...otherDispatchProps
} = dispatchProps } = dispatchProps
@ -182,18 +187,27 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => {
onSubmit: (gasLimit, gasPrice) => { onSubmit: (gasLimit, gasPrice) => {
if (isConfirm) { if (isConfirm) {
dispatchUpdateConfirmTxGasAndCalculate(gasLimit, gasPrice) dispatchUpdateConfirmTxGasAndCalculate(gasLimit, gasPrice)
dispatchHideModal()
} else if (isSpeedUp) { } else if (isSpeedUp) {
dispatchCreateSpeedUpTransaction(txId, gasPrice) dispatchCreateSpeedUpTransaction(txId, gasPrice)
dispatchHideSidebar() dispatchHideSidebar()
dispatchCancelAndClose()
} else { } else {
dispatchSetGasData(gasLimit, gasPrice) dispatchSetGasData(gasLimit, gasPrice)
dispatchHideGasButtonGroup() dispatchHideGasButtonGroup()
dispatchCancelAndClose()
} }
}, },
gasPriceButtonGroupProps: { gasPriceButtonGroupProps: {
...gasPriceButtonGroupProps, ...gasPriceButtonGroupProps,
handleGasPriceSelection: dispatchUpdateCustomGasPrice, handleGasPriceSelection: dispatchUpdateCustomGasPrice,
}, },
cancelAndClose: () => {
dispatchCancelAndClose()
if (isSpeedUp) {
dispatchHideSidebar()
}
},
} }
} }
@ -241,20 +255,29 @@ function addHexWEIsToRenderableFiat (aHexWEI, bHexWEI, convertedCurrency, conver
} }
function getRenderableTimeEstimate (currentGasPrice, gasPrices, estimatedTimes) { function getRenderableTimeEstimate (currentGasPrice, gasPrices, estimatedTimes) {
const minGasPrice = gasPrices[0]
const maxGasPrice = gasPrices[gasPrices.length - 1]
let priceForEstimation = currentGasPrice
if (currentGasPrice < minGasPrice) {
priceForEstimation = minGasPrice
} else if (currentGasPrice > maxGasPrice) {
priceForEstimation = maxGasPrice
}
const { const {
closestLowerValueIndex, closestLowerValueIndex,
closestHigherValueIndex, closestHigherValueIndex,
closestHigherValue, closestHigherValue,
closestLowerValue, closestLowerValue,
} = getAdjacentGasPrices({ gasPrices, priceToPosition: currentGasPrice }) } = getAdjacentGasPrices({ gasPrices, priceToPosition: priceForEstimation })
const newTimeEstimate = extrapolateY({ const newTimeEstimate = extrapolateY({
higherY: estimatedTimes[closestHigherValueIndex], higherY: estimatedTimes[closestHigherValueIndex],
lowerY: estimatedTimes[closestLowerValueIndex], lowerY: estimatedTimes[closestLowerValueIndex],
higherX: closestHigherValue, higherX: closestHigherValue,
lowerX: closestLowerValue, lowerX: closestLowerValue,
xForExtrapolation: currentGasPrice, xForExtrapolation: priceForEstimation,
}) })
return formatTimeEstimate(newTimeEstimate) return formatTimeEstimate(newTimeEstimate, currentGasPrice > maxGasPrice, currentGasPrice < minGasPrice)
} }

View File

@ -44,6 +44,7 @@
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: flex-start; align-items: flex-start;
margin-right: 0;
} }
&__subtitle { &__subtitle {

View File

@ -82,10 +82,10 @@ describe('gas-modal-page-container container', () => {
}, },
gasEstimatesLoading: false, gasEstimatesLoading: false,
priceAndTimeEstimates: [ priceAndTimeEstimates: [
{ gasprice: 3, expectedTime: '31' }, { gasprice: 3, expectedTime: 31 },
{ gasprice: 4, expectedTime: '62' }, { gasprice: 4, expectedTime: 62 },
{ gasprice: 5, expectedTime: '93' }, { gasprice: 5, expectedTime: 93 },
{ gasprice: 6, expectedTime: '124' }, { gasprice: 6, expectedTime: 124 },
], ],
}, },
confirmTransaction: { confirmTransaction: {
@ -235,7 +235,7 @@ describe('gas-modal-page-container container', () => {
describe('updateConfirmTxGasAndCalculate()', () => { describe('updateConfirmTxGasAndCalculate()', () => {
it('should dispatch a updateGasAndCalculate action with the correct props', () => { it('should dispatch a updateGasAndCalculate action with the correct props', () => {
mapDispatchToPropsObject.updateConfirmTxGasAndCalculate('ffff', 'aaaa') mapDispatchToPropsObject.updateConfirmTxGasAndCalculate('ffff', 'aaaa')
assert(dispatchSpy.calledOnce) assert.equal(dispatchSpy.callCount, 3)
assert(confirmTransactionActionSpies.updateGasAndCalculate.calledOnce) assert(confirmTransactionActionSpies.updateGasAndCalculate.calledOnce)
assert.deepEqual(confirmTransactionActionSpies.updateGasAndCalculate.getCall(0).args[0], { gasLimit: 'ffff', gasPrice: 'aaaa' }) assert.deepEqual(confirmTransactionActionSpies.updateGasAndCalculate.getCall(0).args[0], { gasLimit: 'ffff', gasPrice: 'aaaa' })
}) })
@ -265,6 +265,8 @@ describe('gas-modal-page-container container', () => {
someOtherDispatchProp: sinon.spy(), someOtherDispatchProp: sinon.spy(),
createSpeedUpTransaction: sinon.spy(), createSpeedUpTransaction: sinon.spy(),
hideSidebar: sinon.spy(), hideSidebar: sinon.spy(),
hideModal: sinon.spy(),
cancelAndClose: sinon.spy(),
} }
ownProps = { someOwnProp: 123 } ownProps = { someOwnProp: 123 }
}) })
@ -277,6 +279,7 @@ describe('gas-modal-page-container container', () => {
dispatchProps.someOtherDispatchProp.resetHistory() dispatchProps.someOtherDispatchProp.resetHistory()
dispatchProps.createSpeedUpTransaction.resetHistory() dispatchProps.createSpeedUpTransaction.resetHistory()
dispatchProps.hideSidebar.resetHistory() dispatchProps.hideSidebar.resetHistory()
dispatchProps.hideModal.resetHistory()
}) })
it('should return the expected props when isConfirm is true', () => { it('should return the expected props when isConfirm is true', () => {
const result = mergeProps(stateProps, dispatchProps, ownProps) const result = mergeProps(stateProps, dispatchProps, ownProps)
@ -290,12 +293,14 @@ describe('gas-modal-page-container container', () => {
assert.equal(dispatchProps.updateConfirmTxGasAndCalculate.callCount, 0) assert.equal(dispatchProps.updateConfirmTxGasAndCalculate.callCount, 0)
assert.equal(dispatchProps.setGasData.callCount, 0) assert.equal(dispatchProps.setGasData.callCount, 0)
assert.equal(dispatchProps.hideGasButtonGroup.callCount, 0) assert.equal(dispatchProps.hideGasButtonGroup.callCount, 0)
assert.equal(dispatchProps.hideModal.callCount, 0)
result.onSubmit() result.onSubmit()
assert.equal(dispatchProps.updateConfirmTxGasAndCalculate.callCount, 1) assert.equal(dispatchProps.updateConfirmTxGasAndCalculate.callCount, 1)
assert.equal(dispatchProps.setGasData.callCount, 0) assert.equal(dispatchProps.setGasData.callCount, 0)
assert.equal(dispatchProps.hideGasButtonGroup.callCount, 0) assert.equal(dispatchProps.hideGasButtonGroup.callCount, 0)
assert.equal(dispatchProps.hideModal.callCount, 1)
assert.equal(dispatchProps.updateCustomGasPrice.callCount, 0) assert.equal(dispatchProps.updateCustomGasPrice.callCount, 0)
result.gasPriceButtonGroupProps.handleGasPriceSelection() result.gasPriceButtonGroupProps.handleGasPriceSelection()
@ -318,6 +323,7 @@ describe('gas-modal-page-container container', () => {
assert.equal(dispatchProps.updateConfirmTxGasAndCalculate.callCount, 0) assert.equal(dispatchProps.updateConfirmTxGasAndCalculate.callCount, 0)
assert.equal(dispatchProps.setGasData.callCount, 0) assert.equal(dispatchProps.setGasData.callCount, 0)
assert.equal(dispatchProps.hideGasButtonGroup.callCount, 0) assert.equal(dispatchProps.hideGasButtonGroup.callCount, 0)
assert.equal(dispatchProps.cancelAndClose.callCount, 0)
result.onSubmit('mockNewLimit', 'mockNewPrice') result.onSubmit('mockNewLimit', 'mockNewPrice')
@ -325,6 +331,7 @@ describe('gas-modal-page-container container', () => {
assert.equal(dispatchProps.setGasData.callCount, 1) assert.equal(dispatchProps.setGasData.callCount, 1)
assert.deepEqual(dispatchProps.setGasData.getCall(0).args, ['mockNewLimit', 'mockNewPrice']) assert.deepEqual(dispatchProps.setGasData.getCall(0).args, ['mockNewLimit', 'mockNewPrice'])
assert.equal(dispatchProps.hideGasButtonGroup.callCount, 1) assert.equal(dispatchProps.hideGasButtonGroup.callCount, 1)
assert.equal(dispatchProps.cancelAndClose.callCount, 1)
assert.equal(dispatchProps.updateCustomGasPrice.callCount, 0) assert.equal(dispatchProps.updateCustomGasPrice.callCount, 0)
result.gasPriceButtonGroupProps.handleGasPriceSelection() result.gasPriceButtonGroupProps.handleGasPriceSelection()
@ -343,6 +350,7 @@ describe('gas-modal-page-container container', () => {
assert.equal(dispatchProps.updateConfirmTxGasAndCalculate.callCount, 0) assert.equal(dispatchProps.updateConfirmTxGasAndCalculate.callCount, 0)
assert.equal(dispatchProps.setGasData.callCount, 0) assert.equal(dispatchProps.setGasData.callCount, 0)
assert.equal(dispatchProps.hideGasButtonGroup.callCount, 0) assert.equal(dispatchProps.hideGasButtonGroup.callCount, 0)
assert.equal(dispatchProps.cancelAndClose.callCount, 1)
assert.equal(dispatchProps.createSpeedUpTransaction.callCount, 1) assert.equal(dispatchProps.createSpeedUpTransaction.callCount, 1)
assert.equal(dispatchProps.hideSidebar.callCount, 1) assert.equal(dispatchProps.hideSidebar.callCount, 1)

View File

@ -137,7 +137,7 @@
.gas-price-button-group--alt { .gas-price-button-group--alt {
display: flex; display: flex;
justify-content: stretch; justify-content: stretch;
max-width: 342px; width: 95%;
&__button-fiat-price { &__button-fiat-price {
font-size: 13px; font-size: 13px;

View File

@ -19,7 +19,7 @@ export default class GasPriceChart extends Component {
gasPrices: PropTypes.array, gasPrices: PropTypes.array,
estimatedTimes: PropTypes.array, estimatedTimes: PropTypes.array,
gasPricesMax: PropTypes.number, gasPricesMax: PropTypes.number,
estimatedTimesMax: PropTypes.string, estimatedTimesMax: PropTypes.number,
currentPrice: PropTypes.number, currentPrice: PropTypes.number,
updateCustomGasPrice: PropTypes.func, updateCustomGasPrice: PropTypes.func,
} }

View File

@ -1,5 +1,11 @@
import * as d3 from 'd3' import * as d3 from 'd3'
import c3 from 'c3' import c3 from 'c3'
import BigNumber from 'bignumber.js'
const newBigSigDig = n => (new BigNumber(n.toPrecision(15)))
const createOp = (a, b, op) => (newBigSigDig(a))[op](newBigSigDig(b))
const bigNumMinus = (a = 0, b = 0) => createOp(a, b, 'minus')
const bigNumDiv = (a = 0, b = 1) => createOp(a, b, 'div')
export function handleMouseMove ({ xMousePos, chartXStart, chartWidth, gasPrices, estimatedTimes, chart }) { export function handleMouseMove ({ xMousePos, chartXStart, chartWidth, gasPrices, estimatedTimes, chart }) {
const { currentPosValue, newTimeEstimate } = getNewXandTimeEstimate({ const { currentPosValue, newTimeEstimate } = getNewXandTimeEstimate({
@ -24,7 +30,8 @@ export function handleMouseMove ({ xMousePos, chartXStart, chartWidth, gasPrices
} }
export function getCoordinateData (selector) { export function getCoordinateData (selector) {
return d3.select(selector).node().getBoundingClientRect() const node = d3.select(selector).node()
return node ? node.getBoundingClientRect() : {}
} }
export function generateDataUIObj (x, index, value) { export function generateDataUIObj (x, index, value) {
@ -70,19 +77,22 @@ export function getAdjacentGasPrices ({ gasPrices, priceToPosition }) {
} }
} }
export function extrapolateY ({ higherY, lowerY, higherX, lowerX, xForExtrapolation }) { export function extrapolateY ({ higherY = 0, lowerY = 0, higherX = 0, lowerX = 0, xForExtrapolation = 0 }) {
const slope = (higherY - lowerY) / (higherX - lowerX) const slope = bigNumMinus(higherY, lowerY).div(bigNumMinus(higherX, lowerX))
const newTimeEstimate = -1 * (slope * (higherX - xForExtrapolation) - higherY) const newTimeEstimate = slope.times(bigNumMinus(higherX, xForExtrapolation)).minus(newBigSigDig(higherY)).negated()
return newTimeEstimate return newTimeEstimate.toNumber()
} }
export function getNewXandTimeEstimate ({ xMousePos, chartXStart, chartWidth, gasPrices, estimatedTimes }) { export function getNewXandTimeEstimate ({ xMousePos, chartXStart, chartWidth, gasPrices, estimatedTimes }) {
const chartMouseXPos = xMousePos - chartXStart const chartMouseXPos = bigNumMinus(xMousePos, chartXStart)
const posPercentile = chartMouseXPos / chartWidth const posPercentile = bigNumDiv(chartMouseXPos, chartWidth)
const currentPosValue = (gasPrices[gasPrices.length - 1] - gasPrices[0]) * posPercentile + gasPrices[0] const currentPosValue = (bigNumMinus(gasPrices[gasPrices.length - 1], gasPrices[0]))
.times(newBigSigDig(posPercentile))
.plus(newBigSigDig(gasPrices[0]))
.toNumber()
const { const {
closestLowerValueIndex, closestLowerValueIndex,
@ -162,20 +172,28 @@ export function setSelectedCircle ({
closestHigherValue, closestHigherValue,
}) { }) {
const numberOfValues = chart.internal.data.xs.data1.length const numberOfValues = chart.internal.data.xs.data1.length
const { x: lowerX, y: lowerY } = getCoordinateData(`.c3-circle-${closestLowerValueIndex}`) const { x: lowerX, y: lowerY } = getCoordinateData(`.c3-circle-${closestLowerValueIndex}`)
let { x: higherX, y: higherY } = getCoordinateData(`.c3-circle-${closestHigherValueIndex}`) let { x: higherX, y: higherY } = getCoordinateData(`.c3-circle-${closestHigherValueIndex}`)
let count = closestHigherValueIndex + 1
if (lowerX === higherX) { if (lowerX && higherX) {
const { x: higherXAdjusted, y: higherYAdjusted } = getCoordinateData(`.c3-circle-${closestHigherValueIndex + 1}`) while (lowerX === higherX) {
higherY = higherYAdjusted higherX = getCoordinateData(`.c3-circle-${count}`).x
higherX = higherXAdjusted higherY = getCoordinateData(`.c3-circle-${count}`).y
count++
}
} }
const currentX = lowerX + (higherX - lowerX) * (newPrice - closestLowerValue) / (closestHigherValue - closestLowerValue) const currentX = bigNumMinus(higherX, lowerX)
.times(bigNumMinus(newPrice, closestLowerValue))
.div(bigNumMinus(closestHigherValue, closestLowerValue))
.plus(newBigSigDig(lowerX))
const newTimeEstimate = extrapolateY({ higherY, lowerY, higherX, lowerX, xForExtrapolation: currentX }) const newTimeEstimate = extrapolateY({ higherY, lowerY, higherX, lowerX, xForExtrapolation: currentX })
chart.internal.selectPoint( chart.internal.selectPoint(
generateDataUIObj(currentX, numberOfValues, newTimeEstimate), generateDataUIObj(currentX.toNumber(), numberOfValues, newTimeEstimate),
numberOfValues numberOfValues
) )
} }
@ -282,8 +300,8 @@ export function generateChart (gasPrices, estimatedTimes, gasPricesMax, estimate
.style('margin-top', flipTooltip ? '-16px' : '4px') .style('margin-top', flipTooltip ? '-16px' : '4px')
return { return {
top: circleY - chartYStart - 19 + (flipTooltip ? circleWidth + 38 : 0), top: bigNumMinus(circleY, chartYStart).minus(19).plus(flipTooltip ? circleWidth + 38 : 0).toNumber(),
left: circleX - chartXStart + circleWidth - (gasPricesMaxPadded / 50), left: bigNumMinus(circleX, chartXStart).plus(newBigSigDig(circleWidth)).minus(bigNumDiv(gasPricesMaxPadded, 50)).toNumber(),
} }
}, },
show: true, show: true,
@ -298,8 +316,8 @@ export function generateChart (gasPrices, estimatedTimes, gasPricesMax, estimate
appendOrUpdateCircle.bind(this)({ appendOrUpdateCircle.bind(this)({
data, data,
itemIndex, itemIndex,
cx: () => data.x - chartXStart + 11, cx: () => bigNumMinus(data.x, chartXStart).plus(11).toNumber(),
cy: () => data.value - chartYStart + 10, cy: () => bigNumMinus(data.value, chartYStart).plus(10).toNumber(),
cssId: 'set-circle', cssId: 'set-circle',
appendOnly: true, appendOnly: true,
}) })

View File

@ -399,7 +399,7 @@ function mapDispatchToProps (dispatch) {
return { return {
hideModal: (customOnHideOpts) => { hideModal: (customOnHideOpts) => {
dispatch(actions.hideModal()) dispatch(actions.hideModal())
if (customOnHideOpts.action) { if (customOnHideOpts && customOnHideOpts.action) {
dispatch(customOnHideOpts.action(...customOnHideOpts.args)) dispatch(customOnHideOpts.action(...customOnHideOpts.args))
} }
}, },

View File

@ -12,7 +12,7 @@ export default class PageContainerFooter extends Component {
submitText: PropTypes.string, submitText: PropTypes.string,
disabled: PropTypes.bool, disabled: PropTypes.bool,
submitButtonType: PropTypes.string, submitButtonType: PropTypes.string,
hideCancel: PropTypes.func, hideCancel: PropTypes.bool,
} }
static contextTypes = { static contextTypes = {

View File

@ -1,6 +1,11 @@
.sidebar-left { .sidebar-left {
display: flex;
.gas-modal-page-container { .gas-modal-page-container {
display: flex;
.page-container { .page-container {
flex: 1;
max-width: 100%; max-width: 100%;
&__content { &__content {
@ -12,6 +17,10 @@
max-width: 344px; max-width: 344px;
min-height: auto; min-height: auto;
} }
@media screen and (min-width: $break-small) {
max-height: none;
}
} }
.gas-price-chart { .gas-price-chart {
@ -34,9 +43,10 @@
} }
.basic-tab-content { .basic-tab-content {
height: 318px; height: auto;
margin-bottom: 0px; margin-bottom: 0px;
border-bottom: 1px solid #d2d8dd; border-bottom: 1px solid #d2d8dd;
flex: 1 1 70%;
@media screen and (max-width: $break-small) { @media screen and (max-width: $break-small) {
padding-left: 14px; padding-left: 14px;
@ -55,6 +65,10 @@
} }
.advanced-tab { .advanced-tab {
@media screen and (min-width: $break-small) {
flex: 1 1 70%;
}
&__fee-chart { &__fee-chart {
height: 320px; height: 320px;
@ -72,8 +86,19 @@
} }
} }
.gas-modal-content {
display: flex;
flex-direction: column;
width: 100%;
.gas-modal-content__info-row { &__info-row-wrapper {
display: flex;
@media screen and (min-width: $break-small) {
flex: 1 1 30%;
}
}
&__info-row {
height: 170px; height: 170px;
@media screen and (max-width: $break-small) { @media screen and (max-width: $break-small) {
@ -83,4 +108,5 @@
} }
} }
} }
}
} }

View File

@ -220,18 +220,25 @@ export function fetchBasicGasAndTimeEstimates () {
) )
.then(r => r.json()) .then(r => r.json())
.then(({ .then(({
average, average: averageTimes10,
avgWait, avgWait,
block_time: blockTime, block_time: blockTime,
blockNum, blockNum,
fast, fast: fastTimes10,
fastest, fastest: fastestTimes10,
fastestWait, fastestWait,
fastWait, fastWait,
safeLow, safeLow: safeLowTimes10,
safeLowWait, safeLowWait,
speed, speed,
}) => { }) => {
const [average, fast, fastest, safeLow] = [
averageTimes10,
fastTimes10,
fastestTimes10,
safeLowTimes10,
].map(price => (new BigNumber(price)).div(10).toNumber())
const basicEstimates = { const basicEstimates = {
average, average,
avgWait, avgWait,

View File

@ -32,15 +32,15 @@ describe('Gas Duck', () => {
let tempFetch let tempFetch
let tempDateNow let tempDateNow
const mockEthGasApiResponse = { const mockEthGasApiResponse = {
average: 'mockAverage', average: 20,
avgWait: 'mockAvgWait', avgWait: 'mockAvgWait',
block_time: 'mockBlock_time', block_time: 'mockBlock_time',
blockNum: 'mockBlockNum', blockNum: 'mockBlockNum',
fast: 'mockFast', fast: 30,
fastest: 'mockFastest', fastest: 40,
fastestWait: 'mockFastestWait', fastestWait: 'mockFastestWait',
fastWait: 'mockFastWait', fastWait: 'mockFastWait',
safeLow: 'mockSafeLow', safeLow: 10,
safeLowWait: 'mockSafeLowWait', safeLowWait: 'mockSafeLowWait',
speed: 'mockSpeed', speed: 'mockSpeed',
} }
@ -338,15 +338,15 @@ describe('Gas Duck', () => {
[{ [{
type: SET_BASIC_GAS_ESTIMATE_DATA, type: SET_BASIC_GAS_ESTIMATE_DATA,
value: { value: {
average: 'mockAverage', average: 2,
avgWait: 'mockAvgWait', avgWait: 'mockAvgWait',
blockTime: 'mockBlock_time', blockTime: 'mockBlock_time',
blockNum: 'mockBlockNum', blockNum: 'mockBlockNum',
fast: 'mockFast', fast: 3,
fastest: 'mockFastest', fastest: 4,
fastestWait: 'mockFastestWait', fastestWait: 'mockFastestWait',
fastWait: 'mockFastWait', fastWait: 'mockFastWait',
safeLow: 'mockSafeLow', safeLow: 1,
safeLowWait: 'mockSafeLowWait', safeLowWait: 'mockSafeLowWait',
speed: 'mockSpeed', speed: 'mockSpeed',
}, },

View File

@ -85,9 +85,9 @@ function getAveragePriceEstimateInHexWEI (state) {
return getGasPriceInHexWei(averagePriceEstimate || '0x0') return getGasPriceInHexWei(averagePriceEstimate || '0x0')
} }
function getFastPriceEstimateInHexWEI (state, convertFromDecGWEI) { function getFastPriceEstimateInHexWEI (state) {
const fastPriceEstimate = state.gas.basicEstimates.fast const fastPriceEstimate = state.gas.basicEstimates.fast
return getGasPriceInHexWei(fastPriceEstimate || '0x0', convertFromDecGWEI) return getGasPriceInHexWei(fastPriceEstimate || '0x0')
} }
function getDefaultActiveButtonIndex (gasButtonInfo, customGasPriceInHex, gasPrice) { function getDefaultActiveButtonIndex (gasButtonInfo, customGasPriceInHex, gasPrice) {
@ -100,15 +100,6 @@ function getBasicGasEstimateBlockTime (state) {
return state.gas.basicEstimates.blockTime return state.gas.basicEstimates.blockTime
} }
function apiEstimateModifiedToGWEI (estimate) {
return multiplyCurrencies(estimate, 0.10, {
toNumericBase: 'hex',
multiplicandBase: 10,
multiplierBase: 10,
numberOfDecimals: 9,
})
}
function basicPriceEstimateToETHTotal (estimate, gasLimit, numberOfDecimals = 9) { function basicPriceEstimateToETHTotal (estimate, gasLimit, numberOfDecimals = 9) {
return conversionUtil(calcGasTotal(gasLimit, estimate), { return conversionUtil(calcGasTotal(gasLimit, estimate), {
fromNumericBase: 'hex', fromNumericBase: 'hex',
@ -118,26 +109,18 @@ function basicPriceEstimateToETHTotal (estimate, gasLimit, numberOfDecimals = 9)
}) })
} }
function getRenderableEthFee (estimate, gasLimit, numberOfDecimals = 9, convertFromDecGWEI) { function getRenderableEthFee (estimate, gasLimit, numberOfDecimals = 9) {
const initialConversion = convertFromDecGWEI
? x => conversionUtil(x, { fromNumericBase: 'dec', toNumericBase: 'hex' })
: apiEstimateModifiedToGWEI
return pipe( return pipe(
initialConversion, x => conversionUtil(x, { fromNumericBase: 'dec', toNumericBase: 'hex' }),
partialRight(basicPriceEstimateToETHTotal, [gasLimit, numberOfDecimals]), partialRight(basicPriceEstimateToETHTotal, [gasLimit, numberOfDecimals]),
formatETHFee formatETHFee
)(estimate, gasLimit) )(estimate, gasLimit)
} }
function getRenderableConvertedCurrencyFee (estimate, gasLimit, convertedCurrency, conversionRate, convertFromDecGWEI) { function getRenderableConvertedCurrencyFee (estimate, gasLimit, convertedCurrency, conversionRate) {
const initialConversion = convertFromDecGWEI
? x => conversionUtil(x, { fromNumericBase: 'dec', toNumericBase: 'hex' })
: apiEstimateModifiedToGWEI
return pipe( return pipe(
initialConversion, x => conversionUtil(x, { fromNumericBase: 'dec', toNumericBase: 'hex' }),
partialRight(basicPriceEstimateToETHTotal, [gasLimit]), partialRight(basicPriceEstimateToETHTotal, [gasLimit]),
partialRight(ethTotalToConvertedCurrency, [convertedCurrency, conversionRate]), partialRight(ethTotalToConvertedCurrency, [convertedCurrency, conversionRate]),
partialRight(formatCurrency, [convertedCurrency]) partialRight(formatCurrency, [convertedCurrency])
@ -153,14 +136,26 @@ function getTimeEstimateInSeconds (blockWaitEstimate) {
}) })
} }
function formatTimeEstimate (totalSeconds) { function formatTimeEstimate (totalSeconds, greaterThanMax, lessThanMin) {
const minutes = Math.floor(totalSeconds / 60) const minutes = Math.floor(totalSeconds / 60)
const seconds = Math.floor(totalSeconds % 60) const seconds = Math.floor(totalSeconds % 60)
if (!minutes && !seconds) {
return '...'
}
let symbol = '~'
if (greaterThanMax) {
symbol = '< '
} else if (lessThanMin) {
symbol = '> '
}
const formattedMin = `${minutes ? minutes + ' min' : ''}` const formattedMin = `${minutes ? minutes + ' min' : ''}`
const formattedSec = `${seconds ? seconds + ' sec' : ''}` const formattedSec = `${seconds ? seconds + ' sec' : ''}`
const formattedCombined = formattedMin && formattedSec const formattedCombined = formattedMin && formattedSec
? `~${formattedMin} ${formattedSec}` ? `${symbol}${formattedMin} ${formattedSec}`
: '~' + [formattedMin, formattedSec].find(t => t) : symbol + [formattedMin, formattedSec].find(t => t)
return formattedCombined return formattedCombined
} }
@ -182,13 +177,9 @@ function priceEstimateToWei (priceEstimate) {
}) })
} }
function getGasPriceInHexWei (price, convertFromDecGWEI) { function getGasPriceInHexWei (price) {
const initialConversion = convertFromDecGWEI
? x => conversionUtil(x, { fromNumericBase: 'dec', toNumericBase: 'hex' })
: apiEstimateModifiedToGWEI
return pipe( return pipe(
initialConversion, x => conversionUtil(x, { fromNumericBase: 'dec', toNumericBase: 'hex' }),
priceEstimateToWei, priceEstimateToWei,
addHexPrefix addHexPrefix
)(price) )(price)
@ -259,19 +250,19 @@ function getRenderableEstimateDataForSmallButtonsFromGWEI (state) {
return [ return [
{ {
labelKey: 'fastest', labelKey: 'fastest',
feeInSecondaryCurrency: getRenderableConvertedCurrencyFee(fastest, gasLimit, currentCurrency, conversionRate, true), feeInSecondaryCurrency: getRenderableConvertedCurrencyFee(fastest, gasLimit, currentCurrency, conversionRate),
feeInPrimaryCurrency: getRenderableEthFee(fastest, gasLimit, NUMBER_OF_DECIMALS_SM_BTNS, true), feeInPrimaryCurrency: getRenderableEthFee(fastest, gasLimit, NUMBER_OF_DECIMALS_SM_BTNS, true),
priceInHexWei: getGasPriceInHexWei(fastest, true), priceInHexWei: getGasPriceInHexWei(fastest, true),
}, },
{ {
labelKey: 'fast', labelKey: 'fast',
feeInSecondaryCurrency: getRenderableConvertedCurrencyFee(fast, gasLimit, currentCurrency, conversionRate, true), feeInSecondaryCurrency: getRenderableConvertedCurrencyFee(fast, gasLimit, currentCurrency, conversionRate),
feeInPrimaryCurrency: getRenderableEthFee(fast, gasLimit, NUMBER_OF_DECIMALS_SM_BTNS, true), feeInPrimaryCurrency: getRenderableEthFee(fast, gasLimit, NUMBER_OF_DECIMALS_SM_BTNS, true),
priceInHexWei: getGasPriceInHexWei(fast, true), priceInHexWei: getGasPriceInHexWei(fast, true),
}, },
{ {
labelKey: 'slow', labelKey: 'slow',
feeInSecondaryCurrency: getRenderableConvertedCurrencyFee(safeLow, gasLimit, currentCurrency, conversionRate, true), feeInSecondaryCurrency: getRenderableConvertedCurrencyFee(safeLow, gasLimit, currentCurrency, conversionRate),
feeInPrimaryCurrency: getRenderableEthFee(safeLow, gasLimit, NUMBER_OF_DECIMALS_SM_BTNS, true), feeInPrimaryCurrency: getRenderableEthFee(safeLow, gasLimit, NUMBER_OF_DECIMALS_SM_BTNS, true),
priceInHexWei: getGasPriceInHexWei(safeLow, true), priceInHexWei: getGasPriceInHexWei(safeLow, true),
}, },

View File

@ -109,11 +109,11 @@ describe('custom-gas selectors', () => {
gas: { gas: {
basicEstimates: { basicEstimates: {
blockTime: 14.16326530612245, blockTime: 14.16326530612245,
safeLow: 25, safeLow: 2.5,
safeLowWait: 6.6, safeLowWait: 6.6,
fast: 50, fast: 5,
fastWait: 3.3, fastWait: 3.3,
fastest: 100, fastest: 10,
fastestWait: 0.5, fastestWait: 0.5,
}, },
}, },
@ -154,11 +154,11 @@ describe('custom-gas selectors', () => {
gas: { gas: {
basicEstimates: { basicEstimates: {
blockTime: 14.16326530612245, blockTime: 14.16326530612245,
safeLow: 50, safeLow: 5,
safeLowWait: 13.2, safeLowWait: 13.2,
fast: 100, fast: 10,
fastWait: 6.6, fastWait: 6.6,
fastest: 200, fastest: 20,
fastestWait: 1.0, fastestWait: 1.0,
}, },
}, },