diff options
author | brunobar79 <brunobar79@gmail.com> | 2018-11-07 09:21:19 +0800 |
---|---|---|
committer | brunobar79 <brunobar79@gmail.com> | 2018-11-07 09:21:19 +0800 |
commit | c651212025ab7cd0309d96be6687c1eba35ab9fa (patch) | |
tree | fa5a6020daae332c5df47bdee3492e8a079375e3 /ui/app | |
parent | 9c1b2108f69334884473eb95758e2d1c02c984d6 (diff) | |
parent | 7f944923d170d6e7e04f6e238d2fd5f80abe3161 (diff) | |
download | tangerine-wallet-browser-c651212025ab7cd0309d96be6687c1eba35ab9fa.tar.gz tangerine-wallet-browser-c651212025ab7cd0309d96be6687c1eba35ab9fa.tar.zst tangerine-wallet-browser-c651212025ab7cd0309d96be6687c1eba35ab9fa.zip |
fix merge conflicts
Diffstat (limited to 'ui/app')
110 files changed, 1224 insertions, 444 deletions
diff --git a/ui/app/actions.js b/ui/app/actions.js index f8a375e2f..501fef76d 100644 --- a/ui/app/actions.js +++ b/ui/app/actions.js @@ -309,7 +309,7 @@ var actions = { setPreference, updatePreferences, UPDATE_PREFERENCES: 'UPDATE_PREFERENCES', - setUseETHAsPrimaryCurrencyPreference, + setUseNativeCurrencyAsPrimaryCurrencyPreference, setMouseUserState, SET_MOUSE_USER_STATE: 'SET_MOUSE_USER_STATE', @@ -325,6 +325,9 @@ var actions = { clearPendingTokens, createCancelTransaction, + approveProviderRequest, + rejectProviderRequest, + clearApprovedOrigins, } module.exports = actions @@ -1874,10 +1877,10 @@ function updateProviderType (type) { } } -function setRpcTarget (newRpc) { +function setRpcTarget (newRpc, chainId, ticker = 'ETH', nickname = '') { return (dispatch) => { - log.debug(`background.setRpcTarget: ${newRpc}`) - background.setCustomRpc(newRpc, (err, result) => { + log.debug(`background.setRpcTarget: ${newRpc} ${chainId} ${ticker} ${nickname}`) + background.setCustomRpc(newRpc, chainId, ticker, nickname, (err, result) => { if (err) { log.error(err) return dispatch(actions.displayWarning('Had a problem changing networks!')) @@ -2330,8 +2333,8 @@ function updatePreferences (value) { } } -function setUseETHAsPrimaryCurrencyPreference (value) { - return setPreference('useETHAsPrimaryCurrency', value) +function setUseNativeCurrencyAsPrimaryCurrencyPreference (value) { + return setPreference('useNativeCurrencyAsPrimaryCurrency', value) } function setNetworkNonce (networkNonce) { @@ -2484,3 +2487,21 @@ function setPendingTokens (pendingTokens) { payload: tokens, } } + +function approveProviderRequest (origin) { + return (dispatch) => { + background.approveProviderRequest(origin) + } +} + +function rejectProviderRequest (origin) { + return (dispatch) => { + background.rejectProviderRequest(origin) + } +} + +function clearApprovedOrigins () { + return (dispatch) => { + background.clearApprovedOrigins() + } +} diff --git a/ui/app/app.js b/ui/app/app.js index aeb3d05ee..b3aff1f39 100644 --- a/ui/app/app.js +++ b/ui/app/app.js @@ -101,7 +101,7 @@ class App extends Component { network, isMouseUser, provider, - frequentRpcList, + frequentRpcListDetail, currentView, setMouseUserState, sidebar, @@ -147,7 +147,7 @@ class App extends Component { // network dropdown h(NetworkDropdown, { provider, - frequentRpcList, + frequentRpcListDetail, }, []), h(AccountMenu), @@ -230,7 +230,7 @@ App.propTypes = { alertMessage: PropTypes.string, network: PropTypes.string, provider: PropTypes.object, - frequentRpcList: PropTypes.array, + frequentRpcListDetail: PropTypes.array, currentView: PropTypes.object, sidebar: PropTypes.object, alertOpen: PropTypes.bool, @@ -322,7 +322,7 @@ function mapStateToProps (state) { forgottenPassword: state.appState.forgottenPassword, nextUnreadNotice, lostAccounts, - frequentRpcList: state.metamask.frequentRpcList || [], + frequentRpcListDetail: state.metamask.frequentRpcListDetail || [], currentCurrency: state.metamask.currentCurrency, isMouseUser: state.appState.isMouseUser, betaUI: state.metamask.featureFlags.betaUI, diff --git a/ui/app/components/account-dropdowns.js b/ui/app/components/account-dropdowns.js index 043008a36..06376e48b 100644 --- a/ui/app/components/account-dropdowns.js +++ b/ui/app/components/account-dropdowns.js @@ -6,10 +6,11 @@ const genAccountLink = require('etherscan-link').createAccountLink const connect = require('react-redux').connect const Dropdown = require('./dropdown').Dropdown const DropdownMenuItem = require('./dropdown').DropdownMenuItem -const Identicon = require('./identicon') const copyToClipboard = require('copy-to-clipboard') const { checksumAddress } = require('../util') +import Identicon from './identicon' + class AccountDropdowns extends Component { constructor (props) { super(props) diff --git a/ui/app/components/account-menu/index.js b/ui/app/components/account-menu/index.js index c9c5b60e1..94eae8d07 100644 --- a/ui/app/components/account-menu/index.js +++ b/ui/app/components/account-menu/index.js @@ -7,10 +7,10 @@ const PropTypes = require('prop-types') const h = require('react-hyperscript') const actions = require('../../actions') const { Menu, Item, Divider, CloseArea } = require('../dropdowns/components/menu') -const Identicon = require('../identicon') const { ENVIRONMENT_TYPE_POPUP } = require('../../../../app/scripts/lib/enums') const { getEnvironmentType } = require('../../../../app/scripts/lib/util') const Tooltip = require('../tooltip') +import Identicon from '../identicon' import UserPreferencedCurrencyDisplay from '../user-preferenced-currency-display' import { PRIMARY } from '../../constants/common' diff --git a/ui/app/components/account-panel.js b/ui/app/components/account-panel.js index abaaf8163..a379ed3ac 100644 --- a/ui/app/components/account-panel.js +++ b/ui/app/components/account-panel.js @@ -1,7 +1,7 @@ const inherits = require('util').inherits const Component = require('react').Component const h = require('react-hyperscript') -const Identicon = require('./identicon') +import Identicon from './identicon' const formatBalance = require('../util').formatBalance const addressSummary = require('../util').addressSummary diff --git a/ui/app/components/app-header/app-header.component.js b/ui/app/components/app-header/app-header.component.js index b8b002dcc..c82dc1de9 100644 --- a/ui/app/components/app-header/app-header.component.js +++ b/ui/app/components/app-header/app-header.component.js @@ -2,13 +2,13 @@ import React, { PureComponent } from 'react' import PropTypes from 'prop-types' import classnames from 'classnames' import { matchPath } from 'react-router-dom' +import Identicon from '../identicon' const { ENVIRONMENT_TYPE_NOTIFICATION, ENVIRONMENT_TYPE_POPUP, } = require('../../../../app/scripts/lib/enums') const { DEFAULT_ROUTE, INITIALIZE_ROUTE, CONFIRM_TRANSACTION_ROUTE } = require('../../routes') -const Identicon = require('../identicon') const NetworkIndicator = require('../network') export default class AppHeader extends PureComponent { diff --git a/ui/app/components/balance-component.js b/ui/app/components/balance-component.js index e1fcf08e0..0f8514c81 100644 --- a/ui/app/components/balance-component.js +++ b/ui/app/components/balance-component.js @@ -2,11 +2,11 @@ const Component = require('react').Component const connect = require('react-redux').connect const h = require('react-hyperscript') const inherits = require('util').inherits -const TokenBalance = require('./token-balance') -const Identicon = require('./identicon') +import TokenBalance from './token-balance' +import Identicon from './identicon' import UserPreferencedCurrencyDisplay from './user-preferenced-currency-display' import { PRIMARY, SECONDARY } from '../constants/common' -const { getAssetImages, conversionRateSelector, getCurrentCurrency} = require('../selectors') +const { getNativeCurrency, getAssetImages, conversionRateSelector, getCurrentCurrency} = require('../selectors') const { formatBalance } = require('../util') @@ -21,6 +21,7 @@ function mapStateToProps (state) { return { account, network, + nativeCurrency: getNativeCurrency(state), conversionRate: conversionRateSelector(state), currentCurrency: getCurrentCurrency(state), assetImages: getAssetImages(state), @@ -66,10 +67,10 @@ BalanceComponent.prototype.renderTokenBalance = function () { BalanceComponent.prototype.renderBalance = function () { const props = this.props - const { account } = props + const { account, nativeCurrency } = props const balanceValue = account && account.balance const needsParse = 'needsParse' in props ? props.needsParse : true - const formattedBalance = balanceValue ? formatBalance(balanceValue, 6, needsParse) : '...' + const formattedBalance = balanceValue ? formatBalance(balanceValue, 6, needsParse, nativeCurrency) : '...' const showFiat = 'showFiat' in props ? props.showFiat : true if (formattedBalance === 'None' || formattedBalance === '...') { @@ -81,11 +82,12 @@ BalanceComponent.prototype.renderBalance = function () { } return h('div.flex-column.balance-display', {}, [ - h('div.token-amount', {}, h(UserPreferencedCurrencyDisplay, { + h(UserPreferencedCurrencyDisplay, { + className: 'token-amount', value: balanceValue, type: PRIMARY, ethNumberOfDecimals: 3, - })), + }), showFiat && h(UserPreferencedCurrencyDisplay, { value: balanceValue, diff --git a/ui/app/components/currency-display/currency-display.component.js b/ui/app/components/currency-display/currency-display.component.js index 5f5717be3..2d7413b57 100644 --- a/ui/app/components/currency-display/currency-display.component.js +++ b/ui/app/components/currency-display/currency-display.component.js @@ -1,7 +1,7 @@ import React, { PureComponent } from 'react' import PropTypes from 'prop-types' import classnames from 'classnames' -import { ETH, GWEI } from '../../constants/common' +import { GWEI } from '../../constants/common' export default class CurrencyDisplay extends PureComponent { static propTypes = { @@ -10,8 +10,9 @@ export default class CurrencyDisplay extends PureComponent { prefix: PropTypes.string, prefixComponent: PropTypes.node, style: PropTypes.object, + suffix: PropTypes.string, // Used in container - currency: PropTypes.oneOf([ETH]), + currency: PropTypes.string, denomination: PropTypes.oneOf([GWEI]), value: PropTypes.string, numberOfDecimals: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), @@ -19,17 +20,25 @@ export default class CurrencyDisplay extends PureComponent { } render () { - const { className, displayValue, prefix, prefixComponent, style } = this.props + const { className, displayValue, prefix, prefixComponent, style, suffix } = this.props const text = `${prefix || ''}${displayValue}` + const title = `${text} ${suffix}` return ( <div className={classnames('currency-display-component', className)} style={style} - title={text} + title={title} > { prefixComponent} <span className="currency-display-component__text">{ text }</span> + { + suffix && ( + <span className="currency-display-component__suffix"> + { suffix } + </span> + ) + } </div> ) } diff --git a/ui/app/components/currency-display/currency-display.container.js b/ui/app/components/currency-display/currency-display.container.js index b387229b5..6ddf07172 100644 --- a/ui/app/components/currency-display/currency-display.container.js +++ b/ui/app/components/currency-display/currency-display.container.js @@ -3,16 +3,17 @@ import CurrencyDisplay from './currency-display.component' import { getValueFromWeiHex, formatCurrency } from '../../helpers/confirm-transaction/util' const mapStateToProps = state => { - const { metamask: { currentCurrency, conversionRate } } = state + const { metamask: { nativeCurrency, currentCurrency, conversionRate } } = state return { currentCurrency, conversionRate, + nativeCurrency, } } const mergeProps = (stateProps, dispatchProps, ownProps) => { - const { currentCurrency, conversionRate, ...restStateProps } = stateProps + const { nativeCurrency, currentCurrency, conversionRate, ...restStateProps } = stateProps const { value, numberOfDecimals = 2, @@ -24,16 +25,17 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => { const toCurrency = currency || currentCurrency const convertedValue = getValueFromWeiHex({ - value, toCurrency, conversionRate, numberOfDecimals, toDenomination: denomination, + value, fromCurrency: nativeCurrency, toCurrency, conversionRate, numberOfDecimals, toDenomination: denomination, }) - const formattedValue = formatCurrency(convertedValue, toCurrency) - const displayValue = hideLabel ? formattedValue : `${formattedValue} ${toCurrency.toUpperCase()}` + const displayValue = formatCurrency(convertedValue, toCurrency) + const suffix = hideLabel ? undefined : toCurrency.toUpperCase() return { ...restStateProps, ...dispatchProps, ...restOwnProps, displayValue, + suffix, } } diff --git a/ui/app/components/currency-display/index.scss b/ui/app/components/currency-display/index.scss index 8c0196102..313c932b8 100644 --- a/ui/app/components/currency-display/index.scss +++ b/ui/app/components/currency-display/index.scss @@ -7,4 +7,8 @@ overflow: hidden; text-overflow: ellipsis; } + + &__suffix { + padding-left: 4px; + } } diff --git a/ui/app/components/currency-display/tests/currency-display.container.test.js b/ui/app/components/currency-display/tests/currency-display.container.test.js index b9f98c543..0c886af50 100644 --- a/ui/app/components/currency-display/tests/currency-display.container.test.js +++ b/ui/app/components/currency-display/tests/currency-display.container.test.js @@ -20,12 +20,14 @@ describe('CurrencyDisplay container', () => { metamask: { conversionRate: 280.45, currentCurrency: 'usd', + nativeCurrency: 'ETH', }, } assert.deepEqual(mapStateToProps(mockState), { conversionRate: 280.45, currentCurrency: 'usd', + nativeCurrency: 'ETH', }) }) }) @@ -35,6 +37,7 @@ describe('CurrencyDisplay container', () => { const mockStateProps = { conversionRate: 280.45, currentCurrency: 'usd', + nativeCurrency: 'ETH', } const tests = [ @@ -43,71 +46,93 @@ describe('CurrencyDisplay container', () => { value: '0x2386f26fc10000', numberOfDecimals: 2, currency: 'usd', + nativeCurrency: 'ETH', }, result: { - displayValue: '$2.80 USD', + displayValue: '$2.80', + suffix: 'USD', + nativeCurrency: 'ETH', }, }, { props: { value: '0x2386f26fc10000', + currency: 'usd', + nativeCurrency: 'ETH', }, result: { - displayValue: '$2.80 USD', + displayValue: '$2.80', + suffix: 'USD', + nativeCurrency: 'ETH', }, }, { props: { value: '0x1193461d01595930', currency: 'ETH', + nativeCurrency: 'ETH', numberOfDecimals: 3, }, result: { - displayValue: '1.266 ETH', + displayValue: '1.266', + suffix: 'ETH', + nativeCurrency: 'ETH', }, }, { props: { value: '0x1193461d01595930', currency: 'ETH', + nativeCurrency: 'ETH', numberOfDecimals: 3, hideLabel: true, }, result: { + nativeCurrency: 'ETH', displayValue: '1.266', + suffix: undefined, }, }, { props: { value: '0x3b9aca00', currency: 'ETH', + nativeCurrency: 'ETH', denomination: 'GWEI', hideLabel: true, }, result: { + nativeCurrency: 'ETH', displayValue: '1', + suffix: undefined, }, }, { props: { value: '0x3b9aca00', currency: 'ETH', + nativeCurrency: 'ETH', denomination: 'WEI', hideLabel: true, }, result: { + nativeCurrency: 'ETH', displayValue: '1000000000', + suffix: undefined, }, }, { props: { value: '0x3b9aca00', currency: 'ETH', + nativeCurrency: 'ETH', numberOfDecimals: 100, hideLabel: true, }, result: { + nativeCurrency: 'ETH', displayValue: '1e-9', + suffix: undefined, }, }, ] diff --git a/ui/app/components/currency-input/currency-input.component.js b/ui/app/components/currency-input/currency-input.component.js index 54cd0e1ac..0761a75c5 100644 --- a/ui/app/components/currency-input/currency-input.component.js +++ b/ui/app/components/currency-input/currency-input.component.js @@ -14,6 +14,7 @@ export default class CurrencyInput extends PureComponent { static propTypes = { conversionRate: PropTypes.number, currentCurrency: PropTypes.string, + nativeCurrency: PropTypes.string, onChange: PropTypes.func, onBlur: PropTypes.func, suffix: PropTypes.string, @@ -77,13 +78,13 @@ export default class CurrencyInput extends PureComponent { } renderConversionComponent () { - const { useFiat, currentCurrency } = this.props + const { useFiat, currentCurrency, nativeCurrency } = this.props const { hexValue } = this.state let currency, numberOfDecimals if (useFiat) { // Display ETH - currency = ETH + currency = nativeCurrency || ETH numberOfDecimals = 6 } else { // Display Fiat diff --git a/ui/app/components/currency-input/currency-input.container.js b/ui/app/components/currency-input/currency-input.container.js index 18e5533de..1d1ed7b41 100644 --- a/ui/app/components/currency-input/currency-input.container.js +++ b/ui/app/components/currency-input/currency-input.container.js @@ -3,18 +3,19 @@ import CurrencyInput from './currency-input.component' import { ETH } from '../../constants/common' const mapStateToProps = state => { - const { metamask: { currentCurrency, conversionRate } } = state + const { metamask: { nativeCurrency, currentCurrency, conversionRate } } = state return { + nativeCurrency, currentCurrency, conversionRate, } } const mergeProps = (stateProps, dispatchProps, ownProps) => { - const { currentCurrency } = stateProps + const { nativeCurrency, currentCurrency } = stateProps const { useFiat } = ownProps - const suffix = useFiat ? currentCurrency.toUpperCase() : ETH + const suffix = useFiat ? currentCurrency.toUpperCase() : nativeCurrency || ETH return { ...stateProps, diff --git a/ui/app/components/currency-input/tests/currency-input.component.test.js b/ui/app/components/currency-input/tests/currency-input.component.test.js index 8de0ef863..a33889f94 100644 --- a/ui/app/components/currency-input/tests/currency-input.component.test.js +++ b/ui/app/components/currency-input/tests/currency-input.component.test.js @@ -22,6 +22,7 @@ describe('CurrencyInput Component', () => { it('should render properly with a suffix', () => { const mockStore = { metamask: { + nativeCurrency: 'ETH', currentCurrency: 'usd', conversionRate: 231.06, }, @@ -32,6 +33,7 @@ describe('CurrencyInput Component', () => { <Provider store={store}> <CurrencyInput suffix="ETH" + nativeCurrency="ETH" /> </Provider> ) @@ -45,6 +47,7 @@ describe('CurrencyInput Component', () => { it('should render properly with an ETH value', () => { const mockStore = { metamask: { + nativeCurrency: 'ETH', currentCurrency: 'usd', conversionRate: 231.06, }, @@ -56,6 +59,7 @@ describe('CurrencyInput Component', () => { <CurrencyInput value="de0b6b3a7640000" suffix="ETH" + nativeCurrency="ETH" currentCurrency="usd" conversionRate={231.06} /> @@ -69,12 +73,13 @@ describe('CurrencyInput Component', () => { assert.equal(wrapper.find('.unit-input__suffix').length, 1) assert.equal(wrapper.find('.unit-input__suffix').text(), 'ETH') assert.equal(wrapper.find('.unit-input__input').props().value, '1') - assert.equal(wrapper.find('.currency-display-component').text(), '$231.06 USD') + assert.equal(wrapper.find('.currency-display-component').text(), '$231.06USD') }) it('should render properly with a fiat value', () => { const mockStore = { metamask: { + nativeCurrency: 'ETH', currentCurrency: 'usd', conversionRate: 231.06, }, @@ -87,6 +92,7 @@ describe('CurrencyInput Component', () => { value="f602f2234d0ea" suffix="USD" useFiat + nativeCurrency="ETH" currentCurrency="usd" conversionRate={231.06} /> @@ -100,7 +106,7 @@ describe('CurrencyInput Component', () => { assert.equal(wrapper.find('.unit-input__suffix').length, 1) assert.equal(wrapper.find('.unit-input__suffix').text(), 'USD') assert.equal(wrapper.find('.unit-input__input').props().value, '1') - assert.equal(wrapper.find('.currency-display-component').text(), '0.004328 ETH') + assert.equal(wrapper.find('.currency-display-component').text(), '0.004328ETH') }) }) @@ -116,6 +122,7 @@ describe('CurrencyInput Component', () => { it('should call onChange and onBlur on input changes with the hex value for ETH', () => { const mockStore = { metamask: { + nativeCurrency: 'ETH', currentCurrency: 'usd', conversionRate: 231.06, }, @@ -127,6 +134,7 @@ describe('CurrencyInput Component', () => { onChange={handleChangeSpy} onBlur={handleBlurSpy} suffix="ETH" + nativeCurrency="ETH" currentCurrency="usd" conversionRate={231.06} /> @@ -140,14 +148,14 @@ describe('CurrencyInput Component', () => { const currencyInputInstance = wrapper.find(CurrencyInput).at(0).instance() assert.equal(currencyInputInstance.state.decimalValue, 0) assert.equal(currencyInputInstance.state.hexValue, undefined) - assert.equal(wrapper.find('.currency-display-component').text(), '$0.00 USD') + assert.equal(wrapper.find('.currency-display-component').text(), '$0.00USD') const input = wrapper.find('input') assert.equal(input.props().value, 0) input.simulate('change', { target: { value: 1 } }) assert.equal(handleChangeSpy.callCount, 1) assert.ok(handleChangeSpy.calledWith('de0b6b3a7640000')) - assert.equal(wrapper.find('.currency-display-component').text(), '$231.06 USD') + assert.equal(wrapper.find('.currency-display-component').text(), '$231.06USD') assert.equal(currencyInputInstance.state.decimalValue, 1) assert.equal(currencyInputInstance.state.hexValue, 'de0b6b3a7640000') @@ -160,6 +168,7 @@ describe('CurrencyInput Component', () => { it('should call onChange and onBlur on input changes with the hex value for fiat', () => { const mockStore = { metamask: { + nativeCurrency: 'ETH', currentCurrency: 'usd', conversionRate: 231.06, }, @@ -171,6 +180,7 @@ describe('CurrencyInput Component', () => { onChange={handleChangeSpy} onBlur={handleBlurSpy} suffix="USD" + nativeCurrency="ETH" currentCurrency="usd" conversionRate={231.06} useFiat @@ -185,14 +195,14 @@ describe('CurrencyInput Component', () => { const currencyInputInstance = wrapper.find(CurrencyInput).at(0).instance() assert.equal(currencyInputInstance.state.decimalValue, 0) assert.equal(currencyInputInstance.state.hexValue, undefined) - assert.equal(wrapper.find('.currency-display-component').text(), '0 ETH') + assert.equal(wrapper.find('.currency-display-component').text(), '0ETH') const input = wrapper.find('input') assert.equal(input.props().value, 0) input.simulate('change', { target: { value: 1 } }) assert.equal(handleChangeSpy.callCount, 1) assert.ok(handleChangeSpy.calledWith('f602f2234d0ea')) - assert.equal(wrapper.find('.currency-display-component').text(), '0.004328 ETH') + assert.equal(wrapper.find('.currency-display-component').text(), '0.004328ETH') assert.equal(currencyInputInstance.state.decimalValue, 1) assert.equal(currencyInputInstance.state.hexValue, 'f602f2234d0ea') @@ -205,6 +215,7 @@ describe('CurrencyInput Component', () => { it('should change the state and pass in a new decimalValue when props.value changes', () => { const mockStore = { metamask: { + nativeCurrency: 'ETH', currentCurrency: 'usd', conversionRate: 231.06, }, @@ -216,6 +227,7 @@ describe('CurrencyInput Component', () => { onChange={handleChangeSpy} onBlur={handleBlurSpy} suffix="USD" + nativeCurrency="ETH" currentCurrency="usd" conversionRate={231.06} useFiat diff --git a/ui/app/components/currency-input/tests/currency-input.container.test.js b/ui/app/components/currency-input/tests/currency-input.container.test.js index e77945e4d..5d72958e6 100644 --- a/ui/app/components/currency-input/tests/currency-input.container.test.js +++ b/ui/app/components/currency-input/tests/currency-input.container.test.js @@ -20,12 +20,14 @@ describe('CurrencyInput container', () => { metamask: { conversionRate: 280.45, currentCurrency: 'usd', + nativeCurrency: 'ETH', }, } assert.deepEqual(mapStateToProps(mockState), { conversionRate: 280.45, currentCurrency: 'usd', + nativeCurrency: 'ETH', }) }) }) @@ -35,12 +37,14 @@ describe('CurrencyInput container', () => { const mockStateProps = { conversionRate: 280.45, currentCurrency: 'usd', + nativeCurrency: 'ETH', } const mockDispatchProps = {} assert.deepEqual(mergeProps(mockStateProps, mockDispatchProps, { useFiat: true }), { conversionRate: 280.45, currentCurrency: 'usd', + nativeCurrency: 'ETH', useFiat: true, suffix: 'USD', }) @@ -48,6 +52,7 @@ describe('CurrencyInput container', () => { assert.deepEqual(mergeProps(mockStateProps, mockDispatchProps, {}), { conversionRate: 280.45, currentCurrency: 'usd', + nativeCurrency: 'ETH', suffix: 'ETH', }) }) diff --git a/ui/app/components/dropdowns/components/account-dropdowns.js b/ui/app/components/dropdowns/components/account-dropdowns.js index b497f5c09..e6b3e0c0c 100644 --- a/ui/app/components/dropdowns/components/account-dropdowns.js +++ b/ui/app/components/dropdowns/components/account-dropdowns.js @@ -6,7 +6,7 @@ const genAccountLink = require('../../../../lib/account-link.js') const connect = require('react-redux').connect const Dropdown = require('./dropdown').Dropdown const DropdownMenuItem = require('./dropdown').DropdownMenuItem -const Identicon = require('../../identicon') +import Identicon from '../../identicon' const { checksumAddress } = require('../../../util') const copyToClipboard = require('copy-to-clipboard') const { formatBalance } = require('../../../util') @@ -26,14 +26,14 @@ class AccountDropdowns extends Component { } renderAccounts () { - const { identities, accounts, selected, menuItemStyles, actions, keyrings } = this.props + const { identities, accounts, selected, menuItemStyles, actions, keyrings, ticker } = this.props return Object.keys(identities).map((key, index) => { const identity = identities[key] const isSelected = identity.address === selected const balanceValue = accounts[key].balance - const formattedBalance = balanceValue ? formatBalance(balanceValue, 6) : '...' + const formattedBalance = balanceValue ? formatBalance(balanceValue, 6, true, ticker) : '...' const simpleAddress = identity.address.substring(2).toLowerCase() const keyring = keyrings.find((kr) => { @@ -421,6 +421,7 @@ AccountDropdowns.propTypes = { network: PropTypes.number, // actions.showExportPrivateKeyModal: , style: PropTypes.object, + ticker: PropTypes.string, enableAccountsSelector: PropTypes.bool, enableAccountOption: PropTypes.bool, enableAccountOptions: PropTypes.bool, @@ -458,6 +459,7 @@ const mapDispatchToProps = (dispatch) => { function mapStateToProps (state) { return { + ticker: state.metamask.ticker, keyrings: state.metamask.keyrings, sidebarOpen: state.appState.sidebar.isOpen, } diff --git a/ui/app/components/dropdowns/network-dropdown.js b/ui/app/components/dropdowns/network-dropdown.js index b252b25d9..d4cc695a6 100644 --- a/ui/app/components/dropdowns/network-dropdown.js +++ b/ui/app/components/dropdowns/network-dropdown.js @@ -24,8 +24,9 @@ const notToggleElementClassnames = [ function mapStateToProps (state) { return { provider: state.metamask.provider, - frequentRpcList: state.metamask.frequentRpcList || [], + frequentRpcListDetail: state.metamask.frequentRpcListDetail || [], networkDropdownOpen: state.appState.networkDropdownOpen, + network: state.metamask.network, } } @@ -40,8 +41,8 @@ function mapDispatchToProps (dispatch) { setDefaultRpcTarget: type => { dispatch(actions.setDefaultRpcTarget(type)) }, - setRpcTarget: (target) => { - dispatch(actions.setRpcTarget(target)) + setRpcTarget: (target, network, ticker, nickname) => { + dispatch(actions.setRpcTarget(target, network, ticker, nickname)) }, delRpcTarget: (target) => { dispatch(actions.delRpcTarget(target)) @@ -71,7 +72,7 @@ module.exports = compose( NetworkDropdown.prototype.render = function () { const props = this.props const { provider: { type: providerType, rpcTarget: activeNetwork } } = props - const rpcList = props.frequentRpcList + const rpcListDetail = props.frequentRpcListDetail const isOpen = this.props.networkDropdownOpen const dropdownMenuItemStyle = { fontSize: '16px', @@ -225,7 +226,7 @@ NetworkDropdown.prototype.render = function () { ), this.renderCustomOption(props.provider), - this.renderCommonRpc(rpcList, props.provider), + this.renderCommonRpc(rpcListDetail, props.provider), h( DropdownMenuItem, @@ -267,28 +268,33 @@ NetworkDropdown.prototype.getNetworkName = function () { } else if (providerName === 'rinkeby') { name = this.context.t('rinkeby') } else { - name = this.context.t('unknownNetwork') + name = provider.nickname || this.context.t('unknownNetwork') } return name } -NetworkDropdown.prototype.renderCommonRpc = function (rpcList, provider) { +NetworkDropdown.prototype.renderCommonRpc = function (rpcListDetail, provider) { const props = this.props - const reversedRpcList = rpcList.slice().reverse() + const reversedRpcListDetail = rpcListDetail.slice().reverse() + const network = props.network - return reversedRpcList.map((rpc) => { + return reversedRpcListDetail.map((entry) => { + const rpc = entry.rpcUrl + const ticker = entry.ticker || 'ETH' + const nickname = entry.nickname || '' const currentRpcTarget = provider.type === 'rpc' && rpc === provider.rpcTarget if ((rpc === 'http://localhost:8545') || currentRpcTarget) { return null } else { + const chainId = entry.chainId || network return h( DropdownMenuItem, { key: `common${rpc}`, closeMenu: () => this.props.hideNetworkDropdown(), - onClick: () => props.setRpcTarget(rpc), + onClick: () => props.setRpcTarget(rpc, chainId, ticker, nickname), style: { fontSize: '16px', lineHeight: '20px', @@ -302,7 +308,7 @@ NetworkDropdown.prototype.renderCommonRpc = function (rpcList, provider) { style: { color: currentRpcTarget ? '#ffffff' : '#9b9b9b', }, - }, rpc), + }, nickname || rpc), h('i.fa.fa-times.delete', { onClick: (e) => { @@ -317,8 +323,9 @@ NetworkDropdown.prototype.renderCommonRpc = function (rpcList, provider) { } NetworkDropdown.prototype.renderCustomOption = function (provider) { - const { rpcTarget, type } = provider + const { rpcTarget, type, ticker, nickname } = provider const props = this.props + const network = props.network if (type !== 'rpc') return null @@ -332,7 +339,7 @@ NetworkDropdown.prototype.renderCustomOption = function (provider) { DropdownMenuItem, { key: rpcTarget, - onClick: () => props.setRpcTarget(rpcTarget), + onClick: () => props.setRpcTarget(rpcTarget, network, ticker, nickname), closeMenu: () => this.props.hideNetworkDropdown(), style: { fontSize: '16px', @@ -347,7 +354,7 @@ NetworkDropdown.prototype.renderCustomOption = function (provider) { style: { color: '#ffffff', }, - }, rpcTarget), + }, nickname || rpcTarget), ] ) } diff --git a/ui/app/components/dropdowns/tests/network-dropdown.test.js b/ui/app/components/dropdowns/tests/network-dropdown.test.js index 699b54605..88ad56851 100644 --- a/ui/app/components/dropdowns/tests/network-dropdown.test.js +++ b/ui/app/components/dropdowns/tests/network-dropdown.test.js @@ -45,8 +45,8 @@ describe('Network Dropdown', () => { provider: { 'type': 'test', }, - frequentRpcList: [ - 'http://localhost:7545', + frequentRpcListDetail: [ + { rpcUrl: 'http://localhost:7545' }, ], }, appState: { diff --git a/ui/app/components/eth-balance.js b/ui/app/components/eth-balance.js index c3d084bdc..2f6395a2d 100644 --- a/ui/app/components/eth-balance.js +++ b/ui/app/components/eth-balance.js @@ -1,5 +1,6 @@ const { Component } = require('react') const h = require('react-hyperscript') +const connect = require('react-redux').connect const { inherits } = require('util') const { formatBalance, @@ -8,7 +9,12 @@ const { const Tooltip = require('./tooltip.js') const FiatValue = require('./fiat-value.js') -module.exports = EthBalanceComponent +module.exports = connect(mapStateToProps)(EthBalanceComponent) +function mapStateToProps (state) { + return { + ticker: state.metamask.ticker, + } +} inherits(EthBalanceComponent, Component) function EthBalanceComponent () { @@ -17,9 +23,9 @@ function EthBalanceComponent () { EthBalanceComponent.prototype.render = function () { const props = this.props - const { value, style, width, needsParse = true } = props + const { ticker, value, style, width, needsParse = true } = props - const formattedValue = value ? formatBalance(value, 6, needsParse) : '...' + const formattedValue = value ? formatBalance(value, 6, needsParse, ticker) : '...' return ( diff --git a/ui/app/components/identicon.js b/ui/app/components/identicon.js deleted file mode 100644 index 7bd921892..000000000 --- a/ui/app/components/identicon.js +++ /dev/null @@ -1,124 +0,0 @@ -const Component = require('react').Component -const h = require('react-hyperscript') -const inherits = require('util').inherits -const connect = require('react-redux').connect -const isNode = require('detect-node') -const findDOMNode = require('react-dom').findDOMNode -const jazzicon = require('jazzicon') -const iconFactoryGen = require('../../lib/icon-factory') -const iconFactory = iconFactoryGen(jazzicon) -const { toDataUrl } = require('../../lib/blockies') - -module.exports = connect(mapStateToProps)(IdenticonComponent) - -inherits(IdenticonComponent, Component) -function IdenticonComponent () { - Component.call(this) - - this.defaultDiameter = 46 -} - -function mapStateToProps (state) { - return { - useBlockie: state.metamask.useBlockie, - } -} - -IdenticonComponent.prototype.render = function () { - var props = this.props - const { className = '', address, image } = props - var diameter = props.diameter || this.defaultDiameter - const style = { - height: diameter, - width: diameter, - borderRadius: diameter / 2, - } - if (image) { - return h('img', { - className: `${className} identicon`, - src: image, - style: { - ...style, - }, - }) - } else if (address) { - return h('div', { - className: `${className} identicon`, - key: 'identicon-' + address, - style: { - display: 'flex', - flexShrink: 0, - alignItems: 'center', - justifyContent: 'center', - ...style, - overflow: 'hidden', - }, - }) - } else { - return h('img.balance-icon', { - className, - src: './images/eth_logo.svg', - style: { - ...style, - }, - }) - } -} - -IdenticonComponent.prototype.componentDidMount = function () { - var props = this.props - const { address, useBlockie } = props - - if (!address) return - - if (!isNode) { - // eslint-disable-next-line react/no-find-dom-node - var container = findDOMNode(this) - - const diameter = props.diameter || this.defaultDiameter - - if (useBlockie) { - _generateBlockie(container, address, diameter) - } else { - _generateJazzicon(container, address, diameter) - } - } -} - -IdenticonComponent.prototype.componentDidUpdate = function () { - var props = this.props - const { address, useBlockie } = props - - if (!address) return - - if (!isNode) { - // eslint-disable-next-line react/no-find-dom-node - var container = findDOMNode(this) - - var children = container.children - for (var i = 0; i < children.length; i++) { - container.removeChild(children[i]) - } - - const diameter = props.diameter || this.defaultDiameter - - if (useBlockie) { - _generateBlockie(container, address, diameter) - } else { - _generateJazzicon(container, address, diameter) - } - } -} - -function _generateBlockie (container, address, diameter) { - const img = new Image() - img.src = toDataUrl(address) - img.height = diameter - img.width = diameter - container.appendChild(img) -} - -function _generateJazzicon (container, address, diameter) { - const img = iconFactory.iconForAddress(address, diameter) - container.appendChild(img) -} diff --git a/ui/app/components/identicon/identicon.component.js b/ui/app/components/identicon/identicon.component.js new file mode 100644 index 000000000..b892e5ae5 --- /dev/null +++ b/ui/app/components/identicon/identicon.component.js @@ -0,0 +1,99 @@ +import React, { PureComponent } from 'react' +import PropTypes from 'prop-types' +import classnames from 'classnames' +import { toDataUrl } from '../../../lib/blockies' +import contractMap from 'eth-contract-metadata' +import { checksumAddress } from '../../../app/util' +import Jazzicon from '../jazzicon' + +const getStyles = diameter => ( + { + height: diameter, + width: diameter, + borderRadius: diameter / 2, + } +) + +export default class Identicon extends PureComponent { + static propTypes = { + address: PropTypes.string, + className: PropTypes.string, + diameter: PropTypes.number, + image: PropTypes.string, + useBlockie: PropTypes.bool, + } + + static defaultProps = { + diameter: 46, + } + + renderImage () { + const { className, diameter, image } = this.props + + return ( + <img + className={classnames('identicon', className)} + src={image} + style={getStyles(diameter)} + /> + ) + } + + renderJazzicon () { + const { address, className, diameter } = this.props + + return ( + <Jazzicon + address={address} + diameter={diameter} + className={classnames('identicon', className)} + style={getStyles(diameter)} + /> + ) + } + + renderBlockie () { + const { address, className, diameter } = this.props + + return ( + <div + className={classnames('identicon', className)} + style={getStyles(diameter)} + > + <img + src={toDataUrl(address)} + height={diameter} + width={diameter} + /> + </div> + ) + } + + render () { + const { className, address, image, diameter, useBlockie } = this.props + + if (image) { + return this.renderImage() + } + + if (address) { + const checksummedAddress = checksumAddress(address) + + if (contractMap[checksummedAddress] && contractMap[checksummedAddress].logo) { + return this.renderJazzicon() + } + + return useBlockie + ? this.renderBlockie() + : this.renderJazzicon() + } + + return ( + <img + className={classnames('balance-icon', className)} + src="./images/eth_logo.svg" + style={getStyles(diameter)} + /> + ) + } +} diff --git a/ui/app/components/identicon/identicon.container.js b/ui/app/components/identicon/identicon.container.js new file mode 100644 index 000000000..bc49bc18e --- /dev/null +++ b/ui/app/components/identicon/identicon.container.js @@ -0,0 +1,12 @@ +import { connect } from 'react-redux' +import Identicon from './identicon.component' + +const mapStateToProps = state => { + const { metamask: { useBlockie } } = state + + return { + useBlockie, + } +} + +export default connect(mapStateToProps)(Identicon) diff --git a/ui/app/components/identicon/index.js b/ui/app/components/identicon/index.js new file mode 100644 index 000000000..799c886f2 --- /dev/null +++ b/ui/app/components/identicon/index.js @@ -0,0 +1 @@ +export { default } from './identicon.container' diff --git a/ui/app/components/identicon/index.scss b/ui/app/components/identicon/index.scss new file mode 100644 index 000000000..657afc48f --- /dev/null +++ b/ui/app/components/identicon/index.scss @@ -0,0 +1,7 @@ +.identicon { + display: flex; + flex-shrink: 0; + align-items: center; + justify-content: center; + overflow: hidden; +} diff --git a/ui/app/components/identicon/tests/identicon.component.test.js b/ui/app/components/identicon/tests/identicon.component.test.js new file mode 100644 index 000000000..2944818f5 --- /dev/null +++ b/ui/app/components/identicon/tests/identicon.component.test.js @@ -0,0 +1,51 @@ +import React from 'react' +import assert from 'assert' +import thunk from 'redux-thunk' +import configureMockStore from 'redux-mock-store' +import { mount } from 'enzyme' +import Identicon from '../identicon.component' + +describe('Identicon', () => { + const state = { + metamask: { + useBlockie: false, + }, + } + + const middlewares = [thunk] + const mockStore = configureMockStore(middlewares) + const store = mockStore(state) + + it('renders default eth_logo identicon with no props', () => { + const wrapper = mount( + <Identicon store={store}/> + ) + + assert.equal(wrapper.find('img.balance-icon').prop('src'), './images/eth_logo.svg') + }) + + it('renders custom image and add className props', () => { + const wrapper = mount( + <Identicon + store={store} + className="test-image" + image="test-image" + /> + ) + + assert.equal(wrapper.find('img.test-image').prop('className'), 'identicon test-image') + assert.equal(wrapper.find('img.test-image').prop('src'), 'test-image') + }) + + it('renders div with address prop', () => { + const wrapper = mount( + <Identicon + store={store} + className="test-address" + address="0xTest" + /> + ) + + assert.equal(wrapper.find('div.test-address').prop('className'), 'identicon test-address') + }) +}) diff --git a/ui/app/components/index.scss b/ui/app/components/index.scss index beffdb221..e27b0f182 100644 --- a/ui/app/components/index.scss +++ b/ui/app/components/index.scss @@ -16,6 +16,8 @@ @import './export-text-container/index'; +@import './identicon/index'; + @import './info-box/index'; @import './menu-bar/index'; @@ -30,6 +32,8 @@ @import './pages/index'; +@import './provider-page-container/index'; + @import './selected-account/index'; @import './sender-to-recipient/index'; diff --git a/ui/app/components/jazzicon/index.js b/ui/app/components/jazzicon/index.js new file mode 100644 index 000000000..bea900ab9 --- /dev/null +++ b/ui/app/components/jazzicon/index.js @@ -0,0 +1 @@ +export { default } from './jazzicon.component' diff --git a/ui/app/components/jazzicon/jazzicon.component.js b/ui/app/components/jazzicon/jazzicon.component.js new file mode 100644 index 000000000..fcb1c59b1 --- /dev/null +++ b/ui/app/components/jazzicon/jazzicon.component.js @@ -0,0 +1,69 @@ +import React, { PureComponent } from 'react' +import PropTypes from 'prop-types' +import isNode from 'detect-node' +import { findDOMNode } from 'react-dom' +import jazzicon from 'jazzicon' +import iconFactoryGenerator from '../../../lib/icon-factory' +const iconFactory = iconFactoryGenerator(jazzicon) + +/** + * Wrapper around the jazzicon library to return a React component, as the library returns an + * HTMLDivElement which needs to be appended. + */ +export default class Jazzicon extends PureComponent { + static propTypes = { + address: PropTypes.string.isRequired, + className: PropTypes.string, + diameter: PropTypes.number, + style: PropTypes.object, + } + + static defaultProps = { + diameter: 46, + } + + componentDidMount () { + if (!isNode) { + this.appendJazzicon() + } + } + + componentDidUpdate (prevProps) { + const { address: prevAddress } = prevProps + const { address } = this.props + + if (!isNode && address !== prevAddress) { + this.removeExistingChildren() + this.appendJazzicon() + } + } + + removeExistingChildren () { + // eslint-disable-next-line react/no-find-dom-node + const container = findDOMNode(this) + const { children } = container + + for (let i = 0; i < children.length; i++) { + container.removeChild(children[i]) + } + } + + appendJazzicon () { + // eslint-disable-next-line react/no-find-dom-node + const container = findDOMNode(this) + const { address, diameter } = this.props + const image = iconFactory.iconForAddress(address, diameter) + container.appendChild(image) + } + + render () { + const { className, style } = this.props + + return ( + <div + className={className} + style={style} + /> + ) + } +} diff --git a/ui/app/components/modals/account-modal-container.js b/ui/app/components/modals/account-modal-container.js index aa0593df8..2a6c655e1 100644 --- a/ui/app/components/modals/account-modal-container.js +++ b/ui/app/components/modals/account-modal-container.js @@ -5,7 +5,7 @@ const inherits = require('util').inherits const connect = require('react-redux').connect const actions = require('../../actions') const { getSelectedIdentity } = require('../../selectors') -const Identicon = require('../identicon') +import Identicon from '../identicon' function mapStateToProps (state, ownProps) { return { diff --git a/ui/app/components/modals/clear-approved-origins/clear-approved-origins.component.js b/ui/app/components/modals/clear-approved-origins/clear-approved-origins.component.js new file mode 100644 index 000000000..ceaa20a95 --- /dev/null +++ b/ui/app/components/modals/clear-approved-origins/clear-approved-origins.component.js @@ -0,0 +1,39 @@ +import React, { PureComponent } from 'react' +import PropTypes from 'prop-types' +import Modal, { ModalContent } from '../../modal' + +export default class ClearApprovedOrigins extends PureComponent { + static propTypes = { + hideModal: PropTypes.func.isRequired, + clearApprovedOrigins: PropTypes.func.isRequired, + } + + static contextTypes = { + t: PropTypes.func, + } + + handleClear = () => { + const { clearApprovedOrigins, hideModal } = this.props + clearApprovedOrigins() + hideModal() + } + + render () { + const { t } = this.context + + return ( + <Modal + onSubmit={this.handleClear} + onCancel={() => this.props.hideModal()} + submitText={t('ok')} + cancelText={t('nevermind')} + submitType="secondary" + > + <ModalContent + title={t('clearApprovalData')} + description={t('confirmClear')} + /> + </Modal> + ) + } +} diff --git a/ui/app/components/modals/clear-approved-origins/clear-approved-origins.container.js b/ui/app/components/modals/clear-approved-origins/clear-approved-origins.container.js new file mode 100644 index 000000000..3a801a062 --- /dev/null +++ b/ui/app/components/modals/clear-approved-origins/clear-approved-origins.container.js @@ -0,0 +1,16 @@ +import { connect } from 'react-redux' +import { compose } from 'recompose' +import withModalProps from '../../../higher-order-components/with-modal-props' +import ClearApprovedOriginsComponent from './clear-approved-origins.component' +import { clearApprovedOrigins } from '../../../actions' + +const mapDispatchToProps = dispatch => { + return { + clearApprovedOrigins: () => dispatch(clearApprovedOrigins()), + } +} + +export default compose( + withModalProps, + connect(null, mapDispatchToProps) +)(ClearApprovedOriginsComponent) diff --git a/ui/app/components/modals/clear-approved-origins/index.js b/ui/app/components/modals/clear-approved-origins/index.js new file mode 100644 index 000000000..b3e321995 --- /dev/null +++ b/ui/app/components/modals/clear-approved-origins/index.js @@ -0,0 +1 @@ +export { default } from './clear-approved-origins.container' diff --git a/ui/app/components/modals/hide-token-confirmation-modal.js b/ui/app/components/modals/hide-token-confirmation-modal.js index fb38516d3..43f3009a5 100644 --- a/ui/app/components/modals/hide-token-confirmation-modal.js +++ b/ui/app/components/modals/hide-token-confirmation-modal.js @@ -4,7 +4,7 @@ const h = require('react-hyperscript') const inherits = require('util').inherits const connect = require('react-redux').connect const actions = require('../../actions') -const Identicon = require('../identicon') +import Identicon from '../identicon' function mapStateToProps (state) { return { diff --git a/ui/app/components/modals/modal.js b/ui/app/components/modals/modal.js index 15ca9deaa..5aff4f5e1 100644 --- a/ui/app/components/modals/modal.js +++ b/ui/app/components/modals/modal.js @@ -27,8 +27,8 @@ import TransactionConfirmed from './transaction-confirmed' import ConfirmCustomizeGasModal from './customize-gas' import CancelTransaction from './cancel-transaction' import WelcomeBeta from './welcome-beta' -import TransactionDetails from './transaction-details' import RejectTransactions from './reject-transactions' +import ClearApprovedOrigins from './clear-approved-origins' const modalContainerBaseStyle = { transform: 'translate3d(-50%, 0, 0px)', @@ -213,6 +213,19 @@ const MODALS = { }, }, + CLEAR_APPROVED_ORIGINS: { + contents: h(ClearApprovedOrigins), + mobileModalStyle: { + ...modalContainerMobileStyle, + }, + laptopModalStyle: { + ...modalContainerLaptopStyle, + }, + contentStyle: { + borderRadius: '8px', + }, + }, + OLD_UI_NOTIFICATION_MODAL: { contents: [ h(NotifcationModal, { @@ -366,19 +379,6 @@ const MODALS = { }, }, - TRANSACTION_DETAILS: { - contents: h(TransactionDetails), - mobileModalStyle: { - ...modalContainerMobileStyle, - }, - laptopModalStyle: { - ...modalContainerLaptopStyle, - }, - contentStyle: { - borderRadius: '8px', - }, - }, - REJECT_TRANSACTIONS: { contents: h(RejectTransactions), mobileModalStyle: { diff --git a/ui/app/components/modals/transaction-details/index.js b/ui/app/components/modals/transaction-details/index.js deleted file mode 100644 index 1fc42c662..000000000 --- a/ui/app/components/modals/transaction-details/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './transaction-details.container' diff --git a/ui/app/components/modals/transaction-details/transaction-details.component.js b/ui/app/components/modals/transaction-details/transaction-details.component.js deleted file mode 100644 index f2fec3409..000000000 --- a/ui/app/components/modals/transaction-details/transaction-details.component.js +++ /dev/null @@ -1,54 +0,0 @@ -import React, { PureComponent } from 'react' -import PropTypes from 'prop-types' -import Modal from '../../modal' -import TransactionListItemDetails from '../../transaction-list-item-details' -import { hexToDecimal } from '../../../helpers/conversions.util' - -export default class TransactionConfirmed extends PureComponent { - static contextTypes = { - t: PropTypes.func, - } - - static propTypes = { - hideModal: PropTypes.func, - transaction: PropTypes.object, - onRetry: PropTypes.func, - showRetry: PropTypes.bool, - onCancel: PropTypes.func, - showCancel: PropTypes.bool, - } - - handleSubmit = () => { - this.props.hideModal() - } - - handleRetry = () => { - const { onRetry, hideModal } = this.props - - Promise.resolve(onRetry()).then(() => hideModal()) - } - - render () { - const { t } = this.context - const { transaction, showRetry, onCancel, showCancel } = this.props - const { txParams: { nonce } = {} } = transaction - const decimalNonce = nonce && hexToDecimal(nonce) - - return ( - <Modal - onSubmit={this.handleSubmit} - onClose={this.handleSubmit} - submitText={t('ok')} - headerText={t('transactionWithNonce', [`#${decimalNonce}`])} - > - <TransactionListItemDetails - transaction={transaction} - onRetry={this.handleRetry} - showRetry={showRetry} - onCancel={() => onCancel()} - showCancel={showCancel} - /> - </Modal> - ) - } -} diff --git a/ui/app/components/modals/transaction-details/transaction-details.container.js b/ui/app/components/modals/transaction-details/transaction-details.container.js deleted file mode 100644 index f212920bb..000000000 --- a/ui/app/components/modals/transaction-details/transaction-details.container.js +++ /dev/null @@ -1,4 +0,0 @@ -import TransactionDetails from './transaction-details.component' -import withModalProps from '../../../higher-order-components/with-modal-props' - -export default withModalProps(TransactionDetails) diff --git a/ui/app/components/network-display/index.scss b/ui/app/components/network-display/index.scss index 2085cff67..e9f2f2057 100644 --- a/ui/app/components/network-display/index.scss +++ b/ui/app/components/network-display/index.scss @@ -3,11 +3,14 @@ display: flex; align-items: center; justify-content: flex-start; - background-color: lighten(rgb(125, 128, 130), 45%); padding: 0 10px; border-radius: 4px; height: 25px; + &--colored { + background-color: lighten(rgb(125, 128, 130), 45%); + } + &--mainnet { background-color: lighten($blue-lagoon, 68%); } diff --git a/ui/app/components/network-display/network-display.component.js b/ui/app/components/network-display/network-display.component.js index 38626af20..22d617099 100644 --- a/ui/app/components/network-display/network-display.component.js +++ b/ui/app/components/network-display/network-display.component.js @@ -16,7 +16,12 @@ const networkToClassHash = { } export default class NetworkDisplay extends Component { + static defaultProps = { + colored: true, + } + static propTypes = { + colored: PropTypes.bool, network: PropTypes.string, provider: PropTypes.object, } @@ -41,14 +46,16 @@ export default class NetworkDisplay extends Component { } render () { - const { network, provider: { type } } = this.props + const { colored, network, provider: { type, nickname } } = this.props const networkClass = networkToClassHash[network] return ( - <div className={classnames( - 'network-display__container', - networkClass && ('network-display__container--' + networkClass) - )}> + <div + className={classnames('network-display__container', { + 'network-display__container--colored': colored, + ['network-display__container--' + networkClass]: colored && networkClass, + })} + > { networkClass ? <div className={`network-display__icon network-display__icon--${networkClass}`} /> @@ -61,7 +68,7 @@ export default class NetworkDisplay extends Component { /> } <div className="network-display__name"> - { this.context.t(type) } + { type === 'rpc' && nickname ? nickname : this.context.t(type) } </div> </div> ) diff --git a/ui/app/components/network.js b/ui/app/components/network.js index 83297c4f2..611aadb7b 100644 --- a/ui/app/components/network.js +++ b/ui/app/components/network.js @@ -23,9 +23,10 @@ Network.prototype.render = function () { const props = this.props const context = this.context const networkNumber = props.network - let providerName + let providerName, providerNick try { providerName = props.provider.type + providerNick = props.provider.nickname || '' } catch (e) { providerName = null } @@ -131,7 +132,7 @@ Network.prototype.render = function () { }, }), - h('.network-name', context.t('privateNetwork')), + h('.network-name', providerNick || context.t('privateNetwork')), h('i.fa.fa-chevron-down.fa-lg.network-caret'), ]) } diff --git a/ui/app/components/page-container/index.scss b/ui/app/components/page-container/index.scss index 6742e3082..ba1215e84 100644 --- a/ui/app/components/page-container/index.scss +++ b/ui/app/components/page-container/index.scss @@ -101,6 +101,7 @@ font-size: 2rem; font-weight: 500; line-height: 2rem; + margin-right: 1.5rem; } &__subtitle { 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 c92867afe..7d01aaffb 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 @@ -136,7 +136,7 @@ export default class ConfirmTransactionBase extends Component { if (simulationFails) { return { valid: true, - errorKey: TRANSACTION_ERROR_KEY, + errorKey: simulationFails.errorKey ? simulationFails.errorKey : TRANSACTION_ERROR_KEY, } } diff --git a/ui/app/components/pages/home/home.component.js b/ui/app/components/pages/home/home.component.js index d3c71c4f6..b9ec3c258 100644 --- a/ui/app/components/pages/home/home.component.js +++ b/ui/app/components/pages/home/home.component.js @@ -4,6 +4,8 @@ import Media from 'react-media' import { Redirect } from 'react-router-dom' import WalletView from '../../wallet-view' import TransactionView from '../../transaction-view' +import ProviderApproval from '../provider-approval' + import { INITIALIZE_BACKUP_PHRASE_ROUTE, RESTORE_VAULT_ROUTE, @@ -21,6 +23,7 @@ export default class Home extends PureComponent { seedWords: PropTypes.string, suggestedTokens: PropTypes.object, unconfirmedTransactionsCount: PropTypes.number, + providerRequests: PropTypes.array, } componentDidMount () { @@ -46,6 +49,7 @@ export default class Home extends PureComponent { lostAccounts, forgottenPassword, seedWords, + providerRequests, } = this.props // notices @@ -62,6 +66,12 @@ export default class Home extends PureComponent { return <Redirect to={{ pathname: RESTORE_VAULT_ROUTE }} /> } + if (providerRequests && providerRequests.length > 0) { + return ( + <ProviderApproval providerRequest={providerRequests[0]} /> + ) + } + return ( <div className="main-container"> <div className="account-and-transaction-details"> diff --git a/ui/app/components/pages/home/home.container.js b/ui/app/components/pages/home/home.container.js index 58001df6b..bb8cf5e81 100644 --- a/ui/app/components/pages/home/home.container.js +++ b/ui/app/components/pages/home/home.container.js @@ -11,6 +11,7 @@ const mapStateToProps = state => { lostAccounts, seedWords, suggestedTokens, + providerRequests, } = metamask const { forgottenPassword } = appState @@ -21,6 +22,7 @@ const mapStateToProps = state => { seedWords, suggestedTokens, unconfirmedTransactionsCount: unconfirmedTransactionsCountSelector(state), + providerRequests, } } diff --git a/ui/app/components/pages/provider-approval/index.js b/ui/app/components/pages/provider-approval/index.js new file mode 100644 index 000000000..4162f3155 --- /dev/null +++ b/ui/app/components/pages/provider-approval/index.js @@ -0,0 +1 @@ +export { default } from './provider-approval.container' diff --git a/ui/app/components/pages/provider-approval/provider-approval.component.js b/ui/app/components/pages/provider-approval/provider-approval.component.js new file mode 100644 index 000000000..da98bc3fc --- /dev/null +++ b/ui/app/components/pages/provider-approval/provider-approval.component.js @@ -0,0 +1,28 @@ +import PropTypes from 'prop-types' +import React, { Component } from 'react' +import ProviderPageContainer from '../../provider-page-container' + +export default class ProviderApproval extends Component { + static propTypes = { + approveProviderRequest: PropTypes.func.isRequired, + providerRequest: PropTypes.object.isRequired, + rejectProviderRequest: PropTypes.func.isRequired, + }; + + static contextTypes = { + t: PropTypes.func, + }; + + render () { + const { approveProviderRequest, providerRequest, rejectProviderRequest } = this.props + return ( + <ProviderPageContainer + approveProviderRequest={approveProviderRequest} + origin={providerRequest.origin} + rejectProviderRequest={rejectProviderRequest} + siteImage={providerRequest.siteImage} + siteTitle={providerRequest.siteTitle} + /> + ) + } +} diff --git a/ui/app/components/pages/provider-approval/provider-approval.container.js b/ui/app/components/pages/provider-approval/provider-approval.container.js new file mode 100644 index 000000000..b223244a1 --- /dev/null +++ b/ui/app/components/pages/provider-approval/provider-approval.container.js @@ -0,0 +1,12 @@ +import { connect } from 'react-redux' +import ProviderApproval from './provider-approval.component' +import { approveProviderRequest, rejectProviderRequest } from '../../../actions' + +function mapDispatchToProps (dispatch) { + return { + approveProviderRequest: origin => dispatch(approveProviderRequest(origin)), + rejectProviderRequest: origin => dispatch(rejectProviderRequest(origin)), + } +} + +export default connect(null, mapDispatchToProps)(ProviderApproval) diff --git a/ui/app/components/pages/settings/settings-tab/index.scss b/ui/app/components/pages/settings/settings-tab/index.scss index 3bf840c86..ef32b0e4c 100644 --- a/ui/app/components/pages/settings/settings-tab/index.scss +++ b/ui/app/components/pages/settings/settings-tab/index.scss @@ -5,12 +5,9 @@ color: $crimson; } - &__rpc-save-button { - align-self: flex-end; - padding: 5px; - text-transform: uppercase; - color: $dusty-gray; - cursor: pointer; + &__advanced-link { + color: $curious-blue; + padding-left: 5px; } &__rpc-save-button { @@ -19,6 +16,9 @@ text-transform: uppercase; color: $dusty-gray; cursor: pointer; + width: 25%; + min-width: 80px; + height: 33px; } &__button--red { 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 index a9e2a723e..a0a8ed47e 100644 --- a/ui/app/components/pages/settings/settings-tab/settings-tab.component.js +++ b/ui/app/components/pages/settings/settings-tab/settings-tab.component.js @@ -39,12 +39,15 @@ export default class SettingsTab extends PureComponent { metamask: PropTypes.object, setUseBlockie: PropTypes.func, setHexDataFeatureFlag: PropTypes.func, + setPrivacyMode: PropTypes.func, + privacyMode: PropTypes.bool, setCurrentCurrency: PropTypes.func, setRpcTarget: PropTypes.func, delRpcTarget: PropTypes.func, displayWarning: PropTypes.func, revealSeedConfirmation: PropTypes.func, setFeatureFlagToBeta: PropTypes.func, + showClearApprovalModal: PropTypes.func, showResetAccountConfirmationModal: PropTypes.func, warning: PropTypes.string, history: PropTypes.object, @@ -55,12 +58,17 @@ export default class SettingsTab extends PureComponent { sendHexData: PropTypes.bool, currentCurrency: PropTypes.string, conversionDate: PropTypes.number, - useETHAsPrimaryCurrency: PropTypes.bool, - setUseETHAsPrimaryCurrencyPreference: PropTypes.func, + nativeCurrency: PropTypes.string, + useNativeCurrencyAsPrimaryCurrency: PropTypes.bool, + setUseNativeCurrencyAsPrimaryCurrencyPreference: PropTypes.func, } state = { newRpc: '', + chainId: '', + showOptions: false, + ticker: '', + nickname: '', } renderCurrentConversion () { @@ -121,37 +129,98 @@ export default class SettingsTab extends PureComponent { renderNewRpcUrl () { const { t } = this.context - const { newRpc } = this.state + const { newRpc, chainId, ticker, nickname } = this.state return ( <div className="settings-page__content-row"> <div className="settings-page__content-item"> - <span>{ t('newRPC') }</span> + <span>{ t('newNetwork') }</span> </div> <div className="settings-page__content-item"> <div className="settings-page__content-item-col"> <TextField type="text" id="new-rpc" - placeholder={t('newRPC')} + placeholder={t('rpcURL')} value={newRpc} onChange={e => this.setState({ newRpc: e.target.value })} onKeyPress={e => { if (e.key === 'Enter') { - this.validateRpc(newRpc) + this.validateRpc(newRpc, chainId, ticker, nickname) } }} fullWidth - margin="none" + margin="dense" /> - <div - className="settings-tab__rpc-save-button" - onClick={e => { - e.preventDefault() - this.validateRpc(newRpc) + <TextField + type="text" + id="chainid" + placeholder={t('optionalChainId')} + value={chainId} + onChange={e => this.setState({ chainId: e.target.value })} + onKeyPress={e => { + if (e.key === 'Enter') { + this.validateRpc(newRpc, chainId, ticker, nickname) + } }} - > - { t('save') } + style={{ + display: this.state.showOptions ? null : 'none', + }} + fullWidth + margin="dense" + /> + <TextField + type="text" + id="ticker" + placeholder={t('optionalSymbol')} + value={ticker} + onChange={e => this.setState({ ticker: e.target.value })} + onKeyPress={e => { + if (e.key === 'Enter') { + this.validateRpc(newRpc, chainId, ticker, nickname) + } + }} + style={{ + display: this.state.showOptions ? null : 'none', + }} + fullWidth + margin="dense" + /> + <TextField + type="text" + id="nickname" + placeholder={t('optionalNickname')} + value={nickname} + onChange={e => this.setState({ nickname: e.target.value })} + onKeyPress={e => { + if (e.key === 'Enter') { + this.validateRpc(newRpc, chainId, ticker, nickname) + } + }} + style={{ + display: this.state.showOptions ? null : 'none', + }} + fullWidth + margin="dense" + /> + <div className="flex-row flex-align-center space-between"> + <span className="settings-tab__advanced-link" + onClick={e => { + e.preventDefault() + this.setState({ showOptions: !this.state.showOptions }) + }} + > + { t(this.state.showOptions ? 'hideAdvancedOptions' : 'showAdvancedOptions') } + </span> + <button + className="button btn-primary settings-tab__rpc-save-button" + onClick={e => { + e.preventDefault() + this.validateRpc(newRpc, chainId, ticker, nickname) + }} + > + { t('save') } + </button> </div> </div> </div> @@ -159,11 +228,11 @@ export default class SettingsTab extends PureComponent { ) } - validateRpc (newRpc) { + validateRpc (newRpc, chainId, ticker = 'ETH', nickname) { const { setRpcTarget, displayWarning } = this.props if (validUrl.isWebUri(newRpc)) { - setRpcTarget(newRpc) + setRpcTarget(newRpc, chainId, ticker, nickname) } else { const appendedRpc = `http://${newRpc}` @@ -210,6 +279,36 @@ export default class SettingsTab extends PureComponent { ) } + renderClearApproval () { + const { t } = this.context + const { showClearApprovalModal } = this.props + return ( + <div className="settings-page__content-row"> + <div className="settings-page__content-item"> + <span>{ t('approvalData') }</span> + <span className="settings-page__content-description"> + { t('approvalDataDescription') } + </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() + showClearApprovalModal() + }} + > + { t('clearApprovalData') } + </Button> + </div> + </div> + </div> + ) + } + renderSeedWords () { const { t } = this.context const { history } = this.props @@ -341,9 +440,13 @@ export default class SettingsTab extends PureComponent { ) } - renderUseEthAsPrimaryCurrency () { + renderUsePrimaryCurrencyOptions () { const { t } = this.context - const { useETHAsPrimaryCurrency, setUseETHAsPrimaryCurrencyPreference } = this.props + const { + nativeCurrency, + setUseNativeCurrencyAsPrimaryCurrencyPreference, + useNativeCurrencyAsPrimaryCurrency, + } = this.props return ( <div className="settings-page__content-row"> @@ -359,23 +462,23 @@ export default class SettingsTab extends PureComponent { <div className="settings-tab__radio-button"> <input type="radio" - id="eth-primary-currency" - onChange={() => setUseETHAsPrimaryCurrencyPreference(true)} - checked={Boolean(useETHAsPrimaryCurrency)} + id="native-primary-currency" + onChange={() => setUseNativeCurrencyAsPrimaryCurrencyPreference(true)} + checked={Boolean(useNativeCurrencyAsPrimaryCurrency)} /> <label - htmlFor="eth-primary-currency" + htmlFor="native-primary-currency" className="settings-tab__radio-label" > - { t('eth') } + { nativeCurrency } </label> </div> <div className="settings-tab__radio-button"> <input type="radio" id="fiat-primary-currency" - onChange={() => setUseETHAsPrimaryCurrencyPreference(false)} - checked={!useETHAsPrimaryCurrency} + onChange={() => setUseNativeCurrencyAsPrimaryCurrencyPreference(false)} + checked={!useNativeCurrencyAsPrimaryCurrency} /> <label htmlFor="fiat-primary-currency" @@ -391,6 +494,32 @@ export default class SettingsTab extends PureComponent { ) } + renderPrivacyOptIn () { + const { t } = this.context + const { privacyMode, setPrivacyMode } = this.props + + return ( + <div className="settings-page__content-row"> + <div className="settings-page__content-item"> + <span>{ t('privacyMode') }</span> + <div className="settings-page__content-description"> + { t('privacyModeDescription') } + </div> + </div> + <div className="settings-page__content-item"> + <div className="settings-page__content-item-col"> + <ToggleButton + value={privacyMode} + onToggle={value => setPrivacyMode(!value)} + activeLabel="" + inactiveLabel="" + /> + </div> + </div> + </div> + ) + } + render () { const { warning, isMascara } = this.props @@ -398,15 +527,17 @@ export default class SettingsTab extends PureComponent { <div className="settings-page__content"> { warning && <div className="settings-tab__error">{ warning }</div> } { this.renderCurrentConversion() } - { this.renderUseEthAsPrimaryCurrency() } + { this.renderUsePrimaryCurrencyOptions() } { this.renderCurrentLocale() } { this.renderNewRpcUrl() } { this.renderStateLogs() } { this.renderSeedWords() } { !isMascara && this.renderOldUI() } { this.renderResetAccount() } - { this.renderBlockieOptIn() } + { this.renderClearApproval() } + { this.renderPrivacyOptIn() } { this.renderHexDataOptIn() } + { this.renderBlockieOptIn() } </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 index de30f309c..b6c33a5b2 100644 --- a/ui/app/components/pages/settings/settings-tab/settings-tab.container.js +++ b/ui/app/components/pages/settings/settings-tab/settings-tab.container.js @@ -11,7 +11,7 @@ import { updateCurrentLocale, setFeatureFlag, showModal, - setUseETHAsPrimaryCurrencyPreference, + setUseNativeCurrencyAsPrimaryCurrencyPreference, } from '../../../../actions' import { preferencesSelector } from '../../../../selectors' @@ -20,13 +20,17 @@ const mapStateToProps = state => { const { currentCurrency, conversionDate, + nativeCurrency, useBlockie, - featureFlags: { sendHexData } = {}, + featureFlags: { + sendHexData, + privacyMode, + } = {}, provider = {}, isMascara, currentLocale, } = metamask - const { useETHAsPrimaryCurrency } = preferencesSelector(state) + const { useNativeCurrencyAsPrimaryCurrency } = preferencesSelector(state) return { warning, @@ -34,17 +38,19 @@ const mapStateToProps = state => { currentLocale, currentCurrency, conversionDate, + nativeCurrency, useBlockie, sendHexData, + privacyMode, provider, - useETHAsPrimaryCurrency, + useNativeCurrencyAsPrimaryCurrency, } } const mapDispatchToProps = dispatch => { return { setCurrentCurrency: currency => dispatch(setCurrentCurrency(currency)), - setRpcTarget: newRpc => dispatch(setRpcTarget(newRpc)), + setRpcTarget: (newRpc, chainId, ticker, nickname) => dispatch(setRpcTarget(newRpc, chainId, ticker, nickname)), displayWarning: warning => dispatch(displayWarning(warning)), revealSeedConfirmation: () => dispatch(revealSeedConfirmation()), setUseBlockie: value => dispatch(setUseBlockie(value)), @@ -53,10 +59,12 @@ const mapDispatchToProps = dispatch => { return dispatch(setFeatureFlag('betaUI', false, 'OLD_UI_NOTIFICATION_MODAL')) }, setHexDataFeatureFlag: shouldShow => dispatch(setFeatureFlag('sendHexData', shouldShow)), + setPrivacyMode: enabled => dispatch(setFeatureFlag('privacyMode', enabled)), showResetAccountConfirmationModal: () => dispatch(showModal({ name: 'CONFIRM_RESET_ACCOUNT' })), - setUseETHAsPrimaryCurrencyPreference: value => { - return dispatch(setUseETHAsPrimaryCurrencyPreference(value)) + setUseNativeCurrencyAsPrimaryCurrencyPreference: value => { + return dispatch(setUseNativeCurrencyAsPrimaryCurrencyPreference(value)) }, + showClearApprovalModal: () => dispatch(showModal({ name: 'CLEAR_APPROVED_ORIGINS' })), } } diff --git a/ui/app/components/provider-page-container/index.js b/ui/app/components/provider-page-container/index.js new file mode 100644 index 000000000..927c35940 --- /dev/null +++ b/ui/app/components/provider-page-container/index.js @@ -0,0 +1,3 @@ +export {default} from './provider-page-container.component' +export {default as ProviderPageContainerContent} from './provider-page-container-content' +export {default as ProviderPageContainerHeader} from './provider-page-container-header' diff --git a/ui/app/components/provider-page-container/index.scss b/ui/app/components/provider-page-container/index.scss new file mode 100644 index 000000000..8d35ac179 --- /dev/null +++ b/ui/app/components/provider-page-container/index.scss @@ -0,0 +1,121 @@ +.provider-approval-container { + display: flex; + + &__header { + display: flex; + flex-direction: column; + align-items: flex-end; + border-bottom: 1px solid $geyser; + padding: 9px; + } + + &__content { + display: flex; + overflow-y: auto; + flex: 1; + flex-direction: column; + justify-content: space-between; + color: #7C808E; + + h1, h2 { + color: #4A4A4A; + display: flex; + justify-content: center; + text-align: center; + } + + h2 { + font-size: 16px; + line-height: 18px; + padding: 20px; + } + + h1 { + font-size: 22px; + line-height: 26px; + padding: 20px; + } + + p { + padding: 0 40px; + text-align: center; + font-size: 12px; + line-height: 18px; + } + + a, a:hover { + color: $dodger-blue; + } + + .provider-approval-visual { + display: flex; + flex-direction: row; + justify-content: space-evenly; + position: relative; + margin: 0 32px; + + section { + display: flex; + flex-direction: column; + align-items: center; + flex: 1; + } + + h1 { + font-size: 14px; + line-height: 18px; + padding: 8px 0 0; + } + + h2 { + font-size: 10px; + line-height: 14px; + padding: 0; + color: #A2A4AC; + } + + &__check { + width: 40px; + height: 40px; + background: white url("/images/provider-approval-check.svg") no-repeat; + margin-top: 14px; + } + + &__identicon { + width: 64px; + height: 64px; + + &--default { + background-color: #777A87; + color: white; + width: 64px; + height: 64px; + border-radius: 32px; + display: flex; + align-items: center; + justify-content: center; + font-weight: bold; + } + } + + &:before { + border-top: 2px dashed #CDD1E4; + content: ""; + margin: 0 auto; + position: absolute; + top: 32px; + left: 0; + bottom: 0; + right: 0; + width: 65%; + z-index: -1; + } + } + + .secure-badge { + display: flex; + justify-content: center; + padding: 25px; + } + } +} diff --git a/ui/app/components/provider-page-container/provider-page-container-content/index.js b/ui/app/components/provider-page-container/provider-page-container-content/index.js new file mode 100644 index 000000000..73e491adc --- /dev/null +++ b/ui/app/components/provider-page-container/provider-page-container-content/index.js @@ -0,0 +1 @@ +export {default} from './provider-page-container-content.container' diff --git a/ui/app/components/provider-page-container/provider-page-container-content/provider-page-container-content.component.js b/ui/app/components/provider-page-container/provider-page-container-content/provider-page-container-content.component.js new file mode 100644 index 000000000..268db613f --- /dev/null +++ b/ui/app/components/provider-page-container/provider-page-container-content/provider-page-container-content.component.js @@ -0,0 +1,77 @@ +import PropTypes from 'prop-types' +import React, {PureComponent} from 'react' +import Identicon from '../../identicon' + +export default class ProviderPageContainerContent extends PureComponent { + static propTypes = { + origin: PropTypes.string.isRequired, + selectedIdentity: PropTypes.string.isRequired, + siteImage: PropTypes.string, + siteTitle: PropTypes.string.isRequired, + } + + static contextTypes = { + t: PropTypes.func, + }; + + renderConnectVisual = () => { + const { origin, selectedIdentity, siteImage, siteTitle } = this.props + + return ( + <div className="provider-approval-visual"> + <section> + {siteImage ? ( + <img + className="provider-approval-visual__identicon" + src={siteImage} + /> + ) : ( + <i className="provider-approval-visual__identicon--default"> + {siteTitle.charAt(0).toUpperCase()} + </i> + )} + <h1>{siteTitle}</h1> + <h2>{origin}</h2> + </section> + <span className="provider-approval-visual__check" /> + <section> + <Identicon + className="provider-approval-visual__identicon" + address={selectedIdentity.address} + diameter={64} + /> + <h1>{selectedIdentity.name}</h1> + </section> + </div> + ) + } + + render () { + const { siteTitle } = this.props + const { t } = this.context + + return ( + <div className="provider-approval-container__content"> + <section> + <h2>{t('connectRequest')}</h2> + {this.renderConnectVisual()} + <h1>{t('providerRequest', [siteTitle])}</h1> + <p> + {t('providerRequestInfo')} + <br/> + <a + href="https://medium.com/metamask/introducing-privacy-mode-42549d4870fa" + target="_blank" + rel="noopener noreferrer" + > + {t('learnMore')}. + </a> + </p> + </section> + <section className="secure-badge"> + <img src="/images/mm-secure.svg" /> + </section> + </div> + ) + } +} diff --git a/ui/app/components/provider-page-container/provider-page-container-content/provider-page-container-content.container.js b/ui/app/components/provider-page-container/provider-page-container-content/provider-page-container-content.container.js new file mode 100644 index 000000000..3ea1ce20e --- /dev/null +++ b/ui/app/components/provider-page-container/provider-page-container-content/provider-page-container-content.container.js @@ -0,0 +1,11 @@ +import { connect } from 'react-redux' +import ProviderPageContainerContent from './provider-page-container-content.component' +import { getSelectedIdentity } from '../../../selectors' + +const mapStateToProps = (state) => { + return { + selectedIdentity: getSelectedIdentity(state), + } +} + +export default connect(mapStateToProps)(ProviderPageContainerContent) diff --git a/ui/app/components/provider-page-container/provider-page-container-header/index.js b/ui/app/components/provider-page-container/provider-page-container-header/index.js new file mode 100644 index 000000000..430627d3a --- /dev/null +++ b/ui/app/components/provider-page-container/provider-page-container-header/index.js @@ -0,0 +1 @@ +export {default} from './provider-page-container-header.component' diff --git a/ui/app/components/provider-page-container/provider-page-container-header/provider-page-container-header.component.js b/ui/app/components/provider-page-container/provider-page-container-header/provider-page-container-header.component.js new file mode 100644 index 000000000..41bf6c3dd --- /dev/null +++ b/ui/app/components/provider-page-container/provider-page-container-header/provider-page-container-header.component.js @@ -0,0 +1,12 @@ +import React, {PureComponent} from 'react' +import NetworkDisplay from '../../network-display' + +export default class ProviderPageContainerHeader extends PureComponent { + render () { + return ( + <div className="provider-approval-container__header"> + <NetworkDisplay colored={false} /> + </div> + ) + } +} diff --git a/ui/app/components/provider-page-container/provider-page-container.component.js b/ui/app/components/provider-page-container/provider-page-container.component.js new file mode 100644 index 000000000..902733616 --- /dev/null +++ b/ui/app/components/provider-page-container/provider-page-container.component.js @@ -0,0 +1,50 @@ +import PropTypes from 'prop-types' +import React, {PureComponent} from 'react' +import { ProviderPageContainerContent, ProviderPageContainerHeader } from './' +import { PageContainerFooter } from '../page-container' + +export default class ProviderPageContainer extends PureComponent { + static propTypes = { + approveProviderRequest: PropTypes.func.isRequired, + origin: PropTypes.string.isRequired, + rejectProviderRequest: PropTypes.func.isRequired, + siteImage: PropTypes.string, + siteTitle: PropTypes.string.isRequired, + }; + + static contextTypes = { + t: PropTypes.func, + }; + + onCancel = () => { + const { origin, rejectProviderRequest } = this.props + rejectProviderRequest(origin) + } + + onSubmit = () => { + const { approveProviderRequest, origin } = this.props + approveProviderRequest(origin) + } + + render () { + const {origin, siteImage, siteTitle} = this.props + + return ( + <div className="page-container provider-approval-container"> + <ProviderPageContainerHeader /> + <ProviderPageContainerContent + origin={origin} + siteImage={siteImage} + siteTitle={siteTitle} + /> + <PageContainerFooter + onCancel={() => this.onCancel()} + cancelText={this.context.t('cancel')} + onSubmit={() => this.onSubmit()} + submitText={this.context.t('connect')} + submitButtonType="confirm" + /> + </div> + ) + } +} diff --git a/ui/app/components/send/account-list-item/account-list-item.container.js b/ui/app/components/send/account-list-item/account-list-item.container.js index 4b4519288..f8e73d923 100644 --- a/ui/app/components/send/account-list-item/account-list-item.container.js +++ b/ui/app/components/send/account-list-item/account-list-item.container.js @@ -2,6 +2,7 @@ import { connect } from 'react-redux' import { getConversionRate, getCurrentCurrency, + getNativeCurrency, } from '../send.selectors.js' import AccountListItem from './account-list-item.component' @@ -11,5 +12,6 @@ function mapStateToProps (state) { return { conversionRate: getConversionRate(state), currentCurrency: getCurrentCurrency(state), + nativeCurrency: getNativeCurrency(state), } } diff --git a/ui/app/components/send/account-list-item/tests/account-list-item-component.test.js b/ui/app/components/send/account-list-item/tests/account-list-item-component.test.js index f88c0dbd0..6ffc0b1c6 100644 --- a/ui/app/components/send/account-list-item/tests/account-list-item-component.test.js +++ b/ui/app/components/send/account-list-item/tests/account-list-item-component.test.js @@ -28,6 +28,7 @@ describe('AccountListItem Component', function () { className={'mockClassName'} conversionRate={4} currentCurrency={'mockCurrentyCurrency'} + nativeCurrency={'ETH'} displayAddress={false} displayBalance={false} handleClick={propsMethodSpies.handleClick} diff --git a/ui/app/components/send/account-list-item/tests/account-list-item-container.test.js b/ui/app/components/send/account-list-item/tests/account-list-item-container.test.js index af0859117..7c2f5fcb2 100644 --- a/ui/app/components/send/account-list-item/tests/account-list-item-container.test.js +++ b/ui/app/components/send/account-list-item/tests/account-list-item-container.test.js @@ -13,6 +13,7 @@ proxyquire('../account-list-item.container.js', { '../send.selectors.js': { getConversionRate: (s) => `mockConversionRate:${s}`, getCurrentCurrency: (s) => `mockCurrentCurrency:${s}`, + getNativeCurrency: (s) => `mockNativeCurrency:${s}`, }, }) @@ -24,6 +25,7 @@ describe('account-list-item container', () => { assert.deepEqual(mapStateToProps('mockState'), { conversionRate: 'mockConversionRate:mockState', currentCurrency: 'mockCurrentCurrency:mockState', + nativeCurrency: 'mockNativeCurrency:mockState', }) }) diff --git a/ui/app/components/send/send-content/send-amount-row/tests/send-amount-row-component.test.js b/ui/app/components/send/send-content/send-amount-row/tests/send-amount-row-component.test.js index e63db4a2d..56e80cb83 100644 --- a/ui/app/components/send/send-content/send-amount-row/tests/send-amount-row-component.test.js +++ b/ui/app/components/send/send-content/send-amount-row/tests/send-amount-row-component.test.js @@ -151,7 +151,6 @@ describe('SendAmountRow Component', function () { }) it('should render a UserPreferencedTokenInput as the second child of the SendRowWrapper', () => { - console.log('HI', wrapper.find(SendRowWrapper).childAt(1)) assert(wrapper.find(SendRowWrapper).childAt(1).is(UserPreferencedTokenInput)) }) diff --git a/ui/app/components/send/send.selectors.js b/ui/app/components/send/send.selectors.js index 22e379693..eb22a08b7 100644 --- a/ui/app/components/send/send.selectors.js +++ b/ui/app/components/send/send.selectors.js @@ -19,6 +19,7 @@ const selectors = { getCurrentNetwork, getCurrentViewContext, getForceGasMin, + getNativeCurrency, getGasLimit, getGasPrice, getGasPriceFromRecentBlocks, @@ -111,6 +112,10 @@ function getCurrentCurrency (state) { return state.metamask.currentCurrency } +function getNativeCurrency (state) { + return state.metamask.nativeCurrency +} + function getCurrentNetwork (state) { return state.metamask.network } diff --git a/ui/app/components/send/send.utils.js b/ui/app/components/send/send.utils.js index a18a9e4b3..eb1667c63 100644 --- a/ui/app/components/send/send.utils.js +++ b/ui/app/components/send/send.utils.js @@ -215,7 +215,9 @@ async function estimateGas ({ // if recipient has no code, gas is 21k max: if (!selectedToken && !data) { const code = Boolean(to) && await global.eth.getCode(to) - if (!code || code === '0x') { + // Geth will return '0x', and ganache-core v2.2.1 will return '0x0' + const codeIsEmpty = !code || code === '0x' || code === '0x0' + if (codeIsEmpty) { return SIMPLE_GAS_COST } } else if (selectedToken && !to) { diff --git a/ui/app/components/send/tests/send-selectors-test-data.js b/ui/app/components/send/tests/send-selectors-test-data.js index 8b939dadb..30a2666cf 100644 --- a/ui/app/components/send/tests/send-selectors-test-data.js +++ b/ui/app/components/send/tests/send-selectors-test-data.js @@ -26,6 +26,7 @@ module.exports = { 'currentCurrency': 'USD', 'conversionRate': 1200.88200327, 'conversionDate': 1489013762, + 'nativeCurrency': 'ETH', 'noActiveNotices': true, 'frequentRpcList': [], 'network': '3', diff --git a/ui/app/components/send/tests/send-selectors.test.js b/ui/app/components/send/tests/send-selectors.test.js index 1a47cd209..e7e901f0d 100644 --- a/ui/app/components/send/tests/send-selectors.test.js +++ b/ui/app/components/send/tests/send-selectors.test.js @@ -12,6 +12,7 @@ const { getCurrentCurrency, getCurrentNetwork, getCurrentViewContext, + getNativeCurrency, getForceGasMin, getGasLimit, getGasPrice, @@ -178,6 +179,15 @@ describe('send selectors', () => { }) }) + describe('getNativeCurrency()', () => { + it('should return the ticker symbol of the selected network', () => { + assert.equal( + getNativeCurrency(mockState), + 'ETH' + ) + }) + }) + describe('getCurrentNetwork()', () => { it('should return the id of the currently selected network', () => { assert.equal( diff --git a/ui/app/components/signature-request.js b/ui/app/components/signature-request.js index d76eb5ef8..85af3b00b 100644 --- a/ui/app/components/signature-request.js +++ b/ui/app/components/signature-request.js @@ -2,7 +2,7 @@ const Component = require('react').Component const PropTypes = require('prop-types') const h = require('react-hyperscript') const inherits = require('util').inherits -const Identicon = require('./identicon') +import Identicon from './identicon' const connect = require('react-redux').connect const ethUtil = require('ethereumjs-util') const classnames = require('classnames') diff --git a/ui/app/components/token-cell.js b/ui/app/components/token-cell.js index 477d97597..75ba347fa 100644 --- a/ui/app/components/token-cell.js +++ b/ui/app/components/token-cell.js @@ -2,7 +2,7 @@ const Component = require('react').Component const h = require('react-hyperscript') const inherits = require('util').inherits const connect = require('react-redux').connect -const Identicon = require('./identicon') +import Identicon from './identicon' const prefixForNetwork = require('../../lib/etherscan-prefix-for-network') const selectors = require('../selectors') const actions = require('../actions') diff --git a/ui/app/components/token-input/tests/token-input.component.test.js b/ui/app/components/token-input/tests/token-input.component.test.js index 2131e7705..2dacb9bc4 100644 --- a/ui/app/components/token-input/tests/token-input.component.test.js +++ b/ui/app/components/token-input/tests/token-input.component.test.js @@ -122,7 +122,7 @@ describe('TokenInput Component', () => { assert.equal(wrapper.find('.unit-input__suffix').length, 1) assert.equal(wrapper.find('.unit-input__suffix').text(), 'ABC') assert.equal(wrapper.find('.unit-input__input').props().value, '1') - assert.equal(wrapper.find('.currency-display-component').text(), '2 ETH') + assert.equal(wrapper.find('.currency-display-component').text(), '2ETH') }) it('should render properly with a token value for fiat', () => { @@ -157,7 +157,7 @@ describe('TokenInput Component', () => { assert.equal(wrapper.find('.unit-input__suffix').length, 1) assert.equal(wrapper.find('.unit-input__suffix').text(), 'ABC') assert.equal(wrapper.find('.unit-input__input').props().value, '1') - assert.equal(wrapper.find('.currency-display-component').text(), '$462.12 USD') + assert.equal(wrapper.find('.currency-display-component').text(), '$462.12USD') }) }) @@ -201,14 +201,14 @@ describe('TokenInput Component', () => { const tokenInputInstance = wrapper.find(TokenInput).at(0).instance() assert.equal(tokenInputInstance.state.decimalValue, 0) assert.equal(tokenInputInstance.state.hexValue, undefined) - assert.equal(wrapper.find('.currency-display-component').text(), '0 ETH') + assert.equal(wrapper.find('.currency-display-component').text(), '0ETH') const input = wrapper.find('input') assert.equal(input.props().value, 0) input.simulate('change', { target: { value: 1 } }) assert.equal(handleChangeSpy.callCount, 1) assert.ok(handleChangeSpy.calledWith('2710')) - assert.equal(wrapper.find('.currency-display-component').text(), '2 ETH') + assert.equal(wrapper.find('.currency-display-component').text(), '2ETH') assert.equal(tokenInputInstance.state.decimalValue, 1) assert.equal(tokenInputInstance.state.hexValue, '2710') @@ -250,14 +250,14 @@ describe('TokenInput Component', () => { const tokenInputInstance = wrapper.find(TokenInput).at(0).instance() assert.equal(tokenInputInstance.state.decimalValue, 0) assert.equal(tokenInputInstance.state.hexValue, undefined) - assert.equal(wrapper.find('.currency-display-component').text(), '$0.00 USD') + assert.equal(wrapper.find('.currency-display-component').text(), '$0.00USD') const input = wrapper.find('input') assert.equal(input.props().value, 0) input.simulate('change', { target: { value: 1 } }) assert.equal(handleChangeSpy.callCount, 1) assert.ok(handleChangeSpy.calledWith('2710')) - assert.equal(wrapper.find('.currency-display-component').text(), '$462.12 USD') + assert.equal(wrapper.find('.currency-display-component').text(), '$462.12USD') assert.equal(tokenInputInstance.state.decimalValue, 1) assert.equal(tokenInputInstance.state.hexValue, '2710') diff --git a/ui/app/components/transaction-activity-log/tests/transaction-activity-log.container.test.js b/ui/app/components/transaction-activity-log/tests/transaction-activity-log.container.test.js index 85d56a6a2..a7c35f51e 100644 --- a/ui/app/components/transaction-activity-log/tests/transaction-activity-log.container.test.js +++ b/ui/app/components/transaction-activity-log/tests/transaction-activity-log.container.test.js @@ -18,10 +18,11 @@ describe('TransactionActivityLog container', () => { const mockState = { metamask: { conversionRate: 280.45, + nativeCurrency: 'ETH', }, } - assert.deepEqual(mapStateToProps(mockState), { conversionRate: 280.45 }) + assert.deepEqual(mapStateToProps(mockState), { conversionRate: 280.45, nativeCurrency: 'ETH' }) }) }) }) diff --git a/ui/app/components/transaction-activity-log/transaction-activity-log.component.js b/ui/app/components/transaction-activity-log/transaction-activity-log.component.js index c4cf57d14..58d932a0f 100644 --- a/ui/app/components/transaction-activity-log/transaction-activity-log.component.js +++ b/ui/app/components/transaction-activity-log/transaction-activity-log.component.js @@ -4,7 +4,6 @@ import classnames from 'classnames' import { getActivities } from './transaction-activity-log.util' import Card from '../card' import { getEthConversionFromWeiHex, getValueFromWeiHex } from '../../helpers/conversions.util' -import { ETH } from '../../constants/common' import { formatDate } from '../../util' export default class TransactionActivityLog extends PureComponent { @@ -16,6 +15,7 @@ export default class TransactionActivityLog extends PureComponent { transaction: PropTypes.object, className: PropTypes.string, conversionRate: PropTypes.number, + nativeCurrency: PropTypes.string, } state = { @@ -27,10 +27,14 @@ export default class TransactionActivityLog extends PureComponent { } componentDidUpdate (prevProps) { - const { transaction: { history: prevHistory = [] } = {} } = prevProps - const { transaction: { history = [] } = {} } = this.props + const { + transaction: { history: prevHistory = [], txReceipt: { status: prevStatus } = {} } = {}, + } = prevProps + const { + transaction: { history = [], txReceipt: { status } = {} } = {}, + } = this.props - if (prevHistory.length !== history.length) { + if (prevHistory.length !== history.length || prevStatus !== status) { this.setActivites() } } @@ -41,16 +45,17 @@ export default class TransactionActivityLog extends PureComponent { } renderActivity (activity, index) { - const { conversionRate } = this.props + const { conversionRate, nativeCurrency } = this.props const { eventKey, value, timestamp } = activity const ethValue = index === 0 ? `${getValueFromWeiHex({ value, - toCurrency: ETH, + fromCurrency: nativeCurrency, + toCurrency: nativeCurrency, conversionRate, numberOfDecimals: 6, - })} ${ETH}` - : getEthConversionFromWeiHex({ value, toCurrency: ETH, conversionRate }) + })} ${nativeCurrency}` + : getEthConversionFromWeiHex({ value, fromCurrency: nativeCurrency, conversionRate }) const formattedTimestamp = formatDate(timestamp) const activityText = this.context.t(eventKey, [ethValue, formattedTimestamp]) diff --git a/ui/app/components/transaction-activity-log/transaction-activity-log.container.js b/ui/app/components/transaction-activity-log/transaction-activity-log.container.js index 4c8b6d971..622f77df1 100644 --- a/ui/app/components/transaction-activity-log/transaction-activity-log.container.js +++ b/ui/app/components/transaction-activity-log/transaction-activity-log.container.js @@ -1,10 +1,11 @@ import { connect } from 'react-redux' import TransactionActivityLog from './transaction-activity-log.component' -import { conversionRateSelector } from '../../selectors' +import { conversionRateSelector, getNativeCurrency } from '../../selectors' const mapStateToProps = state => { return { conversionRate: conversionRateSelector(state), + nativeCurrency: getNativeCurrency(state), } } diff --git a/ui/app/components/transaction-activity-log/transaction-activity-log.util.js b/ui/app/components/transaction-activity-log/transaction-activity-log.util.js index 97aa9a8f1..16597ae1a 100644 --- a/ui/app/components/transaction-activity-log/transaction-activity-log.util.js +++ b/ui/app/components/transaction-activity-log/transaction-activity-log.util.js @@ -18,6 +18,7 @@ const TRANSACTION_SUBMITTED_EVENT = 'transactionSubmitted' const TRANSACTION_CONFIRMED_EVENT = 'transactionConfirmed' const TRANSACTION_DROPPED_EVENT = 'transactionDropped' const TRANSACTION_UPDATED_EVENT = 'transactionUpdated' +const TRANSACTION_ERRORED_EVENT = 'transactionErrored' const eventPathsHash = { [STATUS_PATH]: true, @@ -39,9 +40,9 @@ function eventCreator (eventKey, timestamp, value) { } export function getActivities (transaction) { - const { history = [] } = transaction + const { history = [], txReceipt: { status } = {} } = transaction - return history.reduce((acc, base) => { + const historyActivities = history.reduce((acc, base) => { // First history item should be transaction creation if (!Array.isArray(base) && base.status === UNAPPROVED_STATUS && base.txParams) { const { time, txParams: { value } = {} } = base @@ -83,4 +84,10 @@ export function getActivities (transaction) { return acc }, []) + + // If txReceipt.status is '0x0', that means that an on-chain error occured for the transaction, + // so we add an error entry to the Activity Log. + return status === '0x0' + ? historyActivities.concat(eventCreator(TRANSACTION_ERRORED_EVENT)) + : historyActivities } diff --git a/ui/app/components/transaction-breakdown/index.js b/ui/app/components/transaction-breakdown/index.js index c887f504f..4a5b52663 100644 --- a/ui/app/components/transaction-breakdown/index.js +++ b/ui/app/components/transaction-breakdown/index.js @@ -1 +1 @@ -export { default } from './transaction-breakdown.component' +export { default } from './transaction-breakdown.container' diff --git a/ui/app/components/transaction-breakdown/transaction-breakdown.component.js b/ui/app/components/transaction-breakdown/transaction-breakdown.component.js index 77bedcad7..3a7647873 100644 --- a/ui/app/components/transaction-breakdown/transaction-breakdown.component.js +++ b/ui/app/components/transaction-breakdown/transaction-breakdown.component.js @@ -6,7 +6,7 @@ import Card from '../card' import CurrencyDisplay from '../currency-display' import UserPreferencedCurrencyDisplay from '../user-preferenced-currency-display' import HexToDecimal from '../hex-to-decimal' -import { ETH, GWEI, PRIMARY, SECONDARY } from '../../constants/common' +import { GWEI, PRIMARY, SECONDARY } from '../../constants/common' import { getHexGasTotal } from '../../helpers/confirm-transaction/util' import { sumHexes } from '../../helpers/transactions.util' @@ -18,6 +18,7 @@ export default class TransactionBreakdown extends PureComponent { static propTypes = { transaction: PropTypes.object, className: PropTypes.string, + nativeCurrency: PropTypes.string.isRequired, } static defaultProps = { @@ -26,7 +27,7 @@ export default class TransactionBreakdown extends PureComponent { render () { const { t } = this.context - const { transaction, className } = this.props + const { transaction, className, nativeCurrency } = this.props const { txParams: { gas, gasPrice, value } = {}, txReceipt: { gasUsed } = {} } = transaction const gasLimit = typeof gasUsed === 'string' ? gasUsed : gas @@ -72,7 +73,7 @@ export default class TransactionBreakdown extends PureComponent { <TransactionBreakdownRow title={t('gasPrice')}> <CurrencyDisplay className="transaction-breakdown__value" - currency={ETH} + currency={nativeCurrency} denomination={GWEI} value={gasPrice} hideLabel diff --git a/ui/app/components/transaction-breakdown/transaction-breakdown.container.js b/ui/app/components/transaction-breakdown/transaction-breakdown.container.js new file mode 100644 index 000000000..ed2708e03 --- /dev/null +++ b/ui/app/components/transaction-breakdown/transaction-breakdown.container.js @@ -0,0 +1,11 @@ +import { connect } from 'react-redux' +import TransactionBreakdown from './transaction-breakdown.component' +import { getNativeCurrency } from '../../selectors' + +const mapStateToProps = (state) => { + return { + nativeCurrency: getNativeCurrency(state), + } +} + +export default connect(mapStateToProps)(TransactionBreakdown) diff --git a/ui/app/components/transaction-list-item/index.scss b/ui/app/components/transaction-list-item/index.scss index 9d694546b..ac0e7beeb 100644 --- a/ui/app/components/transaction-list-item/index.scss +++ b/ui/app/components/transaction-list-item/index.scss @@ -85,6 +85,7 @@ text-align: end; grid-area: primary-amount; align-self: end; + justify-self: end; @media screen and (max-width: $break-small) { padding-bottom: 2px; @@ -97,6 +98,7 @@ color: #5e6064; grid-area: secondary-amount; align-self: start; + justify-self: end; } } diff --git a/ui/app/components/transaction-list-item/transaction-list-item.component.js b/ui/app/components/transaction-list-item/transaction-list-item.component.js index 88573d2d5..696634fe0 100644 --- a/ui/app/components/transaction-list-item/transaction-list-item.component.js +++ b/ui/app/components/transaction-list-item/transaction-list-item.component.js @@ -10,7 +10,6 @@ import TransactionListItemDetails from '../transaction-list-item-details' import { CONFIRM_TRANSACTION_ROUTE } from '../../routes' import { UNAPPROVED_STATUS, TOKEN_METHOD_TRANSFER } from '../../constants/transactions' import { PRIMARY, SECONDARY } from '../../constants/common' -import { ENVIRONMENT_TYPE_FULLSCREEN } from '../../../../app/scripts/lib/enums' import { getStatusKey } from '../../helpers/transactions.util' export default class TransactionListItem extends PureComponent { @@ -24,7 +23,6 @@ export default class TransactionListItem extends PureComponent { showCancelModal: PropTypes.func, showCancel: PropTypes.bool, showRetry: PropTypes.bool, - showTransactionDetailsModal: PropTypes.func, token: PropTypes.object, tokenData: PropTypes.object, transaction: PropTypes.object, @@ -39,31 +37,16 @@ export default class TransactionListItem extends PureComponent { const { transaction, history, - showTransactionDetailsModal, - methodData, - showCancel, - showRetry, } = this.props const { id, status } = transaction const { showTransactionDetails } = this.state - const windowType = window.METAMASK_UI_TYPE if (status === UNAPPROVED_STATUS) { history.push(`${CONFIRM_TRANSACTION_ROUTE}/${id}`) return } - if (windowType === ENVIRONMENT_TYPE_FULLSCREEN) { - this.setState({ showTransactionDetails: !showTransactionDetails }) - } else { - showTransactionDetailsModal({ - transaction, - onRetry: this.handleRetry, - showRetry: showRetry && methodData.done, - onCancel: this.handleCancel, - showCancel, - }) - } + this.setState({ showTransactionDetails: !showTransactionDetails }) } handleCancel = () => { diff --git a/ui/app/components/transaction-list-item/transaction-list-item.container.js b/ui/app/components/transaction-list-item/transaction-list-item.container.js index 72f5f5d61..62ed7a73f 100644 --- a/ui/app/components/transaction-list-item/transaction-list-item.container.js +++ b/ui/app/components/transaction-list-item/transaction-list-item.container.js @@ -28,16 +28,6 @@ const mapDispatchToProps = dispatch => { showCancelModal: (transactionId, originalGasPrice) => { return dispatch(showModal({ name: 'CANCEL_TRANSACTION', transactionId, originalGasPrice })) }, - showTransactionDetailsModal: ({ transaction, onRetry, showRetry, onCancel, showCancel }) => { - return dispatch(showModal({ - name: 'TRANSACTION_DETAILS', - transaction, - onRetry, - showRetry, - onCancel, - showCancel, - })) - }, } } diff --git a/ui/app/components/transaction-list/index.scss b/ui/app/components/transaction-list/index.scss index 777f701f9..ba7ffd87b 100644 --- a/ui/app/components/transaction-list/index.scss +++ b/ui/app/components/transaction-list/index.scss @@ -2,9 +2,7 @@ display: flex; flex-direction: column; flex: 1; - overflow-y: hidden; margin-top: 8px; - border-top: 1px solid $geyser; &__completed-transactions { display: flex; @@ -26,7 +24,6 @@ &__transactions { flex: 1; - overflow-y: auto; } &__pending-transactions { diff --git a/ui/app/components/transaction-view-balance/index.scss b/ui/app/components/transaction-view-balance/index.scss index 12045ab6d..659f896ff 100644 --- a/ui/app/components/transaction-view-balance/index.scss +++ b/ui/app/components/transaction-view-balance/index.scss @@ -4,11 +4,13 @@ align-items: center; flex: 1; height: 54px; + min-width: 0; &__balance { - margin-left: 12px; + margin: 0 12px; display: flex; flex-direction: column; + min-width: 0; @media screen and (max-width: $break-small) { align-items: center; @@ -21,7 +23,8 @@ font-size: 1.5rem; @media screen and (max-width: $break-small) { - margin-bottom: 12px; + margin: 12px 0; + margin-left: 0; font-size: 1.75rem; } } @@ -30,7 +33,6 @@ font-size: 1.5rem; @media screen and (max-width: $break-small) { - margin-bottom: 12px; font-size: 1.75rem; } } @@ -45,6 +47,7 @@ display: flex; flex-direction: row; align-items: center; + min-width: 0; @media screen and (max-width: $break-small) { flex-direction: column; diff --git a/ui/app/components/transaction-view-balance/transaction-view-balance.container.js b/ui/app/components/transaction-view-balance/transaction-view-balance.container.js index 30c5cab16..cb8078ec1 100644 --- a/ui/app/components/transaction-view-balance/transaction-view-balance.container.js +++ b/ui/app/components/transaction-view-balance/transaction-view-balance.container.js @@ -2,7 +2,7 @@ import { connect } from 'react-redux' import { withRouter } from 'react-router-dom' import { compose } from 'recompose' import TransactionViewBalance from './transaction-view-balance.component' -import { getSelectedToken, getSelectedAddress, getSelectedTokenAssetImage } from '../../selectors' +import { getSelectedToken, getSelectedAddress, getNativeCurrency, getSelectedTokenAssetImage } from '../../selectors' import { showModal } from '../../actions' const mapStateToProps = state => { @@ -15,6 +15,7 @@ const mapStateToProps = state => { selectedToken: getSelectedToken(state), network, balance, + nativeCurrency: getNativeCurrency(state), assetImage: getSelectedTokenAssetImage(state), } } diff --git a/ui/app/components/transaction-view/index.scss b/ui/app/components/transaction-view/index.scss index af9771ce0..13187f0e5 100644 --- a/ui/app/components/transaction-view/index.scss +++ b/ui/app/components/transaction-view/index.scss @@ -4,6 +4,7 @@ min-width: 0; display: flex; flex-direction: column; + overflow-y: auto; &__balance-wrapper { @media screen and (max-width: $break-small) { diff --git a/ui/app/components/user-preferenced-currency-display/tests/user-preferenced-currency-display.container.test.js b/ui/app/components/user-preferenced-currency-display/tests/user-preferenced-currency-display.container.test.js index 41ad3b73e..ba1c23d83 100644 --- a/ui/app/components/user-preferenced-currency-display/tests/user-preferenced-currency-display.container.test.js +++ b/ui/app/components/user-preferenced-currency-display/tests/user-preferenced-currency-display.container.test.js @@ -18,14 +18,16 @@ describe('UserPreferencedCurrencyDisplay container', () => { it('should return the correct props', () => { const mockState = { metamask: { + nativeCurrency: 'ETH', preferences: { - useETHAsPrimaryCurrency: true, + useNativeCurrencyAsPrimaryCurrency: true, }, }, } assert.deepEqual(mapStateToProps(mockState), { - useETHAsPrimaryCurrency: true, + nativeCurrency: 'ETH', + useNativeCurrencyAsPrimaryCurrency: true, }) }) }) @@ -37,33 +39,38 @@ describe('UserPreferencedCurrencyDisplay container', () => { const tests = [ { stateProps: { - useETHAsPrimaryCurrency: true, + useNativeCurrencyAsPrimaryCurrency: true, + nativeCurrency: 'ETH', }, ownProps: { type: 'PRIMARY', }, result: { currency: 'ETH', + nativeCurrency: 'ETH', numberOfDecimals: 6, prefix: undefined, }, }, { stateProps: { - useETHAsPrimaryCurrency: false, + useNativeCurrencyAsPrimaryCurrency: false, + nativeCurrency: 'ETH', }, ownProps: { type: 'PRIMARY', }, result: { currency: undefined, + nativeCurrency: 'ETH', numberOfDecimals: 2, prefix: undefined, }, }, { stateProps: { - useETHAsPrimaryCurrency: true, + useNativeCurrencyAsPrimaryCurrency: true, + nativeCurrency: 'ETH', }, ownProps: { type: 'SECONDARY', @@ -71,6 +78,7 @@ describe('UserPreferencedCurrencyDisplay container', () => { fiatPrefix: '-', }, result: { + nativeCurrency: 'ETH', currency: undefined, numberOfDecimals: 4, prefix: '-', @@ -78,7 +86,8 @@ describe('UserPreferencedCurrencyDisplay container', () => { }, { stateProps: { - useETHAsPrimaryCurrency: false, + useNativeCurrencyAsPrimaryCurrency: false, + nativeCurrency: 'ETH', }, ownProps: { type: 'SECONDARY', @@ -89,6 +98,7 @@ describe('UserPreferencedCurrencyDisplay container', () => { }, result: { currency: 'ETH', + nativeCurrency: 'ETH', numberOfDecimals: 3, prefix: 'b', }, diff --git a/ui/app/components/user-preferenced-currency-display/user-preferenced-currency-display.component.js b/ui/app/components/user-preferenced-currency-display/user-preferenced-currency-display.component.js index 4d948ca6a..f2a834ea7 100644 --- a/ui/app/components/user-preferenced-currency-display/user-preferenced-currency-display.component.js +++ b/ui/app/components/user-preferenced-currency-display/user-preferenced-currency-display.component.js @@ -21,6 +21,7 @@ export default class UserPreferencedCurrencyDisplay extends PureComponent { fiatPrefix: PropTypes.string, // From container currency: PropTypes.string, + nativeCurrency: PropTypes.string, } renderEthLogo () { diff --git a/ui/app/components/user-preferenced-currency-display/user-preferenced-currency-display.container.js b/ui/app/components/user-preferenced-currency-display/user-preferenced-currency-display.container.js index 23240c649..7999301ad 100644 --- a/ui/app/components/user-preferenced-currency-display/user-preferenced-currency-display.container.js +++ b/ui/app/components/user-preferenced-currency-display/user-preferenced-currency-display.container.js @@ -4,15 +4,16 @@ import { preferencesSelector } from '../../selectors' import { ETH, PRIMARY, SECONDARY } from '../../constants/common' const mapStateToProps = (state, ownProps) => { - const { useETHAsPrimaryCurrency } = preferencesSelector(state) + const { useNativeCurrencyAsPrimaryCurrency } = preferencesSelector(state) return { - useETHAsPrimaryCurrency, + useNativeCurrencyAsPrimaryCurrency, + nativeCurrency: state.metamask.nativeCurrency, } } const mergeProps = (stateProps, dispatchProps, ownProps) => { - const { useETHAsPrimaryCurrency, ...restStateProps } = stateProps + const { useNativeCurrencyAsPrimaryCurrency, nativeCurrency, ...restStateProps } = stateProps const { type, numberOfDecimals: propsNumberOfDecimals, @@ -26,14 +27,14 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => { let currency, numberOfDecimals, prefix - if (type === PRIMARY && useETHAsPrimaryCurrency || - type === SECONDARY && !useETHAsPrimaryCurrency) { + if (type === PRIMARY && useNativeCurrencyAsPrimaryCurrency || + type === SECONDARY && !useNativeCurrencyAsPrimaryCurrency) { // Display ETH - currency = ETH + currency = nativeCurrency || ETH numberOfDecimals = propsNumberOfDecimals || ethNumberOfDecimals || 6 prefix = propsPrefix || ethPrefix - } else if (type === SECONDARY && useETHAsPrimaryCurrency || - type === PRIMARY && !useETHAsPrimaryCurrency) { + } else if (type === SECONDARY && useNativeCurrencyAsPrimaryCurrency || + type === PRIMARY && !useNativeCurrencyAsPrimaryCurrency) { // Display Fiat numberOfDecimals = propsNumberOfDecimals || fiatNumberOfDecimals || 2 prefix = propsPrefix || fiatPrefix @@ -43,6 +44,7 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => { ...restStateProps, ...dispatchProps, ...restOwnProps, + nativeCurrency, currency, numberOfDecimals, prefix, diff --git a/ui/app/components/user-preferenced-currency-input/tests/user-preferenced-currency-input.component.test.js b/ui/app/components/user-preferenced-currency-input/tests/user-preferenced-currency-input.component.test.js index 0af80a03d..710b5d519 100644 --- a/ui/app/components/user-preferenced-currency-input/tests/user-preferenced-currency-input.component.test.js +++ b/ui/app/components/user-preferenced-currency-input/tests/user-preferenced-currency-input.component.test.js @@ -15,17 +15,17 @@ describe('UserPreferencedCurrencyInput Component', () => { assert.equal(wrapper.find(CurrencyInput).length, 1) }) - it('should render useFiat for CurrencyInput based on preferences.useETHAsPrimaryCurrency', () => { + it('should render useFiat for CurrencyInput based on preferences.useNativeCurrencyAsPrimaryCurrency', () => { const wrapper = shallow( <UserPreferencedCurrencyInput - useETHAsPrimaryCurrency + useNativeCurrencyAsPrimaryCurrency /> ) assert.ok(wrapper) assert.equal(wrapper.find(CurrencyInput).length, 1) assert.equal(wrapper.find(CurrencyInput).props().useFiat, false) - wrapper.setProps({ useETHAsPrimaryCurrency: false }) + wrapper.setProps({ useNativeCurrencyAsPrimaryCurrency: false }) assert.equal(wrapper.find(CurrencyInput).props().useFiat, true) }) }) diff --git a/ui/app/components/user-preferenced-currency-input/tests/user-preferenced-currency-input.container.test.js b/ui/app/components/user-preferenced-currency-input/tests/user-preferenced-currency-input.container.test.js index d860c38da..959726443 100644 --- a/ui/app/components/user-preferenced-currency-input/tests/user-preferenced-currency-input.container.test.js +++ b/ui/app/components/user-preferenced-currency-input/tests/user-preferenced-currency-input.container.test.js @@ -18,13 +18,13 @@ describe('UserPreferencedCurrencyInput container', () => { const mockState = { metamask: { preferences: { - useETHAsPrimaryCurrency: true, + useNativeCurrencyAsPrimaryCurrency: true, }, }, } assert.deepEqual(mapStateToProps(mockState), { - useETHAsPrimaryCurrency: true, + useNativeCurrencyAsPrimaryCurrency: true, }) }) }) diff --git a/ui/app/components/user-preferenced-currency-input/user-preferenced-currency-input.component.js b/ui/app/components/user-preferenced-currency-input/user-preferenced-currency-input.component.js index 6e0e00a1d..463e66b80 100644 --- a/ui/app/components/user-preferenced-currency-input/user-preferenced-currency-input.component.js +++ b/ui/app/components/user-preferenced-currency-input/user-preferenced-currency-input.component.js @@ -4,16 +4,16 @@ import CurrencyInput from '../currency-input' export default class UserPreferencedCurrencyInput extends PureComponent { static propTypes = { - useETHAsPrimaryCurrency: PropTypes.bool, + useNativeCurrencyAsPrimaryCurrency: PropTypes.bool, } render () { - const { useETHAsPrimaryCurrency, ...restProps } = this.props + const { useNativeCurrencyAsPrimaryCurrency, ...restProps } = this.props return ( <CurrencyInput {...restProps} - useFiat={!useETHAsPrimaryCurrency} + useFiat={!useNativeCurrencyAsPrimaryCurrency} /> ) } diff --git a/ui/app/components/user-preferenced-currency-input/user-preferenced-currency-input.container.js b/ui/app/components/user-preferenced-currency-input/user-preferenced-currency-input.container.js index 397cdc7cc..0b88eb5a7 100644 --- a/ui/app/components/user-preferenced-currency-input/user-preferenced-currency-input.container.js +++ b/ui/app/components/user-preferenced-currency-input/user-preferenced-currency-input.container.js @@ -3,10 +3,10 @@ import UserPreferencedCurrencyInput from './user-preferenced-currency-input.comp import { preferencesSelector } from '../../selectors' const mapStateToProps = state => { - const { useETHAsPrimaryCurrency } = preferencesSelector(state) + const { useNativeCurrencyAsPrimaryCurrency } = preferencesSelector(state) return { - useETHAsPrimaryCurrency, + useNativeCurrencyAsPrimaryCurrency, } } diff --git a/ui/app/components/user-preferenced-token-input/tests/user-preferenced-token-input.component.test.js b/ui/app/components/user-preferenced-token-input/tests/user-preferenced-token-input.component.test.js index 910c7089f..d85bddeeb 100644 --- a/ui/app/components/user-preferenced-token-input/tests/user-preferenced-token-input.component.test.js +++ b/ui/app/components/user-preferenced-token-input/tests/user-preferenced-token-input.component.test.js @@ -15,17 +15,17 @@ describe('UserPreferencedCurrencyInput Component', () => { assert.equal(wrapper.find(TokenInput).length, 1) }) - it('should render showFiat for TokenInput based on preferences.useETHAsPrimaryCurrency', () => { + it('should render showFiat for TokenInput based on preferences.useNativeCurrencyAsPrimaryCurrency', () => { const wrapper = shallow( <UserPreferencedTokenInput - useETHAsPrimaryCurrency + useNativeCurrencyAsPrimaryCurrency /> ) assert.ok(wrapper) assert.equal(wrapper.find(TokenInput).length, 1) assert.equal(wrapper.find(TokenInput).props().showFiat, false) - wrapper.setProps({ useETHAsPrimaryCurrency: false }) + wrapper.setProps({ useNativeCurrencyAsPrimaryCurrency: false }) assert.equal(wrapper.find(TokenInput).props().showFiat, true) }) }) diff --git a/ui/app/components/user-preferenced-token-input/tests/user-preferenced-token-input.container.test.js b/ui/app/components/user-preferenced-token-input/tests/user-preferenced-token-input.container.test.js index e3509149a..2f89fba90 100644 --- a/ui/app/components/user-preferenced-token-input/tests/user-preferenced-token-input.container.test.js +++ b/ui/app/components/user-preferenced-token-input/tests/user-preferenced-token-input.container.test.js @@ -18,13 +18,13 @@ describe('UserPreferencedTokenInput container', () => { const mockState = { metamask: { preferences: { - useETHAsPrimaryCurrency: true, + useNativeCurrencyAsPrimaryCurrency: true, }, }, } assert.deepEqual(mapStateToProps(mockState), { - useETHAsPrimaryCurrency: true, + useNativeCurrencyAsPrimaryCurrency: true, }) }) }) diff --git a/ui/app/components/user-preferenced-token-input/user-preferenced-token-input.component.js b/ui/app/components/user-preferenced-token-input/user-preferenced-token-input.component.js index f2b537f11..8f14231ac 100644 --- a/ui/app/components/user-preferenced-token-input/user-preferenced-token-input.component.js +++ b/ui/app/components/user-preferenced-token-input/user-preferenced-token-input.component.js @@ -4,16 +4,16 @@ import TokenInput from '../token-input' export default class UserPreferencedTokenInput extends PureComponent { static propTypes = { - useETHAsPrimaryCurrency: PropTypes.bool, + useNativeCurrencyAsPrimaryCurrency: PropTypes.bool, } render () { - const { useETHAsPrimaryCurrency, ...restProps } = this.props + const { useNativeCurrencyAsPrimaryCurrency, ...restProps } = this.props return ( <TokenInput {...restProps} - showFiat={!useETHAsPrimaryCurrency} + showFiat={!useNativeCurrencyAsPrimaryCurrency} /> ) } diff --git a/ui/app/components/user-preferenced-token-input/user-preferenced-token-input.container.js b/ui/app/components/user-preferenced-token-input/user-preferenced-token-input.container.js index 416d069dd..3305d4e29 100644 --- a/ui/app/components/user-preferenced-token-input/user-preferenced-token-input.container.js +++ b/ui/app/components/user-preferenced-token-input/user-preferenced-token-input.container.js @@ -3,10 +3,10 @@ import UserPreferencedTokenInput from './user-preferenced-token-input.component' import { preferencesSelector } from '../../selectors' const mapStateToProps = state => { - const { useETHAsPrimaryCurrency } = preferencesSelector(state) + const { useNativeCurrencyAsPrimaryCurrency } = preferencesSelector(state) return { - useETHAsPrimaryCurrency, + useNativeCurrencyAsPrimaryCurrency, } } diff --git a/ui/app/components/wallet-view.js b/ui/app/components/wallet-view.js index 8a7cb0f8d..e050e0ee6 100644 --- a/ui/app/components/wallet-view.js +++ b/ui/app/components/wallet-view.js @@ -7,7 +7,7 @@ const { compose } = require('recompose') const inherits = require('util').inherits const classnames = require('classnames') const { checksumAddress } = require('../util') -const Identicon = require('./identicon') +import Identicon from './identicon' // const AccountDropdowns = require('./dropdowns/index.js').AccountDropdowns const Tooltip = require('./tooltip-v2.js').default const copyToClipboard = require('copy-to-clipboard') @@ -127,7 +127,6 @@ WalletView.prototype.render = function () { identities, } = this.props // temporary logs + fake extra wallets - // console.log('walletview, selectedAccount:', selectedAccount) const checksummedAddress = checksumAddress(selectedAddress) diff --git a/ui/app/constants/error-keys.js b/ui/app/constants/error-keys.js index f70ed3b19..704064c96 100644 --- a/ui/app/constants/error-keys.js +++ b/ui/app/constants/error-keys.js @@ -1,3 +1,4 @@ export const INSUFFICIENT_FUNDS_ERROR_KEY = 'insufficientFunds' export const GAS_LIMIT_TOO_LOW_ERROR_KEY = 'gasLimitTooLow' export const TRANSACTION_ERROR_KEY = 'transactionError' +export const TRANSACTION_NO_CONTRACT_ERROR_KEY = 'transactionErrorNoContract' diff --git a/ui/app/css/itcss/components/buttons.scss b/ui/app/css/itcss/components/buttons.scss index 655188a3e..5826a8b49 100644 --- a/ui/app/css/itcss/components/buttons.scss +++ b/ui/app/css/itcss/components/buttons.scss @@ -3,7 +3,7 @@ */ .button { - height: 44px; + min-height: 44px; background: $white; display: flex; justify-content: center; @@ -82,12 +82,13 @@ box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.08); padding: 6px; height: initial; + min-height: initial; width: initial; min-width: initial; } .btn--large { - height: 54px; + min-height: 54px; } .btn-green { diff --git a/ui/app/css/itcss/components/pages/index.scss b/ui/app/css/itcss/components/pages/index.scss index 709f8baf6..45e47f447 100644 --- a/ui/app/css/itcss/components/pages/index.scss +++ b/ui/app/css/itcss/components/pages/index.scss @@ -1 +1,3 @@ @import './reveal-seed.scss'; + +@import './provider-approval.scss'; diff --git a/ui/app/css/itcss/components/pages/provider-approval.scss b/ui/app/css/itcss/components/pages/provider-approval.scss new file mode 100644 index 000000000..f172165ab --- /dev/null +++ b/ui/app/css/itcss/components/pages/provider-approval.scss @@ -0,0 +1,11 @@ +.provider_approval_content { + height: auto; + overflow: auto; + padding: 16px; +} + +.provider_approval_origin { + font-weight: 999; + margin-top: 16px; + word-wrap: break-word; +} diff --git a/ui/app/css/itcss/components/request-signature.scss b/ui/app/css/itcss/components/request-signature.scss index 445b9ebf5..6c950d846 100644 --- a/ui/app/css/itcss/components/request-signature.scss +++ b/ui/app/css/itcss/components/request-signature.scss @@ -19,7 +19,7 @@ } @media screen and (min-width: $break-large) { - max-height: 620px; + height: 620px; } } diff --git a/ui/app/css/itcss/components/wallet-balance.scss b/ui/app/css/itcss/components/wallet-balance.scss index 293771550..3c3349ae0 100644 --- a/ui/app/css/itcss/components/wallet-balance.scss +++ b/ui/app/css/itcss/components/wallet-balance.scss @@ -29,7 +29,7 @@ $wallet-balance-breakpoint-range: "screen and (min-width: #{$break-large}) and ( align-items: center; margin: 20px 24px; flex-direction: row; - flex-grow: 3; + min-width: 0; @media #{$wallet-balance-breakpoint-range} { margin: 10% 4%; @@ -38,8 +38,7 @@ $wallet-balance-breakpoint-range: "screen and (min-width: #{$break-large}) and ( .balance-display { margin-left: 15px; - justify-content: flex-start; - align-items: flex-start; + min-width: 0; .token-amount { font-size: 1.5rem; diff --git a/ui/app/ducks/confirm-transaction.duck.js b/ui/app/ducks/confirm-transaction.duck.js index 2ceafbe08..275eb1551 100644 --- a/ui/app/ducks/confirm-transaction.duck.js +++ b/ui/app/ducks/confirm-transaction.duck.js @@ -2,6 +2,7 @@ import { conversionRateSelector, currentCurrencySelector, unconfirmedTransactionsHashSelector, + getNativeCurrency, } from '../selectors/confirm-transaction' import { @@ -292,16 +293,17 @@ export function updateTxDataAndCalculate (txData) { const state = getState() const currentCurrency = currentCurrencySelector(state) const conversionRate = conversionRateSelector(state) + const nativeCurrency = getNativeCurrency(state) dispatch(updateTxData(txData)) const { txParams: { value = '0x0', gas: gasLimit = '0x0', gasPrice = '0x0' } = {} } = txData const fiatTransactionAmount = getValueFromWeiHex({ - value, toCurrency: currentCurrency, conversionRate, numberOfDecimals: 2, + value, fromCurrency: nativeCurrency, toCurrency: currentCurrency, conversionRate, numberOfDecimals: 2, }) const ethTransactionAmount = getValueFromWeiHex({ - value, toCurrency: 'ETH', conversionRate, numberOfDecimals: 6, + value, fromCurrency: nativeCurrency, toCurrency: nativeCurrency, conversionRate, numberOfDecimals: 6, }) dispatch(updateTransactionAmounts({ @@ -314,13 +316,15 @@ export function updateTxDataAndCalculate (txData) { const fiatTransactionFee = getTransactionFee({ value: hexTransactionFee, + fromCurrency: nativeCurrency, toCurrency: currentCurrency, numberOfDecimals: 2, conversionRate, }) const ethTransactionFee = getTransactionFee({ value: hexTransactionFee, - toCurrency: 'ETH', + fromCurrency: nativeCurrency, + toCurrency: nativeCurrency, numberOfDecimals: 6, conversionRate, }) @@ -329,7 +333,6 @@ export function updateTxDataAndCalculate (txData) { const fiatTransactionTotal = addFiat(fiatTransactionFee, fiatTransactionAmount) const ethTransactionTotal = addEth(ethTransactionFee, ethTransactionAmount) - console.log('HIHIH', value, hexTransactionFee) const hexTransactionTotal = sumHexes(value, hexTransactionFee) dispatch(updateTransactionTotals({ diff --git a/ui/app/helpers/confirm-transaction/util.js b/ui/app/helpers/confirm-transaction/util.js index bcac22500..eb334a4b8 100644 --- a/ui/app/helpers/confirm-transaction/util.js +++ b/ui/app/helpers/confirm-transaction/util.js @@ -55,6 +55,7 @@ export function addFiat (...args) { export function getValueFromWeiHex ({ value, + fromCurrency = 'ETH', toCurrency, conversionRate, numberOfDecimals, @@ -63,7 +64,7 @@ export function getValueFromWeiHex ({ return conversionUtil(value, { fromNumericBase: 'hex', toNumericBase: 'dec', - fromCurrency: 'ETH', + fromCurrency, toCurrency, numberOfDecimals, fromDenomination: 'WEI', @@ -74,6 +75,7 @@ export function getValueFromWeiHex ({ export function getTransactionFee ({ value, + fromCurrency = 'ETH', toCurrency, conversionRate, numberOfDecimals, @@ -82,7 +84,7 @@ export function getTransactionFee ({ fromNumericBase: 'BN', toNumericBase: 'dec', fromDenomination: 'WEI', - fromCurrency: 'ETH', + fromCurrency, toCurrency, numberOfDecimals, conversionRate, @@ -99,6 +101,7 @@ export function formatCurrency (value, currencyCode) { export function convertTokenToFiat ({ value, + fromCurrency = 'ETH', toCurrency, conversionRate, contractExchangeRate, @@ -108,6 +111,7 @@ export function convertTokenToFiat ({ return conversionUtil(value, { fromNumericBase: 'dec', toNumericBase: 'dec', + fromCurrency, toCurrency, numberOfDecimals: 2, conversionRate: totalExchangeRate, diff --git a/ui/app/helpers/conversions.util.js b/ui/app/helpers/conversions.util.js index 777537e1e..cb5e1b90b 100644 --- a/ui/app/helpers/conversions.util.js +++ b/ui/app/helpers/conversions.util.js @@ -20,8 +20,8 @@ export function decimalToHex (decimal) { }) } -export function getEthConversionFromWeiHex ({ value, conversionRate, numberOfDecimals = 6 }) { - const denominations = [ETH, GWEI, WEI] +export function getEthConversionFromWeiHex ({ value, fromCurrency = ETH, conversionRate, numberOfDecimals = 6 }) { + const denominations = [fromCurrency, GWEI, WEI] let nonZeroDenomination @@ -29,7 +29,8 @@ export function getEthConversionFromWeiHex ({ value, conversionRate, numberOfDec const convertedValue = getValueFromWeiHex({ value, conversionRate, - toCurrency: ETH, + fromCurrency, + toCurrency: fromCurrency, numberOfDecimals, toDenomination: denominations[i], }) @@ -45,6 +46,7 @@ export function getEthConversionFromWeiHex ({ value, conversionRate, numberOfDec export function getValueFromWeiHex ({ value, + fromCurrency = ETH, toCurrency, conversionRate, numberOfDecimals, @@ -53,7 +55,7 @@ export function getValueFromWeiHex ({ return conversionUtil(value, { fromNumericBase: 'hex', toNumericBase: 'dec', - fromCurrency: ETH, + fromCurrency, toCurrency, numberOfDecimals, fromDenomination: WEI, diff --git a/ui/app/helpers/transactions.util.js b/ui/app/helpers/transactions.util.js index e50196196..2f4b1d095 100644 --- a/ui/app/helpers/transactions.util.js +++ b/ui/app/helpers/transactions.util.js @@ -27,10 +27,21 @@ export function getTokenData (data = '') { const registry = new MethodRegistry({ provider: global.ethereumProvider }) +/** + * Attempts to return the method data from the MethodRegistry library, if the method exists in the + * registry. Otherwise, returns an empty object. + * @param {string} data - The hex data (@code txParams.data) of a transaction + * @returns {Object} + */ export async function getMethodData (data = '') { const prefixedData = ethUtil.addHexPrefix(data) const fourBytePrefix = prefixedData.slice(0, 10) const sig = await registry.lookup(fourBytePrefix) + + if (!sig) { + return {} + } + const parsedResult = registry.parse(sig) return { @@ -114,7 +125,9 @@ export function getLatestSubmittedTxWithNonce (transactions = [], nonce = '0x0') export async function isSmartContractAddress (address) { const code = await global.eth.getCode(address) - return code && code !== '0x' + // Geth will return '0x', and ganache-core v2.2.1 will return '0x0' + const codeIsEmpty = !code || code === '0x' || code === '0x0' + return !codeIsEmpty } export function sumHexes (...args) { diff --git a/ui/app/reducers.js b/ui/app/reducers.js index 80e76d570..e1a982f93 100644 --- a/ui/app/reducers.js +++ b/ui/app/reducers.js @@ -1,3 +1,4 @@ +const clone = require('clone') const extend = require('xtend') const copyToClipboard = require('copy-to-clipboard') @@ -52,19 +53,24 @@ function rootReducer (state, action) { return state } +window.getCleanAppState = function () { + const state = clone(window.METAMASK_CACHED_LOG_STATE) + // append additional information + state.version = global.platform.getVersion() + state.browser = window.navigator.userAgent + // ensure seedWords are not included + if (state.metamask) delete state.metamask.seedWords + if (state.appState.currentView) delete state.appState.currentView.seedWords + return state +} + window.logStateString = function (cb) { - const state = window.METAMASK_CACHED_LOG_STATE - const version = global.platform.getVersion() - const browser = window.navigator.userAgent - return global.platform.getPlatformInfo((err, platform) => { - if (err) { - return cb(err) - } - state.version = version + const state = window.getCleanAppState() + global.platform.getPlatformInfo((err, platform) => { + if (err) return cb(err) state.platform = platform - state.browser = browser const stateString = JSON.stringify(state, removeSeedWords, 2) - return cb(null, stateString) + cb(null, stateString) }) } diff --git a/ui/app/reducers/metamask.js b/ui/app/reducers/metamask.js index 37d8a9187..22fa53098 100644 --- a/ui/app/reducers/metamask.js +++ b/ui/app/reducers/metamask.js @@ -52,7 +52,7 @@ function reduceMetamask (state, action) { welcomeScreenSeen: false, currentLocale: '', preferences: { - useETHAsPrimaryCurrency: true, + useNativeCurrencyAsPrimaryCurrency: true, }, }, state.metamask) diff --git a/ui/app/selectors.js b/ui/app/selectors.js index 9f11551be..7209f19d1 100644 --- a/ui/app/selectors.js +++ b/ui/app/selectors.js @@ -26,6 +26,7 @@ const selectors = { getAddressBook, getSendFrom, getCurrentCurrency, + getNativeCurrency, getSendAmount, getSelectedTokenToFiatRate, getSelectedTokenContract, @@ -143,6 +144,10 @@ function getCurrentCurrency (state) { return state.metamask.currentCurrency } +function getNativeCurrency (state) { + return state.metamask.nativeCurrency +} + function getSelectedTokenToFiatRate (state) { const selectedTokenExchangeRate = getSelectedTokenExchangeRate(state) const conversionRate = conversionRateSelector(state) diff --git a/ui/app/selectors/confirm-transaction.js b/ui/app/selectors/confirm-transaction.js index 86b10bac3..90924c036 100644 --- a/ui/app/selectors/confirm-transaction.js +++ b/ui/app/selectors/confirm-transaction.js @@ -93,6 +93,7 @@ export const unconfirmedTransactionsCountSelector = createSelector( export const currentCurrencySelector = state => state.metamask.currentCurrency export const conversionRateSelector = state => state.metamask.conversionRate +export const getNativeCurrency = state => state.metamask.nativeCurrency const txDataSelector = state => state.confirmTransaction.txData const tokenDataSelector = state => state.confirmTransaction.tokenData diff --git a/ui/app/util.js b/ui/app/util.js index 37c0fb698..b19a028cc 100644 --- a/ui/app/util.js +++ b/ui/app/util.js @@ -128,7 +128,7 @@ function parseBalance (balance) { // Takes wei hex, returns an object with three properties. // Its "formatted" property is what we generally use to render values. -function formatBalance (balance, decimalsToKeep, needsParse = true) { +function formatBalance (balance, decimalsToKeep, needsParse = true, ticker = 'ETH') { var parsed = needsParse ? parseBalance(balance) : balance.split('.') var beforeDecimal = parsed[0] var afterDecimal = parsed[1] @@ -138,14 +138,14 @@ function formatBalance (balance, decimalsToKeep, needsParse = true) { if (afterDecimal !== '0') { var sigFigs = afterDecimal.match(/^0*(.{2})/) // default: grabs 2 most significant digits if (sigFigs) { afterDecimal = sigFigs[0] } - formatted = '0.' + afterDecimal + ' ETH' + formatted = '0.' + afterDecimal + ` ${ticker}` } } else { - formatted = beforeDecimal + '.' + afterDecimal.slice(0, 3) + ' ETH' + formatted = beforeDecimal + '.' + afterDecimal.slice(0, 3) + ` ${ticker}` } } else { afterDecimal += Array(decimalsToKeep).join('0') - formatted = beforeDecimal + '.' + afterDecimal.slice(0, decimalsToKeep) + ' ETH' + formatted = beforeDecimal + '.' + afterDecimal.slice(0, decimalsToKeep) + ` ${ticker}` } return formatted } |