mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
Adds speed up slide-in gas customization sidebar
This commit is contained in:
parent
9b9a2cc2e0
commit
e3f015c88f
@ -325,6 +325,8 @@ var actions = {
|
||||
clearPendingTokens,
|
||||
|
||||
createCancelTransaction,
|
||||
createSpeedUpTransaction,
|
||||
|
||||
approveProviderRequest,
|
||||
rejectProviderRequest,
|
||||
clearApprovedOrigins,
|
||||
@ -1837,6 +1839,28 @@ function createCancelTransaction (txId, customGasPrice) {
|
||||
}
|
||||
}
|
||||
|
||||
function createSpeedUpTransaction (txId, customGasPrice) {
|
||||
log.debug('background.createSpeedUpTransaction')
|
||||
let newTx
|
||||
|
||||
return dispatch => {
|
||||
return new Promise((resolve, reject) => {
|
||||
background.createSpeedUpTransaction(txId, customGasPrice, (err, newState) => {
|
||||
if (err) {
|
||||
dispatch(actions.displayWarning(err.message))
|
||||
reject(err)
|
||||
}
|
||||
|
||||
const { selectedAddressTxList } = newState
|
||||
newTx = selectedAddressTxList[selectedAddressTxList.length - 1]
|
||||
resolve(newState)
|
||||
})
|
||||
})
|
||||
.then(newState => dispatch(actions.updateMetamaskState(newState)))
|
||||
.then(() => newTx)
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// config
|
||||
//
|
||||
@ -1937,12 +1961,13 @@ function hideModal (payload) {
|
||||
}
|
||||
}
|
||||
|
||||
function showSidebar ({ transitionName, type }) {
|
||||
function showSidebar ({ transitionName, type, props }) {
|
||||
return {
|
||||
type: actions.SIDEBAR_OPEN,
|
||||
value: {
|
||||
transitionName,
|
||||
type,
|
||||
props,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -43,6 +43,10 @@ const Alert = require('./components/alert')
|
||||
import AppHeader from './components/app-header'
|
||||
import UnlockPage from './components/pages/unlock-page'
|
||||
|
||||
import {
|
||||
submittedPendingTransactionsSelector,
|
||||
} from './selectors/transactions'
|
||||
|
||||
// Routes
|
||||
const {
|
||||
DEFAULT_ROUTE,
|
||||
@ -106,12 +110,20 @@ class App extends Component {
|
||||
currentView,
|
||||
setMouseUserState,
|
||||
sidebar,
|
||||
submittedPendingTransactions,
|
||||
} = this.props
|
||||
const isLoadingNetwork = network === 'loading' && currentView.name !== 'config'
|
||||
const loadMessage = loadingMessage || isLoadingNetwork ?
|
||||
this.getConnectingLabel(loadingMessage) : null
|
||||
log.debug('Main ui render function')
|
||||
|
||||
const {
|
||||
isOpen: sidebarIsOpen,
|
||||
transitionName: sidebarTransitionName,
|
||||
type: sidebarType,
|
||||
props: { transaction: sidebarTransaction },
|
||||
} = sidebar
|
||||
|
||||
return (
|
||||
h('.flex-column.full-height', {
|
||||
className: classnames({ 'mouse-user-styles': isMouseUser }),
|
||||
@ -139,10 +151,12 @@ class App extends Component {
|
||||
|
||||
// sidebar
|
||||
h(Sidebar, {
|
||||
sidebarOpen: sidebar.isOpen,
|
||||
sidebarOpen: sidebarIsOpen,
|
||||
sidebarShouldClose: sidebarTransaction && !submittedPendingTransactions.find(({ id }) => id === sidebarTransaction.id),
|
||||
hideSidebar: this.props.hideSidebar,
|
||||
transitionName: sidebar.transitionName,
|
||||
type: sidebar.type,
|
||||
transitionName: sidebarTransitionName,
|
||||
type: sidebarType,
|
||||
sidebarProps: sidebar.props,
|
||||
}),
|
||||
|
||||
// network dropdown
|
||||
@ -254,6 +268,7 @@ App.propTypes = {
|
||||
activeAddress: PropTypes.string,
|
||||
unapprovedTxs: PropTypes.object,
|
||||
seedWords: PropTypes.string,
|
||||
submittedPendingTransactions: PropTypes.array,
|
||||
unapprovedMsgCount: PropTypes.number,
|
||||
unapprovedPersonalMsgCount: PropTypes.number,
|
||||
unapprovedTypedMessagesCount: PropTypes.number,
|
||||
@ -313,6 +328,7 @@ function mapStateToProps (state) {
|
||||
isOnboarding: Boolean(!noActiveNotices || seedWords || !isInitialized),
|
||||
isPopup: state.metamask.isPopup,
|
||||
seedWords: state.metamask.seedWords,
|
||||
submittedPendingTransactions: submittedPendingTransactionsSelector(state),
|
||||
unapprovedTxs,
|
||||
unapprovedMsgs: state.metamask.unapprovedMsgs,
|
||||
unapprovedMsgCount,
|
||||
|
@ -1,5 +1,6 @@
|
||||
import React, { Component } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import classnames from 'classnames'
|
||||
import GasPriceChart from '../../gas-price-chart'
|
||||
|
||||
export default class AdvancedTabContent extends Component {
|
||||
@ -16,23 +17,31 @@ export default class AdvancedTabContent extends Component {
|
||||
totalFee: PropTypes.string,
|
||||
timeRemaining: PropTypes.string,
|
||||
gasChartProps: PropTypes.object,
|
||||
insufficientBalance: PropTypes.bool,
|
||||
}
|
||||
|
||||
gasInput (value, onChange, min, precision, showGWEI) {
|
||||
gasInput (value, onChange, min, insufficientBalance, precision, showGWEI) {
|
||||
return (
|
||||
<div className="advanced-tab__gas-edit-row__input-wrapper">
|
||||
<input
|
||||
className="advanced-tab__gas-edit-row__input"
|
||||
className={classnames('advanced-tab__gas-edit-row__input', {
|
||||
'advanced-tab__gas-edit-row__input--error': insufficientBalance,
|
||||
})}
|
||||
type="number"
|
||||
value={value}
|
||||
min={min}
|
||||
precision={precision}
|
||||
onChange={event => onChange(Number(event.target.value))}
|
||||
/>
|
||||
<div className="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,
|
||||
})}>
|
||||
<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"><i className="fa fa-sm fa-angle-down" onClick={() => onChange(value - 1)} /></div>
|
||||
</div>
|
||||
{insufficientBalance && <div className="advanced-tab__gas-edit-row__insufficient-balance">
|
||||
Insufficient Balance
|
||||
</div>}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@ -70,11 +79,11 @@ export default class AdvancedTabContent extends Component {
|
||||
)
|
||||
}
|
||||
|
||||
renderGasEditRows (customGasPrice, updateCustomGasPrice, customGasLimit, updateCustomGasLimit) {
|
||||
renderGasEditRows (customGasPrice, updateCustomGasPrice, customGasLimit, updateCustomGasLimit, insufficientBalance) {
|
||||
return (
|
||||
<div className="advanced-tab__gas-edit-rows">
|
||||
{ this.renderGasEditRow('gasPrice', customGasPrice, updateCustomGasPrice, customGasPrice, 9, true) }
|
||||
{ this.renderGasEditRow('gasLimit', customGasLimit, updateCustomGasLimit, customGasLimit, 0) }
|
||||
{ this.renderGasEditRow('gasPrice', customGasPrice, updateCustomGasPrice, customGasPrice, insufficientBalance, 9, true) }
|
||||
{ this.renderGasEditRow('gasLimit', customGasLimit, updateCustomGasLimit, customGasLimit, insufficientBalance, 0) }
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@ -86,6 +95,7 @@ export default class AdvancedTabContent extends Component {
|
||||
timeRemaining,
|
||||
customGasPrice,
|
||||
customGasLimit,
|
||||
insufficientBalance,
|
||||
totalFee,
|
||||
gasChartProps,
|
||||
} = this.props
|
||||
@ -98,7 +108,8 @@ export default class AdvancedTabContent extends Component {
|
||||
customGasPrice,
|
||||
updateCustomGasPrice,
|
||||
customGasLimit,
|
||||
updateCustomGasLimit
|
||||
updateCustomGasLimit,
|
||||
insufficientBalance
|
||||
) }
|
||||
<div className="advanced-tab__fee-chart__title">Live Gas Price Predictions</div>
|
||||
<GasPriceChart {...gasChartProps} updateCustomGasPrice={updateCustomGasPrice} />
|
||||
|
@ -102,6 +102,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
&__insufficient-balance {
|
||||
font-size: 12px;
|
||||
color: red;
|
||||
}
|
||||
|
||||
|
||||
&__input-wrapper {
|
||||
position: relative;
|
||||
@ -119,6 +124,10 @@
|
||||
margin-top: 7px;
|
||||
}
|
||||
|
||||
&__input--error {
|
||||
border: 1px solid $red;
|
||||
}
|
||||
|
||||
&__input-arrows {
|
||||
position: absolute;
|
||||
top: 7px;
|
||||
@ -155,6 +164,9 @@
|
||||
}
|
||||
}
|
||||
|
||||
&__input-arrows--error {
|
||||
border: 1px solid $red;
|
||||
}
|
||||
|
||||
input[type="number"]::-webkit-inner-spin-button {
|
||||
-webkit-appearance: none;
|
||||
|
@ -27,6 +27,7 @@ describe('AdvancedTabContent Component', function () {
|
||||
customGasLimit={23456}
|
||||
timeRemaining={21500}
|
||||
totalFee={'$0.25'}
|
||||
insufficientBalance={false}
|
||||
/>, { context: { t: (str1, str2) => str2 ? str1 + str2 : str1 } })
|
||||
})
|
||||
|
||||
@ -63,11 +64,13 @@ describe('AdvancedTabContent Component', function () {
|
||||
assert.equal(AdvancedTabContent.prototype.renderGasEditRows.callCount, 1)
|
||||
const renderDataSummaryArgs = AdvancedTabContent.prototype.renderDataSummary.getCall(0).args
|
||||
assert.deepEqual(renderDataSummaryArgs, ['$0.25', 21500])
|
||||
})
|
||||
|
||||
it('should call renderGasEditRows with the expected params', () => {
|
||||
assert.equal(AdvancedTabContent.prototype.renderGasEditRows.callCount, 1)
|
||||
const renderGasEditRowArgs = AdvancedTabContent.prototype.renderGasEditRows.getCall(0).args
|
||||
assert.deepEqual(renderGasEditRowArgs, [
|
||||
11, propsMethodSpies.updateCustomGasPrice, 23456, propsMethodSpies.updateCustomGasLimit,
|
||||
11, propsMethodSpies.updateCustomGasPrice, 23456, propsMethodSpies.updateCustomGasLimit, false,
|
||||
])
|
||||
})
|
||||
})
|
||||
@ -142,7 +145,8 @@ describe('AdvancedTabContent Component', function () {
|
||||
'mockGasPrice',
|
||||
() => 'mockUpdateCustomGasPriceReturn',
|
||||
'mockGasLimit',
|
||||
() => 'mockUpdateCustomGasLimitReturn'
|
||||
() => 'mockUpdateCustomGasLimitReturn',
|
||||
false
|
||||
))
|
||||
})
|
||||
|
||||
@ -161,10 +165,10 @@ describe('AdvancedTabContent Component', function () {
|
||||
const renderGasEditRowSpyArgs = AdvancedTabContent.prototype.renderGasEditRow.args
|
||||
assert.equal(renderGasEditRowSpyArgs.length, 2)
|
||||
assert.deepEqual(renderGasEditRowSpyArgs[0].map(String), [
|
||||
'gasPrice', 'mockGasPrice', () => 'mockUpdateCustomGasPriceReturn', 'mockGasPrice', 9, true,
|
||||
'gasPrice', 'mockGasPrice', () => 'mockUpdateCustomGasPriceReturn', 'mockGasPrice', false, 9, true,
|
||||
].map(String))
|
||||
assert.deepEqual(renderGasEditRowSpyArgs[1].map(String), [
|
||||
'gasLimit', 'mockGasLimit', () => 'mockUpdateCustomGasLimitReturn', 'mockGasLimit', 0,
|
||||
'gasLimit', 'mockGasLimit', () => 'mockUpdateCustomGasLimitReturn', 'mockGasLimit', false, 0,
|
||||
].map(String))
|
||||
})
|
||||
})
|
||||
@ -195,8 +199,8 @@ describe('AdvancedTabContent Component', function () {
|
||||
321,
|
||||
value => value + 7,
|
||||
0,
|
||||
8,
|
||||
false
|
||||
false,
|
||||
8
|
||||
))
|
||||
})
|
||||
|
||||
@ -204,7 +208,7 @@ describe('AdvancedTabContent Component', function () {
|
||||
assert(gasInput.hasClass('advanced-tab__gas-edit-row__input-wrapper'))
|
||||
})
|
||||
|
||||
it('should render an input, but not a GWEI symbol', () => {
|
||||
it('should render two children, including an input', () => {
|
||||
assert.equal(gasInput.children().length, 2)
|
||||
assert(gasInput.children().at(0).hasClass('advanced-tab__gas-edit-row__input'))
|
||||
})
|
||||
|
@ -48,6 +48,7 @@ export default class GasModalPageContainer extends Component {
|
||||
newTotalFiat,
|
||||
gasChartProps,
|
||||
currentTimeEstimate,
|
||||
insufficientBalance,
|
||||
}) {
|
||||
const { transactionFee } = this.props
|
||||
return (
|
||||
@ -60,6 +61,7 @@ export default class GasModalPageContainer extends Component {
|
||||
transactionFee={transactionFee}
|
||||
totalFee={newTotalFiat}
|
||||
gasChartProps={gasChartProps}
|
||||
insufficientBalance={insufficientBalance}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@ -139,7 +141,7 @@ export default class GasModalPageContainer extends Component {
|
||||
title={this.context.t('customGas')}
|
||||
subtitle={this.context.t('customGasSubTitle')}
|
||||
tabsComponent={this.renderTabs(infoRowProps, tabProps)}
|
||||
disabled={false}
|
||||
disabled={tabProps.insufficientBalance}
|
||||
onCancel={() => cancelAndClose()}
|
||||
onClose={() => cancelAndClose()}
|
||||
onSubmit={() => {
|
||||
|
@ -5,6 +5,8 @@ import {
|
||||
hideModal,
|
||||
setGasLimit,
|
||||
setGasPrice,
|
||||
createSpeedUpTransaction,
|
||||
hideSidebar,
|
||||
} from '../../../actions'
|
||||
import {
|
||||
setCustomGasPrice,
|
||||
@ -22,6 +24,7 @@ import {
|
||||
getCurrentCurrency,
|
||||
conversionRateSelector as getConversionRate,
|
||||
getSelectedToken,
|
||||
getCurrentEthBalance,
|
||||
} from '../../../selectors.js'
|
||||
import {
|
||||
formatTimeEstimate,
|
||||
@ -34,6 +37,9 @@ import {
|
||||
getEstimatedGasTimes,
|
||||
getRenderableBasicEstimateData,
|
||||
} from '../../../selectors/custom-gas'
|
||||
import {
|
||||
submittedPendingTransactionsSelector,
|
||||
} from '../../../selectors/transactions'
|
||||
import {
|
||||
formatCurrency,
|
||||
} from '../../../helpers/confirm-transaction/util'
|
||||
@ -48,17 +54,19 @@ import {
|
||||
} from '../../../helpers/formatters'
|
||||
import {
|
||||
calcGasTotal,
|
||||
isBalanceSufficient,
|
||||
} from '../../send/send.utils'
|
||||
import { addHexPrefix } from 'ethereumjs-util'
|
||||
import { getAdjacentGasPrices, extrapolateY } from '../gas-price-chart/gas-price-chart.utils'
|
||||
|
||||
const mapStateToProps = state => {
|
||||
const mapStateToProps = (state, ownProps) => {
|
||||
const { transaction = {} } = ownProps
|
||||
const buttonDataLoading = getBasicGasEstimateLoadingStatus(state)
|
||||
const { gasPrice: currentGasPrice, gas: currentGasLimit, value } = getTxParams(state)
|
||||
const gasTotal = calcGasTotal(currentGasLimit, currentGasPrice)
|
||||
|
||||
const { gasPrice: currentGasPrice, gas: currentGasLimit, value } = getTxParams(state, transaction.id)
|
||||
const customModalGasPriceInHex = getCustomGasPrice(state) || currentGasPrice
|
||||
const customModalGasLimitInHex = getCustomGasLimit(state) || currentGasLimit
|
||||
const gasTotal = calcGasTotal(customModalGasLimitInHex, customModalGasPriceInHex)
|
||||
|
||||
const customGasTotal = calcGasTotal(customModalGasLimitInHex, customModalGasPriceInHex)
|
||||
|
||||
const gasButtonInfo = getRenderableBasicEstimateData(state)
|
||||
@ -74,6 +82,14 @@ const mapStateToProps = state => {
|
||||
|
||||
const gasPrices = getEstimatedGasPrices(state)
|
||||
const estimatedTimes = getEstimatedGasTimes(state)
|
||||
const balance = getCurrentEthBalance(state)
|
||||
|
||||
const insufficientBalance = !isBalanceSufficient({
|
||||
amount: value,
|
||||
gasTotal,
|
||||
balance,
|
||||
conversionRate,
|
||||
})
|
||||
|
||||
return {
|
||||
hideBasic,
|
||||
@ -104,6 +120,9 @@ const mapStateToProps = state => {
|
||||
transactionFee: addHexWEIsToRenderableEth('0x0', customGasTotal),
|
||||
sendAmount: addHexWEIsToRenderableEth(value, '0x0'),
|
||||
},
|
||||
isSpeedUp: transaction.status === 'submitted',
|
||||
txId: transaction.id,
|
||||
insufficientBalance,
|
||||
}
|
||||
}
|
||||
|
||||
@ -125,18 +144,24 @@ const mapDispatchToProps = dispatch => {
|
||||
updateConfirmTxGasAndCalculate: (gasLimit, gasPrice) => {
|
||||
return dispatch(updateGasAndCalculate({ gasLimit, gasPrice }))
|
||||
},
|
||||
createSpeedUpTransaction: (txId, gasPrice) => {
|
||||
return dispatch(createSpeedUpTransaction(txId, gasPrice))
|
||||
},
|
||||
hideGasButtonGroup: () => dispatch(hideGasButtonGroup()),
|
||||
setCustomTimeEstimate: (timeEstimateInSeconds) => dispatch(setCustomTimeEstimate(timeEstimateInSeconds)),
|
||||
hideSidebar: () => dispatch(hideSidebar()),
|
||||
}
|
||||
}
|
||||
|
||||
const mergeProps = (stateProps, dispatchProps, ownProps) => {
|
||||
const { gasPriceButtonGroupProps, isConfirm } = stateProps
|
||||
const { gasPriceButtonGroupProps, isConfirm, isSpeedUp, txId } = stateProps
|
||||
const {
|
||||
updateCustomGasPrice: dispatchUpdateCustomGasPrice,
|
||||
hideGasButtonGroup: dispatchHideGasButtonGroup,
|
||||
setGasData: dispatchSetGasData,
|
||||
updateConfirmTxGasAndCalculate: dispatchUpdateConfirmTxGasAndCalculate,
|
||||
createSpeedUpTransaction: dispatchCreateSpeedUpTransaction,
|
||||
hideSidebar: dispatchHideSidebar,
|
||||
...otherDispatchProps
|
||||
} = dispatchProps
|
||||
|
||||
@ -144,11 +169,16 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => {
|
||||
...stateProps,
|
||||
...otherDispatchProps,
|
||||
...ownProps,
|
||||
onSubmit: isConfirm
|
||||
? dispatchUpdateConfirmTxGasAndCalculate
|
||||
: (newLimit, newPrice) => {
|
||||
dispatchSetGasData(newLimit, newPrice)
|
||||
onSubmit: (gasLimit, gasPrice) => {
|
||||
if (isConfirm) {
|
||||
dispatchUpdateConfirmTxGasAndCalculate(gasLimit, gasPrice)
|
||||
} else if (isSpeedUp) {
|
||||
dispatchCreateSpeedUpTransaction(txId, gasPrice)
|
||||
dispatchHideSidebar()
|
||||
} else {
|
||||
dispatchSetGasData(gasLimit, gasPrice)
|
||||
dispatchHideGasButtonGroup()
|
||||
}
|
||||
},
|
||||
gasPriceButtonGroupProps: {
|
||||
...gasPriceButtonGroupProps,
|
||||
@ -171,9 +201,12 @@ function calcCustomGasLimit (customGasLimitInHex) {
|
||||
return parseInt(customGasLimitInHex, 16)
|
||||
}
|
||||
|
||||
function getTxParams (state) {
|
||||
function getTxParams (state, transactionId) {
|
||||
const { confirmTransaction: { txData }, metamask: { send } } = state
|
||||
return txData.txParams || {
|
||||
const pendingTransactions = submittedPendingTransactionsSelector(state)
|
||||
const pendingTransaction = pendingTransactions.find(({ id }) => id === transactionId)
|
||||
const { txParams: pendingTxParams } = pendingTransaction || {}
|
||||
return txData.txParams || pendingTxParams || {
|
||||
from: send.from,
|
||||
gas: send.gasLimit,
|
||||
gasPrice: send.gasPrice || getAveragePriceEstimateInHexWEI(state),
|
||||
|
@ -68,6 +68,7 @@ describe('GasModalPageContainer Component', function () {
|
||||
currentTimeEstimate={'1 min 31 sec'}
|
||||
customGasPriceInHex={'mockCustomGasPriceInHex'}
|
||||
customGasLimitInHex={'mockCustomGasLimitInHex'}
|
||||
insufficientBalance={false}
|
||||
/>, { context: { t: (str1, str2) => str2 ? str1 + str2 : str1 } })
|
||||
})
|
||||
|
||||
|
@ -44,14 +44,16 @@ proxyquire('../gas-modal-page-container.container.js', {
|
||||
'../../../ducks/gas.duck': gasActionSpies,
|
||||
'../../../ducks/confirm-transaction.duck': confirmTransactionActionSpies,
|
||||
'../../../ducks/send.duck': sendActionSpies,
|
||||
'../../../selectors.js': {
|
||||
getCurrentEthBalance: (state) => state.metamask.balance || '0x0',
|
||||
},
|
||||
})
|
||||
|
||||
describe('gas-modal-page-container container', () => {
|
||||
|
||||
describe('mapStateToProps()', () => {
|
||||
|
||||
it('should map the correct properties to props', () => {
|
||||
const mockState2 = {
|
||||
const baseMockState = {
|
||||
appState: {
|
||||
modal: {
|
||||
modalState: {
|
||||
@ -92,9 +94,7 @@ describe('gas-modal-page-container container', () => {
|
||||
},
|
||||
},
|
||||
}
|
||||
const result2 = mapStateToProps(mockState2)
|
||||
|
||||
assert.deepEqual(result2, {
|
||||
const baseExpectedResult = {
|
||||
isConfirm: true,
|
||||
customGasPrice: 4.294967295,
|
||||
customGasLimit: 2863311530,
|
||||
@ -116,13 +116,40 @@ describe('gas-modal-page-container container', () => {
|
||||
},
|
||||
hideBasic: true,
|
||||
infoRowProps: {
|
||||
originalTotalFiat: '22.58',
|
||||
originalTotalEth: '0.451569 ETH',
|
||||
originalTotalFiat: '637.41',
|
||||
originalTotalEth: '12.748189 ETH',
|
||||
newTotalFiat: '637.41',
|
||||
newTotalEth: '12.748189 ETH',
|
||||
sendAmount: '0.45036 ETH',
|
||||
transactionFee: '12.297829 ETH',
|
||||
},
|
||||
insufficientBalance: true,
|
||||
isSpeedUp: false,
|
||||
txId: 34,
|
||||
}
|
||||
const baseMockOwnProps = { transaction: { id: 34 } }
|
||||
const tests = [
|
||||
{ mockState: baseMockState, expectedResult: baseExpectedResult, mockOwnProps: baseMockOwnProps },
|
||||
{
|
||||
mockState: Object.assign({}, baseMockState, {
|
||||
metamask: { ...baseMockState.metamask, balance: '0xfffffffffffffffffffff' },
|
||||
}),
|
||||
expectedResult: Object.assign({}, baseExpectedResult, { insufficientBalance: false }),
|
||||
mockOwnProps: baseMockOwnProps,
|
||||
},
|
||||
{
|
||||
mockState: baseMockState,
|
||||
mockOwnProps: Object.assign({}, baseMockOwnProps, {
|
||||
transaction: { id: 34, status: 'submitted' },
|
||||
}),
|
||||
expectedResult: Object.assign({}, baseExpectedResult, { isSpeedUp: true }),
|
||||
},
|
||||
]
|
||||
|
||||
let result
|
||||
tests.forEach(({ mockState, mockOwnProps, expectedResult}) => {
|
||||
result = mapStateToProps(mockState, mockOwnProps)
|
||||
assert.deepEqual(result, expectedResult)
|
||||
})
|
||||
})
|
||||
|
||||
@ -230,9 +257,21 @@ describe('gas-modal-page-container container', () => {
|
||||
setGasData: sinon.spy(),
|
||||
updateConfirmTxGasAndCalculate: sinon.spy(),
|
||||
someOtherDispatchProp: sinon.spy(),
|
||||
createSpeedUpTransaction: sinon.spy(),
|
||||
hideSidebar: sinon.spy(),
|
||||
}
|
||||
ownProps = { someOwnProp: 123 }
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
dispatchProps.updateCustomGasPrice.resetHistory()
|
||||
dispatchProps.hideGasButtonGroup.resetHistory()
|
||||
dispatchProps.setGasData.resetHistory()
|
||||
dispatchProps.updateConfirmTxGasAndCalculate.resetHistory()
|
||||
dispatchProps.someOtherDispatchProp.resetHistory()
|
||||
dispatchProps.createSpeedUpTransaction.resetHistory()
|
||||
dispatchProps.hideSidebar.resetHistory()
|
||||
})
|
||||
it('should return the expected props when isConfirm is true', () => {
|
||||
const result = mergeProps(stateProps, dispatchProps, ownProps)
|
||||
|
||||
@ -289,6 +328,19 @@ describe('gas-modal-page-container container', () => {
|
||||
result.someOtherDispatchProp()
|
||||
assert.equal(dispatchProps.someOtherDispatchProp.callCount, 1)
|
||||
})
|
||||
|
||||
it('should dispatch the expected actions from obSubmit when isConfirm is false and isSpeedUp is true', () => {
|
||||
const result = mergeProps(Object.assign({}, stateProps, { isSpeedUp: true, isConfirm: false }), dispatchProps, ownProps)
|
||||
|
||||
result.onSubmit()
|
||||
|
||||
assert.equal(dispatchProps.updateConfirmTxGasAndCalculate.callCount, 0)
|
||||
assert.equal(dispatchProps.setGasData.callCount, 0)
|
||||
assert.equal(dispatchProps.hideGasButtonGroup.callCount, 0)
|
||||
|
||||
assert.equal(dispatchProps.createSpeedUpTransaction.callCount, 1)
|
||||
assert.equal(dispatchProps.hideSidebar.callCount, 1)
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
@ -112,6 +112,7 @@ export default class PageContainer extends PureComponent {
|
||||
tabs={this.renderTabs()}
|
||||
headerCloseText={headerCloseText}
|
||||
/>
|
||||
<div className="page-container__bottom">
|
||||
<div className="page-container__content">
|
||||
{ this.renderContent() }
|
||||
</div>
|
||||
@ -124,6 +125,7 @@ export default class PageContainer extends PureComponent {
|
||||
disabled={disabled}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
@import './sidebar-content';
|
||||
|
||||
.sidebar-right-enter {
|
||||
transition: transform 300ms ease-in-out;
|
||||
transform: translateX(-100%);
|
||||
|
40
ui/app/components/sidebars/sidebar-content.scss
Normal file
40
ui/app/components/sidebars/sidebar-content.scss
Normal file
@ -0,0 +1,40 @@
|
||||
.sidebar-left {
|
||||
.gas-modal-page-container {
|
||||
.page-container {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.gas-price-chart {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.page-container__bottom {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-flow: space-between;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.page-container__content {
|
||||
overflow-y: inherit;
|
||||
}
|
||||
|
||||
.basic-tab-content {
|
||||
height: 377px;
|
||||
margin-bottom: 0px;
|
||||
border-bottom: 1px solid #d2d8dd;
|
||||
}
|
||||
|
||||
.advanced-tab__fee-chart {
|
||||
height: 320px;
|
||||
}
|
||||
|
||||
.advanced-tab__fee-chart__speed-buttons {
|
||||
bottom: 77px;
|
||||
}
|
||||
|
||||
.gas-modal-content__info-row {
|
||||
height: 170px;
|
||||
}
|
||||
}
|
||||
}
|
@ -3,14 +3,17 @@ import PropTypes from 'prop-types'
|
||||
import ReactCSSTransitionGroup from 'react-addons-css-transition-group'
|
||||
import WalletView from '../wallet-view'
|
||||
import { WALLET_VIEW_SIDEBAR } from './sidebar.constants'
|
||||
import CustomizeGas from '../gas-customization/gas-modal-page-container/'
|
||||
|
||||
export default class Sidebar extends Component {
|
||||
|
||||
static propTypes = {
|
||||
sidebarOpen: PropTypes.bool,
|
||||
hideSidebar: PropTypes.func,
|
||||
sidebarShouldClose: PropTypes.bool,
|
||||
transitionName: PropTypes.string,
|
||||
type: PropTypes.string,
|
||||
sidebarProps: PropTypes.object,
|
||||
};
|
||||
|
||||
renderOverlay () {
|
||||
@ -18,19 +21,27 @@ export default class Sidebar extends Component {
|
||||
}
|
||||
|
||||
renderSidebarContent () {
|
||||
const { type } = this.props
|
||||
|
||||
const { type, sidebarProps = {} } = this.props
|
||||
const { transaction = {} } = sidebarProps
|
||||
switch (type) {
|
||||
case WALLET_VIEW_SIDEBAR:
|
||||
return <WalletView responsiveDisplayClassname={'sidebar-right' } />
|
||||
case 'customize-gas':
|
||||
return <div className={'sidebar-left'}><CustomizeGas transaction={transaction} /></div>
|
||||
default:
|
||||
return null
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
componentDidUpdate (prevProps) {
|
||||
if (!prevProps.sidebarShouldClose && this.props.sidebarShouldClose) {
|
||||
this.props.hideSidebar()
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
const { transitionName, sidebarOpen } = this.props
|
||||
const { transitionName, sidebarOpen, sidebarShouldClose } = this.props
|
||||
|
||||
return (
|
||||
<div>
|
||||
@ -39,9 +50,9 @@ export default class Sidebar extends Component {
|
||||
transitionEnterTimeout={300}
|
||||
transitionLeaveTimeout={200}
|
||||
>
|
||||
{ sidebarOpen ? this.renderSidebarContent() : null }
|
||||
{ sidebarOpen && !sidebarShouldClose ? this.renderSidebarContent() : null }
|
||||
</ReactCSSTransitionGroup>
|
||||
{ sidebarOpen ? this.renderOverlay() : null }
|
||||
{ sidebarOpen && !sidebarShouldClose ? this.renderOverlay() : null }
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import ReactCSSTransitionGroup from 'react-addons-css-transition-group'
|
||||
import Sidebar from '../sidebar.component.js'
|
||||
|
||||
import WalletView from '../../wallet-view'
|
||||
import CustomizeGas from '../../gas-customization/gas-modal-page-container/'
|
||||
|
||||
const propsMethodSpies = {
|
||||
hideSidebar: sinon.spy(),
|
||||
@ -59,6 +60,14 @@ describe('Sidebar Component', function () {
|
||||
assert.equal(renderSidebarContent.props.responsiveDisplayClassname, 'sidebar-right')
|
||||
})
|
||||
|
||||
it('should render sidebar content with the correct props', () => {
|
||||
wrapper.setProps({ type: 'customize-gas' })
|
||||
renderSidebarContent = wrapper.instance().renderSidebarContent()
|
||||
const renderedSidebarContent = shallow(renderSidebarContent)
|
||||
assert(renderedSidebarContent.hasClass('sidebar-left'))
|
||||
assert(renderedSidebarContent.childAt(0).is(CustomizeGas))
|
||||
})
|
||||
|
||||
it('should not render with an unrecognized type', () => {
|
||||
wrapper.setProps({ type: 'foobar' })
|
||||
renderSidebarContent = wrapper.instance().renderSidebarContent()
|
||||
|
@ -26,6 +26,7 @@ export default class TransactionListItemDetails extends PureComponent {
|
||||
|
||||
const prefix = prefixForNetwork(metamaskNetworkId)
|
||||
const etherscanUrl = `https://${prefix}etherscan.io/tx/${hash}`
|
||||
|
||||
global.platform.openWindow({ url: etherscanUrl })
|
||||
this.setState({ showTransactionDetails: true })
|
||||
}
|
||||
|
@ -27,6 +27,8 @@ export default class TransactionListItem extends PureComponent {
|
||||
tokenData: PropTypes.object,
|
||||
transaction: PropTypes.object,
|
||||
value: PropTypes.string,
|
||||
fetchBasicGasEstimates: PropTypes.func,
|
||||
fetchGasEstimates: PropTypes.func,
|
||||
}
|
||||
|
||||
state = {
|
||||
@ -69,9 +71,12 @@ export default class TransactionListItem extends PureComponent {
|
||||
}
|
||||
|
||||
resubmit () {
|
||||
const { transaction: { id }, retryTransaction, history } = this.props
|
||||
return retryTransaction(id)
|
||||
.then(id => history.push(`${CONFIRM_TRANSACTION_ROUTE}/${id}`))
|
||||
const { transaction, retryTransaction, fetchBasicGasEstimates, fetchGasEstimates } = this.props
|
||||
fetchBasicGasEstimates().then(basicEstimates => {
|
||||
fetchGasEstimates(basicEstimates.blockTime)
|
||||
}).then(() => {
|
||||
retryTransaction(transaction)
|
||||
})
|
||||
}
|
||||
|
||||
renderPrimaryCurrency () {
|
||||
|
@ -3,10 +3,16 @@ import { withRouter } from 'react-router-dom'
|
||||
import { compose } from 'recompose'
|
||||
import withMethodData from '../../higher-order-components/with-method-data'
|
||||
import TransactionListItem from './transaction-list-item.component'
|
||||
import { setSelectedToken, retryTransaction, showModal } from '../../actions'
|
||||
import { setSelectedToken, showModal, showSidebar } from '../../actions'
|
||||
import { hexToDecimal } from '../../helpers/conversions.util'
|
||||
import { getTokenData } from '../../helpers/transactions.util'
|
||||
import { formatDate } from '../../util'
|
||||
import {
|
||||
fetchBasicGasEstimates,
|
||||
fetchGasEstimates,
|
||||
setCustomGasPrice,
|
||||
setCustomGasLimit,
|
||||
} from '../../ducks/gas.duck'
|
||||
|
||||
const mapStateToProps = (state, ownProps) => {
|
||||
const { transaction: { txParams: { value, nonce, data } = {}, time } = {} } = ownProps
|
||||
@ -23,8 +29,18 @@ const mapStateToProps = (state, ownProps) => {
|
||||
|
||||
const mapDispatchToProps = dispatch => {
|
||||
return {
|
||||
fetchBasicGasEstimates: () => dispatch(fetchBasicGasEstimates()),
|
||||
fetchGasEstimates: (blockTime) => dispatch(fetchGasEstimates(blockTime)),
|
||||
setSelectedToken: tokenAddress => dispatch(setSelectedToken(tokenAddress)),
|
||||
retryTransaction: transactionId => dispatch(retryTransaction(transactionId)),
|
||||
retryTransaction: (transaction) => {
|
||||
dispatch(setCustomGasPrice(transaction.txParams.gasPrice))
|
||||
dispatch(setCustomGasLimit(transaction.txParams.gas))
|
||||
dispatch(showSidebar({
|
||||
transitionName: 'sidebar-left',
|
||||
type: 'customize-gas',
|
||||
props: { transaction },
|
||||
}))
|
||||
},
|
||||
showCancelModal: (transactionId, originalGasPrice) => {
|
||||
return dispatch(showModal({ name: 'CANCEL_TRANSACTION', transactionId, originalGasPrice }))
|
||||
},
|
||||
|
@ -52,6 +52,7 @@ function reduceApp (state, action) {
|
||||
isOpen: false,
|
||||
transitionName: '',
|
||||
type: '',
|
||||
props: {},
|
||||
},
|
||||
alertOpen: false,
|
||||
alertMessage: null,
|
||||
|
@ -35,6 +35,7 @@ const selectors = {
|
||||
getTotalUnapprovedCount,
|
||||
preferencesSelector,
|
||||
getMetaMaskAccounts,
|
||||
getCurrentEthBalance,
|
||||
}
|
||||
|
||||
module.exports = selectors
|
||||
@ -137,6 +138,10 @@ function getCurrentAccountWithSendEtherInfo (state) {
|
||||
return accounts.find(({ address }) => address === currentAddress)
|
||||
}
|
||||
|
||||
function getCurrentEthBalance (state) {
|
||||
return getCurrentAccountWithSendEtherInfo(state).balance
|
||||
}
|
||||
|
||||
function getGasIsLoading (state) {
|
||||
return state.appState.gasIsLoading
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user