aboutsummaryrefslogtreecommitdiffstats
path: root/ui/app
diff options
context:
space:
mode:
authorWhymarrh Whitby <whymarrh.whitby@gmail.com>2018-10-02 08:32:47 +0800
committerGitHub <noreply@github.com>2018-10-02 08:32:47 +0800
commitb11a8ca2f74af108f48604b1b427b224a9df1292 (patch)
tree8b870d4ccc788fcd74863af811441314e61d59bf /ui/app
parentf2fc91b80b30fbd5fa12d24b7bb634f1825de006 (diff)
parent65ba018aad5c09cf5f5b96df559cb543e4a9e980 (diff)
downloadtangerine-wallet-browser-b11a8ca2f74af108f48604b1b427b224a9df1292.tar
tangerine-wallet-browser-b11a8ca2f74af108f48604b1b427b224a9df1292.tar.gz
tangerine-wallet-browser-b11a8ca2f74af108f48604b1b427b224a9df1292.tar.bz2
tangerine-wallet-browser-b11a8ca2f74af108f48604b1b427b224a9df1292.tar.lz
tangerine-wallet-browser-b11a8ca2f74af108f48604b1b427b224a9df1292.tar.xz
tangerine-wallet-browser-b11a8ca2f74af108f48604b1b427b224a9df1292.tar.zst
tangerine-wallet-browser-b11a8ca2f74af108f48604b1b427b224a9df1292.zip
Merge pull request #5329 from whymarrh/confirm-features
Add batch reject to confirm screen
Diffstat (limited to 'ui/app')
-rw-r--r--ui/app/actions.js42
-rw-r--r--ui/app/components/confirm-page-container/confirm-page-container.component.js13
-rw-r--r--ui/app/components/modals/modal.js14
-rw-r--r--ui/app/components/modals/reject-transactions/index.js1
-rw-r--r--ui/app/components/modals/reject-transactions/index.scss6
-rw-r--r--ui/app/components/modals/reject-transactions/reject-transactions.component.js45
-rw-r--r--ui/app/components/modals/reject-transactions/reject-transactions.container.js17
-rw-r--r--ui/app/components/page-container/index.scss27
-rw-r--r--ui/app/components/page-container/page-container-footer/page-container-footer.component.js46
-rw-r--r--ui/app/components/page-container/page-container-footer/tests/page-container-footer.component.test.js12
-rw-r--r--ui/app/components/pages/confirm-transaction-base/confirm-transaction-base.component.js25
-rw-r--r--ui/app/components/pages/confirm-transaction-base/confirm-transaction-base.container.js22
12 files changed, 245 insertions, 25 deletions
diff --git a/ui/app/actions.js b/ui/app/actions.js
index 66dc80509..8f6586139 100644
--- a/ui/app/actions.js
+++ b/ui/app/actions.js
@@ -167,6 +167,7 @@ var actions = {
updateTransaction,
updateAndApproveTx,
cancelTx: cancelTx,
+ cancelTxs,
completedTx: completedTx,
txError: txError,
nextTx: nextTx,
@@ -1302,6 +1303,47 @@ function cancelTx (txData) {
}
}
+/**
+ * Cancels all of the given transactions
+ * @param {Array<object>} txDataList a list of tx data objects
+ * @return {function(*): Promise<void>}
+ */
+function cancelTxs (txDataList) {
+ return async (dispatch, getState) => {
+ dispatch(actions.showLoadingIndication())
+ const txIds = txDataList.map(({id}) => id)
+ const cancellations = txIds.map((id) => new Promise((resolve, reject) => {
+ background.cancelTransaction(id, (err) => {
+ if (err) {
+ return reject(err)
+ }
+
+ resolve()
+ })
+ }))
+
+ await Promise.all(cancellations)
+ const newState = await updateMetamaskStateFromBackground()
+ dispatch(actions.updateMetamaskState(newState))
+ dispatch(actions.clearSend())
+
+ txIds.forEach((id) => {
+ dispatch(actions.completedTx(id))
+ })
+
+ dispatch(actions.hideLoadingIndication())
+
+ if (global.METAMASK_UI_TYPE === ENVIRONMENT_TYPE_NOTIFICATION) {
+ return global.platform.closeCurrentWindow()
+ }
+ }
+}
+
+/**
+ * @deprecated
+ * @param {Array<object>} txsData
+ * @return {Function}
+ */
function cancelAllTx (txsData) {
return (dispatch) => {
txsData.forEach((txData, i) => {
diff --git a/ui/app/components/confirm-page-container/confirm-page-container.component.js b/ui/app/components/confirm-page-container/confirm-page-container.component.js
index b1582051e..36d5a1f58 100644
--- a/ui/app/components/confirm-page-container/confirm-page-container.component.js
+++ b/ui/app/components/confirm-page-container/confirm-page-container.component.js
@@ -41,7 +41,9 @@ export default class ConfirmPageContainer extends Component {
assetImage: PropTypes.string,
summaryComponent: PropTypes.node,
warning: PropTypes.string,
+ unapprovedTxCount: PropTypes.number,
// Footer
+ onCancelAll: PropTypes.func,
onCancel: PropTypes.func,
onSubmit: PropTypes.func,
disabled: PropTypes.bool,
@@ -67,10 +69,12 @@ export default class ConfirmPageContainer extends Component {
summaryComponent,
detailsComponent,
dataComponent,
+ onCancelAll,
onCancel,
onSubmit,
identiconAddress,
nonce,
+ unapprovedTxCount,
assetImage,
warning,
} = this.props
@@ -112,11 +116,18 @@ export default class ConfirmPageContainer extends Component {
}
<PageContainerFooter
onCancel={() => onCancel()}
+ cancelText={this.context.t('reject')}
onSubmit={() => onSubmit()}
submitText={this.context.t('confirm')}
submitButtonType="confirm"
disabled={disabled}
- />
+ >
+ {unapprovedTxCount > 1 && (
+ <a onClick={() => onCancelAll()}>
+ {this.context.t('rejectTxsN', [unapprovedTxCount])}
+ </a>
+ )}
+ </PageContainerFooter>
</div>
)
}
diff --git a/ui/app/components/modals/modal.js b/ui/app/components/modals/modal.js
index 6054002c8..15ca9deaa 100644
--- a/ui/app/components/modals/modal.js
+++ b/ui/app/components/modals/modal.js
@@ -28,6 +28,7 @@ import ConfirmCustomizeGasModal from './customize-gas'
import CancelTransaction from './cancel-transaction'
import WelcomeBeta from './welcome-beta'
import TransactionDetails from './transaction-details'
+import RejectTransactions from './reject-transactions'
const modalContainerBaseStyle = {
transform: 'translate3d(-50%, 0, 0px)',
@@ -378,6 +379,19 @@ const MODALS = {
},
},
+ REJECT_TRANSACTIONS: {
+ contents: h(RejectTransactions),
+ mobileModalStyle: {
+ ...modalContainerMobileStyle,
+ },
+ laptopModalStyle: {
+ ...modalContainerLaptopStyle,
+ },
+ contentStyle: {
+ borderRadius: '8px',
+ },
+ },
+
DEFAULT: {
contents: [],
mobileModalStyle: {},
diff --git a/ui/app/components/modals/reject-transactions/index.js b/ui/app/components/modals/reject-transactions/index.js
new file mode 100644
index 000000000..fcdc372b6
--- /dev/null
+++ b/ui/app/components/modals/reject-transactions/index.js
@@ -0,0 +1 @@
+export { default } from './reject-transactions.container'
diff --git a/ui/app/components/modals/reject-transactions/index.scss b/ui/app/components/modals/reject-transactions/index.scss
new file mode 100644
index 000000000..753466883
--- /dev/null
+++ b/ui/app/components/modals/reject-transactions/index.scss
@@ -0,0 +1,6 @@
+.reject-transactions {
+ &__description {
+ text-align: center;
+ font-size: .875rem;
+ }
+}
diff --git a/ui/app/components/modals/reject-transactions/reject-transactions.component.js b/ui/app/components/modals/reject-transactions/reject-transactions.component.js
new file mode 100644
index 000000000..60b259bdc
--- /dev/null
+++ b/ui/app/components/modals/reject-transactions/reject-transactions.component.js
@@ -0,0 +1,45 @@
+import PropTypes from 'prop-types'
+import React, { PureComponent } from 'react'
+import Modal from '../../modal'
+
+export default class RejectTransactionsModal extends PureComponent {
+ static contextTypes = {
+ t: PropTypes.func.isRequired,
+ }
+
+ static propTypes = {
+ onSubmit: PropTypes.func.isRequired,
+ hideModal: PropTypes.func.isRequired,
+ unapprovedTxCount: PropTypes.number.isRequired,
+ }
+
+ onSubmit = async () => {
+ const { onSubmit, hideModal } = this.props
+
+ await onSubmit()
+ hideModal()
+ }
+
+ render () {
+ const { t } = this.context
+ const { hideModal, unapprovedTxCount } = this.props
+
+ return (
+ <Modal
+ headerText={t('rejectTxsN', [unapprovedTxCount])}
+ onClose={hideModal}
+ onSubmit={this.onSubmit}
+ onCancel={hideModal}
+ submitText={t('rejectAll')}
+ cancelText={t('cancel')}
+ submitType="secondary"
+ >
+ <div>
+ <div className="reject-transactions__description">
+ { t('rejectTxsDescription', [unapprovedTxCount]) }
+ </div>
+ </div>
+ </Modal>
+ )
+ }
+}
diff --git a/ui/app/components/modals/reject-transactions/reject-transactions.container.js b/ui/app/components/modals/reject-transactions/reject-transactions.container.js
new file mode 100644
index 000000000..81e98d3ff
--- /dev/null
+++ b/ui/app/components/modals/reject-transactions/reject-transactions.container.js
@@ -0,0 +1,17 @@
+import { connect } from 'react-redux'
+import { compose } from 'recompose'
+import RejectTransactionsModal from './reject-transactions.component'
+import withModalProps from '../../../higher-order-components/with-modal-props'
+
+const mapStateToProps = (state, ownProps) => {
+ const { unapprovedTxCount } = ownProps
+
+ return {
+ unapprovedTxCount,
+ }
+}
+
+export default compose(
+ withModalProps,
+ connect(mapStateToProps),
+)(RejectTransactionsModal)
diff --git a/ui/app/components/page-container/index.scss b/ui/app/components/page-container/index.scss
index 61434cbcf..6742e3082 100644
--- a/ui/app/components/page-container/index.scss
+++ b/ui/app/components/page-container/index.scss
@@ -43,16 +43,39 @@
&__footer {
display: flex;
- flex-flow: row;
+ flex-flow: column;
justify-content: center;
border-top: 1px solid $geyser;
- padding: 16px;
flex: 0 0 auto;
.btn-default,
.btn-confirm {
font-size: 1rem;
}
+
+ header {
+ display: flex;
+ flex-flow: row;
+ justify-content: center;
+ padding: 16px;
+ flex: 0 0 auto;
+ }
+
+ footer {
+ display: flex;
+ flex-flow: row;
+ justify-content: space-around;
+ padding: 0 16px 16px;
+ flex: 0 0 auto;
+
+ a, a:hover {
+ text-decoration: none;
+ cursor: pointer;
+ font-size: 0.75rem;
+ text-transform: uppercase;
+ color: #2f9ae0;
+ }
+ }
}
&__footer-button {
diff --git a/ui/app/components/page-container/page-container-footer/page-container-footer.component.js b/ui/app/components/page-container/page-container-footer/page-container-footer.component.js
index 3d15df294..773fe1f56 100644
--- a/ui/app/components/page-container/page-container-footer/page-container-footer.component.js
+++ b/ui/app/components/page-container/page-container-footer/page-container-footer.component.js
@@ -5,6 +5,7 @@ import Button from '../../button'
export default class PageContainerFooter extends Component {
static propTypes = {
+ children: PropTypes.node,
onCancel: PropTypes.func,
cancelText: PropTypes.string,
onSubmit: PropTypes.func,
@@ -19,6 +20,7 @@ export default class PageContainerFooter extends Component {
render () {
const {
+ children,
onCancel,
cancelText,
onSubmit,
@@ -30,24 +32,32 @@ export default class PageContainerFooter extends Component {
return (
<div className="page-container__footer">
- <Button
- type="default"
- large
- className="page-container__footer-button"
- onClick={e => onCancel(e)}
- >
- { cancelText || this.context.t('cancel') }
- </Button>
-
- <Button
- type={submitButtonType || 'primary'}
- large
- className="page-container__footer-button"
- disabled={disabled}
- onClick={e => onSubmit(e)}
- >
- { submitText || this.context.t('next') }
- </Button>
+ <header>
+ <Button
+ type="default"
+ large
+ className="page-container__footer-button"
+ onClick={e => onCancel(e)}
+ >
+ { cancelText || this.context.t('cancel') }
+ </Button>
+
+ <Button
+ type={submitButtonType || 'primary'}
+ large
+ className="page-container__footer-button"
+ disabled={disabled}
+ onClick={e => onSubmit(e)}
+ >
+ { submitText || this.context.t('next') }
+ </Button>
+ </header>
+
+ {children && (
+ <footer>
+ {children}
+ </footer>
+ )}
</div>
)
diff --git a/ui/app/components/page-container/page-container-footer/tests/page-container-footer.component.test.js b/ui/app/components/page-container/page-container-footer/tests/page-container-footer.component.test.js
index 5e5dbf00b..64efabab0 100644
--- a/ui/app/components/page-container/page-container-footer/tests/page-container-footer.component.test.js
+++ b/ui/app/components/page-container/page-container-footer/tests/page-container-footer.component.test.js
@@ -25,6 +25,17 @@ describe('Page Footer', () => {
assert.equal(wrapper.find('.page-container__footer').length, 1)
})
+ it('should render a footer inside page-container__footer when given children', () => {
+ const wrapper = shallow(
+ <PageFooter>
+ <div>Works</div>
+ </PageFooter>,
+ { context: { t: sinon.spy((k) => `[${k}]`) } }
+ )
+
+ assert.equal(wrapper.find('.page-container__footer footer').length, 1)
+ })
+
it('renders two button components', () => {
assert.equal(wrapper.find(Button).length, 2)
})
@@ -65,5 +76,4 @@ describe('Page Footer', () => {
assert.equal(onSubmit.callCount, 1)
})
})
-
})
diff --git a/ui/app/components/pages/confirm-transaction-base/confirm-transaction-base.component.js b/ui/app/components/pages/confirm-transaction-base/confirm-transaction-base.component.js
index 40d8faf50..707dad62d 100644
--- a/ui/app/components/pages/confirm-transaction-base/confirm-transaction-base.component.js
+++ b/ui/app/components/pages/confirm-transaction-base/confirm-transaction-base.component.js
@@ -22,6 +22,7 @@ export default class ConfirmTransactionBase extends Component {
// Redux props
balance: PropTypes.string,
cancelTransaction: PropTypes.func,
+ cancelAllTransactions: PropTypes.func,
clearConfirmTransaction: PropTypes.func,
clearSend: PropTypes.func,
conversionRate: PropTypes.number,
@@ -43,12 +44,14 @@ export default class ConfirmTransactionBase extends Component {
sendTransaction: PropTypes.func,
showCustomizeGasModal: PropTypes.func,
showTransactionConfirmedModal: PropTypes.func,
+ showRejectTransactionsConfirmationModal: PropTypes.func,
toAddress: PropTypes.string,
tokenData: PropTypes.object,
tokenProps: PropTypes.object,
toName: PropTypes.string,
transactionStatus: PropTypes.string,
txData: PropTypes.object,
+ unapprovedTxCount: PropTypes.number,
// Component props
action: PropTypes.string,
contentComponent: PropTypes.node,
@@ -249,6 +252,25 @@ export default class ConfirmTransactionBase extends Component {
onEdit({ txData, tokenData, tokenProps })
}
+ handleCancelAll () {
+ const {
+ cancelAllTransactions,
+ clearConfirmTransaction,
+ history,
+ showRejectTransactionsConfirmationModal,
+ unapprovedTxCount,
+ } = this.props
+
+ showRejectTransactionsConfirmationModal({
+ unapprovedTxCount,
+ async onSubmit () {
+ await cancelAllTransactions()
+ clearConfirmTransaction()
+ history.push(DEFAULT_ROUTE)
+ },
+ })
+ }
+
handleCancel () {
const { onCancel, txData, cancelTransaction, history, clearConfirmTransaction } = this.props
@@ -314,6 +336,7 @@ export default class ConfirmTransactionBase extends Component {
nonce,
assetImage,
warning,
+ unapprovedTxCount,
} = this.props
const { submitting, submitError } = this.state
@@ -337,6 +360,7 @@ export default class ConfirmTransactionBase extends Component {
dataComponent={this.renderData()}
contentComponent={contentComponent}
nonce={nonce}
+ unapprovedTxCount={unapprovedTxCount}
assetImage={assetImage}
identiconAddress={identiconAddress}
errorMessage={errorMessage || submitError}
@@ -344,6 +368,7 @@ export default class ConfirmTransactionBase extends Component {
warning={warning}
disabled={!propsValid || !valid || submitting}
onEdit={() => this.handleEdit()}
+ onCancelAll={() => this.handleCancelAll()}
onCancel={() => this.handleCancel()}
onSubmit={() => this.handleSubmit()}
/>
diff --git a/ui/app/components/pages/confirm-transaction-base/confirm-transaction-base.container.js b/ui/app/components/pages/confirm-transaction-base/confirm-transaction-base.container.js
index ae31eba17..b34067686 100644
--- a/ui/app/components/pages/confirm-transaction-base/confirm-transaction-base.container.js
+++ b/ui/app/components/pages/confirm-transaction-base/confirm-transaction-base.container.js
@@ -8,7 +8,7 @@ import {
clearConfirmTransaction,
updateGasAndCalculate,
} from '../../../ducks/confirm-transaction.duck'
-import { clearSend, cancelTx, updateAndApproveTx, showModal } from '../../../actions'
+import { clearSend, cancelTx, cancelTxs, updateAndApproveTx, showModal } from '../../../actions'
import {
INSUFFICIENT_FUNDS_ERROR_KEY,
GAS_LIMIT_TOO_LOW_ERROR_KEY,
@@ -17,7 +17,7 @@ import { getHexGasTotal } from '../../../helpers/confirm-transaction/util'
import { isBalanceSufficient } from '../../send/send.utils'
import { conversionGreaterThan } from '../../../conversion-util'
import { MIN_GAS_LIMIT_DEC } from '../../send/send.constants'
-import { addressSlicer } from '../../../util'
+import { addressSlicer, valuesFor } from '../../../util'
const casedContractMap = Object.keys(contractMap).reduce((acc, base) => {
return {
@@ -53,6 +53,8 @@ const mapStateToProps = (state, props) => {
selectedAddress,
selectedAddressTxList,
assetImages,
+ network,
+ unapprovedTxs,
} = metamask
const assetImage = assetImages[txParamsToAddress]
const { balance } = accounts[selectedAddress]
@@ -67,6 +69,12 @@ const mapStateToProps = (state, props) => {
const transaction = R.find(({ id }) => id === transactionId)(selectedAddressTxList)
const transactionStatus = transaction ? transaction.status : ''
+ const currentNetworkUnapprovedTxs = R.filter(
+ ({ metamaskNetworkId }) => metamaskNetworkId === network,
+ valuesFor(unapprovedTxs),
+ )
+ const unapprovedTxCount = currentNetworkUnapprovedTxs.length
+
return {
balance,
fromAddress,
@@ -90,6 +98,8 @@ const mapStateToProps = (state, props) => {
transactionStatus,
nonce,
assetImage,
+ unapprovedTxs,
+ unapprovedTxCount,
}
}
@@ -106,7 +116,11 @@ const mapDispatchToProps = dispatch => {
updateGasAndCalculate: ({ gasLimit, gasPrice }) => {
return dispatch(updateGasAndCalculate({ gasLimit, gasPrice }))
},
+ showRejectTransactionsConfirmationModal: ({ onSubmit, unapprovedTxCount }) => {
+ return dispatch(showModal({ name: 'REJECT_TRANSACTIONS', onSubmit, unapprovedTxCount }))
+ },
cancelTransaction: ({ id }) => dispatch(cancelTx({ id })),
+ cancelAllTransactions: (txList) => dispatch(cancelTxs(txList)),
sendTransaction: txData => dispatch(updateAndApproveTx(txData)),
}
}
@@ -156,8 +170,9 @@ const getValidateEditGas = ({ balance, conversionRate, txData }) => {
}
const mergeProps = (stateProps, dispatchProps, ownProps) => {
- const { balance, conversionRate, txData } = stateProps
+ const { balance, conversionRate, txData, unapprovedTxs } = stateProps
const {
+ cancelAllTransactions: dispatchCancelAllTransactions,
showCustomizeGasModal: dispatchShowCustomizeGasModal,
updateGasAndCalculate: dispatchUpdateGasAndCalculate,
...otherDispatchProps
@@ -174,6 +189,7 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => {
onSubmit: txData => dispatchUpdateGasAndCalculate(txData),
validate: validateEditGas,
}),
+ cancelAllTransactions: () => dispatchCancelAllTransactions(valuesFor(unapprovedTxs)),
}
}