Adds speed up slide-in gas customization sidebar

feature/default_network_editable
Dan Miller 6 years ago
parent 9b9a2cc2e0
commit e3f015c88f
  1. 27
      ui/app/actions.js
  2. 22
      ui/app/app.js
  3. 25
      ui/app/components/gas-customization/gas-modal-page-container/advanced-tab-content/advanced-tab-content.component.js
  4. 12
      ui/app/components/gas-customization/gas-modal-page-container/advanced-tab-content/index.scss
  5. 18
      ui/app/components/gas-customization/gas-modal-page-container/advanced-tab-content/tests/advanced-tab-content-component.test.js
  6. 4
      ui/app/components/gas-customization/gas-modal-page-container/gas-modal-page-container.component.js
  7. 57
      ui/app/components/gas-customization/gas-modal-page-container/gas-modal-page-container.container.js
  8. 1
      ui/app/components/gas-customization/gas-modal-page-container/tests/gas-modal-page-container-component.test.js
  9. 66
      ui/app/components/gas-customization/gas-modal-page-container/tests/gas-modal-page-container-container.test.js
  10. 22
      ui/app/components/page-container/page-container.component.js
  11. 2
      ui/app/components/sidebars/index.scss
  12. 40
      ui/app/components/sidebars/sidebar-content.scss
  13. 21
      ui/app/components/sidebars/sidebar.component.js
  14. 9
      ui/app/components/sidebars/tests/sidebars-component.test.js
  15. 1
      ui/app/components/transaction-list-item-details/transaction-list-item-details.component.js
  16. 11
      ui/app/components/transaction-list-item/transaction-list-item.component.js
  17. 20
      ui/app/components/transaction-list-item/transaction-list-item.container.js
  18. 1
      ui/app/reducers/app.js
  19. 5
      ui/app/selectors.js

@ -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,12 +169,17 @@ 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,
handleGasPriceSelection: dispatchUpdateCustomGasPrice,
@ -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,17 +112,19 @@ export default class PageContainer extends PureComponent {
tabs={this.renderTabs()}
headerCloseText={headerCloseText}
/>
<div className="page-container__content">
{ this.renderContent() }
<div className="page-container__bottom">
<div className="page-container__content">
{ this.renderContent() }
</div>
<PageContainerFooter
onCancel={onCancel}
cancelText={cancelText}
hideCancel={hideCancel}
onSubmit={onSubmit}
submitText={submitText}
disabled={disabled}
/>
</div>
<PageContainerFooter
onCancel={onCancel}
cancelText={cancelText}
hideCancel={hideCancel}
onSubmit={onSubmit}
submitText={submitText}
disabled={disabled}
/>
</div>
)
}

@ -1,3 +1,5 @@
@import './sidebar-content';
.sidebar-right-enter {
transition: transform 300ms ease-in-out;
transform: translateX(-100%);

@ -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…
Cancel
Save