mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
Merge pull request #5282 from MetaMask/tx-cancel-ui
Add Cancel Transaction feature. Refactor modals. Add Transaction Details modal.
This commit is contained in:
commit
5d57c7c4fd
@ -61,6 +61,12 @@
|
|||||||
"attemptingConnect": {
|
"attemptingConnect": {
|
||||||
"message": "Attempting to connect to blockchain."
|
"message": "Attempting to connect to blockchain."
|
||||||
},
|
},
|
||||||
|
"attemptToCancel": {
|
||||||
|
"message": "Attempt to Cancel?"
|
||||||
|
},
|
||||||
|
"attemptToCancelDescription": {
|
||||||
|
"message": "Attempting to cancel does not guarantee your original transaction will be cancelled. If cancelled, you are still required to pay a transaction fee to the network."
|
||||||
|
},
|
||||||
"attributions": {
|
"attributions": {
|
||||||
"message": "Attributions"
|
"message": "Attributions"
|
||||||
},
|
},
|
||||||
@ -116,6 +122,12 @@
|
|||||||
"cancel": {
|
"cancel": {
|
||||||
"message": "Cancel"
|
"message": "Cancel"
|
||||||
},
|
},
|
||||||
|
"cancelAttempt": {
|
||||||
|
"message": "Cancel Attempt"
|
||||||
|
},
|
||||||
|
"cancellationGasFee": {
|
||||||
|
"message": "Cancellation Gas Fee"
|
||||||
|
},
|
||||||
"classicInterface": {
|
"classicInterface": {
|
||||||
"message": "Use classic interface"
|
"message": "Use classic interface"
|
||||||
},
|
},
|
||||||
@ -1109,6 +1121,9 @@
|
|||||||
"transactionCreated": {
|
"transactionCreated": {
|
||||||
"message": "Transaction created with a value of $1 on $2."
|
"message": "Transaction created with a value of $1 on $2."
|
||||||
},
|
},
|
||||||
|
"transactionWithNonce": {
|
||||||
|
"message": "Transaction $1"
|
||||||
|
},
|
||||||
"transactionDropped": {
|
"transactionDropped": {
|
||||||
"message": "Transaction dropped on $2."
|
"message": "Transaction dropped on $2."
|
||||||
},
|
},
|
||||||
@ -1240,6 +1255,9 @@
|
|||||||
"whatsThis": {
|
"whatsThis": {
|
||||||
"message": "What's this?"
|
"message": "What's this?"
|
||||||
},
|
},
|
||||||
|
"yesLetsTry": {
|
||||||
|
"message": "Yes, let's try"
|
||||||
|
},
|
||||||
"youNeedToAllowCameraAccess": {
|
"youNeedToAllowCameraAccess": {
|
||||||
"message": "You need to allow camera access to use this feature."
|
"message": "You need to allow camera access to use this feature."
|
||||||
},
|
},
|
||||||
|
@ -18,7 +18,7 @@ const {
|
|||||||
TRANSACTION_STATUS_APPROVED,
|
TRANSACTION_STATUS_APPROVED,
|
||||||
} = require('./enums')
|
} = require('./enums')
|
||||||
|
|
||||||
const { hexToBn, bnToHex } = require('../../lib/util')
|
const { hexToBn, bnToHex, BnMultiplyByFraction } = require('../../lib/util')
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Transaction Controller is an aggregate of sub-controllers and trackers
|
Transaction Controller is an aggregate of sub-controllers and trackers
|
||||||
@ -244,7 +244,8 @@ class TransactionController extends EventEmitter {
|
|||||||
const originalTxMeta = this.txStateManager.getTx(originalTxId)
|
const originalTxMeta = this.txStateManager.getTx(originalTxId)
|
||||||
const { txParams } = originalTxMeta
|
const { txParams } = originalTxMeta
|
||||||
const { gasPrice: lastGasPrice, from, nonce } = txParams
|
const { gasPrice: lastGasPrice, from, nonce } = txParams
|
||||||
const newGasPrice = customGasPrice || bnToHex(hexToBn(lastGasPrice).mul(1.1))
|
|
||||||
|
const newGasPrice = customGasPrice || bnToHex(BnMultiplyByFraction(hexToBn(lastGasPrice), 11, 10))
|
||||||
const newTxMeta = this.txStateManager.generateTxMeta({
|
const newTxMeta = this.txStateManager.generateTxMeta({
|
||||||
txParams: {
|
txParams: {
|
||||||
from,
|
from,
|
||||||
|
@ -34,7 +34,7 @@ async function runTxListItemsTest (assert, done) {
|
|||||||
|
|
||||||
const retryTxGrid = await findAsync($(txListItems[1]), '.transaction-list-item__grid')
|
const retryTxGrid = await findAsync($(txListItems[1]), '.transaction-list-item__grid')
|
||||||
retryTxGrid[0].click()
|
retryTxGrid[0].click()
|
||||||
const retryTxDetails = await findAsync($(txListItems[1]), '.transaction-list-item-details')
|
const retryTxDetails = await findAsync($, '.transaction-list-item-details')
|
||||||
const headerButtons = await findAsync($(retryTxDetails[0]), '.transaction-list-item-details__header-button')
|
const headerButtons = await findAsync($(retryTxDetails[0]), '.transaction-list-item-details__header-button')
|
||||||
assert.equal(headerButtons[0].textContent, 'speed up')
|
assert.equal(headerButtons[0].textContent, 'speed up')
|
||||||
|
|
||||||
|
@ -2,11 +2,8 @@ import React, { Component } from 'react'
|
|||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import classnames from 'classnames'
|
import classnames from 'classnames'
|
||||||
import { Tabs, Tab } from '../../tabs'
|
import { Tabs, Tab } from '../../tabs'
|
||||||
import {
|
import { ConfirmPageContainerSummary, ConfirmPageContainerWarning } from './'
|
||||||
ConfirmPageContainerSummary,
|
import ErrorMessage from '../../error-message'
|
||||||
ConfirmPageContainerError,
|
|
||||||
ConfirmPageContainerWarning,
|
|
||||||
} from './'
|
|
||||||
|
|
||||||
export default class ConfirmPageContainerContent extends Component {
|
export default class ConfirmPageContainerContent extends Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
@ -95,7 +92,7 @@ export default class ConfirmPageContainerContent extends Component {
|
|||||||
{
|
{
|
||||||
(errorKey || errorMessage) && (
|
(errorKey || errorMessage) && (
|
||||||
<div className="confirm-page-container-content__error-container">
|
<div className="confirm-page-container-content__error-container">
|
||||||
<ConfirmPageContainerError
|
<ErrorMessage
|
||||||
errorMessage={errorMessage}
|
errorMessage={errorMessage}
|
||||||
errorKey={errorKey}
|
errorKey={errorKey}
|
||||||
/>
|
/>
|
||||||
|
@ -1 +0,0 @@
|
|||||||
export { default } from './confirm-page-container-error.component'
|
|
@ -1,4 +1,3 @@
|
|||||||
export { default } from './confirm-page-container-content.component'
|
export { default } from './confirm-page-container-content.component'
|
||||||
export { default as ConfirmPageContainerSummary } from './confirm-page-container-summary'
|
export { default as ConfirmPageContainerSummary } from './confirm-page-container-summary'
|
||||||
export { default as ConfirmPageContainerError } from './confirm-page-container-error'
|
|
||||||
export { default as ConfirmPageContainerWarning } from './confirm-page-container-warning'
|
export { default as ConfirmPageContainerWarning } from './confirm-page-container-warning'
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
@import './confirm-page-container-error/index';
|
|
||||||
|
|
||||||
@import './confirm-page-container-warning/index';
|
@import './confirm-page-container-warning/index';
|
||||||
|
|
||||||
@import './confirm-page-container-summary/index';
|
@import './confirm-page-container-summary/index';
|
||||||
|
@ -1,30 +1,30 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
|
|
||||||
const ConfirmPageContainerError = (props, context) => {
|
const ErrorMessage = (props, context) => {
|
||||||
const { errorMessage, errorKey } = props
|
const { errorMessage, errorKey } = props
|
||||||
const error = errorKey ? context.t(errorKey) : errorMessage
|
const error = errorKey ? context.t(errorKey) : errorMessage
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="confirm-page-container-error">
|
<div className="error-message">
|
||||||
<img
|
<img
|
||||||
src="/images/alert-red.svg"
|
src="/images/alert-red.svg"
|
||||||
className="confirm-page-container-error__icon"
|
className="error-message__icon"
|
||||||
/>
|
/>
|
||||||
<div className="confirm-page-container-error__text">
|
<div className="error-message__text">
|
||||||
{ `ALERT: ${error}` }
|
{ `ALERT: ${error}` }
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
ConfirmPageContainerError.propTypes = {
|
ErrorMessage.propTypes = {
|
||||||
errorMessage: PropTypes.string,
|
errorMessage: PropTypes.string,
|
||||||
errorKey: PropTypes.string,
|
errorKey: PropTypes.string,
|
||||||
}
|
}
|
||||||
|
|
||||||
ConfirmPageContainerError.contextTypes = {
|
ErrorMessage.contextTypes = {
|
||||||
t: PropTypes.func,
|
t: PropTypes.func,
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ConfirmPageContainerError
|
export default ErrorMessage
|
1
ui/app/components/error-message/index.js
Normal file
1
ui/app/components/error-message/index.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default } from './error-message.component'
|
@ -1,4 +1,4 @@
|
|||||||
.confirm-page-container-error {
|
.error-message {
|
||||||
min-height: 32px;
|
min-height: 32px;
|
||||||
border: 1px solid $monzo;
|
border: 1px solid $monzo;
|
||||||
color: $monzo;
|
color: $monzo;
|
@ -0,0 +1,36 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import assert from 'assert'
|
||||||
|
import { shallow } from 'enzyme'
|
||||||
|
import ErrorMessage from '../error-message.component'
|
||||||
|
|
||||||
|
describe('ErrorMessage Component', () => {
|
||||||
|
const t = key => `translate ${key}`
|
||||||
|
|
||||||
|
it('should render a message from props.errorMessage', () => {
|
||||||
|
const wrapper = shallow(
|
||||||
|
<ErrorMessage
|
||||||
|
errorMessage="This is an error."
|
||||||
|
/>,
|
||||||
|
{ context: { t }}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.ok(wrapper)
|
||||||
|
assert.equal(wrapper.find('.error-message').length, 1)
|
||||||
|
assert.equal(wrapper.find('.error-message__icon').length, 1)
|
||||||
|
assert.equal(wrapper.find('.error-message__text').text(), 'ALERT: This is an error.')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should render a message translated from props.errorKey', () => {
|
||||||
|
const wrapper = shallow(
|
||||||
|
<ErrorMessage
|
||||||
|
errorKey="testKey"
|
||||||
|
/>,
|
||||||
|
{ context: { t }}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.ok(wrapper)
|
||||||
|
assert.equal(wrapper.find('.error-message').length, 1)
|
||||||
|
assert.equal(wrapper.find('.error-message__icon').length, 1)
|
||||||
|
assert.equal(wrapper.find('.error-message__text').text(), 'ALERT: translate testKey')
|
||||||
|
})
|
||||||
|
})
|
@ -6,12 +6,16 @@
|
|||||||
|
|
||||||
@import './confirm-page-container/index';
|
@import './confirm-page-container/index';
|
||||||
|
|
||||||
|
@import './error-message/index';
|
||||||
|
|
||||||
@import './export-text-container/index';
|
@import './export-text-container/index';
|
||||||
|
|
||||||
@import './info-box/index';
|
@import './info-box/index';
|
||||||
|
|
||||||
@import './menu-bar/index';
|
@import './menu-bar/index';
|
||||||
|
|
||||||
|
@import './modal/index';
|
||||||
|
|
||||||
@import './modals/index';
|
@import './modals/index';
|
||||||
|
|
||||||
@import './network-display/index';
|
@import './network-display/index';
|
||||||
|
2
ui/app/components/modal/index.js
Normal file
2
ui/app/components/modal/index.js
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export { default } from './modal.component'
|
||||||
|
export { default as ModalContent } from './modal-content'
|
62
ui/app/components/modal/index.scss
Normal file
62
ui/app/components/modal/index.scss
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
@import './modal-content/index';
|
||||||
|
|
||||||
|
.modal-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: #fff;
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column;
|
||||||
|
border-radius: 8px;
|
||||||
|
|
||||||
|
@media screen and (max-width: 575px) {
|
||||||
|
max-height: 450px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__content {
|
||||||
|
overflow-y: auto;
|
||||||
|
flex: 1;
|
||||||
|
padding: 16px 32px;
|
||||||
|
|
||||||
|
@media screen and (max-width: 575px) {
|
||||||
|
justify-content: center;
|
||||||
|
padding: 28px 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__header {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
padding: 12px;
|
||||||
|
justify-content: center;
|
||||||
|
border-bottom: 1px solid #d2d8dd;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__header-close::after {
|
||||||
|
content: '\00D7';
|
||||||
|
font-size: 40px;
|
||||||
|
color: $dusty-gray;
|
||||||
|
position: absolute;
|
||||||
|
top: -5px;
|
||||||
|
right: 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__footer {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: row;
|
||||||
|
justify-content: center;
|
||||||
|
border-top: 1px solid #d2d8dd;
|
||||||
|
padding: 16px;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
|
||||||
|
&-button {
|
||||||
|
min-width: 0;
|
||||||
|
margin-right: 16px;
|
||||||
|
|
||||||
|
&:last-of-type {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1
ui/app/components/modal/modal-content/index.js
Normal file
1
ui/app/components/modal/modal-content/index.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default } from './modal-content.component'
|
19
ui/app/components/modal/modal-content/index.scss
Normal file
19
ui/app/components/modal/modal-content/index.scss
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
.modal-content {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
padding: 16px 0;
|
||||||
|
|
||||||
|
&__title {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: 500;
|
||||||
|
padding: 16px 0;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__description {
|
||||||
|
text-align: center;
|
||||||
|
font-size: .875rem;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
import React, { PureComponent } from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
|
||||||
|
export default class ModalContent extends PureComponent {
|
||||||
|
static propTypes = {
|
||||||
|
title: PropTypes.string,
|
||||||
|
description: PropTypes.string,
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const { title, description } = this.props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="modal-content">
|
||||||
|
{
|
||||||
|
title && (
|
||||||
|
<div className="modal-content__title">
|
||||||
|
{ title }
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
description && (
|
||||||
|
<div className="modal-content__description">
|
||||||
|
{ description }
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import assert from 'assert'
|
||||||
|
import { shallow } from 'enzyme'
|
||||||
|
import ModalContent from '../modal-content.component'
|
||||||
|
|
||||||
|
describe('ModalContent Component', () => {
|
||||||
|
it('should render a title', () => {
|
||||||
|
const wrapper = shallow(
|
||||||
|
<ModalContent
|
||||||
|
title="Modal Title"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.equal(wrapper.find('.modal-content__title').length, 1)
|
||||||
|
assert.equal(wrapper.find('.modal-content__title').text(), 'Modal Title')
|
||||||
|
assert.equal(wrapper.find('.modal-content__description').length, 0)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should render a description', () => {
|
||||||
|
const wrapper = shallow(
|
||||||
|
<ModalContent
|
||||||
|
description="Modal Description"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.equal(wrapper.find('.modal-content__title').length, 0)
|
||||||
|
assert.equal(wrapper.find('.modal-content__description').length, 1)
|
||||||
|
assert.equal(wrapper.find('.modal-content__description').text(), 'Modal Description')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should render both a title and a description', () => {
|
||||||
|
const wrapper = shallow(
|
||||||
|
<ModalContent
|
||||||
|
title="Modal Title"
|
||||||
|
description="Modal Description"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.equal(wrapper.find('.modal-content__title').length, 1)
|
||||||
|
assert.equal(wrapper.find('.modal-content__title').text(), 'Modal Title')
|
||||||
|
assert.equal(wrapper.find('.modal-content__description').length, 1)
|
||||||
|
assert.equal(wrapper.find('.modal-content__description').text(), 'Modal Description')
|
||||||
|
})
|
||||||
|
})
|
80
ui/app/components/modal/modal.component.js
Normal file
80
ui/app/components/modal/modal.component.js
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
import React, { PureComponent } from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import Button from '../button'
|
||||||
|
|
||||||
|
export default class Modal extends PureComponent {
|
||||||
|
static propTypes = {
|
||||||
|
children: PropTypes.node,
|
||||||
|
// Header text
|
||||||
|
headerText: PropTypes.string,
|
||||||
|
onClose: PropTypes.func,
|
||||||
|
// Submit button (right button)
|
||||||
|
onSubmit: PropTypes.func,
|
||||||
|
submitType: PropTypes.string,
|
||||||
|
submitText: PropTypes.string,
|
||||||
|
// Cancel button (left button)
|
||||||
|
onCancel: PropTypes.func,
|
||||||
|
cancelType: PropTypes.string,
|
||||||
|
cancelText: PropTypes.string,
|
||||||
|
}
|
||||||
|
|
||||||
|
static defaultProps = {
|
||||||
|
submitType: 'primary',
|
||||||
|
cancelType: 'default',
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const {
|
||||||
|
children,
|
||||||
|
headerText,
|
||||||
|
onClose,
|
||||||
|
onSubmit,
|
||||||
|
submitType,
|
||||||
|
submitText,
|
||||||
|
onCancel,
|
||||||
|
cancelType,
|
||||||
|
cancelText,
|
||||||
|
} = this.props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="modal-container">
|
||||||
|
{
|
||||||
|
headerText && (
|
||||||
|
<div className="modal-container__header">
|
||||||
|
<div className="modal-container__header-text">
|
||||||
|
{ headerText }
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="modal-container__header-close"
|
||||||
|
onClick={onClose}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
<div className="modal-container__content">
|
||||||
|
{ children }
|
||||||
|
</div>
|
||||||
|
<div className="modal-container__footer">
|
||||||
|
{
|
||||||
|
onCancel && (
|
||||||
|
<Button
|
||||||
|
type={cancelType}
|
||||||
|
onClick={onCancel}
|
||||||
|
className="modal-container__footer-button"
|
||||||
|
>
|
||||||
|
{ cancelText }
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
<Button
|
||||||
|
type={submitType}
|
||||||
|
onClick={onSubmit}
|
||||||
|
className="modal-container__footer-button"
|
||||||
|
>
|
||||||
|
{ submitText }
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
103
ui/app/components/modal/tests/modal.component.test.js
Normal file
103
ui/app/components/modal/tests/modal.component.test.js
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import assert from 'assert'
|
||||||
|
import { shallow } from 'enzyme'
|
||||||
|
import sinon from 'sinon'
|
||||||
|
import Modal from '../modal.component'
|
||||||
|
import Button from '../../button'
|
||||||
|
|
||||||
|
describe('Modal Component', () => {
|
||||||
|
it('should render a modal with a submit button', () => {
|
||||||
|
const wrapper = shallow(<Modal />)
|
||||||
|
|
||||||
|
assert.equal(wrapper.find('.modal-container').length, 1)
|
||||||
|
const buttons = wrapper.find(Button)
|
||||||
|
assert.equal(buttons.length, 1)
|
||||||
|
assert.equal(buttons.at(0).props().type, 'primary')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should render a modal with a cancel and a submit button', () => {
|
||||||
|
const handleCancel = sinon.spy()
|
||||||
|
const handleSubmit = sinon.spy()
|
||||||
|
const wrapper = shallow(
|
||||||
|
<Modal
|
||||||
|
onCancel={handleCancel}
|
||||||
|
cancelText="Cancel"
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
submitText="Submit"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
|
||||||
|
const buttons = wrapper.find(Button)
|
||||||
|
assert.equal(buttons.length, 2)
|
||||||
|
const cancelButton = buttons.at(0)
|
||||||
|
const submitButton = buttons.at(1)
|
||||||
|
|
||||||
|
assert.equal(cancelButton.props().type, 'default')
|
||||||
|
assert.equal(cancelButton.props().children, 'Cancel')
|
||||||
|
assert.equal(handleCancel.callCount, 0)
|
||||||
|
cancelButton.simulate('click')
|
||||||
|
assert.equal(handleCancel.callCount, 1)
|
||||||
|
|
||||||
|
assert.equal(submitButton.props().type, 'primary')
|
||||||
|
assert.equal(submitButton.props().children, 'Submit')
|
||||||
|
assert.equal(handleSubmit.callCount, 0)
|
||||||
|
submitButton.simulate('click')
|
||||||
|
assert.equal(handleSubmit.callCount, 1)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should render a modal with different button types', () => {
|
||||||
|
const wrapper = shallow(
|
||||||
|
<Modal
|
||||||
|
onCancel={() => {}}
|
||||||
|
cancelText="Cancel"
|
||||||
|
cancelType="secondary"
|
||||||
|
onSubmit={() => {}}
|
||||||
|
submitText="Submit"
|
||||||
|
submitType="confirm"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
|
||||||
|
const buttons = wrapper.find(Button)
|
||||||
|
assert.equal(buttons.length, 2)
|
||||||
|
assert.equal(buttons.at(0).props().type, 'secondary')
|
||||||
|
assert.equal(buttons.at(1).props().type, 'confirm')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should render a modal with children', () => {
|
||||||
|
const wrapper = shallow(
|
||||||
|
<Modal
|
||||||
|
onCancel={() => {}}
|
||||||
|
cancelText="Cancel"
|
||||||
|
onSubmit={() => {}}
|
||||||
|
submitText="Submit"
|
||||||
|
>
|
||||||
|
<div className="test-child" />
|
||||||
|
</Modal>
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.ok(wrapper.find('.test-class'))
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should render a modal with a header', () => {
|
||||||
|
const handleCancel = sinon.spy()
|
||||||
|
const handleSubmit = sinon.spy()
|
||||||
|
const wrapper = shallow(
|
||||||
|
<Modal
|
||||||
|
onCancel={handleCancel}
|
||||||
|
cancelText="Cancel"
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
submitText="Submit"
|
||||||
|
headerText="My Header"
|
||||||
|
onClose={handleCancel}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.ok(wrapper.find('.modal-container__header'))
|
||||||
|
assert.equal(wrapper.find('.modal-container__header-text').text(), 'My Header')
|
||||||
|
assert.equal(handleCancel.callCount, 0)
|
||||||
|
assert.equal(handleSubmit.callCount, 0)
|
||||||
|
wrapper.find('.modal-container__header-close').simulate('click')
|
||||||
|
assert.equal(handleCancel.callCount, 1)
|
||||||
|
assert.equal(handleSubmit.callCount, 0)
|
||||||
|
})
|
||||||
|
})
|
@ -0,0 +1,29 @@
|
|||||||
|
import React, { PureComponent } from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import CurrencyDisplay from '../../../currency-display'
|
||||||
|
import { ETH } from '../../../../constants/common'
|
||||||
|
|
||||||
|
export default class CancelTransaction extends PureComponent {
|
||||||
|
static propTypes = {
|
||||||
|
value: PropTypes.string,
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const { value } = this.props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="cancel-transaction-gas-fee">
|
||||||
|
<CurrencyDisplay
|
||||||
|
className="cancel-transaction-gas-fee__eth"
|
||||||
|
currency={ETH}
|
||||||
|
value={value}
|
||||||
|
numberOfDecimals={6}
|
||||||
|
/>
|
||||||
|
<CurrencyDisplay
|
||||||
|
className="cancel-transaction-gas-fee__fiat"
|
||||||
|
value={value}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
export { default } from './cancel-transaction-gas-fee.component'
|
@ -0,0 +1,17 @@
|
|||||||
|
.cancel-transaction-gas-fee {
|
||||||
|
background: #F1F4F9;
|
||||||
|
padding: 16px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
padding: 12px;
|
||||||
|
|
||||||
|
&__eth {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__fiat {
|
||||||
|
font-size: .75rem;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import assert from 'assert'
|
||||||
|
import { shallow } from 'enzyme'
|
||||||
|
import CancelTransactionGasFee from '../cancel-transaction-gas-fee.component'
|
||||||
|
import CurrencyDisplay from '../../../../currency-display'
|
||||||
|
|
||||||
|
describe('CancelTransactionGasFee Component', () => {
|
||||||
|
it('should render', () => {
|
||||||
|
const wrapper = shallow(
|
||||||
|
<CancelTransactionGasFee
|
||||||
|
value="0x3b9aca00"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.ok(wrapper)
|
||||||
|
assert.equal(wrapper.find(CurrencyDisplay).length, 2)
|
||||||
|
const ethDisplay = wrapper.find(CurrencyDisplay).at(0)
|
||||||
|
const fiatDisplay = wrapper.find(CurrencyDisplay).at(1)
|
||||||
|
|
||||||
|
assert.equal(ethDisplay.props().value, '0x3b9aca00')
|
||||||
|
assert.equal(ethDisplay.props().currency, 'ETH')
|
||||||
|
assert.equal(ethDisplay.props().className, 'cancel-transaction-gas-fee__eth')
|
||||||
|
|
||||||
|
assert.equal(fiatDisplay.props().value, '0x3b9aca00')
|
||||||
|
assert.equal(fiatDisplay.props().className, 'cancel-transaction-gas-fee__fiat')
|
||||||
|
})
|
||||||
|
})
|
@ -0,0 +1,68 @@
|
|||||||
|
import React, { PureComponent } from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import Modal from '../../modal'
|
||||||
|
import CancelTransactionGasFee from './cancel-transaction-gas-fee'
|
||||||
|
import { SUBMITTED_STATUS } from '../../../constants/transactions'
|
||||||
|
|
||||||
|
export default class CancelTransaction extends PureComponent {
|
||||||
|
static contextTypes = {
|
||||||
|
t: PropTypes.func,
|
||||||
|
}
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
createCancelTransaction: PropTypes.func,
|
||||||
|
hideModal: PropTypes.func,
|
||||||
|
showTransactionConfirmedModal: PropTypes.func,
|
||||||
|
transactionStatus: PropTypes.string,
|
||||||
|
newGasFee: PropTypes.string,
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate () {
|
||||||
|
const { transactionStatus, showTransactionConfirmedModal } = this.props
|
||||||
|
|
||||||
|
if (transactionStatus !== SUBMITTED_STATUS) {
|
||||||
|
showTransactionConfirmedModal()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSubmit = async () => {
|
||||||
|
const { createCancelTransaction, hideModal } = this.props
|
||||||
|
|
||||||
|
await createCancelTransaction()
|
||||||
|
hideModal()
|
||||||
|
}
|
||||||
|
|
||||||
|
handleCancel = () => {
|
||||||
|
this.props.hideModal()
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const { t } = this.context
|
||||||
|
const { newGasFee } = this.props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
headerText={t('attemptToCancel')}
|
||||||
|
onClose={this.handleCancel}
|
||||||
|
onSubmit={this.handleSubmit}
|
||||||
|
onCancel={this.handleCancel}
|
||||||
|
submitText={t('yesLetsTry')}
|
||||||
|
cancelText={t('nevermind')}
|
||||||
|
submitType="secondary"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<div className="cancel-transaction__title">
|
||||||
|
{ t('cancellationGasFee') }
|
||||||
|
</div>
|
||||||
|
<div className="cancel-transaction__cancel-transaction-gas-fee-container">
|
||||||
|
<CancelTransactionGasFee value={newGasFee} />
|
||||||
|
</div>
|
||||||
|
<div className="cancel-transaction__description">
|
||||||
|
{ t('attemptToCancelDescription') }
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,62 @@
|
|||||||
|
import { connect } from 'react-redux'
|
||||||
|
import { compose } from 'recompose'
|
||||||
|
import ethUtil from 'ethereumjs-util'
|
||||||
|
import { multiplyCurrencies } from '../../../conversion-util'
|
||||||
|
import withModalProps from '../../../higher-order-components/with-modal-props'
|
||||||
|
import CancelTransaction from './cancel-transaction.component'
|
||||||
|
import { showModal, createCancelTransaction } from '../../../actions'
|
||||||
|
import { getHexGasTotal } from '../../../helpers/confirm-transaction/util'
|
||||||
|
|
||||||
|
const mapStateToProps = (state, ownProps) => {
|
||||||
|
const { metamask } = state
|
||||||
|
const { transactionId, originalGasPrice } = ownProps
|
||||||
|
const { selectedAddressTxList } = metamask
|
||||||
|
const transaction = selectedAddressTxList.find(({ id }) => id === transactionId)
|
||||||
|
const transactionStatus = transaction ? transaction.status : ''
|
||||||
|
|
||||||
|
const defaultNewGasPrice = ethUtil.addHexPrefix(
|
||||||
|
multiplyCurrencies(originalGasPrice, 1.1, {
|
||||||
|
toNumericBase: 'hex',
|
||||||
|
multiplicandBase: 16,
|
||||||
|
multiplierBase: 10,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
const newGasFee = getHexGasTotal({ gasPrice: defaultNewGasPrice, gasLimit: '0x5208' })
|
||||||
|
|
||||||
|
return {
|
||||||
|
transactionId,
|
||||||
|
transactionStatus,
|
||||||
|
originalGasPrice,
|
||||||
|
newGasFee,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapDispatchToProps = dispatch => {
|
||||||
|
return {
|
||||||
|
createCancelTransaction: txId => dispatch(createCancelTransaction(txId)),
|
||||||
|
showTransactionConfirmedModal: () => dispatch(showModal({ name: 'TRANSACTION_CONFIRMED' })),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const mergeProps = (stateProps, dispatchProps, ownProps) => {
|
||||||
|
const { transactionId, ...restStateProps } = stateProps
|
||||||
|
const {
|
||||||
|
createCancelTransaction: dispatchCreateCancelTransaction,
|
||||||
|
...restDispatchProps
|
||||||
|
} = dispatchProps
|
||||||
|
|
||||||
|
return {
|
||||||
|
...restStateProps,
|
||||||
|
...restDispatchProps,
|
||||||
|
...ownProps,
|
||||||
|
createCancelTransaction: newGasPrice => {
|
||||||
|
return dispatchCreateCancelTransaction(transactionId, newGasPrice)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default compose(
|
||||||
|
withModalProps,
|
||||||
|
connect(mapStateToProps, mapDispatchToProps, mergeProps),
|
||||||
|
)(CancelTransaction)
|
1
ui/app/components/modals/cancel-transaction/index.js
Normal file
1
ui/app/components/modals/cancel-transaction/index.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default } from './cancel-transaction.container'
|
18
ui/app/components/modals/cancel-transaction/index.scss
Normal file
18
ui/app/components/modals/cancel-transaction/index.scss
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
@import './cancel-transaction-gas-fee/index';
|
||||||
|
|
||||||
|
.cancel-transaction {
|
||||||
|
&__title {
|
||||||
|
font-weight: 500;
|
||||||
|
padding-bottom: 16px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__description {
|
||||||
|
text-align: center;
|
||||||
|
font-size: .875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__cancel-transaction-gas-fee-container {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,56 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import assert from 'assert'
|
||||||
|
import { shallow } from 'enzyme'
|
||||||
|
import sinon from 'sinon'
|
||||||
|
import CancelTransaction from '../cancel-transaction.component'
|
||||||
|
import CancelTransactionGasFee from '../cancel-transaction-gas-fee'
|
||||||
|
import Modal from '../../../modal'
|
||||||
|
|
||||||
|
describe('CancelTransaction Component', () => {
|
||||||
|
const t = key => key
|
||||||
|
|
||||||
|
it('should render a CancelTransaction modal', () => {
|
||||||
|
const wrapper = shallow(
|
||||||
|
<CancelTransaction
|
||||||
|
newGasFee="0x1319718a5000"
|
||||||
|
/>,
|
||||||
|
{ context: { t }}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.ok(wrapper)
|
||||||
|
assert.equal(wrapper.find(Modal).length, 1)
|
||||||
|
assert.equal(wrapper.find(CancelTransactionGasFee).length, 1)
|
||||||
|
assert.equal(wrapper.find(CancelTransactionGasFee).props().value, '0x1319718a5000')
|
||||||
|
assert.equal(wrapper.find('.cancel-transaction__title').text(), 'cancellationGasFee')
|
||||||
|
assert.equal(wrapper.find('.cancel-transaction__description').text(), 'attemptToCancelDescription')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should pass the correct props to the Modal component', async () => {
|
||||||
|
const createCancelTransactionSpy = sinon.stub().callsFake(() => Promise.resolve())
|
||||||
|
const hideModalSpy = sinon.spy()
|
||||||
|
|
||||||
|
const wrapper = shallow(
|
||||||
|
<CancelTransaction
|
||||||
|
defaultNewGasPrice="0x3b9aca00"
|
||||||
|
createCancelTransaction={createCancelTransactionSpy}
|
||||||
|
hideModal={hideModalSpy}
|
||||||
|
/>,
|
||||||
|
{ context: { t }}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.equal(wrapper.find(Modal).length, 1)
|
||||||
|
const modalProps = wrapper.find(Modal).props()
|
||||||
|
|
||||||
|
assert.equal(modalProps.headerText, 'attemptToCancel')
|
||||||
|
assert.equal(modalProps.submitText, 'yesLetsTry')
|
||||||
|
assert.equal(modalProps.cancelText, 'nevermind')
|
||||||
|
|
||||||
|
assert.equal(createCancelTransactionSpy.callCount, 0)
|
||||||
|
assert.equal(hideModalSpy.callCount, 0)
|
||||||
|
await modalProps.onSubmit()
|
||||||
|
assert.equal(createCancelTransactionSpy.callCount, 1)
|
||||||
|
assert.equal(hideModalSpy.callCount, 1)
|
||||||
|
modalProps.onCancel()
|
||||||
|
assert.equal(hideModalSpy.callCount, 2)
|
||||||
|
})
|
||||||
|
})
|
@ -1,11 +1,11 @@
|
|||||||
import React, { Component } from 'react'
|
import React, { Component } from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import Button from '../../button'
|
import Modal from '../../modal'
|
||||||
import { addressSummary } from '../../../util'
|
import { addressSummary } from '../../../util'
|
||||||
import Identicon from '../../identicon'
|
import Identicon from '../../identicon'
|
||||||
import genAccountLink from '../../../../lib/account-link'
|
import genAccountLink from '../../../../lib/account-link'
|
||||||
|
|
||||||
class ConfirmRemoveAccount extends Component {
|
export default class ConfirmRemoveAccount extends Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
hideModal: PropTypes.func.isRequired,
|
hideModal: PropTypes.func.isRequired,
|
||||||
removeAccount: PropTypes.func.isRequired,
|
removeAccount: PropTypes.func.isRequired,
|
||||||
@ -17,30 +17,34 @@ class ConfirmRemoveAccount extends Component {
|
|||||||
t: PropTypes.func,
|
t: PropTypes.func,
|
||||||
}
|
}
|
||||||
|
|
||||||
handleRemove () {
|
handleRemove = () => {
|
||||||
this.props.removeAccount(this.props.identity.address)
|
this.props.removeAccount(this.props.identity.address)
|
||||||
.then(() => this.props.hideModal())
|
.then(() => this.props.hideModal())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleCancel = () => {
|
||||||
|
this.props.hideModal()
|
||||||
|
}
|
||||||
|
|
||||||
renderSelectedAccount () {
|
renderSelectedAccount () {
|
||||||
const { identity } = this.props
|
const { identity } = this.props
|
||||||
return (
|
return (
|
||||||
<div className="modal-container__account">
|
<div className="confirm-remove-account__account">
|
||||||
<div className="modal-container__account__identicon">
|
<div className="confirm-remove-account__account__identicon">
|
||||||
<Identicon
|
<Identicon
|
||||||
address={identity.address}
|
address={identity.address}
|
||||||
diameter={32}
|
diameter={32}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="modal-container__account__name">
|
<div className="confirm-remove-account__account__name">
|
||||||
<span className="modal-container__account__label">Name</span>
|
<span className="confirm-remove-account__account__label">Name</span>
|
||||||
<span className="account_value">{identity.name}</span>
|
<span className="account_value">{identity.name}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="modal-container__account__address">
|
<div className="confirm-remove-account__account__address">
|
||||||
<span className="modal-container__account__label">Public Address</span>
|
<span className="confirm-remove-account__account__label">Public Address</span>
|
||||||
<span className="account_value">{ addressSummary(identity.address, 4, 4) }</span>
|
<span className="account_value">{ addressSummary(identity.address, 4, 4) }</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="modal-container__account__link">
|
<div className="confirm-remove-account__account__link">
|
||||||
<a
|
<a
|
||||||
className=""
|
className=""
|
||||||
href={genAccountLink(identity.address, this.props.network)}
|
href={genAccountLink(identity.address, this.props.network)}
|
||||||
@ -58,36 +62,28 @@ class ConfirmRemoveAccount extends Component {
|
|||||||
const { t } = this.context
|
const { t } = this.context
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="modal-container">
|
<Modal
|
||||||
<div className="modal-container__content">
|
headerText={`${t('removeAccount')}?`}
|
||||||
<div className="modal-container__title">
|
onClose={this.handleCancel}
|
||||||
{ `${t('removeAccount')}` }?
|
onSubmit={this.handleRemove}
|
||||||
</div>
|
onCancel={this.handleCancel}
|
||||||
|
submitText={t('remove')}
|
||||||
|
cancelText={t('nevermind')}
|
||||||
|
submitType="secondary"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
{ this.renderSelectedAccount() }
|
{ this.renderSelectedAccount() }
|
||||||
<div className="modal-container__description">
|
<div className="confirm-remove-account__description">
|
||||||
{ t('removeAccountDescription') }
|
{ t('removeAccountDescription') }
|
||||||
<a className="modal-container__link" rel="noopener noreferrer" target="_blank" href="https://consensys.zendesk.com/hc/en-us/articles/360004180111-What-are-imported-accounts-New-UI-">{ t('learnMore') }</a>
|
<a
|
||||||
</div>
|
className="confirm-remove-account__link"
|
||||||
</div>
|
rel="noopener noreferrer"
|
||||||
<div className="modal-container__footer">
|
target="_blank" href="https://consensys.zendesk.com/hc/en-us/articles/360004180111-What-are-imported-accounts-New-UI-">
|
||||||
<Button
|
{ t('learnMore') }
|
||||||
type="default"
|
</a>
|
||||||
className="modal-container__footer-button"
|
|
||||||
onClick={() => this.props.hideModal()}
|
|
||||||
>
|
|
||||||
{ t('nevermind') }
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
type="secondary"
|
|
||||||
className="modal-container__footer-button"
|
|
||||||
onClick={() => this.handleRemove()}
|
|
||||||
>
|
|
||||||
{ t('remove') }
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</Modal>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ConfirmRemoveAccount
|
|
||||||
|
@ -1,20 +1,22 @@
|
|||||||
import { connect } from 'react-redux'
|
import { connect } from 'react-redux'
|
||||||
|
import { compose } from 'recompose'
|
||||||
import ConfirmRemoveAccount from './confirm-remove-account.component'
|
import ConfirmRemoveAccount from './confirm-remove-account.component'
|
||||||
|
import withModalProps from '../../../higher-order-components/with-modal-props'
|
||||||
const { hideModal, removeAccount } = require('../../../actions')
|
import { removeAccount } from '../../../actions'
|
||||||
|
|
||||||
const mapStateToProps = state => {
|
const mapStateToProps = state => {
|
||||||
return {
|
return {
|
||||||
identity: state.appState.modal.modalState.props.identity,
|
|
||||||
network: state.metamask.network,
|
network: state.metamask.network,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapDispatchToProps = dispatch => {
|
const mapDispatchToProps = dispatch => {
|
||||||
return {
|
return {
|
||||||
hideModal: () => dispatch(hideModal()),
|
|
||||||
removeAccount: (address) => dispatch(removeAccount(address)),
|
removeAccount: (address) => dispatch(removeAccount(address)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(ConfirmRemoveAccount)
|
export default compose(
|
||||||
|
withModalProps,
|
||||||
|
connect(mapStateToProps, mapDispatchToProps)
|
||||||
|
)(ConfirmRemoveAccount)
|
||||||
|
@ -1,2 +1 @@
|
|||||||
import ConfirmRemoveAccount from './confirm-remove-account.container'
|
export { default } from './confirm-remove-account.container'
|
||||||
module.exports = ConfirmRemoveAccount
|
|
||||||
|
58
ui/app/components/modals/confirm-remove-account/index.scss
Normal file
58
ui/app/components/modals/confirm-remove-account/index.scss
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
.confirm-remove-account {
|
||||||
|
&__description {
|
||||||
|
text-align: center;
|
||||||
|
font-size: .875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__account {
|
||||||
|
border: 1px solid #b7b7b7;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 10px;
|
||||||
|
display: flex;
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
&__identicon {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__name,
|
||||||
|
&__address {
|
||||||
|
margin-right: 10px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__name {
|
||||||
|
width: 100px;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__label {
|
||||||
|
font-size: 11px;
|
||||||
|
display: block;
|
||||||
|
color: #9b9b9b;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__link {
|
||||||
|
margin-top: 14px;
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 15px;
|
||||||
|
height: 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 575px) {
|
||||||
|
&__name {
|
||||||
|
width: 90px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__link {
|
||||||
|
color: #2f9ae0;
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +1,8 @@
|
|||||||
import React, { Component } from 'react'
|
import React, { PureComponent } from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import Button from '../../button'
|
import Modal, { ModalContent } from '../../modal'
|
||||||
|
|
||||||
class ConfirmResetAccount extends Component {
|
export default class ConfirmResetAccount extends PureComponent {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
hideModal: PropTypes.func.isRequired,
|
hideModal: PropTypes.func.isRequired,
|
||||||
resetAccount: PropTypes.func.isRequired,
|
resetAccount: PropTypes.func.isRequired,
|
||||||
@ -12,7 +12,7 @@ class ConfirmResetAccount extends Component {
|
|||||||
t: PropTypes.func,
|
t: PropTypes.func,
|
||||||
}
|
}
|
||||||
|
|
||||||
handleReset () {
|
handleReset = () => {
|
||||||
this.props.resetAccount()
|
this.props.resetAccount()
|
||||||
.then(() => this.props.hideModal())
|
.then(() => this.props.hideModal())
|
||||||
}
|
}
|
||||||
@ -21,34 +21,18 @@ class ConfirmResetAccount extends Component {
|
|||||||
const { t } = this.context
|
const { t } = this.context
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="modal-container">
|
<Modal
|
||||||
<div className="modal-container__content">
|
onSubmit={this.handleReset}
|
||||||
<div className="modal-container__title">
|
onCancel={() => this.props.hideModal()}
|
||||||
{ `${t('resetAccount')}?` }
|
submitText={t('reset')}
|
||||||
</div>
|
cancelText={t('nevermind')}
|
||||||
<div className="modal-container__description">
|
submitType="secondary"
|
||||||
{ t('resetAccountDescription') }
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="modal-container__footer">
|
|
||||||
<Button
|
|
||||||
type="default"
|
|
||||||
className="modal-container__footer-button"
|
|
||||||
onClick={() => this.props.hideModal()}
|
|
||||||
>
|
>
|
||||||
{ t('nevermind') }
|
<ModalContent
|
||||||
</Button>
|
title={`${t('resetAccount')}?`}
|
||||||
<Button
|
description={t('resetAccountDescription')}
|
||||||
type="secondary"
|
/>
|
||||||
className="modal-container__footer-button"
|
</Modal>
|
||||||
onClick={() => this.handleReset()}
|
|
||||||
>
|
|
||||||
{ t('reset') }
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ConfirmResetAccount
|
|
||||||
|
@ -1,13 +1,16 @@
|
|||||||
import { connect } from 'react-redux'
|
import { connect } from 'react-redux'
|
||||||
|
import { compose } from 'recompose'
|
||||||
|
import withModalProps from '../../../higher-order-components/with-modal-props'
|
||||||
import ConfirmResetAccount from './confirm-reset-account.component'
|
import ConfirmResetAccount from './confirm-reset-account.component'
|
||||||
|
import { resetAccount } from '../../../actions'
|
||||||
const { hideModal, resetAccount } = require('../../../actions')
|
|
||||||
|
|
||||||
const mapDispatchToProps = dispatch => {
|
const mapDispatchToProps = dispatch => {
|
||||||
return {
|
return {
|
||||||
hideModal: () => dispatch(hideModal()),
|
|
||||||
resetAccount: () => dispatch(resetAccount()),
|
resetAccount: () => dispatch(resetAccount()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect(null, mapDispatchToProps)(ConfirmResetAccount)
|
export default compose(
|
||||||
|
withModalProps,
|
||||||
|
connect(null, mapDispatchToProps)
|
||||||
|
)(ConfirmResetAccount)
|
||||||
|
@ -1,2 +1 @@
|
|||||||
import ConfirmResetAccount from './confirm-reset-account.container'
|
export { default } from './confirm-reset-account.container'
|
||||||
module.exports = ConfirmResetAccount
|
|
||||||
|
@ -1,108 +1,9 @@
|
|||||||
|
@import './cancel-transaction/index';
|
||||||
|
|
||||||
|
@import './confirm-remove-account/index';
|
||||||
|
|
||||||
@import './customize-gas/index';
|
@import './customize-gas/index';
|
||||||
|
|
||||||
@import './qr-scanner/index';
|
@import './qr-scanner/index';
|
||||||
|
|
||||||
.modal-container {
|
@import './transaction-confirmed/index';
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background-color: #fff;
|
|
||||||
display: flex;
|
|
||||||
flex-flow: column;
|
|
||||||
border-radius: 8px;
|
|
||||||
|
|
||||||
&__title {
|
|
||||||
font-size: 1.5rem;
|
|
||||||
font-weight: 500;
|
|
||||||
padding: 16px 0;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__description {
|
|
||||||
text-align: center;
|
|
||||||
font-size: .875rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__account {
|
|
||||||
border: 1px solid #b7b7b7;
|
|
||||||
border-radius: 4px;
|
|
||||||
padding: 10px;
|
|
||||||
display: flex;
|
|
||||||
margin-top: 10px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
&__identicon {
|
|
||||||
margin-right: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__name,
|
|
||||||
&__address {
|
|
||||||
margin-right: 10px;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__name {
|
|
||||||
width: 100px;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__label {
|
|
||||||
font-size: 11px;
|
|
||||||
display: block;
|
|
||||||
color: #9b9b9b;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__link {
|
|
||||||
margin-top: 14px;
|
|
||||||
|
|
||||||
img {
|
|
||||||
width: 15px;
|
|
||||||
height: 15px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width: 575px) {
|
|
||||||
&__name {
|
|
||||||
width: 90px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__link {
|
|
||||||
color: #2f9ae0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__content {
|
|
||||||
overflow-y: auto;
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
padding: 32px;
|
|
||||||
|
|
||||||
@media screen and (max-width: 575px) {
|
|
||||||
justify-content: center;
|
|
||||||
padding: 28px 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__footer {
|
|
||||||
display: flex;
|
|
||||||
flex-flow: row;
|
|
||||||
justify-content: center;
|
|
||||||
border-top: 1px solid #d2d8dd;
|
|
||||||
padding: 16px;
|
|
||||||
flex: 0 0 auto;
|
|
||||||
|
|
||||||
&-button {
|
|
||||||
min-width: 0;
|
|
||||||
margin-right: 16px;
|
|
||||||
|
|
||||||
&:last-of-type {
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -19,14 +19,15 @@ const ShapeshiftDepositTxModal = require('./shapeshift-deposit-tx-modal.js')
|
|||||||
const HideTokenConfirmationModal = require('./hide-token-confirmation-modal')
|
const HideTokenConfirmationModal = require('./hide-token-confirmation-modal')
|
||||||
const CustomizeGasModal = require('../customize-gas-modal')
|
const CustomizeGasModal = require('../customize-gas-modal')
|
||||||
const NotifcationModal = require('./notification-modal')
|
const NotifcationModal = require('./notification-modal')
|
||||||
const ConfirmResetAccount = require('./confirm-reset-account')
|
|
||||||
const ConfirmRemoveAccount = require('./confirm-remove-account')
|
|
||||||
const QRScanner = require('./qr-scanner')
|
const QRScanner = require('./qr-scanner')
|
||||||
const TransactionConfirmed = require('./transaction-confirmed')
|
|
||||||
const WelcomeBeta = require('./welcome-beta')
|
|
||||||
const Notification = require('./notification')
|
|
||||||
|
|
||||||
|
import ConfirmRemoveAccount from './confirm-remove-account'
|
||||||
|
import ConfirmResetAccount from './confirm-reset-account'
|
||||||
|
import TransactionConfirmed from './transaction-confirmed'
|
||||||
import ConfirmCustomizeGasModal from './customize-gas'
|
import ConfirmCustomizeGasModal from './customize-gas'
|
||||||
|
import CancelTransaction from './cancel-transaction'
|
||||||
|
import WelcomeBeta from './welcome-beta'
|
||||||
|
import TransactionDetails from './transaction-details'
|
||||||
|
|
||||||
const modalContainerBaseStyle = {
|
const modalContainerBaseStyle = {
|
||||||
transform: 'translate3d(-50%, 0, 0px)',
|
transform: 'translate3d(-50%, 0, 0px)',
|
||||||
@ -199,11 +200,7 @@ const MODALS = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
BETA_UI_NOTIFICATION_MODAL: {
|
BETA_UI_NOTIFICATION_MODAL: {
|
||||||
contents: [
|
contents: h(WelcomeBeta),
|
||||||
h(Notification, [
|
|
||||||
h(WelcomeBeta),
|
|
||||||
]),
|
|
||||||
],
|
|
||||||
mobileModalStyle: {
|
mobileModalStyle: {
|
||||||
...modalContainerMobileStyle,
|
...modalContainerMobileStyle,
|
||||||
},
|
},
|
||||||
@ -307,9 +304,7 @@ const MODALS = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
CONFIRM_CUSTOMIZE_GAS: {
|
CONFIRM_CUSTOMIZE_GAS: {
|
||||||
contents: [
|
contents: h(ConfirmCustomizeGasModal),
|
||||||
h(ConfirmCustomizeGasModal),
|
|
||||||
],
|
|
||||||
mobileModalStyle: {
|
mobileModalStyle: {
|
||||||
width: '100vw',
|
width: '100vw',
|
||||||
height: '100vh',
|
height: '100vh',
|
||||||
@ -332,11 +327,7 @@ const MODALS = {
|
|||||||
|
|
||||||
TRANSACTION_CONFIRMED: {
|
TRANSACTION_CONFIRMED: {
|
||||||
disableBackdropClick: true,
|
disableBackdropClick: true,
|
||||||
contents: [
|
contents: h(TransactionConfirmed),
|
||||||
h(Notification, [
|
|
||||||
h(TransactionConfirmed),
|
|
||||||
]),
|
|
||||||
],
|
|
||||||
mobileModalStyle: {
|
mobileModalStyle: {
|
||||||
...modalContainerMobileStyle,
|
...modalContainerMobileStyle,
|
||||||
},
|
},
|
||||||
@ -347,6 +338,7 @@ const MODALS = {
|
|||||||
borderRadius: '8px',
|
borderRadius: '8px',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
QR_SCANNER: {
|
QR_SCANNER: {
|
||||||
contents: h(QRScanner),
|
contents: h(QRScanner),
|
||||||
mobileModalStyle: {
|
mobileModalStyle: {
|
||||||
@ -360,6 +352,32 @@ const MODALS = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
CANCEL_TRANSACTION: {
|
||||||
|
contents: h(CancelTransaction),
|
||||||
|
mobileModalStyle: {
|
||||||
|
...modalContainerMobileStyle,
|
||||||
|
},
|
||||||
|
laptopModalStyle: {
|
||||||
|
...modalContainerLaptopStyle,
|
||||||
|
},
|
||||||
|
contentStyle: {
|
||||||
|
borderRadius: '8px',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
TRANSACTION_DETAILS: {
|
||||||
|
contents: h(TransactionDetails),
|
||||||
|
mobileModalStyle: {
|
||||||
|
...modalContainerMobileStyle,
|
||||||
|
},
|
||||||
|
laptopModalStyle: {
|
||||||
|
...modalContainerLaptopStyle,
|
||||||
|
},
|
||||||
|
contentStyle: {
|
||||||
|
borderRadius: '8px',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
DEFAULT: {
|
DEFAULT: {
|
||||||
contents: [],
|
contents: [],
|
||||||
mobileModalStyle: {},
|
mobileModalStyle: {},
|
||||||
|
@ -1,2 +0,0 @@
|
|||||||
import Notification from './notification.container'
|
|
||||||
module.exports = Notification
|
|
@ -1,30 +0,0 @@
|
|||||||
import React from 'react'
|
|
||||||
import PropTypes from 'prop-types'
|
|
||||||
import Button from '../../button'
|
|
||||||
|
|
||||||
const Notification = (props, context) => {
|
|
||||||
return (
|
|
||||||
<div className="modal-container">
|
|
||||||
{ props.children }
|
|
||||||
<div className="modal-container__footer">
|
|
||||||
<Button
|
|
||||||
type="primary"
|
|
||||||
onClick={() => props.onHide()}
|
|
||||||
>
|
|
||||||
{ context.t('ok') }
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
Notification.propTypes = {
|
|
||||||
onHide: PropTypes.func.isRequired,
|
|
||||||
children: PropTypes.element,
|
|
||||||
}
|
|
||||||
|
|
||||||
Notification.contextTypes = {
|
|
||||||
t: PropTypes.func,
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Notification
|
|
@ -1,38 +0,0 @@
|
|||||||
import { connect } from 'react-redux'
|
|
||||||
import Notification from './notification.component'
|
|
||||||
|
|
||||||
const { hideModal } = require('../../../actions')
|
|
||||||
|
|
||||||
const mapStateToProps = state => {
|
|
||||||
const { appState: { modal: { modalState: { props } } } } = state
|
|
||||||
const { onHide } = props
|
|
||||||
return {
|
|
||||||
onHide,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const mapDispatchToProps = dispatch => {
|
|
||||||
return {
|
|
||||||
hideModal: () => dispatch(hideModal()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const mergeProps = (stateProps, dispatchProps, ownProps) => {
|
|
||||||
const { onHide, ...otherStateProps } = stateProps
|
|
||||||
const { hideModal, ...otherDispatchProps } = dispatchProps
|
|
||||||
|
|
||||||
return {
|
|
||||||
...otherStateProps,
|
|
||||||
...otherDispatchProps,
|
|
||||||
...ownProps,
|
|
||||||
onHide: () => {
|
|
||||||
hideModal()
|
|
||||||
|
|
||||||
if (onHide && typeof onHide === 'function') {
|
|
||||||
onHide()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps, mergeProps)(Notification)
|
|
@ -1,2 +1 @@
|
|||||||
import TransactionConfirmed from './transaction-confirmed.component'
|
export { default } from './transaction-confirmed.container'
|
||||||
module.exports = TransactionConfirmed
|
|
||||||
|
22
ui/app/components/modals/transaction-confirmed/index.scss
Normal file
22
ui/app/components/modals/transaction-confirmed/index.scss
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
.transaction-confirmed {
|
||||||
|
&__title {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: 500;
|
||||||
|
padding: 16px 0;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__description {
|
||||||
|
text-align: center;
|
||||||
|
font-size: .875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__content {
|
||||||
|
overflow-y: auto;
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
}
|
@ -1,24 +1,45 @@
|
|||||||
import React from 'react'
|
import React, { PureComponent } from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
|
import Modal from '../../modal'
|
||||||
|
|
||||||
const TransactionConfirmed = (props, context) => {
|
export default class TransactionConfirmed extends PureComponent {
|
||||||
const { t } = context
|
static contextTypes = {
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="modal-container__content">
|
|
||||||
<img src="images/check-icon.svg" />
|
|
||||||
<div className="modal-container__title">
|
|
||||||
{ `${t('confirmed')}!` }
|
|
||||||
</div>
|
|
||||||
<div className="modal-container__description">
|
|
||||||
{ t('initialTransactionConfirmed') }
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
TransactionConfirmed.contextTypes = {
|
|
||||||
t: PropTypes.func,
|
t: PropTypes.func,
|
||||||
}
|
}
|
||||||
|
|
||||||
export default TransactionConfirmed
|
static propTypes = {
|
||||||
|
onSubmit: PropTypes.func,
|
||||||
|
hideModal: PropTypes.func,
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSubmit = () => {
|
||||||
|
const { hideModal, onSubmit } = this.props
|
||||||
|
|
||||||
|
hideModal()
|
||||||
|
|
||||||
|
if (onSubmit && typeof onSubmit === 'function') {
|
||||||
|
onSubmit()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const { t } = this.context
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
onSubmit={this.handleSubmit}
|
||||||
|
submitText={t('ok')}
|
||||||
|
>
|
||||||
|
<div className="transaction-confirmed__content">
|
||||||
|
<img src="images/check-icon.svg" />
|
||||||
|
<div className="transaction-confirmed__title">
|
||||||
|
{ `${t('confirmed')}!` }
|
||||||
|
</div>
|
||||||
|
<div className="transaction-confirmed__description">
|
||||||
|
{ t('initialTransactionConfirmed') }
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -0,0 +1,4 @@
|
|||||||
|
import TransactionConfirmed from './transaction-confirmed.component'
|
||||||
|
import withModalProps from '../../../higher-order-components/with-modal-props'
|
||||||
|
|
||||||
|
export default withModalProps(TransactionConfirmed)
|
1
ui/app/components/modals/transaction-details/index.js
Normal file
1
ui/app/components/modals/transaction-details/index.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default } from './transaction-details.container'
|
@ -0,0 +1,48 @@
|
|||||||
|
import React, { PureComponent } from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import Modal from '../../modal'
|
||||||
|
import TransactionListItemDetails from '../../transaction-list-item-details'
|
||||||
|
import { hexToDecimal } from '../../../helpers/conversions.util'
|
||||||
|
|
||||||
|
export default class TransactionConfirmed extends PureComponent {
|
||||||
|
static contextTypes = {
|
||||||
|
t: PropTypes.func,
|
||||||
|
}
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
hideModal: PropTypes.func,
|
||||||
|
transaction: PropTypes.object,
|
||||||
|
onRetry: PropTypes.func,
|
||||||
|
showRetry: PropTypes.bool,
|
||||||
|
onCancel: PropTypes.func,
|
||||||
|
showCancel: PropTypes.bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSubmit = () => {
|
||||||
|
this.props.hideModal()
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const { t } = this.context
|
||||||
|
const { transaction, onRetry, showRetry, onCancel, showCancel } = this.props
|
||||||
|
const { txParams: { nonce } = {} } = transaction
|
||||||
|
const decimalNonce = nonce && hexToDecimal(nonce)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
onSubmit={this.handleSubmit}
|
||||||
|
onClose={this.handleSubmit}
|
||||||
|
submitText={t('ok')}
|
||||||
|
headerText={t('transactionWithNonce', [`#${decimalNonce}`])}
|
||||||
|
>
|
||||||
|
<TransactionListItemDetails
|
||||||
|
transaction={transaction}
|
||||||
|
onRetry={() => onRetry()}
|
||||||
|
showRetry={showRetry}
|
||||||
|
onCancel={() => onCancel()}
|
||||||
|
showCancel={showCancel}
|
||||||
|
/>
|
||||||
|
</Modal>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,4 @@
|
|||||||
|
import TransactionDetails from './transaction-details.component'
|
||||||
|
import withModalProps from '../../../higher-order-components/with-modal-props'
|
||||||
|
|
||||||
|
export default withModalProps(TransactionDetails)
|
@ -1,2 +1 @@
|
|||||||
import WelcomeBeta from './welcome-beta.component'
|
export { default } from './welcome-beta.container'
|
||||||
module.exports = WelcomeBeta
|
|
||||||
|
@ -1,18 +1,21 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
|
import Modal, { ModalContent } from '../../modal'
|
||||||
|
|
||||||
const TransactionConfirmed = (props, context) => {
|
const TransactionConfirmed = (props, context) => {
|
||||||
const { t } = context
|
const { t } = context
|
||||||
|
const { hideModal } = props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="modal-container__content">
|
<Modal
|
||||||
<div className="modal-container__title">
|
onSubmit={() => hideModal()}
|
||||||
{ `${t('uiWelcome')}` }
|
submitText={t('ok')}
|
||||||
</div>
|
>
|
||||||
<div className="modal-container__description">
|
<ModalContent
|
||||||
{ t('uiWelcomeMessage') }
|
title={t('uiWelcome')}
|
||||||
</div>
|
description={t('uiWelcomeMessage')}
|
||||||
</div>
|
/>
|
||||||
|
</Modal>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -20,4 +23,8 @@ TransactionConfirmed.contextTypes = {
|
|||||||
t: PropTypes.func,
|
t: PropTypes.func,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TransactionConfirmed.propTypes = {
|
||||||
|
hideModal: PropTypes.func,
|
||||||
|
}
|
||||||
|
|
||||||
export default TransactionConfirmed
|
export default TransactionConfirmed
|
||||||
|
@ -0,0 +1,4 @@
|
|||||||
|
import WelcomeBeta from './welcome-beta.component'
|
||||||
|
import withModalProps from '../../../higher-order-components/with-modal-props'
|
||||||
|
|
||||||
|
export default withModalProps(WelcomeBeta)
|
@ -8,6 +8,7 @@ import {
|
|||||||
INSUFFICIENT_FUNDS_ERROR_KEY,
|
INSUFFICIENT_FUNDS_ERROR_KEY,
|
||||||
TRANSACTION_ERROR_KEY,
|
TRANSACTION_ERROR_KEY,
|
||||||
} from '../../../constants/error-keys'
|
} from '../../../constants/error-keys'
|
||||||
|
import { CONFIRMED_STATUS, DROPPED_STATUS } from '../../../constants/transactions'
|
||||||
|
|
||||||
export default class ConfirmTransactionBase extends Component {
|
export default class ConfirmTransactionBase extends Component {
|
||||||
static contextTypes = {
|
static contextTypes = {
|
||||||
@ -85,9 +86,9 @@ export default class ConfirmTransactionBase extends Component {
|
|||||||
clearConfirmTransaction,
|
clearConfirmTransaction,
|
||||||
} = this.props
|
} = this.props
|
||||||
|
|
||||||
if (transactionStatus === 'dropped') {
|
if (transactionStatus === DROPPED_STATUS || transactionStatus === CONFIRMED_STATUS) {
|
||||||
showTransactionConfirmedModal({
|
showTransactionConfirmedModal({
|
||||||
onHide: () => {
|
onSubmit: () => {
|
||||||
clearConfirmTransaction()
|
clearConfirmTransaction()
|
||||||
history.push(DEFAULT_ROUTE)
|
history.push(DEFAULT_ROUTE)
|
||||||
},
|
},
|
||||||
|
@ -97,8 +97,8 @@ const mapDispatchToProps = dispatch => {
|
|||||||
return {
|
return {
|
||||||
clearConfirmTransaction: () => dispatch(clearConfirmTransaction()),
|
clearConfirmTransaction: () => dispatch(clearConfirmTransaction()),
|
||||||
clearSend: () => dispatch(clearSend()),
|
clearSend: () => dispatch(clearSend()),
|
||||||
showTransactionConfirmedModal: ({ onHide }) => {
|
showTransactionConfirmedModal: ({ onSubmit }) => {
|
||||||
return dispatch(showModal({ name: 'TRANSACTION_CONFIRMED', onHide }))
|
return dispatch(showModal({ name: 'TRANSACTION_CONFIRMED', onSubmit }))
|
||||||
},
|
},
|
||||||
showCustomizeGasModal: ({ txData, onSubmit, validate }) => {
|
showCustomizeGasModal: ({ txData, onSubmit, validate }) => {
|
||||||
return dispatch(showModal({ name: 'CONFIRM_CUSTOMIZE_GAS', txData, onSubmit, validate }))
|
return dispatch(showModal({ name: 'CONFIRM_CUSTOMIZE_GAS', txData, onSubmit, validate }))
|
||||||
|
@ -5,10 +5,9 @@ import sinon from 'sinon'
|
|||||||
import TransactionAction from '../transaction-action.component'
|
import TransactionAction from '../transaction-action.component'
|
||||||
|
|
||||||
describe('TransactionAction Component', () => {
|
describe('TransactionAction Component', () => {
|
||||||
const tOrDefault = key => key
|
const t = key => key
|
||||||
global.eth = {
|
global.eth = {
|
||||||
getCode: sinon.stub().callsFake(address => {
|
getCode: sinon.stub().callsFake(address => {
|
||||||
console.log('CALLED')
|
|
||||||
const code = address === 'approveAddress' ? 'contract' : '0x'
|
const code = address === 'approveAddress' ? 'contract' : '0x'
|
||||||
return Promise.resolve(code)
|
return Promise.resolve(code)
|
||||||
}),
|
}),
|
||||||
@ -36,7 +35,7 @@ describe('TransactionAction Component', () => {
|
|||||||
methodData={methodData}
|
methodData={methodData}
|
||||||
transaction={transaction}
|
transaction={transaction}
|
||||||
className="transaction-action"
|
className="transaction-action"
|
||||||
/>, { context: { tOrDefault }})
|
/>, { context: { t }})
|
||||||
|
|
||||||
assert.equal(wrapper.find('.transaction-action').length, 1)
|
assert.equal(wrapper.find('.transaction-action').length, 1)
|
||||||
assert.equal(wrapper.text(), '--')
|
assert.equal(wrapper.text(), '--')
|
||||||
@ -63,7 +62,7 @@ describe('TransactionAction Component', () => {
|
|||||||
methodData={methodData}
|
methodData={methodData}
|
||||||
transaction={transaction}
|
transaction={transaction}
|
||||||
className="transaction-action"
|
className="transaction-action"
|
||||||
/>, { context: { tOrDefault }})
|
/>, { context: { t }})
|
||||||
|
|
||||||
assert.equal(wrapper.find('.transaction-action').length, 1)
|
assert.equal(wrapper.find('.transaction-action').length, 1)
|
||||||
wrapper.setState({ transactionAction: 'sentEther' })
|
wrapper.setState({ transactionAction: 'sentEther' })
|
||||||
@ -102,7 +101,7 @@ describe('TransactionAction Component', () => {
|
|||||||
methodData={methodData}
|
methodData={methodData}
|
||||||
transaction={transaction}
|
transaction={transaction}
|
||||||
className="transaction-action"
|
className="transaction-action"
|
||||||
/>, { context: { tOrDefault }})
|
/>, { context: { t }})
|
||||||
|
|
||||||
assert.equal(wrapper.find('.transaction-action').length, 1)
|
assert.equal(wrapper.find('.transaction-action').length, 1)
|
||||||
wrapper.setState({ transactionAction: 'approve' })
|
wrapper.setState({ transactionAction: 'approve' })
|
||||||
|
@ -4,7 +4,7 @@ import { getTransactionActionKey } from '../../helpers/transactions.util'
|
|||||||
|
|
||||||
export default class TransactionAction extends PureComponent {
|
export default class TransactionAction extends PureComponent {
|
||||||
static contextTypes = {
|
static contextTypes = {
|
||||||
tOrDefault: PropTypes.func,
|
t: PropTypes.func,
|
||||||
}
|
}
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
@ -35,7 +35,7 @@ export default class TransactionAction extends PureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const actionKey = await getTransactionActionKey(transaction, data)
|
const actionKey = await getTransactionActionKey(transaction, data)
|
||||||
const action = actionKey && this.context.tOrDefault(actionKey)
|
const action = actionKey && this.context.t(actionKey)
|
||||||
this.setState({ transactionAction: action })
|
this.setState({ transactionAction: action })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,6 +33,10 @@
|
|||||||
&:last-child::after {
|
&:last-child::after {
|
||||||
height: 50%;
|
height: 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:first-child:last-child::after {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__activity-icon {
|
&__activity-icon {
|
||||||
@ -47,10 +51,13 @@
|
|||||||
&__activity-text {
|
&__activity-text {
|
||||||
color: $scorpion;
|
color: $scorpion;
|
||||||
font-size: .75rem;
|
font-size: .75rem;
|
||||||
|
|
||||||
|
@media screen and (min-width: $break-large) {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&__value {
|
&__value {
|
||||||
display: inline;
|
display: inline;
|
||||||
|
@ -13,7 +13,9 @@ export default class TransactionListItemDetails extends PureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
onCancel: PropTypes.func,
|
||||||
onRetry: PropTypes.func,
|
onRetry: PropTypes.func,
|
||||||
|
showCancel: PropTypes.bool,
|
||||||
showRetry: PropTypes.bool,
|
showRetry: PropTypes.bool,
|
||||||
transaction: PropTypes.object,
|
transaction: PropTypes.object,
|
||||||
}
|
}
|
||||||
@ -27,6 +29,13 @@ export default class TransactionListItemDetails extends PureComponent {
|
|||||||
this.setState({ showTransactionDetails: true })
|
this.setState({ showTransactionDetails: true })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleCancel = event => {
|
||||||
|
const { onCancel } = this.props
|
||||||
|
|
||||||
|
event.stopPropagation()
|
||||||
|
onCancel()
|
||||||
|
}
|
||||||
|
|
||||||
handleRetry = event => {
|
handleRetry = event => {
|
||||||
const { onRetry } = this.props
|
const { onRetry } = this.props
|
||||||
|
|
||||||
@ -36,7 +45,7 @@ export default class TransactionListItemDetails extends PureComponent {
|
|||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { t } = this.context
|
const { t } = this.context
|
||||||
const { transaction, showRetry } = this.props
|
const { transaction, showCancel, showRetry } = this.props
|
||||||
const { txParams: { to, from } = {} } = transaction
|
const { txParams: { to, from } = {} } = transaction
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -55,6 +64,17 @@ export default class TransactionListItemDetails extends PureComponent {
|
|||||||
</Button>
|
</Button>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
showCancel && (
|
||||||
|
<Button
|
||||||
|
type="raised"
|
||||||
|
onClick={this.handleCancel}
|
||||||
|
className="transaction-list-item-details__header-button"
|
||||||
|
>
|
||||||
|
{ t('cancel') }
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
}
|
||||||
<Button
|
<Button
|
||||||
type="raised"
|
type="raised"
|
||||||
onClick={this.handleEtherscanClick}
|
onClick={this.handleEtherscanClick}
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
background: $white;
|
||||||
|
|
||||||
&__grid {
|
&__grid {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
@ -117,4 +118,14 @@
|
|||||||
background: #f3f4f7;
|
background: #f3f4f7;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__expander {
|
||||||
|
max-height: 0px;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
&--show {
|
||||||
|
max-height: 1000px;
|
||||||
|
transition: max-height 700ms ease-out;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import React, { PureComponent } from 'react'
|
import React, { PureComponent } from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
|
import classnames from 'classnames'
|
||||||
import Identicon from '../identicon'
|
import Identicon from '../identicon'
|
||||||
import TransactionStatus from '../transaction-status'
|
import TransactionStatus from '../transaction-status'
|
||||||
import TransactionAction from '../transaction-action'
|
import TransactionAction from '../transaction-action'
|
||||||
@ -9,20 +10,24 @@ import TransactionListItemDetails from '../transaction-list-item-details'
|
|||||||
import { CONFIRM_TRANSACTION_ROUTE } from '../../routes'
|
import { CONFIRM_TRANSACTION_ROUTE } from '../../routes'
|
||||||
import { UNAPPROVED_STATUS, TOKEN_METHOD_TRANSFER } from '../../constants/transactions'
|
import { UNAPPROVED_STATUS, TOKEN_METHOD_TRANSFER } from '../../constants/transactions'
|
||||||
import { ETH } from '../../constants/common'
|
import { ETH } from '../../constants/common'
|
||||||
|
import { ENVIRONMENT_TYPE_FULLSCREEN } from '../../../../app/scripts/lib/enums'
|
||||||
|
|
||||||
export default class TransactionListItem extends PureComponent {
|
export default class TransactionListItem extends PureComponent {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
assetImages: PropTypes.object,
|
||||||
history: PropTypes.object,
|
history: PropTypes.object,
|
||||||
transaction: PropTypes.object,
|
|
||||||
value: PropTypes.string,
|
|
||||||
methodData: PropTypes.object,
|
methodData: PropTypes.object,
|
||||||
showRetry: PropTypes.bool,
|
nonceAndDate: PropTypes.string,
|
||||||
retryTransaction: PropTypes.func,
|
retryTransaction: PropTypes.func,
|
||||||
setSelectedToken: PropTypes.func,
|
setSelectedToken: PropTypes.func,
|
||||||
nonceAndDate: PropTypes.string,
|
showCancelModal: PropTypes.func,
|
||||||
|
showCancel: PropTypes.bool,
|
||||||
|
showRetry: PropTypes.bool,
|
||||||
|
showTransactionDetailsModal: PropTypes.func,
|
||||||
token: PropTypes.object,
|
token: PropTypes.object,
|
||||||
assetImages: PropTypes.object,
|
|
||||||
tokenData: PropTypes.object,
|
tokenData: PropTypes.object,
|
||||||
|
transaction: PropTypes.object,
|
||||||
|
value: PropTypes.string,
|
||||||
}
|
}
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
@ -30,16 +35,39 @@ export default class TransactionListItem extends PureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleClick = () => {
|
handleClick = () => {
|
||||||
const { transaction, history } = this.props
|
const {
|
||||||
|
transaction,
|
||||||
|
history,
|
||||||
|
showTransactionDetailsModal,
|
||||||
|
methodData,
|
||||||
|
showCancel,
|
||||||
|
showRetry,
|
||||||
|
} = this.props
|
||||||
const { id, status } = transaction
|
const { id, status } = transaction
|
||||||
const { showTransactionDetails } = this.state
|
const { showTransactionDetails } = this.state
|
||||||
|
const windowType = window.METAMASK_UI_TYPE
|
||||||
|
|
||||||
if (status === UNAPPROVED_STATUS) {
|
if (status === UNAPPROVED_STATUS) {
|
||||||
history.push(`${CONFIRM_TRANSACTION_ROUTE}/${id}`)
|
history.push(`${CONFIRM_TRANSACTION_ROUTE}/${id}`)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (windowType === ENVIRONMENT_TYPE_FULLSCREEN) {
|
||||||
this.setState({ showTransactionDetails: !showTransactionDetails })
|
this.setState({ showTransactionDetails: !showTransactionDetails })
|
||||||
|
} else {
|
||||||
|
showTransactionDetailsModal({
|
||||||
|
transaction,
|
||||||
|
onRetry: this.handleRetry,
|
||||||
|
showRetry: showRetry && methodData.done,
|
||||||
|
onCancel: this.handleCancel,
|
||||||
|
showCancel,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleCancel = () => {
|
||||||
|
const { transaction: { id, txParams: { gasPrice } } = {}, showCancelModal } = this.props
|
||||||
|
showCancelModal(id, gasPrice)
|
||||||
}
|
}
|
||||||
|
|
||||||
handleRetry = () => {
|
handleRetry = () => {
|
||||||
@ -100,12 +128,13 @@ export default class TransactionListItem extends PureComponent {
|
|||||||
|
|
||||||
render () {
|
render () {
|
||||||
const {
|
const {
|
||||||
transaction,
|
|
||||||
methodData,
|
|
||||||
showRetry,
|
|
||||||
nonceAndDate,
|
|
||||||
assetImages,
|
assetImages,
|
||||||
|
methodData,
|
||||||
|
nonceAndDate,
|
||||||
|
showCancel,
|
||||||
|
showRetry,
|
||||||
tokenData,
|
tokenData,
|
||||||
|
transaction,
|
||||||
} = this.props
|
} = this.props
|
||||||
const { txParams = {} } = transaction
|
const { txParams = {} } = transaction
|
||||||
const { showTransactionDetails } = this.state
|
const { showTransactionDetails } = this.state
|
||||||
@ -148,18 +177,24 @@ export default class TransactionListItem extends PureComponent {
|
|||||||
{ this.renderPrimaryCurrency() }
|
{ this.renderPrimaryCurrency() }
|
||||||
{ this.renderSecondaryCurrency() }
|
{ this.renderSecondaryCurrency() }
|
||||||
</div>
|
</div>
|
||||||
|
<div className={classnames('transaction-list-item__expander', {
|
||||||
|
'transaction-list-item__expander--show': showTransactionDetails,
|
||||||
|
})}>
|
||||||
{
|
{
|
||||||
showTransactionDetails && (
|
showTransactionDetails && (
|
||||||
<div className="transaction-list-item__details-container">
|
<div className="transaction-list-item__details-container">
|
||||||
<TransactionListItemDetails
|
<TransactionListItemDetails
|
||||||
transaction={transaction}
|
transaction={transaction}
|
||||||
showRetry={showRetry && methodData.done}
|
|
||||||
onRetry={this.handleRetry}
|
onRetry={this.handleRetry}
|
||||||
|
showRetry={showRetry && methodData.done}
|
||||||
|
onCancel={this.handleCancel}
|
||||||
|
showCancel={showCancel}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ import { withRouter } from 'react-router-dom'
|
|||||||
import { compose } from 'recompose'
|
import { compose } from 'recompose'
|
||||||
import withMethodData from '../../higher-order-components/with-method-data'
|
import withMethodData from '../../higher-order-components/with-method-data'
|
||||||
import TransactionListItem from './transaction-list-item.component'
|
import TransactionListItem from './transaction-list-item.component'
|
||||||
import { setSelectedToken, retryTransaction } from '../../actions'
|
import { setSelectedToken, retryTransaction, showModal } from '../../actions'
|
||||||
import { hexToDecimal } from '../../helpers/conversions.util'
|
import { hexToDecimal } from '../../helpers/conversions.util'
|
||||||
import { getTokenData } from '../../helpers/transactions.util'
|
import { getTokenData } from '../../helpers/transactions.util'
|
||||||
import { formatDate } from '../../util'
|
import { formatDate } from '../../util'
|
||||||
@ -25,6 +25,19 @@ const mapDispatchToProps = dispatch => {
|
|||||||
return {
|
return {
|
||||||
setSelectedToken: tokenAddress => dispatch(setSelectedToken(tokenAddress)),
|
setSelectedToken: tokenAddress => dispatch(setSelectedToken(tokenAddress)),
|
||||||
retryTransaction: transactionId => dispatch(retryTransaction(transactionId)),
|
retryTransaction: transactionId => dispatch(retryTransaction(transactionId)),
|
||||||
|
showCancelModal: (transactionId, originalGasPrice) => {
|
||||||
|
return dispatch(showModal({ name: 'CANCEL_TRANSACTION', transactionId, originalGasPrice }))
|
||||||
|
},
|
||||||
|
showTransactionDetailsModal: ({ transaction, onRetry, showRetry, onCancel, showCancel }) => {
|
||||||
|
return dispatch(showModal({
|
||||||
|
name: 'TRANSACTION_DETAILS',
|
||||||
|
transaction,
|
||||||
|
onRetry,
|
||||||
|
showRetry,
|
||||||
|
onCancel,
|
||||||
|
showCancel,
|
||||||
|
}))
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow-y: hidden;
|
overflow-y: hidden;
|
||||||
|
margin-top: 8px;
|
||||||
|
border-top: 1px solid $geyser;
|
||||||
|
|
||||||
&__completed-transactions {
|
&__completed-transactions {
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -15,7 +17,7 @@
|
|||||||
font-size: .875rem;
|
font-size: .875rem;
|
||||||
color: $dusty-gray;
|
color: $dusty-gray;
|
||||||
border-bottom: 1px solid $geyser;
|
border-bottom: 1px solid $geyser;
|
||||||
padding: 16px 0 8px 20px;
|
padding: 8px 0 8px 20px;
|
||||||
|
|
||||||
@media screen and (max-width: $break-small) {
|
@media screen and (max-width: $break-small) {
|
||||||
padding: 8px 0 8px 16px;
|
padding: 8px 0 8px 16px;
|
||||||
|
@ -56,7 +56,7 @@ export default class TransactionList extends PureComponent {
|
|||||||
</div>
|
</div>
|
||||||
{
|
{
|
||||||
pendingTransactions.map((transaction, index) => (
|
pendingTransactions.map((transaction, index) => (
|
||||||
this.renderTransaction(transaction, index)
|
this.renderTransaction(transaction, index, true)
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
@ -78,7 +78,7 @@ export default class TransactionList extends PureComponent {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
renderTransaction (transaction, index) {
|
renderTransaction (transaction, index, showCancel) {
|
||||||
const { selectedToken, assetImages } = this.props
|
const { selectedToken, assetImages } = this.props
|
||||||
|
|
||||||
return transaction.key === TRANSACTION_TYPE_SHAPESHIFT
|
return transaction.key === TRANSACTION_TYPE_SHAPESHIFT
|
||||||
@ -92,6 +92,7 @@ export default class TransactionList extends PureComponent {
|
|||||||
transaction={transaction}
|
transaction={transaction}
|
||||||
key={transaction.id}
|
key={transaction.id}
|
||||||
showRetry={this.shouldShowRetry(transaction)}
|
showRetry={this.shouldShowRetry(transaction)}
|
||||||
|
showCancel={showCancel}
|
||||||
token={selectedToken}
|
token={selectedToken}
|
||||||
assetImages={assetImages}
|
assetImages={assetImages}
|
||||||
/>
|
/>
|
||||||
|
@ -104,7 +104,7 @@ ConfirmTxScreen.prototype.componentDidUpdate = function (prevProps) {
|
|||||||
if (prevTx && prevTx.status === 'dropped') {
|
if (prevTx && prevTx.status === 'dropped') {
|
||||||
this.props.dispatch(actions.showModal({
|
this.props.dispatch(actions.showModal({
|
||||||
name: 'TRANSACTION_CONFIRMED',
|
name: 'TRANSACTION_CONFIRMED',
|
||||||
onHide: () => history.push(DEFAULT_ROUTE),
|
onSubmit: () => history.push(DEFAULT_ROUTE),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
return
|
return
|
||||||
|
@ -18,5 +18,6 @@ export const SEND_TOKEN_ACTION_KEY = 'sentTokens'
|
|||||||
export const TRANSFER_FROM_ACTION_KEY = 'transferFrom'
|
export const TRANSFER_FROM_ACTION_KEY = 'transferFrom'
|
||||||
export const SIGNATURE_REQUEST_KEY = 'signatureRequest'
|
export const SIGNATURE_REQUEST_KEY = 'signatureRequest'
|
||||||
export const UNKNOWN_FUNCTION_KEY = 'unknownFunction'
|
export const UNKNOWN_FUNCTION_KEY = 'unknownFunction'
|
||||||
|
export const CANCEL_ATTEMPT_ACTION_KEY = 'cancelAttempt'
|
||||||
|
|
||||||
export const TRANSACTION_TYPE_SHAPESHIFT = 'shapeshift'
|
export const TRANSACTION_TYPE_SHAPESHIFT = 'shapeshift'
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
|
import ethUtil from 'ethereumjs-util'
|
||||||
import { conversionUtil } from '../conversion-util'
|
import { conversionUtil } from '../conversion-util'
|
||||||
import { ETH, GWEI, WEI } from '../constants/common'
|
import { ETH, GWEI, WEI } from '../constants/common'
|
||||||
|
|
||||||
|
export function bnToHex (inputBn) {
|
||||||
|
return ethUtil.addHexPrefix(inputBn.toString(16))
|
||||||
|
}
|
||||||
|
|
||||||
export function hexToDecimal (hexValue) {
|
export function hexToDecimal (hexValue) {
|
||||||
return conversionUtil(hexValue, {
|
return conversionUtil(hexValue, {
|
||||||
fromNumericBase: 'hex',
|
fromNumericBase: 'hex',
|
||||||
@ -8,6 +13,13 @@ export function hexToDecimal (hexValue) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function decimalToHex (decimal) {
|
||||||
|
return conversionUtil(decimal, {
|
||||||
|
fromNumericBase: 'dec',
|
||||||
|
toNumericBase: 'hex',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export function getEthConversionFromWeiHex ({ value, conversionRate, numberOfDecimals = 6 }) {
|
export function getEthConversionFromWeiHex ({ value, conversionRate, numberOfDecimals = 6 }) {
|
||||||
const denominations = [ETH, GWEI, WEI]
|
const denominations = [ETH, GWEI, WEI]
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@ import {
|
|||||||
TRANSFER_FROM_ACTION_KEY,
|
TRANSFER_FROM_ACTION_KEY,
|
||||||
SIGNATURE_REQUEST_KEY,
|
SIGNATURE_REQUEST_KEY,
|
||||||
UNKNOWN_FUNCTION_KEY,
|
UNKNOWN_FUNCTION_KEY,
|
||||||
|
CANCEL_ATTEMPT_ACTION_KEY,
|
||||||
} from '../constants/transactions'
|
} from '../constants/transactions'
|
||||||
|
|
||||||
import { addCurrencies } from '../conversion-util'
|
import { addCurrencies } from '../conversion-util'
|
||||||
@ -44,7 +45,11 @@ export function isConfirmDeployContract (txData = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function getTransactionActionKey (transaction, methodData) {
|
export async function getTransactionActionKey (transaction, methodData) {
|
||||||
const { txParams: { data, to } = {}, msgParams } = transaction
|
const { txParams: { data, to } = {}, msgParams, type } = transaction
|
||||||
|
|
||||||
|
if (type === 'cancel') {
|
||||||
|
return CANCEL_ATTEMPT_ACTION_KEY
|
||||||
|
}
|
||||||
|
|
||||||
if (msgParams) {
|
if (msgParams) {
|
||||||
return SIGNATURE_REQUEST_KEY
|
return SIGNATURE_REQUEST_KEY
|
||||||
|
1
ui/app/higher-order-components/with-modal-props/index.js
Normal file
1
ui/app/higher-order-components/with-modal-props/index.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default } from './with-modal-props'
|
@ -0,0 +1,43 @@
|
|||||||
|
|
||||||
|
import assert from 'assert'
|
||||||
|
import configureMockStore from 'redux-mock-store'
|
||||||
|
import { mount } from 'enzyme'
|
||||||
|
import React from 'react'
|
||||||
|
import withModalProps from '../with-modal-props'
|
||||||
|
|
||||||
|
const mockState = {
|
||||||
|
appState: {
|
||||||
|
modal: {
|
||||||
|
modalState: {
|
||||||
|
props: {
|
||||||
|
prop1: 'prop1',
|
||||||
|
prop2: 2,
|
||||||
|
prop3: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('withModalProps', () => {
|
||||||
|
it('should return a component wrapped with modal state props', () => {
|
||||||
|
const TestComponent = props => (
|
||||||
|
<div className="test">Testing</div>
|
||||||
|
)
|
||||||
|
const WrappedComponent = withModalProps(TestComponent)
|
||||||
|
const store = configureMockStore()(mockState)
|
||||||
|
const wrapper = mount(
|
||||||
|
<WrappedComponent store={store} />
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.ok(wrapper)
|
||||||
|
const testComponent = wrapper.find(TestComponent).at(0)
|
||||||
|
assert.equal(testComponent.length, 1)
|
||||||
|
assert.equal(testComponent.find('.test').text(), 'Testing')
|
||||||
|
const testComponentProps = testComponent.props()
|
||||||
|
assert.equal(testComponentProps.prop1, 'prop1')
|
||||||
|
assert.equal(testComponentProps.prop2, 2)
|
||||||
|
assert.equal(testComponentProps.prop3, true)
|
||||||
|
assert.equal(typeof testComponentProps.hideModal, 'function')
|
||||||
|
})
|
||||||
|
})
|
@ -0,0 +1,21 @@
|
|||||||
|
import { connect } from 'react-redux'
|
||||||
|
import { hideModal } from '../../actions'
|
||||||
|
|
||||||
|
const mapStateToProps = state => {
|
||||||
|
const { appState } = state
|
||||||
|
const { props: modalProps } = appState.modal.modalState
|
||||||
|
|
||||||
|
return {
|
||||||
|
...modalProps,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapDispatchToProps = dispatch => {
|
||||||
|
return {
|
||||||
|
hideModal: () => dispatch(hideModal()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function withModalProps (Component) {
|
||||||
|
return connect(mapStateToProps, mapDispatchToProps)(Component)
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user