diff options
23 files changed, 417 insertions, 127 deletions
diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index fa01fea24..34b7477a6 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -402,6 +402,9 @@ "infoHelp": { "message": "Info & Help" }, + "initialTransactionConfirmed": { + "message": "Your initial transaction was confirmed by the network. Click OK to go back." + }, "insufficientFunds": { "message": "Insufficient funds." }, @@ -701,10 +704,10 @@ "save": { "message": "Save" }, - "reprice_title": { - "message": "Reprice Transaction" + "speedUpTitle": { + "message": "Speed Up Transaction" }, - "reprice_subtitle": { + "speedUpSubtitle": { "message": "Increase your gas price to attempt to overwrite and speed up your transaction" }, "saveAsCsvFile": { diff --git a/app/images/check-icon.svg b/app/images/check-icon.svg new file mode 100644 index 000000000..cafa864e5 --- /dev/null +++ b/app/images/check-icon.svg @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<svg width="100px" height="100px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> + <!-- Generator: sketchtool 50.2 (55047) - http://www.bohemiancoding.com/sketch --> + <title>76BCDB09-52B0-41CB-908F-12F9087A2F1B</title> + <desc>Created with sketchtool.</desc> + <defs></defs> + <g id="Confirm-TX-screen" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> + <g id="confirmed-alert" transform="translate(-144.000000, -53.000000)" stroke="#61BA00" stroke-width="4"> + <g id="Group-17-Copy" transform="translate(22.000000, 20.000000)"> + <g id="check-icon" transform="translate(124.000000, 35.000000)"> + <circle id="Oval-5" cx="48" cy="48" r="48"></circle> + <polyline id="Path-3" stroke-linecap="round" points="29.76 52.8 41.0023819 64.32 71.04 34.56"></polyline> + </g> + </g> + </g> + </g> +</svg>
\ No newline at end of file diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 1b1d26886..0457b4476 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -350,7 +350,7 @@ module.exports = class MetamaskController extends EventEmitter { verifySeedPhrase: nodeify(this.verifySeedPhrase, this), clearSeedWordCache: this.clearSeedWordCache.bind(this), resetAccount: nodeify(this.resetAccount, this), - importAccountWithStrategy: this.importAccountWithStrategy.bind(this), + importAccountWithStrategy: nodeify(this.importAccountWithStrategy, this), // vault management submitPassword: nodeify(keyringController.submitPassword, keyringController), @@ -608,15 +608,15 @@ module.exports = class MetamaskController extends EventEmitter { * @param {any} args - The data required by that strategy to import an account. * @param {Function} cb - A callback function called with a state update on success. */ - importAccountWithStrategy (strategy, args, cb) { - accountImporter.importAccount(strategy, args) - .then((privateKey) => { - return this.keyringController.addNewKeyring('Simple Key Pair', [ privateKey ]) - }) - .then(keyring => keyring.getAccounts()) - .then((accounts) => this.preferencesController.setSelectedAddress(accounts[0])) - .then(() => { cb(null, this.keyringController.fullUpdate()) }) - .catch((reason) => { cb(reason) }) + async importAccountWithStrategy (strategy, args) { + const privateKey = await accountImporter.importAccount(strategy, args) + const keyring = await this.keyringController.addNewKeyring('Simple Key Pair', [ privateKey ]) + const accounts = await keyring.getAccounts() + // update accounts in preferences controller + const allAccounts = await this.keyringController.getAccounts() + this.preferencesController.setAddresses(allAccounts) + // set new account as selected + await this.preferencesController.setSelectedAddress(accounts[0]) } // --------------------------------------------------------------------------- diff --git a/mascara/src/app/first-time/create-password-screen.js b/mascara/src/app/first-time/create-password-screen.js index 99d210ed1..6b284f7c5 100644 --- a/mascara/src/app/first-time/create-password-screen.js +++ b/mascara/src/app/first-time/create-password-screen.js @@ -143,6 +143,7 @@ class CreatePasswordScreen extends Component { autoComplete="new-password" margin="normal" fullWidth + largeLabel /> <TextField id="confirm-password" @@ -155,6 +156,7 @@ class CreatePasswordScreen extends Component { autoComplete="confirm-password" margin="normal" fullWidth + largeLabel /> <button className="first-time-flow__button" diff --git a/mascara/src/app/first-time/import-seed-phrase-screen.js b/mascara/src/app/first-time/import-seed-phrase-screen.js index 4fda2bbc1..fd2516ad4 100644 --- a/mascara/src/app/first-time/import-seed-phrase-screen.js +++ b/mascara/src/app/first-time/import-seed-phrase-screen.js @@ -146,6 +146,7 @@ class ImportSeedPhraseScreen extends Component { error={passwordError} autoComplete="new-password" margin="normal" + largeLabel /> <TextField id="confirm-password" @@ -157,6 +158,7 @@ class ImportSeedPhraseScreen extends Component { error={confirmPasswordError} autoComplete="confirm-password" margin="normal" + largeLabel /> <button className="first-time-flow__button" diff --git a/ui/app/actions.js b/ui/app/actions.js index 894e31fde..649f740e9 100644 --- a/ui/app/actions.js +++ b/ui/app/actions.js @@ -1397,16 +1397,24 @@ function markAccountsFound () { function retryTransaction (txId) { log.debug(`background.retryTransaction`) + let newTxId + return (dispatch) => { - background.retryTransaction(txId, (err, newState) => { - if (err) { - return dispatch(actions.displayWarning(err.message)) - } - const { selectedAddressTxList } = newState - const { id: newTxId } = selectedAddressTxList[selectedAddressTxList.length - 1] - dispatch(actions.updateMetamaskState(newState)) - dispatch(actions.viewPendingTx(newTxId)) + return new Promise((resolve, reject) => { + background.retryTransaction(txId, (err, newState) => { + if (err) { + dispatch(actions.displayWarning(err.message)) + reject(err) + } + + const { selectedAddressTxList } = newState + const { id } = selectedAddressTxList[selectedAddressTxList.length - 1] + newTxId = id + resolve(newState) + }) }) + .then(newState => dispatch(actions.updateMetamaskState(newState))) + .then(() => newTxId) } } diff --git a/ui/app/app.js b/ui/app/app.js index 3d2961340..aa2b24422 100644 --- a/ui/app/app.js +++ b/ui/app/app.js @@ -76,7 +76,7 @@ class App extends Component { h(Authenticated, { path: REVEAL_SEED_ROUTE, exact, component: RevealSeedConfirmation }), h(Authenticated, { path: SETTINGS_ROUTE, component: Settings }), h(Authenticated, { path: NOTICE_ROUTE, exact, component: NoticeScreen }), - h(Authenticated, { path: CONFIRM_TRANSACTION_ROUTE, component: ConfirmTxScreen }), + h(Authenticated, { path: `${CONFIRM_TRANSACTION_ROUTE}/:id?`, component: ConfirmTxScreen }), h(Authenticated, { path: SEND_ROUTE, exact, component: SendTransactionScreen2 }), h(Authenticated, { path: ADD_TOKEN_ROUTE, exact, component: AddTokenPage }), h(Authenticated, { path: CONFIRM_ADD_TOKEN_ROUTE, exact, component: ConfirmAddTokenPage }), diff --git a/ui/app/components/index.scss b/ui/app/components/index.scss index f3fe823f8..e69acff63 100644 --- a/ui/app/components/index.scss +++ b/ui/app/components/index.scss @@ -3,3 +3,5 @@ @import './info-box/index'; @import './pages/index'; + +@import './modals/index'; diff --git a/ui/app/components/modals/index.scss b/ui/app/components/modals/index.scss new file mode 100644 index 000000000..ec6207f7e --- /dev/null +++ b/ui/app/components/modals/index.scss @@ -0,0 +1 @@ +@import './transaction-confirmed/index'; diff --git a/ui/app/components/modals/modal.js b/ui/app/components/modals/modal.js index 43dcd20ae..841189277 100644 --- a/ui/app/components/modals/modal.js +++ b/ui/app/components/modals/modal.js @@ -20,6 +20,7 @@ const HideTokenConfirmationModal = require('./hide-token-confirmation-modal') const CustomizeGasModal = require('../customize-gas-modal') const NotifcationModal = require('./notification-modal') const ConfirmResetAccount = require('./notification-modals/confirm-reset-account') +const TransactionConfirmed = require('./transaction-confirmed') const accountModalStyle = { mobileModalStyle: { @@ -265,6 +266,37 @@ const MODALS = { }, }, + TRANSACTION_CONFIRMED: { + disableBackdropClick: true, + contents: [ + h(TransactionConfirmed, {}, []), + ], + mobileModalStyle: { + width: '100%', + height: '100%', + transform: 'none', + left: '0', + right: '0', + margin: '0 auto', + boxShadow: '0 0 7px 0 rgba(0,0,0,0.08)', + top: '0', + display: 'flex', + }, + laptopModalStyle: { + width: '344px', + transform: 'translate3d(-50%, 0, 0px)', + top: '15%', + border: '1px solid #CCCFD1', + borderRadius: '8px', + backgroundColor: '#FFFFFF', + boxShadow: '0 2px 22px 0 rgba(0,0,0,0.2)', + }, + contentStyle: { + borderRadius: '8px', + height: '100%', + }, + }, + DEFAULT: { contents: [], mobileModalStyle: {}, @@ -306,7 +338,7 @@ module.exports = connect(mapStateToProps, mapDispatchToProps)(Modal) Modal.prototype.render = function () { const modal = MODALS[this.props.modalState.name || 'DEFAULT'] - const children = modal.contents + const { contents: children, disableBackdropClick = false } = modal const modalStyle = modal[isMobileView() ? 'mobileModalStyle' : 'laptopModalStyle'] const contentStyle = modal.contentStyle || {} @@ -326,6 +358,7 @@ Modal.prototype.render = function () { modalStyle, contentStyle, backdropStyle: BACKDROPSTYLE, + closeOnClick: !disableBackdropClick, }, children, ) diff --git a/ui/app/components/modals/transaction-confirmed/index.js b/ui/app/components/modals/transaction-confirmed/index.js new file mode 100644 index 000000000..c8db91388 --- /dev/null +++ b/ui/app/components/modals/transaction-confirmed/index.js @@ -0,0 +1,2 @@ +import TransactionConfirmed from './transaction-confirmed.container' +module.exports = TransactionConfirmed diff --git a/ui/app/components/modals/transaction-confirmed/index.scss b/ui/app/components/modals/transaction-confirmed/index.scss new file mode 100644 index 000000000..f8cd1f212 --- /dev/null +++ b/ui/app/components/modals/transaction-confirmed/index.scss @@ -0,0 +1,21 @@ +.transaction-confirmed { + display: flex; + flex-direction: column; + align-items: center; + padding: 32px; + + &__title { + font-size: 2rem; + padding: 16px 0; + } + + &__description { + text-align: center; + font-size: .875rem; + line-height: 1.5rem; + } + + @media screen and (max-width: 575px) { + justify-content: center; + } +} diff --git a/ui/app/components/modals/transaction-confirmed/transaction-confirmed.component.js b/ui/app/components/modals/transaction-confirmed/transaction-confirmed.component.js new file mode 100644 index 000000000..8d3b288ae --- /dev/null +++ b/ui/app/components/modals/transaction-confirmed/transaction-confirmed.component.js @@ -0,0 +1,46 @@ +import React, { Component } from 'react' +import PropTypes from 'prop-types' +import Button from '../../button' + +class TransactionConfirmed extends Component { + render () { + const { t } = this.context + + return ( + <div className="page-container page-container--full-width page-container--full-height"> + <div className="page-container__content transaction-confirmed"> + <img src="images/check-icon.svg" /> + <div className="transaction-confirmed__title"> + { `${t('confirmed')}!` } + </div> + <div className="transaction-confirmed__description"> + { t('initialTransactionConfirmed') } + </div> + </div> + <div className="page-container__footer"> + <Button + type="primary" + className="page-container__footer-button" + onClick={() => { + this.props.hideModal() + this.props.onHide() + }} + > + { t('ok') } + </Button> + </div> + </div> + ) + } +} + +TransactionConfirmed.propTypes = { + hideModal: PropTypes.func.isRequired, + onHide: PropTypes.func.isRequired, +} + +TransactionConfirmed.contextTypes = { + t: PropTypes.func, +} + +export default TransactionConfirmed diff --git a/ui/app/components/modals/transaction-confirmed/transaction-confirmed.container.js b/ui/app/components/modals/transaction-confirmed/transaction-confirmed.container.js new file mode 100644 index 000000000..63872f7f2 --- /dev/null +++ b/ui/app/components/modals/transaction-confirmed/transaction-confirmed.container.js @@ -0,0 +1,20 @@ +import { connect } from 'react-redux' +import TransactionConfirmed from './transaction-confirmed.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()), + } +} + +export default connect(mapStateToProps, mapDispatchToProps)(TransactionConfirmed) diff --git a/ui/app/components/pages/add-token/token-list/token-list-placeholder/index.scss b/ui/app/components/pages/add-token/token-list/token-list-placeholder/index.scss index 9d0f4be32..cc495dfb0 100644 --- a/ui/app/components/pages/add-token/token-list/token-list-placeholder/index.scss +++ b/ui/app/components/pages/add-token/token-list/token-list-placeholder/index.scss @@ -11,6 +11,10 @@ width: 50%; text-align: center; margin-top: 8px; + + @media screen and (max-width: 575px) { + width: 60%; + } } &__link { diff --git a/ui/app/components/pages/unlock-page/unlock-page.component.js b/ui/app/components/pages/unlock-page/unlock-page.component.js index a2f009d8f..b6384b32d 100644 --- a/ui/app/components/pages/unlock-page/unlock-page.component.js +++ b/ui/app/components/pages/unlock-page/unlock-page.component.js @@ -37,8 +37,8 @@ class UnlockPage extends Component { tryUnlockMetamask (password) { const { tryUnlockMetamask, history } = this.props tryUnlockMetamask(password) - .then(() => history.push(DEFAULT_ROUTE)) .catch(({ message }) => this.setState({ error: message })) + .then(() => history.push(DEFAULT_ROUTE)) } handleSubmit (event) { @@ -55,8 +55,8 @@ class UnlockPage extends Component { this.setState({ error: null }) tryUnlockMetamask(password) - .then(() => history.push(DEFAULT_ROUTE)) .catch(({ message }) => this.setState({ error: message })) + .then(() => history.push(DEFAULT_ROUTE)) } handleInputChange ({ target }) { diff --git a/ui/app/components/pending-tx/confirm-send-ether.js b/ui/app/components/pending-tx/confirm-send-ether.js index c07c96ccc..5ad35c269 100644 --- a/ui/app/components/pending-tx/confirm-send-ether.js +++ b/ui/app/components/pending-tx/confirm-send-ether.js @@ -291,18 +291,48 @@ ConfirmSendEther.prototype.convertToRenderableCurrency = function (value, curren : value } -ConfirmSendEther.prototype.editTransaction = function (txMeta) { +ConfirmSendEther.prototype.editTransaction = function () { const { editTransaction, history } = this.props + const txMeta = this.gatherTxMeta() editTransaction(txMeta) history.push(SEND_ROUTE) } -ConfirmSendEther.prototype.renderNetworkDisplay = function () { +ConfirmSendEther.prototype.renderHeaderRow = function (isTxReprice) { const windowType = window.METAMASK_UI_TYPE + const isFullScreen = windowType !== ENVIRONMENT_TYPE_NOTIFICATION && + windowType !== ENVIRONMENT_TYPE_POPUP - return (windowType === ENVIRONMENT_TYPE_NOTIFICATION || windowType === ENVIRONMENT_TYPE_POPUP) - ? h(NetworkDisplay) - : null + if (isTxReprice && isFullScreen) { + return null + } + + return ( + h('.page-container__header-row', [ + h('span.page-container__back-button', { + onClick: () => this.editTransaction(), + style: { + visibility: isTxReprice ? 'hidden' : 'initial', + }, + }, 'Edit'), + !isFullScreen && h(NetworkDisplay), + ]) + ) +} + +ConfirmSendEther.prototype.renderHeader = function (isTxReprice) { + const title = isTxReprice ? this.context.t('speedUpTitle') : this.context.t('confirm') + const subtitle = isTxReprice + ? this.context.t('speedUpSubtitle') + : this.context.t('pleaseReviewTransaction') + + return ( + h('.page-container__header', [ + this.renderHeaderRow(isTxReprice), + h('.page-container__title', title), + h('.page-container__subtitle', subtitle), + ]) + ) } ConfirmSendEther.prototype.render = function () { @@ -320,6 +350,7 @@ ConfirmSendEther.prototype.render = function () { }, } = this.props const txMeta = this.gatherTxMeta() + const isTxReprice = Boolean(txMeta.lastGasPrice) const txParams = txMeta.txParams || {} const { @@ -338,11 +369,6 @@ ConfirmSendEther.prototype.render = function () { totalInETH, } = this.getData() - const title = txMeta.lastGasPrice ? 'Reprice Transaction' : 'Confirm' - const subtitle = txMeta.lastGasPrice - ? 'Increase your gas fee to attempt to overwrite and speed up your transaction' - : 'Please review your transaction.' - const convertedAmountInFiat = this.convertToRenderableCurrency(amountInFIAT, currentCurrency) const convertedTotalInFiat = this.convertToRenderableCurrency(totalInFIAT, currentCurrency) @@ -362,19 +388,7 @@ ConfirmSendEther.prototype.render = function () { return ( // Main Send token Card h('.page-container', [ - h('.page-container__header', [ - h('.page-container__header-row', [ - h('span.page-container__back-button', { - onClick: () => this.editTransaction(txMeta), - style: { - visibility: !txMeta.lastGasPrice ? 'initial' : 'hidden', - }, - }, 'Edit'), - this.renderNetworkDisplay(), - ]), - h('.page-container__title', title), - h('.page-container__subtitle', subtitle), - ]), + this.renderHeader(isTxReprice), h('.page-container__content', [ h(SenderToRecipient, { senderName: fromName, diff --git a/ui/app/components/pending-tx/confirm-send-token.js b/ui/app/components/pending-tx/confirm-send-token.js index 656093b3d..ddaa13d22 100644 --- a/ui/app/components/pending-tx/confirm-send-token.js +++ b/ui/app/components/pending-tx/confirm-send-token.js @@ -12,6 +12,7 @@ const actions = require('../../actions') const clone = require('clone') const Identicon = require('../identicon') const GasFeeDisplay = require('../send/gas-fee-display-v2.js') +const NetworkDisplay = require('../network-display') const ethUtil = require('ethereumjs-util') const BN = ethUtil.BN const { @@ -39,6 +40,11 @@ const { } = require('../../selectors') const { SEND_ROUTE, DEFAULT_ROUTE } = require('../../routes') +const { + ENVIRONMENT_TYPE_POPUP, + ENVIRONMENT_TYPE_NOTIFICATION, +} = require('../../../../app/scripts/lib/enums') + ConfirmSendToken.contextTypes = { t: PropTypes.func, } @@ -430,6 +436,43 @@ ConfirmSendToken.prototype.convertToRenderableCurrency = function (value, curren : value } +ConfirmSendToken.prototype.renderHeaderRow = function (isTxReprice) { + const windowType = window.METAMASK_UI_TYPE + const isFullScreen = windowType !== ENVIRONMENT_TYPE_NOTIFICATION && + windowType !== ENVIRONMENT_TYPE_POPUP + + if (isTxReprice && isFullScreen) { + return null + } + + return ( + h('.page-container__header-row', [ + h('span.page-container__back-button', { + onClick: () => this.editTransaction(), + style: { + visibility: isTxReprice ? 'hidden' : 'initial', + }, + }, 'Edit'), + !isFullScreen && h(NetworkDisplay), + ]) + ) +} + +ConfirmSendToken.prototype.renderHeader = function (isTxReprice) { + const title = isTxReprice ? this.context.t('speedUpTitle') : this.context.t('confirm') + const subtitle = isTxReprice + ? this.context.t('speedUpSubtitle') + : this.context.t('pleaseReviewTransaction') + + return ( + h('.page-container__header', [ + this.renderHeaderRow(isTxReprice), + h('.page-container__title', title), + h('.page-container__subtitle', subtitle), + ]) + ) +} + ConfirmSendToken.prototype.render = function () { const txMeta = this.gatherTxMeta() const { @@ -443,25 +486,13 @@ ConfirmSendToken.prototype.render = function () { }, } = this.getData() - this.inputs = [] - const isTxReprice = Boolean(txMeta.lastGasPrice) - const title = isTxReprice ? this.context.t('reprice_title') : this.context.t('confirm') - const subtitle = isTxReprice - ? this.context.t('reprice_subtitle') - : this.context.t('pleaseReviewTransaction') return ( h('div.confirm-screen-container.confirm-send-token', [ // Main Send token Card h('div.page-container', [ - h('div.page-container__header', [ - !txMeta.lastGasPrice && h('button.confirm-screen-back-button', { - onClick: () => this.editTransaction(txMeta), - }, this.context.t('edit')), - h('div.page-container__title', title), - h('div.page-container__subtitle', subtitle), - ]), + this.renderHeader(isTxReprice), h('.page-container__content', [ h('div.flex-row.flex-center.confirm-screen-identicons', [ h('div.confirm-screen-account-wrapper', [ diff --git a/ui/app/components/text-field/text-field.component.js b/ui/app/components/text-field/text-field.component.js index b695a449a..2c72d8124 100644 --- a/ui/app/components/text-field/text-field.component.js +++ b/ui/app/components/text-field/text-field.component.js @@ -1,8 +1,15 @@ -import React, { Component } from 'react' +import React from 'react' import PropTypes from 'prop-types' import { withStyles } from '@material-ui/core/styles' import { default as MaterialTextField } from '@material-ui/core/TextField' +const inputLabelBase = { + transform: 'none', + transition: 'none', + position: 'initial', + color: '#5b5b5b', +} + const styles = { materialLabel: { '&$materialFocused': { @@ -46,57 +53,57 @@ const styles = { border: '1px solid #2f9ae0', }, }, + largeInputLabel: { + ...inputLabelBase, + fontSize: '1rem', + }, inputLabel: { + ...inputLabelBase, fontSize: '.75rem', - transform: 'none', - transition: 'none', - position: 'initial', - color: '#5b5b5b', }, } -class TextField extends Component { - static defaultProps = { - error: null, - } +const TextField = props => { + const { error, classes, material, startAdornment, largeLabel, ...textFieldProps } = props - static propTypes = { - error: PropTypes.string, - classes: PropTypes.object, - material: PropTypes.bool, - startAdornment: PropTypes.element, - } + return ( + <MaterialTextField + error={Boolean(error)} + helperText={error} + InputLabelProps={{ + shrink: material ? undefined : true, + className: material ? '' : (largeLabel ? classes.largeInputLabel : classes.inputLabel), + FormLabelClasses: { + root: material ? classes.materialLabel : classes.formLabel, + focused: material ? classes.materialFocused : classes.formLabelFocused, + error: classes.materialError, + }, + }} + InputProps={{ + startAdornment: startAdornment || undefined, + disableUnderline: !material, + classes: { + root: material ? '' : classes.inputRoot, + input: material ? '' : classes.input, + underline: material ? classes.materialUnderline : '', + focused: material ? '' : classes.inputFocused, + }, + }} + {...textFieldProps} + /> + ) +} - render () { - const { error, classes, material, startAdornment, ...textFieldProps } = this.props +TextField.defaultProps = { + error: null, +} - return ( - <MaterialTextField - error={Boolean(error)} - helperText={error} - InputLabelProps={{ - shrink: material ? undefined : true, - className: material ? '' : classes.inputLabel, - FormLabelClasses: { - root: material ? classes.materialLabel : classes.formLabel, - focused: material ? classes.materialFocused : classes.formLabelFocused, - error: classes.materialError, - }, - }} - InputProps={{ - startAdornment: startAdornment || undefined, - disableUnderline: !material, - classes: { - root: material ? '' : classes.inputRoot, - input: material ? '' : classes.input, - underline: material ? classes.materialUnderline : '', - focused: material ? '' : classes.inputFocused, - }, - }} - {...textFieldProps} - /> - ) - } +TextField.propTypes = { + error: PropTypes.string, + classes: PropTypes.object, + material: PropTypes.bool, + startAdornment: PropTypes.element, + largeLabel: PropTypes.bool, } export default withStyles(styles)(TextField) diff --git a/ui/app/components/text-field/text-field.stories.js b/ui/app/components/text-field/text-field.stories.js index ee3e5faaf..c00873b8a 100644 --- a/ui/app/components/text-field/text-field.stories.js +++ b/ui/app/components/text-field/text-field.stories.js @@ -22,3 +22,32 @@ storiesOf('TextField', module) error="Invalid value" /> ) + .add('Mascara text', () => + <TextField + label="Text" + type="text" + largeLabel + /> + ) + .add('Material text', () => + <TextField + label="Text" + type="text" + material + /> + ) + .add('Material password', () => + <TextField + label="Password" + type="password" + material + /> + ) + .add('Material error', () => + <TextField + type="text" + label="Name" + error="Invalid value" + material + /> + ) diff --git a/ui/app/components/tx-list-item.js b/ui/app/components/tx-list-item.js index bd4ea80a6..ef441ff73 100644 --- a/ui/app/components/tx-list-item.js +++ b/ui/app/components/tx-list-item.js @@ -1,5 +1,7 @@ const Component = require('react').Component const PropTypes = require('prop-types') +const { compose } = require('recompose') +const { withRouter } = require('react-router-dom') const h = require('react-hyperscript') const connect = require('react-redux').connect const inherits = require('util').inherits @@ -16,13 +18,16 @@ const { conversionUtil, multiplyCurrencies } = require('../conversion-util') const { calcTokenAmount } = require('../token-util') const { getCurrentCurrency } = require('../selectors') +const { CONFIRM_TRANSACTION_ROUTE } = require('../routes') TxListItem.contextTypes = { t: PropTypes.func, } -module.exports = connect(mapStateToProps, mapDispatchToProps)(TxListItem) - +module.exports = compose( + withRouter, + connect(mapStateToProps, mapDispatchToProps) +)(TxListItem) function mapStateToProps (state) { return { @@ -216,6 +221,7 @@ TxListItem.prototype.setSelectedToken = function (tokenAddress) { TxListItem.prototype.resubmit = function () { const { transactionId } = this.props this.props.retryTransaction(transactionId) + .then(id => this.props.history.push(`${CONFIRM_TRANSACTION_ROUTE}/${id}`)) } TxListItem.prototype.render = function () { diff --git a/ui/app/conf-tx.js b/ui/app/conf-tx.js index fb38aaa76..461587cb1 100644 --- a/ui/app/conf-tx.js +++ b/ui/app/conf-tx.js @@ -7,6 +7,7 @@ const { compose } = require('recompose') const actions = require('./actions') const txHelper = require('../lib/tx-helper') const log = require('loglevel') +const R = require('ramda') const PendingTx = require('./components/pending-tx') const SignatureRequest = require('./components/signature-request') @@ -87,37 +88,74 @@ ConfirmTxScreen.prototype.componentDidUpdate = function (prevProps) { network, selectedAddressTxList, send, + history, + match: { params: { id: transactionId } = {} }, } = this.props - const { index: prevIndex, unapprovedTxs: prevUnapprovedTxs } = prevProps - const prevUnconfTxList = txHelper(prevUnapprovedTxs, {}, {}, {}, network) - const prevTxData = prevUnconfTxList[prevIndex] || {} - const prevTx = selectedAddressTxList.find(({ id }) => id === prevTxData.id) || {} + + let prevTx + + if (transactionId) { + prevTx = R.find(({ id }) => id + '' === transactionId)(selectedAddressTxList) + } else { + const { index: prevIndex, unapprovedTxs: prevUnapprovedTxs } = prevProps + const prevUnconfTxList = txHelper(prevUnapprovedTxs, {}, {}, {}, network) + const prevTxData = prevUnconfTxList[prevIndex] || {} + prevTx = selectedAddressTxList.find(({ id }) => id === prevTxData.id) || {} + } + const unconfTxList = txHelper(unapprovedTxs, {}, {}, {}, network) - if (unconfTxList.length === 0 && - (prevTx.status === 'dropped' || !send.to && this.getUnapprovedMessagesTotal() === 0)) { + if (prevTx.status === 'dropped') { + this.props.dispatch(actions.showModal({ + name: 'TRANSACTION_CONFIRMED', + onHide: () => history.push(DEFAULT_ROUTE), + })) + + return + } + + if (unconfTxList.length === 0 && !send.to && this.getUnapprovedMessagesTotal() === 0) { this.props.history.push(DEFAULT_ROUTE) } } -ConfirmTxScreen.prototype.render = function () { - const props = this.props +ConfirmTxScreen.prototype.getTxData = function () { const { network, + index, + unapprovedTxs, + unapprovedMsgs, + unapprovedPersonalMsgs, + unapprovedTypedMessages, + match: { params: { id: transactionId } = {} }, + } = this.props + + const unconfTxList = txHelper( unapprovedTxs, - currentCurrency, unapprovedMsgs, unapprovedPersonalMsgs, unapprovedTypedMessages, + network + ) + + log.info(`rendering a combined ${unconfTxList.length} unconf msgs & txs`) + + return transactionId + ? R.find(({ id }) => id + '' === transactionId)(unconfTxList) + : unconfTxList[index] +} + +ConfirmTxScreen.prototype.render = function () { + const props = this.props + const { + currentCurrency, conversionRate, blockGasLimit, // provider, // computedBalances, } = props - var unconfTxList = txHelper(unapprovedTxs, unapprovedMsgs, unapprovedPersonalMsgs, unapprovedTypedMessages, network) - - var txData = unconfTxList[props.index] || {} + var txData = this.getTxData() || {} var txParams = txData.params || {} // var isNotification = isPopupOrNotification() === 'notification' @@ -136,7 +174,6 @@ ConfirmTxScreen.prototype.render = function () { ]), */ - log.info(`rendering a combined ${unconfTxList.length} unconf msg & txs`) return currentTxView({ // Properties diff --git a/ui/app/reducers/app.js b/ui/app/reducers/app.js index 2b39eb8db..4e9d0848c 100644 --- a/ui/app/reducers/app.js +++ b/ui/app/reducers/app.js @@ -42,6 +42,7 @@ function reduceApp (state, action) { open: false, modalState: { name: null, + props: {}, }, previousModalState: { name: null, @@ -88,13 +89,17 @@ function reduceApp (state, action) { // modal methods: case actions.MODAL_OPEN: + const { name, ...modalProps } = action.payload + return extend(appState, { - modal: Object.assign( - state.appState.modal, - { open: true }, - { modalState: action.payload }, - { previousModalState: appState.modal.modalState}, - ), + modal: { + open: true, + modalState: { + name: name, + props: { ...modalProps }, + }, + previousModalState: { ...appState.modal.modalState }, + }, }) case actions.MODAL_CLOSE: @@ -102,7 +107,7 @@ function reduceApp (state, action) { modal: Object.assign( state.appState.modal, { open: false }, - { modalState: { name: null } }, + { modalState: { name: null, props: {} } }, { previousModalState: appState.modal.modalState}, ), }) |