diff options
author | Kevin Serrano <kevgagser@gmail.com> | 2018-10-15 08:32:29 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-10-15 08:32:29 +0800 |
commit | 85884b21afe6e5e85b58123b24e72776d1437cc6 (patch) | |
tree | 6dbec947089691c0dffd5febbe6e92c1f712e40f /ui/app/components/pages | |
parent | db981b827b07c946e17d3a8aeaefd2143c236ef7 (diff) | |
parent | 7f6b488c04e6ea12b2ef24ac936b6a236ad904c2 (diff) | |
download | tangerine-wallet-browser-85884b21afe6e5e85b58123b24e72776d1437cc6.tar.gz tangerine-wallet-browser-85884b21afe6e5e85b58123b24e72776d1437cc6.tar.zst tangerine-wallet-browser-85884b21afe6e5e85b58123b24e72776d1437cc6.zip |
Merge pull request #5512 from MetaMask/v4.14.0
Version 4.14.0
Diffstat (limited to 'ui/app/components/pages')
39 files changed, 1273 insertions, 984 deletions
diff --git a/ui/app/components/pages/add-token/add-token.component.js b/ui/app/components/pages/add-token/add-token.component.js index bcb93d401..3612e676c 100644 --- a/ui/app/components/pages/add-token/add-token.component.js +++ b/ui/app/components/pages/add-token/add-token.component.js @@ -1,14 +1,14 @@ import React, { Component } from 'react' -import classnames from 'classnames' import PropTypes from 'prop-types' import ethUtil from 'ethereumjs-util' import { checkExistingAddresses } from './util' import { tokenInfoGetter } from '../../../token-util' import { DEFAULT_ROUTE, CONFIRM_ADD_TOKEN_ROUTE } from '../../../routes' -import Button from '../../button' import TextField from '../../text-field' import TokenList from './token-list' import TokenSearch from './token-search' +import PageContainer from '../../page-container' +import { Tabs, Tab } from '../../tabs' const emptyAddr = '0x0000000000000000000000000000000000000000' const SEARCH_TAB = 'SEARCH' @@ -206,7 +206,7 @@ class AddToken extends Component { const validDecimals = customDecimals !== null && customDecimals !== '' && customDecimals >= 0 && - customDecimals < 36 + customDecimals <= 36 let customDecimalsError = null if (!validDecimals) { @@ -285,65 +285,33 @@ class AddToken extends Component { ) } + renderTabs () { + return ( + <Tabs> + <Tab name={this.context.t('search')}> + { this.renderSearchToken() } + </Tab> + <Tab name={this.context.t('customToken')}> + { this.renderCustomTokenForm() } + </Tab> + </Tabs> + ) + } + render () { - const { displayedTab } = this.state const { history, clearPendingTokens } = this.props return ( - <div className="page-container"> - <div className="page-container__header page-container__header--no-padding-bottom"> - <div className="page-container__title"> - { this.context.t('addTokens') } - </div> - <div className="page-container__tabs"> - <div - className={classnames('page-container__tab', { - 'page-container__tab--selected': displayedTab === SEARCH_TAB, - })} - onClick={() => this.setState({ displayedTab: SEARCH_TAB })} - > - { this.context.t('search') } - </div> - <div - className={classnames('page-container__tab', { - 'page-container__tab--selected': displayedTab === CUSTOM_TOKEN_TAB, - })} - onClick={() => this.setState({ displayedTab: CUSTOM_TOKEN_TAB })} - > - { this.context.t('customToken') } - </div> - </div> - </div> - <div className="page-container__content"> - { - displayedTab === CUSTOM_TOKEN_TAB - ? this.renderCustomTokenForm() - : this.renderSearchToken() - } - </div> - <div className="page-container__footer"> - <Button - type="default" - large - className="page-container__footer-button" - onClick={() => { - clearPendingTokens() - history.push(DEFAULT_ROUTE) - }} - > - { this.context.t('cancel') } - </Button> - <Button - type="primary" - large - className="page-container__footer-button" - onClick={() => this.handleNext()} - disabled={this.hasError() || !this.hasSelected()} - > - { this.context.t('next') } - </Button> - </div> - </div> + <PageContainer + title={this.context.t('addTokens')} + tabsComponent={this.renderTabs()} + onSubmit={() => this.handleNext()} + disabled={this.hasError() || !this.hasSelected()} + onCancel={() => { + clearPendingTokens() + history.push(DEFAULT_ROUTE) + }} + /> ) } } diff --git a/ui/app/components/pages/add-token/token-list/token-list-placeholder/token-list-placeholder.component.js b/ui/app/components/pages/add-token/token-list/token-list-placeholder/token-list-placeholder.component.js index 1611f817b..20f550927 100644 --- a/ui/app/components/pages/add-token/token-list/token-list-placeholder/token-list-placeholder.component.js +++ b/ui/app/components/pages/add-token/token-list/token-list-placeholder/token-list-placeholder.component.js @@ -15,7 +15,7 @@ export default class TokenListPlaceholder extends Component { </div> <a className="token-list-placeholder__link" - href="https://consensys.zendesk.com/hc/en-us/articles/360004135092" + href="https://metamask.zendesk.com/hc/en-us/articles/360015489031" target="_blank" rel="noopener noreferrer" > diff --git a/ui/app/components/pages/confirm-add-suggested-token/confirm-add-suggested-token.component.js b/ui/app/components/pages/confirm-add-suggested-token/confirm-add-suggested-token.component.js new file mode 100644 index 000000000..ee5d6fa64 --- /dev/null +++ b/ui/app/components/pages/confirm-add-suggested-token/confirm-add-suggested-token.component.js @@ -0,0 +1,122 @@ +import React, { Component } from 'react' +import PropTypes from 'prop-types' +import { DEFAULT_ROUTE } from '../../../routes' +import Button from '../../button' +import Identicon from '../../../components/identicon' +import TokenBalance from '../../token-balance' + +export default class ConfirmAddSuggestedToken extends Component { + static contextTypes = { + t: PropTypes.func, + } + + static propTypes = { + history: PropTypes.object, + clearPendingTokens: PropTypes.func, + addToken: PropTypes.func, + pendingTokens: PropTypes.object, + removeSuggestedTokens: PropTypes.func, + } + + componentDidMount () { + const { pendingTokens = {}, history } = this.props + + if (Object.keys(pendingTokens).length === 0) { + history.push(DEFAULT_ROUTE) + } + } + + getTokenName (name, symbol) { + return typeof name === 'undefined' + ? symbol + : `${name} (${symbol})` + } + + render () { + const { addToken, pendingTokens, removeSuggestedTokens, history } = this.props + const pendingTokenKey = Object.keys(pendingTokens)[0] + const pendingToken = pendingTokens[pendingTokenKey] + + return ( + <div className="page-container"> + <div className="page-container__header"> + <div className="page-container__title"> + { this.context.t('addSuggestedTokens') } + </div> + <div className="page-container__subtitle"> + { this.context.t('likeToAddTokens') } + </div> + </div> + <div className="page-container__content"> + <div className="confirm-add-token"> + <div className="confirm-add-token__header"> + <div className="confirm-add-token__token"> + { this.context.t('token') } + </div> + <div className="confirm-add-token__balance"> + { this.context.t('balance') } + </div> + </div> + <div className="confirm-add-token__token-list"> + { + Object.entries(pendingTokens) + .map(([ address, token ]) => { + const { name, symbol, image } = token + + return ( + <div + className="confirm-add-token__token-list-item" + key={address} + > + <div className="confirm-add-token__token confirm-add-token__data"> + <Identicon + className="confirm-add-token__token-icon" + diameter={48} + address={address} + image={image} + /> + <div className="confirm-add-token__name"> + { this.getTokenName(name, symbol) } + </div> + </div> + <div className="confirm-add-token__balance"> + <TokenBalance token={token} /> + </div> + </div> + ) + }) + } + </div> + </div> + </div> + <div className="page-container__footer"> + <header> + <Button + type="default" + large + className="page-container__footer-button" + onClick={() => { + removeSuggestedTokens() + .then(() => history.push(DEFAULT_ROUTE)) + }} + > + { this.context.t('cancel') } + </Button> + <Button + type="primary" + large + className="page-container__footer-button" + onClick={() => { + addToken(pendingToken) + .then(() => removeSuggestedTokens()) + .then(() => history.push(DEFAULT_ROUTE)) + }} + > + { this.context.t('addToken') } + </Button> + </header> + </div> + </div> + ) + } +} diff --git a/ui/app/components/pages/confirm-add-suggested-token/confirm-add-suggested-token.container.js b/ui/app/components/pages/confirm-add-suggested-token/confirm-add-suggested-token.container.js new file mode 100644 index 000000000..1f2737e52 --- /dev/null +++ b/ui/app/components/pages/confirm-add-suggested-token/confirm-add-suggested-token.container.js @@ -0,0 +1,29 @@ +import { connect } from 'react-redux' +import { compose } from 'recompose' +import ConfirmAddSuggestedToken from './confirm-add-suggested-token.component' +import { withRouter } from 'react-router-dom' + +const extend = require('xtend') + +const { addToken, removeSuggestedTokens } = require('../../../actions') + +const mapStateToProps = ({ metamask }) => { + const { pendingTokens, suggestedTokens } = metamask + const params = extend(pendingTokens, suggestedTokens) + + return { + pendingTokens: params, + } +} + +const mapDispatchToProps = dispatch => { + return { + addToken: ({address, symbol, decimals, image}) => dispatch(addToken(address, symbol, decimals, image)), + removeSuggestedTokens: () => dispatch(removeSuggestedTokens()), + } +} + +export default compose( + withRouter, + connect(mapStateToProps, mapDispatchToProps) +)(ConfirmAddSuggestedToken) diff --git a/ui/app/components/pages/confirm-add-suggested-token/index.js b/ui/app/components/pages/confirm-add-suggested-token/index.js new file mode 100644 index 000000000..2ca56b43c --- /dev/null +++ b/ui/app/components/pages/confirm-add-suggested-token/index.js @@ -0,0 +1,2 @@ +import ConfirmAddSuggestedToken from './confirm-add-suggested-token.container' +module.exports = ConfirmAddSuggestedToken diff --git a/ui/app/components/pages/confirm-add-token/confirm-add-token.component.js b/ui/app/components/pages/confirm-add-token/confirm-add-token.component.js index 65d654b92..d3fec79d7 100644 --- a/ui/app/components/pages/confirm-add-token/confirm-add-token.component.js +++ b/ui/app/components/pages/confirm-add-token/confirm-add-token.component.js @@ -2,8 +2,8 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' import { DEFAULT_ROUTE, ADD_TOKEN_ROUTE } from '../../../routes' import Button from '../../button' -import Identicon from '../../../components/identicon' -import TokenBalance from './token-balance' +import Identicon from '../../identicon' +import TokenBalance from '../../token-balance' export default class ConfirmAddToken extends Component { static contextTypes = { @@ -86,28 +86,30 @@ export default class ConfirmAddToken extends Component { </div> </div> <div className="page-container__footer"> - <Button - type="default" - large - className="page-container__footer-button" - onClick={() => history.push(ADD_TOKEN_ROUTE)} - > - { this.context.t('back') } - </Button> - <Button - type="primary" - large - className="page-container__footer-button" - onClick={() => { - addTokens(pendingTokens) - .then(() => { - clearPendingTokens() - history.push(DEFAULT_ROUTE) - }) - }} - > - { this.context.t('addTokens') } - </Button> + <header> + <Button + type="default" + large + className="page-container__footer-button" + onClick={() => history.push(ADD_TOKEN_ROUTE)} + > + { this.context.t('back') } + </Button> + <Button + type="primary" + large + className="page-container__footer-button" + onClick={() => { + addTokens(pendingTokens) + .then(() => { + clearPendingTokens() + history.push(DEFAULT_ROUTE) + }) + }} + > + { this.context.t('addTokens') } + </Button> + </header> </div> </div> ) diff --git a/ui/app/components/pages/confirm-add-token/token-balance/index.js b/ui/app/components/pages/confirm-add-token/token-balance/index.js deleted file mode 100644 index 6fb5c8223..000000000 --- a/ui/app/components/pages/confirm-add-token/token-balance/index.js +++ /dev/null @@ -1,2 +0,0 @@ -import TokenBalance from './token-balance.container' -module.exports = TokenBalance diff --git a/ui/app/components/pages/confirm-add-token/token-balance/token-balance.component.js b/ui/app/components/pages/confirm-add-token/token-balance/token-balance.component.js deleted file mode 100644 index 976788d4c..000000000 --- a/ui/app/components/pages/confirm-add-token/token-balance/token-balance.component.js +++ /dev/null @@ -1,16 +0,0 @@ -import React, { Component } from 'react' -import PropTypes from 'prop-types' - -export default class TokenBalance extends Component { - static propTypes = { - string: PropTypes.string, - symbol: PropTypes.string, - error: PropTypes.string, - } - - render () { - return ( - <div className="hide-text-overflow">{ this.props.string }</div> - ) - } -} diff --git a/ui/app/components/pages/confirm-add-token/token-balance/token-balance.container.js b/ui/app/components/pages/confirm-add-token/token-balance/token-balance.container.js deleted file mode 100644 index bc1289ce1..000000000 --- a/ui/app/components/pages/confirm-add-token/token-balance/token-balance.container.js +++ /dev/null @@ -1,16 +0,0 @@ -import { connect } from 'react-redux' -import { compose } from 'recompose' -import withTokenTracker from '../../../../helpers/with-token-tracker' -import TokenBalance from './token-balance.component' -import selectors from '../../../../selectors' - -const mapStateToProps = state => { - return { - userAddress: selectors.getSelectedAddress(state), - } -} - -export default compose( - connect(mapStateToProps), - withTokenTracker -)(TokenBalance) diff --git a/ui/app/components/pages/confirm-transaction-base/confirm-transaction-base.component.js b/ui/app/components/pages/confirm-transaction-base/confirm-transaction-base.component.js index 961aa304e..707dad62d 100644 --- a/ui/app/components/pages/confirm-transaction-base/confirm-transaction-base.component.js +++ b/ui/app/components/pages/confirm-transaction-base/confirm-transaction-base.component.js @@ -8,6 +8,7 @@ import { INSUFFICIENT_FUNDS_ERROR_KEY, TRANSACTION_ERROR_KEY, } from '../../../constants/error-keys' +import { CONFIRMED_STATUS, DROPPED_STATUS } from '../../../constants/transactions' export default class ConfirmTransactionBase extends Component { static contextTypes = { @@ -21,6 +22,7 @@ export default class ConfirmTransactionBase extends Component { // Redux props balance: PropTypes.string, cancelTransaction: PropTypes.func, + cancelAllTransactions: PropTypes.func, clearConfirmTransaction: PropTypes.func, clearSend: PropTypes.func, conversionRate: PropTypes.number, @@ -38,15 +40,18 @@ export default class ConfirmTransactionBase extends Component { isTxReprice: PropTypes.bool, methodData: PropTypes.object, nonce: PropTypes.string, + assetImage: PropTypes.string, sendTransaction: PropTypes.func, showCustomizeGasModal: PropTypes.func, showTransactionConfirmedModal: PropTypes.func, + showRejectTransactionsConfirmationModal: PropTypes.func, toAddress: PropTypes.string, tokenData: PropTypes.object, tokenProps: PropTypes.object, toName: PropTypes.string, transactionStatus: PropTypes.string, txData: PropTypes.object, + unapprovedTxCount: PropTypes.number, // Component props action: PropTypes.string, contentComponent: PropTypes.node, @@ -73,6 +78,7 @@ export default class ConfirmTransactionBase extends Component { state = { submitting: false, + submitError: null, } componentDidUpdate () { @@ -83,9 +89,9 @@ export default class ConfirmTransactionBase extends Component { clearConfirmTransaction, } = this.props - if (transactionStatus === 'dropped') { + if (transactionStatus === DROPPED_STATUS || transactionStatus === CONFIRMED_STATUS) { showTransactionConfirmedModal({ - onHide: () => { + onSubmit: () => { clearConfirmTransaction() history.push(DEFAULT_ROUTE) }, @@ -246,6 +252,25 @@ export default class ConfirmTransactionBase extends Component { onEdit({ txData, tokenData, tokenProps }) } + handleCancelAll () { + const { + cancelAllTransactions, + clearConfirmTransaction, + history, + showRejectTransactionsConfirmationModal, + unapprovedTxCount, + } = this.props + + showRejectTransactionsConfirmationModal({ + unapprovedTxCount, + async onSubmit () { + await cancelAllTransactions() + clearConfirmTransaction() + history.push(DEFAULT_ROUTE) + }, + }) + } + handleCancel () { const { onCancel, txData, cancelTransaction, history, clearConfirmTransaction } = this.props @@ -268,7 +293,7 @@ export default class ConfirmTransactionBase extends Component { return } - this.setState({ submitting: true }) + this.setState({ submitting: true, submitError: null }) if (onSubmit) { Promise.resolve(onSubmit(txData)) @@ -280,7 +305,9 @@ export default class ConfirmTransactionBase extends Component { this.setState({ submitting: false }) history.push(DEFAULT_ROUTE) }) - .catch(() => this.setState({ submitting: false })) + .catch(error => { + this.setState({ submitting: false, submitError: error.message }) + }) } } @@ -307,9 +334,11 @@ export default class ConfirmTransactionBase extends Component { contentComponent, onEdit, nonce, + assetImage, warning, + unapprovedTxCount, } = this.props - const { submitting } = this.state + const { submitting, submitError } = this.state const { name } = methodData const fiatConvertedAmount = formatCurrency(fiatTransactionAmount, currentCurrency) @@ -331,12 +360,15 @@ export default class ConfirmTransactionBase extends Component { dataComponent={this.renderData()} contentComponent={contentComponent} nonce={nonce} + unapprovedTxCount={unapprovedTxCount} + assetImage={assetImage} identiconAddress={identiconAddress} - errorMessage={errorMessage} + errorMessage={errorMessage || submitError} errorKey={propsErrorKey || errorKey} warning={warning} disabled={!propsValid || !valid || submitting} onEdit={() => this.handleEdit()} + onCancelAll={() => this.handleCancelAll()} onCancel={() => this.handleCancel()} onSubmit={() => this.handleSubmit()} /> diff --git a/ui/app/components/pages/confirm-transaction-base/confirm-transaction-base.container.js b/ui/app/components/pages/confirm-transaction-base/confirm-transaction-base.container.js index 0c0deff18..b34067686 100644 --- a/ui/app/components/pages/confirm-transaction-base/confirm-transaction-base.container.js +++ b/ui/app/components/pages/confirm-transaction-base/confirm-transaction-base.container.js @@ -8,7 +8,7 @@ import { clearConfirmTransaction, updateGasAndCalculate, } from '../../../ducks/confirm-transaction.duck' -import { clearSend, cancelTx, updateAndApproveTx, showModal } from '../../../actions' +import { clearSend, cancelTx, cancelTxs, updateAndApproveTx, showModal } from '../../../actions' import { INSUFFICIENT_FUNDS_ERROR_KEY, GAS_LIMIT_TOO_LOW_ERROR_KEY, @@ -17,7 +17,7 @@ import { getHexGasTotal } from '../../../helpers/confirm-transaction/util' import { isBalanceSufficient } from '../../send/send.utils' import { conversionGreaterThan } from '../../../conversion-util' import { MIN_GAS_LIMIT_DEC } from '../../send/send.constants' -import { addressSlicer } from '../../../util' +import { addressSlicer, valuesFor } from '../../../util' const casedContractMap = Object.keys(contractMap).reduce((acc, base) => { return { @@ -52,8 +52,11 @@ const mapStateToProps = (state, props) => { accounts, selectedAddress, selectedAddressTxList, + assetImages, + network, + unapprovedTxs, } = metamask - + const assetImage = assetImages[txParamsToAddress] const { balance } = accounts[selectedAddress] const { name: fromName } = identities[selectedAddress] const toAddress = propsToAddress || txParamsToAddress @@ -66,6 +69,12 @@ const mapStateToProps = (state, props) => { const transaction = R.find(({ id }) => id === transactionId)(selectedAddressTxList) const transactionStatus = transaction ? transaction.status : '' + const currentNetworkUnapprovedTxs = R.filter( + ({ metamaskNetworkId }) => metamaskNetworkId === network, + valuesFor(unapprovedTxs), + ) + const unapprovedTxCount = currentNetworkUnapprovedTxs.length + return { balance, fromAddress, @@ -88,6 +97,9 @@ const mapStateToProps = (state, props) => { conversionRate, transactionStatus, nonce, + assetImage, + unapprovedTxs, + unapprovedTxCount, } } @@ -95,8 +107,8 @@ const mapDispatchToProps = dispatch => { return { clearConfirmTransaction: () => dispatch(clearConfirmTransaction()), clearSend: () => dispatch(clearSend()), - showTransactionConfirmedModal: ({ onHide }) => { - return dispatch(showModal({ name: 'TRANSACTION_CONFIRMED', onHide })) + showTransactionConfirmedModal: ({ onSubmit }) => { + return dispatch(showModal({ name: 'TRANSACTION_CONFIRMED', onSubmit })) }, showCustomizeGasModal: ({ txData, onSubmit, validate }) => { return dispatch(showModal({ name: 'CONFIRM_CUSTOMIZE_GAS', txData, onSubmit, validate })) @@ -104,7 +116,11 @@ const mapDispatchToProps = dispatch => { updateGasAndCalculate: ({ gasLimit, gasPrice }) => { return dispatch(updateGasAndCalculate({ gasLimit, gasPrice })) }, + showRejectTransactionsConfirmationModal: ({ onSubmit, unapprovedTxCount }) => { + return dispatch(showModal({ name: 'REJECT_TRANSACTIONS', onSubmit, unapprovedTxCount })) + }, cancelTransaction: ({ id }) => dispatch(cancelTx({ id })), + cancelAllTransactions: (txList) => dispatch(cancelTxs(txList)), sendTransaction: txData => dispatch(updateAndApproveTx(txData)), } } @@ -154,8 +170,9 @@ const getValidateEditGas = ({ balance, conversionRate, txData }) => { } const mergeProps = (stateProps, dispatchProps, ownProps) => { - const { balance, conversionRate, txData } = stateProps + const { balance, conversionRate, txData, unapprovedTxs } = stateProps const { + cancelAllTransactions: dispatchCancelAllTransactions, showCustomizeGasModal: dispatchShowCustomizeGasModal, updateGasAndCalculate: dispatchUpdateGasAndCalculate, ...otherDispatchProps @@ -172,6 +189,7 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => { onSubmit: txData => dispatchUpdateGasAndCalculate(txData), validate: validateEditGas, }), + cancelAllTransactions: () => dispatchCancelAllTransactions(valuesFor(unapprovedTxs)), } } diff --git a/ui/app/components/pages/confirm-transaction-switch/confirm-transaction-switch.component.js b/ui/app/components/pages/confirm-transaction-switch/confirm-transaction-switch.component.js index 0280f73c6..2c44b6094 100644 --- a/ui/app/components/pages/confirm-transaction-switch/confirm-transaction-switch.component.js +++ b/ui/app/components/pages/confirm-transaction-switch/confirm-transaction-switch.component.js @@ -12,25 +12,27 @@ import { CONFIRM_TOKEN_METHOD_PATH, SIGNATURE_REQUEST_PATH, } from '../../../routes' -import { isConfirmDeployContract } from './confirm-transaction-switch.util' +import { isConfirmDeployContract } from '../../../helpers/transactions.util' import { TOKEN_METHOD_TRANSFER, TOKEN_METHOD_APPROVE, TOKEN_METHOD_TRANSFER_FROM, -} from './confirm-transaction-switch.constants' +} from '../../../constants/transactions' export default class ConfirmTransactionSwitch extends Component { static propTypes = { txData: PropTypes.object, methodData: PropTypes.object, - fetchingMethodData: PropTypes.bool, + fetchingData: PropTypes.bool, + isEtherTransaction: PropTypes.bool, } redirectToTransaction () { const { txData, methodData: { name }, - fetchingMethodData, + fetchingData, + isEtherTransaction, } = this.props const { id, txParams: { data } = {} } = txData @@ -39,10 +41,15 @@ export default class ConfirmTransactionSwitch extends Component { return <Redirect to={{ pathname }} /> } - if (fetchingMethodData) { + if (fetchingData) { return <Loading /> } + if (isEtherTransaction) { + const pathname = `${CONFIRM_TRANSACTION_ROUTE}/${id}${CONFIRM_SEND_ETHER_PATH}` + return <Redirect to={{ pathname }} /> + } + if (data) { const methodName = name && name.toLowerCase() diff --git a/ui/app/components/pages/confirm-transaction-switch/confirm-transaction-switch.constants.js b/ui/app/components/pages/confirm-transaction-switch/confirm-transaction-switch.constants.js deleted file mode 100644 index 9db4a2f96..000000000 --- a/ui/app/components/pages/confirm-transaction-switch/confirm-transaction-switch.constants.js +++ /dev/null @@ -1,3 +0,0 @@ -export const TOKEN_METHOD_TRANSFER = 'transfer' -export const TOKEN_METHOD_APPROVE = 'approve' -export const TOKEN_METHOD_TRANSFER_FROM = 'transferfrom' diff --git a/ui/app/components/pages/confirm-transaction-switch/confirm-transaction-switch.container.js b/ui/app/components/pages/confirm-transaction-switch/confirm-transaction-switch.container.js index 3d7fc78cc..7f2c36af2 100644 --- a/ui/app/components/pages/confirm-transaction-switch/confirm-transaction-switch.container.js +++ b/ui/app/components/pages/confirm-transaction-switch/confirm-transaction-switch.container.js @@ -6,14 +6,16 @@ const mapStateToProps = state => { confirmTransaction: { txData, methodData, - fetchingMethodData, + fetchingData, + toSmartContract, }, } = state return { txData, methodData, - fetchingMethodData, + fetchingData, + isEtherTransaction: !toSmartContract, } } diff --git a/ui/app/components/pages/create-account/connect-hardware/account-list.js b/ui/app/components/pages/create-account/connect-hardware/account-list.js index 488a189ea..2767b2e1f 100644 --- a/ui/app/components/pages/create-account/connect-hardware/account-list.js +++ b/ui/app/components/pages/create-account/connect-hardware/account-list.js @@ -3,6 +3,7 @@ const PropTypes = require('prop-types') const h = require('react-hyperscript') const genAccountLink = require('../../../../../lib/account-link.js') const Select = require('react-select').default +import Button from '../../../button' class AccountList extends Component { constructor (props, context) { @@ -143,22 +144,20 @@ class AccountList extends Component { } return h('div.new-account-connect-form__buttons', {}, [ - h( - 'button.btn-default.btn--large.new-account-connect-form__button', - { - onClick: this.props.onCancel.bind(this), - }, - [this.context.t('cancel')] - ), - - h( - `button.btn-primary.btn--large.new-account-connect-form__button.unlock ${disabled ? '.btn-primary--disabled' : ''}`, - { - onClick: this.props.onUnlockAccount.bind(this, this.props.device), - ...buttonProps, - }, - [this.context.t('unlock')] - ), + h(Button, { + type: 'default', + large: true, + className: 'new-account-connect-form__button', + onClick: this.props.onCancel.bind(this), + }, [this.context.t('cancel')]), + + h(Button, { + type: 'primary', + large: true, + className: 'new-account-connect-form__button unlock', + disabled, + onClick: this.props.onUnlockAccount.bind(this, this.props.device), + }, [this.context.t('unlock')]), ]) } diff --git a/ui/app/components/pages/create-account/connect-hardware/connect-screen.js b/ui/app/components/pages/create-account/connect-hardware/connect-screen.js index b3dfa4ee2..d3abf3119 100644 --- a/ui/app/components/pages/create-account/connect-hardware/connect-screen.js +++ b/ui/app/components/pages/create-account/connect-hardware/connect-screen.js @@ -1,6 +1,7 @@ const { Component } = require('react') const PropTypes = require('prop-types') const h = require('react-hyperscript') +import Button from '../../../button' class ConnectScreen extends Component { constructor (props, context) { @@ -60,13 +61,13 @@ class ConnectScreen extends Component { h('h3.hw-connect__title', {}, this.context.t('browserNotSupported')), h('p.hw-connect__msg', {}, this.context.t('chromeRequiredForHardwareWallets')), ]), - h( - 'button.btn-primary.btn--large', - { - onClick: () => global.platform.openWindow({ - url: 'https://google.com/chrome', - }), - }, + h(Button, { + type: 'primary', + large: true, + onClick: () => global.platform.openWindow({ + url: 'https://google.com/chrome', + }), + }, this.context.t('downloadGoogleChrome') ), ]) diff --git a/ui/app/components/pages/create-account/import-account/index.js b/ui/app/components/pages/create-account/import-account/index.js index e2e973af9..48d8f8838 100644 --- a/ui/app/components/pages/create-account/import-account/index.js +++ b/ui/app/components/pages/create-account/import-account/index.js @@ -46,7 +46,7 @@ AccountImportSubview.prototype.render = function () { }, onClick: () => { global.platform.openWindow({ - url: 'https://consensys.zendesk.com/hc/en-us/articles/360004180111-What-are-imported-accounts-New-UI', + url: 'https://metamask.zendesk.com/hc/en-us/articles/360015289932', }) }, }, this.context.t('here')), diff --git a/ui/app/components/pages/create-account/import-account/json.js b/ui/app/components/pages/create-account/import-account/json.js index dd57256a3..90279bbbd 100644 --- a/ui/app/components/pages/create-account/import-account/json.js +++ b/ui/app/components/pages/create-account/import-account/json.js @@ -8,6 +8,7 @@ const actions = require('../../../../actions') const FileInput = require('react-simple-file-input').default const { DEFAULT_ROUTE } = require('../../../../routes') const HELP_LINK = 'https://support.metamask.io/kb/article/7-importing-accounts' +import Button from '../../../button' class JsonImportSubview extends Component { constructor (props) { @@ -51,17 +52,19 @@ class JsonImportSubview extends Component { h('div.new-account-create-form__buttons', {}, [ - h('button.btn-default.new-account-create-form__button', { + h(Button, { + type: 'default', + large: true, + className: 'new-account-create-form__button', onClick: () => this.props.history.push(DEFAULT_ROUTE), - }, [ - this.context.t('cancel'), - ]), + }, [this.context.t('cancel')]), - h('button.btn-primary.new-account-create-form__button', { + h(Button, { + type: 'primary', + large: true, + className: 'new-account-create-form__button', onClick: () => this.createNewKeychain(), - }, [ - this.context.t('import'), - ]), + }, [this.context.t('import')]), ]), @@ -82,7 +85,7 @@ class JsonImportSubview extends Component { } createNewKeychain () { - const { firstAddress, displayWarning, importNewJsonAccount, setSelectedAddress } = this.props + const { firstAddress, displayWarning, importNewJsonAccount, setSelectedAddress, history } = this.props const state = this.state if (!state) { diff --git a/ui/app/components/pages/create-account/import-account/private-key.js b/ui/app/components/pages/create-account/import-account/private-key.js index 1db999f2f..8db1bfbdd 100644 --- a/ui/app/components/pages/create-account/import-account/private-key.js +++ b/ui/app/components/pages/create-account/import-account/private-key.js @@ -7,6 +7,7 @@ const PropTypes = require('prop-types') const connect = require('react-redux').connect const actions = require('../../../../actions') const { DEFAULT_ROUTE } = require('../../../../routes') +import Button from '../../../button' PrivateKeyImportView.contextTypes = { t: PropTypes.func, @@ -61,20 +62,22 @@ PrivateKeyImportView.prototype.render = function () { h('div.new-account-import-form__buttons', {}, [ - h('button.btn-default.btn--large.new-account-create-form__button', { + h(Button, { + type: 'default', + large: true, + className: 'new-account-create-form__button', onClick: () => { displayWarning(null) this.props.history.push(DEFAULT_ROUTE) }, - }, [ - this.context.t('cancel'), - ]), + }, [this.context.t('cancel')]), - h('button.btn-primary.btn--large.new-account-create-form__button', { + h(Button, { + type: 'primary', + large: true, + className: 'new-account-create-form__button', onClick: () => this.createNewKeychain(), - }, [ - this.context.t('import'), - ]), + }, [this.context.t('import')]), ]), diff --git a/ui/app/components/pages/create-account/new-account.js b/ui/app/components/pages/create-account/new-account.js index 402b8f03b..94a5fa487 100644 --- a/ui/app/components/pages/create-account/new-account.js +++ b/ui/app/components/pages/create-account/new-account.js @@ -4,6 +4,7 @@ const h = require('react-hyperscript') const connect = require('react-redux').connect const actions = require('../../../actions') const { DEFAULT_ROUTE } = require('../../../routes') +import Button from '../../button' class NewAccountCreateForm extends Component { constructor (props, context) { @@ -38,20 +39,22 @@ class NewAccountCreateForm extends Component { h('div.new-account-create-form__buttons', {}, [ - h('button.btn-default.btn--large.new-account-create-form__button', { + h(Button, { + type: 'default', + large: true, + className: 'new-account-create-form__button', onClick: () => history.push(DEFAULT_ROUTE), - }, [ - this.context.t('cancel'), - ]), + }, [this.context.t('cancel')]), - h('button.btn-primary.btn--large.new-account-create-form__button', { + h(Button, { + type: 'primary', + large: true, + className: 'new-account-create-form__button', onClick: () => { createAccount(newAccountName || defaultAccountName) .then(() => history.push(DEFAULT_ROUTE)) }, - }, [ - this.context.t('create'), - ]), + }, [this.context.t('create')]), ]), diff --git a/ui/app/components/pages/home.js b/ui/app/components/pages/home.js deleted file mode 100644 index 5e3fdc9af..000000000 --- a/ui/app/components/pages/home.js +++ /dev/null @@ -1,239 +0,0 @@ -const { Component } = require('react') -const { connect } = require('react-redux') -const PropTypes = require('prop-types') -const { Redirect, withRouter } = require('react-router-dom') -const { compose } = require('recompose') -const h = require('react-hyperscript') -const actions = require('../../actions') -const log = require('loglevel') - -// init -const NewKeyChainScreen = require('../../new-keychain') -// mascara -const MascaraBuyEtherScreen = require('../../../../mascara/src/app/first-time/buy-ether-screen').default - -// accounts -const MainContainer = require('../../main-container') - -// other views -const BuyView = require('../../components/buy-button-subview') -const QrView = require('../../components/qr-code') - -// Routes -const { - INITIALIZE_BACKUP_PHRASE_ROUTE, - RESTORE_VAULT_ROUTE, - CONFIRM_TRANSACTION_ROUTE, - NOTICE_ROUTE, -} = require('../../routes') - -const { unconfirmedTransactionsCountSelector } = require('../../selectors/confirm-transaction') - -class Home extends Component { - componentDidMount () { - const { - history, - unconfirmedTransactionsCount = 0, - } = this.props - - // unapprovedTxs and unapproved messages - if (unconfirmedTransactionsCount > 0) { - history.push(CONFIRM_TRANSACTION_ROUTE) - } - } - - render () { - log.debug('rendering primary') - const { - noActiveNotices, - lostAccounts, - forgottenPassword, - currentView, - activeAddress, - seedWords, - } = this.props - - // notices - if (!noActiveNotices || (lostAccounts && lostAccounts.length > 0)) { - return h(Redirect, { - to: { - pathname: NOTICE_ROUTE, - }, - }) - } - - // seed words - if (seedWords) { - log.debug('rendering seed words') - return h(Redirect, { - to: { - pathname: INITIALIZE_BACKUP_PHRASE_ROUTE, - }, - }) - } - - if (forgottenPassword) { - log.debug('rendering restore vault screen') - return h(Redirect, { - to: { - pathname: RESTORE_VAULT_ROUTE, - }, - }) - } - - // show current view - switch (currentView.name) { - - case 'accountDetail': - log.debug('rendering main container') - return h(MainContainer, {key: 'account-detail'}) - - case 'newKeychain': - log.debug('rendering new keychain screen') - return h(NewKeyChainScreen, {key: 'new-keychain'}) - - case 'buyEth': - log.debug('rendering buy ether screen') - return h(BuyView, {key: 'buyEthView'}) - - case 'onboardingBuyEth': - log.debug('rendering onboarding buy ether screen') - return h(MascaraBuyEtherScreen, {key: 'buyEthView'}) - - case 'qr': - log.debug('rendering show qr screen') - return h('div', { - style: { - position: 'absolute', - height: '100%', - top: '0px', - left: '0px', - }, - }, [ - h('i.fa.fa-arrow-left.fa-lg.cursor-pointer.color-orange', { - onClick: () => this.props.dispatch(actions.backToAccountDetail(activeAddress)), - style: { - marginLeft: '10px', - marginTop: '50px', - }, - }), - h('div', { - style: { - position: 'absolute', - left: '44px', - width: '285px', - }, - }, [ - h(QrView, {key: 'qr'}), - ]), - ]) - - default: - log.debug('rendering default, account detail screen') - return h(MainContainer, {key: 'account-detail'}) - } - } -} - -Home.propTypes = { - currentCurrency: PropTypes.string, - isLoading: PropTypes.bool, - loadingMessage: PropTypes.string, - network: PropTypes.string, - provider: PropTypes.object, - frequentRpcList: PropTypes.array, - currentView: PropTypes.object, - sidebarOpen: PropTypes.bool, - isMascara: PropTypes.bool, - isOnboarding: PropTypes.bool, - isUnlocked: PropTypes.bool, - networkDropdownOpen: PropTypes.bool, - history: PropTypes.object, - dispatch: PropTypes.func, - selectedAddress: PropTypes.string, - noActiveNotices: PropTypes.bool, - lostAccounts: PropTypes.array, - isInitialized: PropTypes.bool, - forgottenPassword: PropTypes.bool, - activeAddress: PropTypes.string, - unapprovedTxs: PropTypes.object, - seedWords: PropTypes.string, - unapprovedMsgCount: PropTypes.number, - unapprovedPersonalMsgCount: PropTypes.number, - unapprovedTypedMessagesCount: PropTypes.number, - welcomeScreenSeen: PropTypes.bool, - isPopup: PropTypes.bool, - isMouseUser: PropTypes.bool, - t: PropTypes.func, - unconfirmedTransactionsCount: PropTypes.number, -} - -function mapStateToProps (state) { - const { appState, metamask } = state - const { - networkDropdownOpen, - sidebarOpen, - isLoading, - loadingMessage, - } = appState - - const { - accounts, - address, - isInitialized, - noActiveNotices, - seedWords, - unapprovedTxs, - nextUnreadNotice, - lostAccounts, - unapprovedMsgCount, - unapprovedPersonalMsgCount, - unapprovedTypedMessagesCount, - } = metamask - const selected = address || Object.keys(accounts)[0] - - return { - // state from plugin - networkDropdownOpen, - sidebarOpen, - isLoading, - loadingMessage, - noActiveNotices, - isInitialized, - isUnlocked: state.metamask.isUnlocked, - selectedAddress: state.metamask.selectedAddress, - currentView: state.appState.currentView, - activeAddress: state.appState.activeAddress, - transForward: state.appState.transForward, - isMascara: state.metamask.isMascara, - isOnboarding: Boolean(!noActiveNotices || seedWords || !isInitialized), - isPopup: state.metamask.isPopup, - seedWords: state.metamask.seedWords, - unapprovedTxs, - unapprovedMsgs: state.metamask.unapprovedMsgs, - unapprovedMsgCount, - unapprovedPersonalMsgCount, - unapprovedTypedMessagesCount, - menuOpen: state.appState.menuOpen, - network: state.metamask.network, - provider: state.metamask.provider, - forgottenPassword: state.appState.forgottenPassword, - nextUnreadNotice, - lostAccounts, - frequentRpcList: state.metamask.frequentRpcList || [], - currentCurrency: state.metamask.currentCurrency, - isMouseUser: state.appState.isMouseUser, - isRevealingSeedWords: state.metamask.isRevealingSeedWords, - Qr: state.appState.Qr, - welcomeScreenSeen: state.metamask.welcomeScreenSeen, - - // state needed to get account dropdown temporarily rendering from app bar - selected, - unconfirmedTransactionsCount: unconfirmedTransactionsCountSelector(state), - } -} - -module.exports = compose( - withRouter, - connect(mapStateToProps) -)(Home) diff --git a/ui/app/components/pages/home/home.component.js b/ui/app/components/pages/home/home.component.js new file mode 100644 index 000000000..d3c71c4f6 --- /dev/null +++ b/ui/app/components/pages/home/home.component.js @@ -0,0 +1,77 @@ +import React, { PureComponent } from 'react' +import PropTypes from 'prop-types' +import Media from 'react-media' +import { Redirect } from 'react-router-dom' +import WalletView from '../../wallet-view' +import TransactionView from '../../transaction-view' +import { + INITIALIZE_BACKUP_PHRASE_ROUTE, + RESTORE_VAULT_ROUTE, + CONFIRM_TRANSACTION_ROUTE, + NOTICE_ROUTE, + CONFIRM_ADD_SUGGESTED_TOKEN_ROUTE, +} from '../../../routes' + +export default class Home extends PureComponent { + static propTypes = { + history: PropTypes.object, + noActiveNotices: PropTypes.bool, + lostAccounts: PropTypes.array, + forgottenPassword: PropTypes.bool, + seedWords: PropTypes.string, + suggestedTokens: PropTypes.object, + unconfirmedTransactionsCount: PropTypes.number, + } + + componentDidMount () { + const { + history, + suggestedTokens = {}, + unconfirmedTransactionsCount = 0, + } = this.props + + // suggested new tokens + if (Object.keys(suggestedTokens).length > 0) { + history.push(CONFIRM_ADD_SUGGESTED_TOKEN_ROUTE) + } + + if (unconfirmedTransactionsCount > 0) { + history.push(CONFIRM_TRANSACTION_ROUTE) + } + } + + render () { + const { + noActiveNotices, + lostAccounts, + forgottenPassword, + seedWords, + } = this.props + + // notices + if (!noActiveNotices || (lostAccounts && lostAccounts.length > 0)) { + return <Redirect to={{ pathname: NOTICE_ROUTE }} /> + } + + // seed words + if (seedWords) { + return <Redirect to={{ pathname: INITIALIZE_BACKUP_PHRASE_ROUTE }}/> + } + + if (forgottenPassword) { + return <Redirect to={{ pathname: RESTORE_VAULT_ROUTE }} /> + } + + return ( + <div className="main-container"> + <div className="account-and-transaction-details"> + <Media + query="(min-width: 576px)" + render={() => <WalletView />} + /> + <TransactionView /> + </div> + </div> + ) + } +} diff --git a/ui/app/components/pages/home/home.container.js b/ui/app/components/pages/home/home.container.js new file mode 100644 index 000000000..58001df6b --- /dev/null +++ b/ui/app/components/pages/home/home.container.js @@ -0,0 +1,30 @@ +import Home from './home.component' +import { compose } from 'recompose' +import { connect } from 'react-redux' +import { withRouter } from 'react-router-dom' +import { unconfirmedTransactionsCountSelector } from '../../../selectors/confirm-transaction' + +const mapStateToProps = state => { + const { metamask, appState } = state + const { + noActiveNotices, + lostAccounts, + seedWords, + suggestedTokens, + } = metamask + const { forgottenPassword } = appState + + return { + noActiveNotices, + lostAccounts, + forgottenPassword, + seedWords, + suggestedTokens, + unconfirmedTransactionsCount: unconfirmedTransactionsCountSelector(state), + } +} + +export default compose( + withRouter, + connect(mapStateToProps) +)(Home) diff --git a/ui/app/components/pages/home/index.js b/ui/app/components/pages/home/index.js new file mode 100644 index 000000000..4474ba5b8 --- /dev/null +++ b/ui/app/components/pages/home/index.js @@ -0,0 +1 @@ +export { default } from './home.container' diff --git a/ui/app/components/pages/index.scss b/ui/app/components/pages/index.scss index b15c59863..6551278f5 100644 --- a/ui/app/components/pages/index.scss +++ b/ui/app/components/pages/index.scss @@ -3,3 +3,5 @@ @import './add-token/index'; @import './confirm-add-token/index'; + +@import './settings/index'; diff --git a/ui/app/components/pages/keychains/reveal-seed.js b/ui/app/components/pages/keychains/reveal-seed.js index 7d7d3f462..32557066f 100644 --- a/ui/app/components/pages/keychains/reveal-seed.js +++ b/ui/app/components/pages/keychains/reveal-seed.js @@ -8,6 +8,8 @@ const { requestRevealSeedWords } = require('../../../actions') const { DEFAULT_ROUTE } = require('../../../routes') const ExportTextContainer = require('../../export-text-container') +import Button from '../../button' + const PASSWORD_PROMPT_SCREEN = 'PASSWORD_PROMPT_SCREEN' const REVEAL_SEED_SCREEN = 'REVEAL_SEED_SCREEN' @@ -106,13 +108,21 @@ class RevealSeedPage extends Component { renderPasswordPromptFooter () { return ( h('.page-container__footer', [ - h('button.btn-default.btn--large.page-container__footer-button', { - onClick: () => this.props.history.push(DEFAULT_ROUTE), - }, this.context.t('cancel')), - h('button.btn-primary.btn--large.page-container__footer-button', { - onClick: event => this.handleSubmit(event), - disabled: this.state.password === '', - }, this.context.t('next')), + h('header', [ + h(Button, { + type: 'default', + large: true, + className: 'page-container__footer-button', + onClick: () => this.props.history.push(DEFAULT_ROUTE), + }, this.context.t('cancel')), + h(Button, { + type: 'primary', + large: true, + className: 'page-container__footer-button', + onClick: event => this.handleSubmit(event), + disabled: this.state.password === '', + }, this.context.t('next')), + ]), ]) ) } @@ -120,7 +130,10 @@ class RevealSeedPage extends Component { renderRevealSeedFooter () { return ( h('.page-container__footer', [ - h('button.btn-default.btn--large.page-container__footer-button', { + h(Button, { + type: 'default', + large: true, + className: 'page-container__footer-button', onClick: () => this.props.history.push(DEFAULT_ROUTE), }, this.context.t('close')), ]) diff --git a/ui/app/components/pages/settings/index.js b/ui/app/components/pages/settings/index.js index aee17e0e8..44a9ffa63 100644 --- a/ui/app/components/pages/settings/index.js +++ b/ui/app/components/pages/settings/index.js @@ -1,64 +1 @@ -const { Component } = require('react') -const { Switch, Route, matchPath } = require('react-router-dom') -const PropTypes = require('prop-types') -const h = require('react-hyperscript') -const TabBar = require('../../tab-bar') -const Settings = require('./settings') -const Info = require('./info') -const { DEFAULT_ROUTE, SETTINGS_ROUTE, INFO_ROUTE } = require('../../../routes') - -class Config extends Component { - renderTabs () { - const { history, location } = this.props - - return h('div.settings__tabs', [ - h(TabBar, { - tabs: [ - { content: this.context.t('settings'), key: SETTINGS_ROUTE }, - { content: this.context.t('info'), key: INFO_ROUTE }, - ], - isActive: key => matchPath(location.pathname, { path: key, exact: true }), - onSelect: key => history.push(key), - }), - ]) - } - - render () { - const { history } = this.props - - return ( - h('.main-container.settings', {}, [ - h('.settings__header', [ - h('div.settings__close-button', { - onClick: () => history.push(DEFAULT_ROUTE), - }), - this.renderTabs(), - ]), - h(Switch, [ - h(Route, { - exact: true, - path: INFO_ROUTE, - component: Info, - }), - h(Route, { - exact: true, - path: SETTINGS_ROUTE, - component: Settings, - }), - ]), - ]) - ) - } -} - -Config.propTypes = { - location: PropTypes.object, - history: PropTypes.object, - t: PropTypes.func, -} - -Config.contextTypes = { - t: PropTypes.func, -} - -module.exports = Config +export { default } from './settings.component' diff --git a/ui/app/components/pages/settings/index.scss b/ui/app/components/pages/settings/index.scss new file mode 100644 index 000000000..138ebcfc5 --- /dev/null +++ b/ui/app/components/pages/settings/index.scss @@ -0,0 +1,80 @@ +@import './info-tab/index'; + +@import './settings-tab/index'; + +.settings-page { + position: relative; + background: $white; + display: flex; + flex-flow: column nowrap; + + &__header { + padding: 25px 25px 0; + } + + &__close-button::after { + content: '\00D7'; + font-size: 40px; + color: $dusty-gray; + position: absolute; + top: 25px; + right: 30px; + cursor: pointer; + } + + &__content { + padding: 25px; + height: auto; + overflow: auto; + } + + &__content-row { + display: flex; + flex-direction: row; + padding: 10px 0 20px; + + @media screen and (max-width: 575px) { + flex-direction: column; + padding: 10px 0; + } + } + + &__content-item { + flex: 1; + min-width: 0; + display: flex; + flex-direction: column; + padding: 0 5px; + min-height: 71px; + + @media screen and (max-width: 575px) { + height: initial; + padding: 5px 0; + } + + &--without-height { + height: initial; + } + } + + &__content-label { + text-transform: capitalize; + } + + &__content-description { + font-size: 14px; + color: $dusty-gray; + padding-top: 5px; + } + + &__content-item-col { + max-width: 300px; + display: flex; + flex-direction: column; + + @media screen and (max-width: 575px) { + max-width: 100%; + width: 100%; + } + } +} diff --git a/ui/app/components/pages/settings/info-tab/index.js b/ui/app/components/pages/settings/info-tab/index.js new file mode 100644 index 000000000..7556a258d --- /dev/null +++ b/ui/app/components/pages/settings/info-tab/index.js @@ -0,0 +1 @@ +export { default } from './info-tab.component' diff --git a/ui/app/components/pages/settings/info-tab/index.scss b/ui/app/components/pages/settings/info-tab/index.scss new file mode 100644 index 000000000..43ad6f652 --- /dev/null +++ b/ui/app/components/pages/settings/info-tab/index.scss @@ -0,0 +1,56 @@ +.info-tab { + &__logo-wrapper { + height: 80px; + margin-bottom: 20px; + } + + &__logo { + max-height: 100%; + max-width: 100%; + } + + &__item { + padding: 10px 0; + } + + &__link-header { + padding-bottom: 15px; + + @media screen and (max-width: 575px) { + padding-bottom: 5px; + } + } + + &__link-item { + padding: 15px 0; + + @media screen and (max-width: 575px) { + padding: 5px 0; + } + } + + &__link-text { + color: $curious-blue; + } + + &__version-number { + padding-top: 5px; + font-size: 13px; + color: $dusty-gray; + } + + &__separator { + margin: 15px 0; + width: 80px; + border-color: $alto; + border: none; + height: 1px; + background-color: $alto; + color: $alto; + } + + &__about { + color: $dusty-gray; + margin-bottom: 15px; + } +} diff --git a/ui/app/components/pages/settings/info-tab/info-tab.component.js b/ui/app/components/pages/settings/info-tab/info-tab.component.js new file mode 100644 index 000000000..72f7d835e --- /dev/null +++ b/ui/app/components/pages/settings/info-tab/info-tab.component.js @@ -0,0 +1,136 @@ +import React, { PureComponent } from 'react' +import PropTypes from 'prop-types' + +export default class InfoTab extends PureComponent { + state = { + version: global.platform.getVersion(), + } + + static propTypes = { + tab: PropTypes.string, + metamask: PropTypes.object, + setCurrentCurrency: PropTypes.func, + setRpcTarget: PropTypes.func, + displayWarning: PropTypes.func, + revealSeedConfirmation: PropTypes.func, + warning: PropTypes.string, + location: PropTypes.object, + history: PropTypes.object, + } + + static contextTypes = { + t: PropTypes.func, + } + + renderInfoLinks () { + const { t } = this.context + + return ( + <div className="settings-page__content-item settings-page__content-item--without-height"> + <div className="info-tab__link-header"> + { t('links') } + </div> + <div className="info-tab__link-item"> + <a + href="https://metamask.io/privacy.html" + target="_blank" + rel="noopener noreferrer" + > + <span className="info-tab__link-text"> + { t('privacyMsg') } + </span> + </a> + </div> + <div className="info-tab__link-item"> + <a + href="https://metamask.io/terms.html" + target="_blank" + rel="noopener noreferrer" + > + <span className="info-tab__link-text"> + { t('terms') } + </span> + </a> + </div> + <div className="info-tab__link-item"> + <a + href="https://metamask.io/attributions.html" + target="_blank" + rel="noopener noreferrer" + > + <span className="info-tab__link-text"> + { t('attributions') } + </span> + </a> + </div> + <hr className="info-tab__separator" /> + <div className="info-tab__link-item"> + <a + href="https://support.metamask.io" + target="_blank" + rel="noopener noreferrer" + > + <span className="info-tab__link-text"> + { t('supportCenter') } + </span> + </a> + </div> + <div className="info-tab__link-item"> + <a + href="https://metamask.io/" + target="_blank" + rel="noopener noreferrer" + > + <span className="info-tab__link-text"> + { t('visitWebSite') } + </span> + </a> + </div> + <div className="info-tab__link-item"> + <a + href="mailto:help@metamask.io?subject=Feedback" + target="_blank" + rel="noopener noreferrer" + > + <span className="info-tab__link-text"> + { t('emailUs') } + </span> + </a> + </div> + </div> + ) + } + + render () { + const { t } = this.context + + return ( + <div className="settings-page__content"> + <div className="settings-page__content-row"> + <div className="settings-page__content-item settings-page__content-item--without-height"> + <div className="info-tab__logo-wrapper"> + <img + src="images/info-logo.png" + className="info-tab__logo" + /> + </div> + <div className="info-tab__item"> + <div className="info-tab__version-header"> + { t('metamaskVersion') } + </div> + <div className="info-tab__version-number"> + { this.state.version } + </div> + </div> + <div className="info-tab__item"> + <div className="info-tab__about"> + { t('builtInCalifornia') } + </div> + </div> + </div> + { this.renderInfoLinks() } + </div> + </div> + ) + } +} diff --git a/ui/app/components/pages/settings/info.js b/ui/app/components/pages/settings/info.js deleted file mode 100644 index bd9040499..000000000 --- a/ui/app/components/pages/settings/info.js +++ /dev/null @@ -1,120 +0,0 @@ -const { Component } = require('react') -const PropTypes = require('prop-types') -const h = require('react-hyperscript') - -class Info extends Component { - constructor (props) { - super(props) - - this.state = { - version: global.platform.getVersion(), - } - } - - renderLogo () { - return ( - h('div.settings__info-logo-wrapper', [ - h('img.settings__info-logo', { src: 'images/info-logo.png' }), - ]) - ) - } - - renderInfoLinks () { - return ( - h('div.settings__content-item.settings__content-item--without-height', [ - h('div.settings__info-link-header', this.context.t('links')), - h('div.settings__info-link-item', [ - h('a', { - href: 'https://metamask.io/privacy.html', - target: '_blank', - }, [ - h('span.settings__info-link', this.context.t('privacyMsg')), - ]), - ]), - h('div.settings__info-link-item', [ - h('a', { - href: 'https://metamask.io/terms.html', - target: '_blank', - }, [ - h('span.settings__info-link', this.context.t('terms')), - ]), - ]), - h('div.settings__info-link-item', [ - h('a', { - href: 'https://metamask.io/attributions.html', - target: '_blank', - }, [ - h('span.settings__info-link', this.context.t('attributions')), - ]), - ]), - h('hr.settings__info-separator'), - h('div.settings__info-link-item', [ - h('a', { - href: 'https://support.metamask.io', - target: '_blank', - }, [ - h('span.settings__info-link', this.context.t('supportCenter')), - ]), - ]), - h('div.settings__info-link-item', [ - h('a', { - href: 'https://metamask.io/', - target: '_blank', - }, [ - h('span.settings__info-link', this.context.t('visitWebSite')), - ]), - ]), - h('div.settings__info-link-item', [ - h('a', { - target: '_blank', - href: 'mailto:help@metamask.io?subject=Feedback', - }, [ - h('span.settings__info-link', this.context.t('emailUs')), - ]), - ]), - ]) - ) - } - - render () { - return ( - h('div.settings__content', [ - h('div.settings__content-row', [ - h('div.settings__content-item.settings__content-item--without-height', [ - this.renderLogo(), - h('div.settings__info-item', [ - h('div.settings__info-version-header', 'MetaMask Version'), - h('div.settings__info-version-number', this.state.version), - ]), - h('div.settings__info-item', [ - h( - 'div.settings__info-about', - this.context.t('builtInCalifornia') - ), - ]), - ]), - this.renderInfoLinks(), - ]), - ]) - ) - } -} - -Info.propTypes = { - tab: PropTypes.string, - metamask: PropTypes.object, - setCurrentCurrency: PropTypes.func, - setRpcTarget: PropTypes.func, - displayWarning: PropTypes.func, - revealSeedConfirmation: PropTypes.func, - warning: PropTypes.string, - location: PropTypes.object, - history: PropTypes.object, - t: PropTypes.func, -} - -Info.contextTypes = { - t: PropTypes.func, -} - -module.exports = Info diff --git a/ui/app/components/pages/settings/settings-tab/index.js b/ui/app/components/pages/settings/settings-tab/index.js new file mode 100644 index 000000000..9fdaafd3f --- /dev/null +++ b/ui/app/components/pages/settings/settings-tab/index.js @@ -0,0 +1 @@ +export { default } from './settings-tab.container' diff --git a/ui/app/components/pages/settings/settings-tab/index.scss b/ui/app/components/pages/settings/settings-tab/index.scss new file mode 100644 index 000000000..76a0cec6f --- /dev/null +++ b/ui/app/components/pages/settings/settings-tab/index.scss @@ -0,0 +1,51 @@ +.settings-tab { + &__error { + padding-bottom: 20px; + text-align: center; + color: $crimson; + } + + &__rpc-save-button { + align-self: flex-end; + padding: 5px; + text-transform: uppercase; + color: $dusty-gray; + cursor: pointer; + } + + &__rpc-save-button { + align-self: flex-end; + padding: 5px; + text-transform: uppercase; + color: $dusty-gray; + cursor: pointer; + } + + &__button--red { + border-color: lighten($monzo, 10%); + color: $monzo; + + &:active { + background: lighten($monzo, 55%); + border-color: $monzo; + } + + &:hover { + border-color: $monzo; + } + } + + &__button--orange { + border-color: lighten($ecstasy, 20%); + color: $ecstasy; + + &:active { + background: lighten($ecstasy, 40%); + border-color: $ecstasy; + } + + &:hover { + border-color: $ecstasy; + } + } +} diff --git a/ui/app/components/pages/settings/settings-tab/settings-tab.component.js b/ui/app/components/pages/settings/settings-tab/settings-tab.component.js new file mode 100644 index 000000000..9da624f56 --- /dev/null +++ b/ui/app/components/pages/settings/settings-tab/settings-tab.component.js @@ -0,0 +1,360 @@ +import React, { PureComponent } from 'react' +import PropTypes from 'prop-types' +import infuraCurrencies from '../../../../infura-conversion.json' +import validUrl from 'valid-url' +import { exportAsFile } from '../../../../util' +import SimpleDropdown from '../../../dropdowns/simple-dropdown' +import ToggleButton from 'react-toggle-button' +import { REVEAL_SEED_ROUTE } from '../../../../routes' +import locales from '../../../../../../app/_locales/index.json' +import TextField from '../../../text-field' +import Button from '../../../button' + +const sortedCurrencies = infuraCurrencies.objects.sort((a, b) => { + return a.quote.name.toLocaleLowerCase().localeCompare(b.quote.name.toLocaleLowerCase()) +}) + +const infuraCurrencyOptions = sortedCurrencies.map(({ quote: { code, name } }) => { + return { + displayValue: `${code.toUpperCase()} - ${name}`, + key: code, + value: code, + } +}) + +const localeOptions = locales.map(locale => { + return { + displayValue: `${locale.name}`, + key: locale.code, + value: locale.code, + } +}) + +export default class SettingsTab extends PureComponent { + static contextTypes = { + t: PropTypes.func, + } + + static propTypes = { + metamask: PropTypes.object, + setUseBlockie: PropTypes.func, + setHexDataFeatureFlag: PropTypes.func, + setCurrentCurrency: PropTypes.func, + setRpcTarget: PropTypes.func, + delRpcTarget: PropTypes.func, + displayWarning: PropTypes.func, + revealSeedConfirmation: PropTypes.func, + setFeatureFlagToBeta: PropTypes.func, + showResetAccountConfirmationModal: PropTypes.func, + warning: PropTypes.string, + history: PropTypes.object, + isMascara: PropTypes.bool, + updateCurrentLocale: PropTypes.func, + currentLocale: PropTypes.string, + useBlockie: PropTypes.bool, + sendHexData: PropTypes.bool, + currentCurrency: PropTypes.string, + conversionDate: PropTypes.number, + } + + state = { + newRpc: '', + } + + renderCurrentConversion () { + const { t } = this.context + const { currentCurrency, conversionDate, setCurrentCurrency } = this.props + + return ( + <div className="settings-page__content-row"> + <div className="settings-page__content-item"> + <span>{ t('currentConversion') }</span> + <span className="settings-page__content-description"> + { t('updatedWithDate', [Date(conversionDate)]) } + </span> + </div> + <div className="settings-page__content-item"> + <div className="settings-page__content-item-col"> + <SimpleDropdown + placeholder={t('selectCurrency')} + options={infuraCurrencyOptions} + selectedOption={currentCurrency} + onSelect={newCurrency => setCurrentCurrency(newCurrency)} + /> + </div> + </div> + </div> + ) + } + + renderCurrentLocale () { + const { t } = this.context + const { updateCurrentLocale, currentLocale } = this.props + const currentLocaleMeta = locales.find(locale => locale.code === currentLocale) + const currentLocaleName = currentLocaleMeta ? currentLocaleMeta.name : '' + + return ( + <div className="settings-page__content-row"> + <div className="settings-page__content-item"> + <span className="settings-page__content-label"> + { t('currentLanguage') } + </span> + <span className="settings-page__content-description"> + { currentLocaleName } + </span> + </div> + <div className="settings-page__content-item"> + <div className="settings-page__content-item-col"> + <SimpleDropdown + placeholder={t('selectLocale')} + options={localeOptions} + selectedOption={currentLocale} + onSelect={async newLocale => updateCurrentLocale(newLocale)} + /> + </div> + </div> + </div> + ) + } + + renderNewRpcUrl () { + const { t } = this.context + const { newRpc } = this.state + + return ( + <div className="settings-page__content-row"> + <div className="settings-page__content-item"> + <span>{ t('newRPC') }</span> + </div> + <div className="settings-page__content-item"> + <div className="settings-page__content-item-col"> + <TextField + type="text" + id="new-rpc" + placeholder={t('newRPC')} + value={newRpc} + onChange={e => this.setState({ newRpc: e.target.value })} + onKeyPress={e => { + if (e.key === 'Enter') { + this.validateRpc(newRpc) + } + }} + fullWidth + margin="none" + /> + <div + className="settings-tab__rpc-save-button" + onClick={e => { + e.preventDefault() + this.validateRpc(newRpc) + }} + > + { t('save') } + </div> + </div> + </div> + </div> + ) + } + + validateRpc (newRpc) { + const { setRpcTarget, displayWarning } = this.props + + if (validUrl.isWebUri(newRpc)) { + setRpcTarget(newRpc) + } else { + const appendedRpc = `http://${newRpc}` + + if (validUrl.isWebUri(appendedRpc)) { + displayWarning(this.context.t('uriErrorMsg')) + } else { + displayWarning(this.context.t('invalidRPC')) + } + } + } + + renderStateLogs () { + const { t } = this.context + const { displayWarning } = this.props + + return ( + <div className="settings-page__content-row"> + <div className="settings-page__content-item"> + <span>{ t('stateLogs') }</span> + <span className="settings-page__content-description"> + { t('stateLogsDescription') } + </span> + </div> + <div className="settings-page__content-item"> + <div className="settings-page__content-item-col"> + <Button + type="primary" + large + onClick={() => { + window.logStateString((err, result) => { + if (err) { + displayWarning(t('stateLogError')) + } else { + exportAsFile('MetaMask State Logs.json', result) + } + }) + }} + > + { t('downloadStateLogs') } + </Button> + </div> + </div> + </div> + ) + } + + renderSeedWords () { + const { t } = this.context + const { history } = this.props + + return ( + <div className="settings-page__content-row"> + <div className="settings-page__content-item"> + <span>{ t('revealSeedWords') }</span> + </div> + <div className="settings-page__content-item"> + <div className="settings-page__content-item-col"> + <Button + type="secondary" + large + onClick={event => { + event.preventDefault() + history.push(REVEAL_SEED_ROUTE) + }} + > + { t('revealSeedWords') } + </Button> + </div> + </div> + </div> + ) + } + + renderOldUI () { + const { t } = this.context + const { setFeatureFlagToBeta } = this.props + + return ( + <div className="settings-page__content-row"> + <div className="settings-page__content-item"> + <span>{ t('useOldUI') }</span> + </div> + <div className="settings-page__content-item"> + <div className="settings-page__content-item-col"> + <Button + type="secondary" + large + className="settings-tab__button--orange" + onClick={event => { + event.preventDefault() + setFeatureFlagToBeta() + }} + > + { t('useOldUI') } + </Button> + </div> + </div> + </div> + ) + } + + renderResetAccount () { + const { t } = this.context + const { showResetAccountConfirmationModal } = this.props + + return ( + <div className="settings-page__content-row"> + <div className="settings-page__content-item"> + <span>{ t('resetAccount') }</span> + </div> + <div className="settings-page__content-item"> + <div className="settings-page__content-item-col"> + <Button + type="secondary" + large + className="settings-tab__button--orange" + onClick={event => { + event.preventDefault() + showResetAccountConfirmationModal() + }} + > + { t('resetAccount') } + </Button> + </div> + </div> + </div> + ) + } + + renderBlockieOptIn () { + const { useBlockie, setUseBlockie } = this.props + + return ( + <div className="settings-page__content-row"> + <div className="settings-page__content-item"> + <span>{ this.context.t('blockiesIdenticon') }</span> + </div> + <div className="settings-page__content-item"> + <div className="settings-page__content-item-col"> + <ToggleButton + value={useBlockie} + onToggle={value => setUseBlockie(!value)} + activeLabel="" + inactiveLabel="" + /> + </div> + </div> + </div> + ) + } + + renderHexDataOptIn () { + const { t } = this.context + const { sendHexData, setHexDataFeatureFlag } = this.props + + return ( + <div className="settings-page__content-row"> + <div className="settings-page__content-item"> + <span>{ t('showHexData') }</span> + <div className="settings-page__content-description"> + { t('showHexDataDescription') } + </div> + </div> + <div className="settings-page__content-item"> + <div className="settings-page__content-item-col"> + <ToggleButton + value={sendHexData} + onToggle={value => setHexDataFeatureFlag(!value)} + activeLabel="" + inactiveLabel="" + /> + </div> + </div> + </div> + ) + } + + render () { + const { warning, isMascara } = this.props + + return ( + <div className="settings-page__content"> + { warning && <div className="settings-tab__error">{ warning }</div> } + { this.renderCurrentConversion() } + { this.renderCurrentLocale() } + { this.renderNewRpcUrl() } + { this.renderStateLogs() } + { this.renderSeedWords() } + { !isMascara && this.renderOldUI() } + { this.renderResetAccount() } + { this.renderBlockieOptIn() } + { this.renderHexDataOptIn() } + </div> + ) + } +} diff --git a/ui/app/components/pages/settings/settings-tab/settings-tab.container.js b/ui/app/components/pages/settings/settings-tab/settings-tab.container.js new file mode 100644 index 000000000..665b56f5c --- /dev/null +++ b/ui/app/components/pages/settings/settings-tab/settings-tab.container.js @@ -0,0 +1,59 @@ +import SettingsTab from './settings-tab.component' +import { compose } from 'recompose' +import { connect } from 'react-redux' +import { withRouter } from 'react-router-dom' +import { + setCurrentCurrency, + setRpcTarget, + displayWarning, + revealSeedConfirmation, + setUseBlockie, + updateCurrentLocale, + setFeatureFlag, + showModal, +} from '../../../../actions' + +const mapStateToProps = state => { + const { appState: { warning }, metamask } = state + const { + currentCurrency, + conversionDate, + useBlockie, + featureFlags: { sendHexData } = {}, + provider = {}, + isMascara, + currentLocale, + } = metamask + + return { + warning, + isMascara, + currentLocale, + currentCurrency, + conversionDate, + useBlockie, + sendHexData, + provider, + } +} + +const mapDispatchToProps = dispatch => { + return { + setCurrentCurrency: currency => dispatch(setCurrentCurrency(currency)), + setRpcTarget: newRpc => dispatch(setRpcTarget(newRpc)), + displayWarning: warning => dispatch(displayWarning(warning)), + revealSeedConfirmation: () => dispatch(revealSeedConfirmation()), + setUseBlockie: value => dispatch(setUseBlockie(value)), + updateCurrentLocale: key => dispatch(updateCurrentLocale(key)), + setFeatureFlagToBeta: () => { + return dispatch(setFeatureFlag('betaUI', false, 'OLD_UI_NOTIFICATION_MODAL')) + }, + setHexDataFeatureFlag: shouldShow => dispatch(setFeatureFlag('sendHexData', shouldShow)), + showResetAccountConfirmationModal: () => dispatch(showModal({ name: 'CONFIRM_RESET_ACCOUNT' })), + } +} + +export default compose( + withRouter, + connect(mapStateToProps, mapDispatchToProps) +)(SettingsTab) diff --git a/ui/app/components/pages/settings/settings.component.js b/ui/app/components/pages/settings/settings.component.js new file mode 100644 index 000000000..94a97bba1 --- /dev/null +++ b/ui/app/components/pages/settings/settings.component.js @@ -0,0 +1,54 @@ +import React, { PureComponent } from 'react' +import PropTypes from 'prop-types' +import { Switch, Route, matchPath } from 'react-router-dom' +import TabBar from '../../tab-bar' +import SettingsTab from './settings-tab' +import InfoTab from './info-tab' +import { DEFAULT_ROUTE, SETTINGS_ROUTE, INFO_ROUTE } from '../../../routes' + +export default class SettingsPage extends PureComponent { + static propTypes = { + location: PropTypes.object, + history: PropTypes.object, + t: PropTypes.func, + } + + static contextTypes = { + t: PropTypes.func, + } + + render () { + const { history, location } = this.props + + return ( + <div className="main-container settings-page"> + <div className="settings-page__header"> + <div + className="settings-page__close-button" + onClick={() => history.push(DEFAULT_ROUTE)} + /> + <TabBar + tabs={[ + { content: this.context.t('settings'), key: SETTINGS_ROUTE }, + { content: this.context.t('info'), key: INFO_ROUTE }, + ]} + isActive={key => matchPath(location.pathname, { path: key, exact: true })} + onSelect={key => history.push(key)} + /> + </div> + <Switch> + <Route + exact + path={INFO_ROUTE} + component={InfoTab} + /> + <Route + exact + path={SETTINGS_ROUTE} + component={SettingsTab} + /> + </Switch> + </div> + ) + } +} diff --git a/ui/app/components/pages/settings/settings.js b/ui/app/components/pages/settings/settings.js deleted file mode 100644 index ff42a13de..000000000 --- a/ui/app/components/pages/settings/settings.js +++ /dev/null @@ -1,365 +0,0 @@ -const { Component } = require('react') -const { withRouter } = require('react-router-dom') -const { compose } = require('recompose') -const PropTypes = require('prop-types') -const h = require('react-hyperscript') -const connect = require('react-redux').connect -const actions = require('../../../actions') -const infuraCurrencies = require('../../../infura-conversion.json') -const validUrl = require('valid-url') -const { exportAsFile } = require('../../../util') -const SimpleDropdown = require('../../dropdowns/simple-dropdown') -const ToggleButton = require('react-toggle-button') -const { REVEAL_SEED_ROUTE } = require('../../../routes') -const locales = require('../../../../../app/_locales/index.json') - -const getInfuraCurrencyOptions = () => { - const sortedCurrencies = infuraCurrencies.objects.sort((a, b) => { - return a.quote.name.toLocaleLowerCase().localeCompare(b.quote.name.toLocaleLowerCase()) - }) - - return sortedCurrencies.map(({ quote: { code, name } }) => { - return { - displayValue: `${code.toUpperCase()} - ${name}`, - key: code, - value: code, - } - }) -} - -const getLocaleOptions = () => { - return locales.map((locale) => { - return { - displayValue: `${locale.name}`, - key: locale.code, - value: locale.code, - } - }) -} - -class Settings extends Component { - constructor (props) { - super(props) - - this.state = { - newRpc: '', - } - } - - renderBlockieOptIn () { - const { metamask: { useBlockie }, setUseBlockie } = this.props - - return h('div.settings__content-row', [ - h('div.settings__content-item', [ - h('span', this.context.t('blockiesIdenticon')), - ]), - h('div.settings__content-item', [ - h('div.settings__content-item-col', [ - h(ToggleButton, { - value: useBlockie, - onToggle: (value) => setUseBlockie(!value), - activeLabel: '', - inactiveLabel: '', - }), - ]), - ]), - ]) - } - - renderCurrentConversion () { - const { metamask: { currentCurrency, conversionDate }, setCurrentCurrency } = this.props - - return h('div.settings__content-row', [ - h('div.settings__content-item', [ - h('span', this.context.t('currentConversion')), - h('span.settings__content-description', `Updated ${Date(conversionDate)}`), - ]), - h('div.settings__content-item', [ - h('div.settings__content-item-col', [ - h(SimpleDropdown, { - placeholder: this.context.t('selectCurrency'), - options: getInfuraCurrencyOptions(), - selectedOption: currentCurrency, - onSelect: newCurrency => setCurrentCurrency(newCurrency), - }), - ]), - ]), - ]) - } - - renderCurrentLocale () { - const { updateCurrentLocale, currentLocale } = this.props - const currentLocaleMeta = locales.find(locale => locale.code === currentLocale) - const currentLocaleName = currentLocaleMeta ? currentLocaleMeta.name : '' - - return h('div.settings__content-row', [ - h('div.settings__content-item', [ - h('span', 'Current Language'), - h('span.settings__content-description', `${currentLocaleName}`), - ]), - h('div.settings__content-item', [ - h('div.settings__content-item-col', [ - h(SimpleDropdown, { - placeholder: 'Select Locale', - options: getLocaleOptions(), - selectedOption: currentLocale, - onSelect: async (newLocale) => { - updateCurrentLocale(newLocale) - }, - }), - ]), - ]), - ]) - } - - renderCurrentProvider () { - const { metamask: { provider = {} } } = this.props - let title, value, color - - switch (provider.type) { - - case 'mainnet': - title = this.context.t('currentNetwork') - value = this.context.t('mainnet') - color = '#038789' - break - - case 'ropsten': - title = this.context.t('currentNetwork') - value = this.context.t('ropsten') - color = '#e91550' - break - - case 'kovan': - title = this.context.t('currentNetwork') - value = this.context.t('kovan') - color = '#690496' - break - - case 'rinkeby': - title = this.context.t('currentNetwork') - value = this.context.t('rinkeby') - color = '#ebb33f' - break - - default: - title = this.context.t('currentRpc') - value = provider.rpcTarget - } - - return h('div.settings__content-row', [ - h('div.settings__content-item', title), - h('div.settings__content-item', [ - h('div.settings__content-item-col', [ - h('div.settings__provider-wrapper', [ - h('div.settings__provider-icon', { style: { background: color } }), - h('div', value), - ]), - ]), - ]), - ]) - } - - renderNewRpcUrl () { - return ( - h('div.settings__content-row', [ - h('div.settings__content-item', [ - h('span', this.context.t('newRPC')), - ]), - h('div.settings__content-item', [ - h('div.settings__content-item-col', [ - h('input.settings__input', { - placeholder: this.context.t('newRPC'), - onChange: event => this.setState({ newRpc: event.target.value }), - onKeyPress: event => { - if (event.key === 'Enter') { - this.validateRpc(this.state.newRpc) - } - }, - }), - h('div.settings__rpc-save-button', { - onClick: event => { - event.preventDefault() - this.validateRpc(this.state.newRpc) - }, - }, this.context.t('save')), - ]), - ]), - ]) - ) - } - - validateRpc (newRpc) { - const { setRpcTarget, displayWarning } = this.props - - if (validUrl.isWebUri(newRpc)) { - setRpcTarget(newRpc) - } else { - const appendedRpc = `http://${newRpc}` - - if (validUrl.isWebUri(appendedRpc)) { - displayWarning(this.context.t('uriErrorMsg')) - } else { - displayWarning(this.context.t('invalidRPC')) - } - } - } - - renderStateLogs () { - return ( - h('div.settings__content-row', [ - h('div.settings__content-item', [ - h('div', this.context.t('stateLogs')), - h( - 'div.settings__content-description', - this.context.t('stateLogsDescription') - ), - ]), - h('div.settings__content-item', [ - h('div.settings__content-item-col', [ - h('button.btn-primary.btn--large.settings__button', { - onClick (event) { - window.logStateString((err, result) => { - if (err) { - this.state.dispatch(actions.displayWarning(this.context.t('stateLogError'))) - } else { - exportAsFile('MetaMask State Logs.json', result) - } - }) - }, - }, this.context.t('downloadStateLogs')), - ]), - ]), - ]) - ) - } - - renderSeedWords () { - const { history } = this.props - - return ( - h('div.settings__content-row', [ - h('div.settings__content-item', this.context.t('revealSeedWords')), - h('div.settings__content-item', [ - h('div.settings__content-item-col', [ - h('button.btn-primary.btn--large.settings__button--red', { - onClick: event => { - event.preventDefault() - history.push(REVEAL_SEED_ROUTE) - }, - }, this.context.t('revealSeedWords')), - ]), - ]), - ]) - ) - } - - renderOldUI () { - const { setFeatureFlagToBeta } = this.props - - return ( - h('div.settings__content-row', [ - h('div.settings__content-item', this.context.t('useOldUI')), - h('div.settings__content-item', [ - h('div.settings__content-item-col', [ - h('button.btn-primary.btn--large.settings__button--orange', { - onClick (event) { - event.preventDefault() - setFeatureFlagToBeta() - }, - }, this.context.t('useOldUI')), - ]), - ]), - ]) - ) - } - - renderResetAccount () { - const { showResetAccountConfirmationModal } = this.props - - return h('div.settings__content-row', [ - h('div.settings__content-item', this.context.t('resetAccount')), - h('div.settings__content-item', [ - h('div.settings__content-item-col', [ - h('button.btn-primary.btn--large.settings__button--orange', { - onClick (event) { - event.preventDefault() - showResetAccountConfirmationModal() - }, - }, this.context.t('resetAccount')), - ]), - ]), - ]) - } - - render () { - const { warning, isMascara } = this.props - - return ( - h('div.settings__content', [ - warning && h('div.settings__error', warning), - this.renderCurrentConversion(), - this.renderCurrentLocale(), - // this.renderCurrentProvider(), - this.renderNewRpcUrl(), - this.renderStateLogs(), - this.renderSeedWords(), - !isMascara && this.renderOldUI(), - this.renderResetAccount(), - this.renderBlockieOptIn(), - ]) - ) - } -} - -Settings.propTypes = { - metamask: PropTypes.object, - setUseBlockie: PropTypes.func, - setCurrentCurrency: PropTypes.func, - setRpcTarget: PropTypes.func, - displayWarning: PropTypes.func, - revealSeedConfirmation: PropTypes.func, - setFeatureFlagToBeta: PropTypes.func, - showResetAccountConfirmationModal: PropTypes.func, - warning: PropTypes.string, - history: PropTypes.object, - isMascara: PropTypes.bool, - updateCurrentLocale: PropTypes.func, - currentLocale: PropTypes.string, - t: PropTypes.func, -} - -const mapStateToProps = state => { - return { - metamask: state.metamask, - warning: state.appState.warning, - isMascara: state.metamask.isMascara, - currentLocale: state.metamask.currentLocale, - } -} - -const mapDispatchToProps = dispatch => { - return { - setCurrentCurrency: currency => dispatch(actions.setCurrentCurrency(currency)), - setRpcTarget: newRpc => dispatch(actions.setRpcTarget(newRpc)), - displayWarning: warning => dispatch(actions.displayWarning(warning)), - revealSeedConfirmation: () => dispatch(actions.revealSeedConfirmation()), - setUseBlockie: value => dispatch(actions.setUseBlockie(value)), - updateCurrentLocale: key => dispatch(actions.updateCurrentLocale(key)), - setFeatureFlagToBeta: () => { - return dispatch(actions.setFeatureFlag('betaUI', false, 'OLD_UI_NOTIFICATION_MODAL')) - }, - showResetAccountConfirmationModal: () => { - return dispatch(actions.showModal({ name: 'CONFIRM_RESET_ACCOUNT' })) - }, - } -} - -Settings.contextTypes = { - t: PropTypes.func, -} - -module.exports = compose( - withRouter, - connect(mapStateToProps, mapDispatchToProps) -)(Settings) diff --git a/ui/app/components/pages/unlock-page/index.scss b/ui/app/components/pages/unlock-page/index.scss index 3d44bd037..6bd52282d 100644 --- a/ui/app/components/pages/unlock-page/index.scss +++ b/ui/app/components/pages/unlock-page/index.scss @@ -14,6 +14,7 @@ align-self: stretch; justify-content: center; flex: 1 0 auto; + height: 100vh; } &__mascot-container { |