diff options
author | Chi Kei Chan <chikeichan@gmail.com> | 2019-02-27 02:30:41 +0800 |
---|---|---|
committer | Whymarrh Whitby <whymarrh.whitby@gmail.com> | 2019-02-27 02:30:41 +0800 |
commit | a2320c76fef084b7ec01839ab9c17b474839b3c0 (patch) | |
tree | 7620668c68c0de4e0de6ef745beb2cdc508ff50b | |
parent | fc1655eecbf3da969dc9c9a8fc3ae95221ffa30b (diff) | |
download | tangerine-wallet-browser-a2320c76fef084b7ec01839ab9c17b474839b3c0.tar tangerine-wallet-browser-a2320c76fef084b7ec01839ab9c17b474839b3c0.tar.gz tangerine-wallet-browser-a2320c76fef084b7ec01839ab9c17b474839b3c0.tar.bz2 tangerine-wallet-browser-a2320c76fef084b7ec01839ab9c17b474839b3c0.tar.lz tangerine-wallet-browser-a2320c76fef084b7ec01839ab9c17b474839b3c0.tar.xz tangerine-wallet-browser-a2320c76fef084b7ec01839ab9c17b474839b3c0.tar.zst tangerine-wallet-browser-a2320c76fef084b7ec01839ab9c17b474839b3c0.zip |
Show/Hide Fiat on Testnets based on User Preference (#6153)
39 files changed, 1169 insertions, 113 deletions
diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 2bfe0ef07..2d0098a24 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -1237,6 +1237,12 @@ "showAdvancedGasInlineDescription": { "message": "Select this to show gas price and limit controls directly on the send and confirm screens." }, + "showFiatConversionInTestnets": { + "message": "Show Conversion in Testnets" + }, + "showFiatConversionInTestnetsDescription": { + "message": "Select this to show fiat conversion in when you are on Testnets" + }, "showPrivateKeys": { "message": "Show Private Keys" }, diff --git a/development/states/confirm-new-ui.json b/development/states/confirm-new-ui.json index 346f06c6b..4310ed5b7 100644 --- a/development/states/confirm-new-ui.json +++ b/development/states/confirm-new-ui.json @@ -131,7 +131,8 @@ }, "currentLocale": "en", "preferences": { - "useNativeCurrencyAsPrimaryCurrency": true + "useNativeCurrencyAsPrimaryCurrency": true, + "showFiatInTestnets": true } }, "appState": { diff --git a/development/states/currency-localization.json b/development/states/currency-localization.json index 7dd1a135d..8288b3020 100644 --- a/development/states/currency-localization.json +++ b/development/states/currency-localization.json @@ -113,7 +113,8 @@ }, "currentLocale": "en", "preferences": { - "useNativeCurrencyAsPrimaryCurrency": true + "useNativeCurrencyAsPrimaryCurrency": true, + "showFiatInTestnets": true } }, "appState": { diff --git a/development/states/send-edit.json b/development/states/send-edit.json index f617910f1..fda7d1a31 100644 --- a/development/states/send-edit.json +++ b/development/states/send-edit.json @@ -135,7 +135,8 @@ }, "currentLocale": "en", "preferences": { - "useNativeCurrencyAsPrimaryCurrency": true + "useNativeCurrencyAsPrimaryCurrency": true, + "showFiatInTestnets": true } }, "appState": { diff --git a/development/states/send-new-ui.json b/development/states/send-new-ui.json index bb4d80c5c..b8a3ff128 100644 --- a/development/states/send-new-ui.json +++ b/development/states/send-new-ui.json @@ -114,7 +114,8 @@ }, "currentLocale": "en", "preferences": { - "useNativeCurrencyAsPrimaryCurrency": true + "useNativeCurrencyAsPrimaryCurrency": true, + "showFiatInTestnets": true } }, "appState": { diff --git a/test/e2e/beta/metamask-beta-ui.spec.js b/test/e2e/beta/metamask-beta-ui.spec.js index bde031301..798cce372 100644 --- a/test/e2e/beta/metamask-beta-ui.spec.js +++ b/test/e2e/beta/metamask-beta-ui.spec.js @@ -495,10 +495,13 @@ describe('MetaMask', function () { await findElement(driver, By.css('.tab-bar')) + const showConversionToggle = await findElement(driver, By.css('.settings-page__content-row:nth-of-type(3) .settings-page__content-item-col > div')) + await showConversionToggle.click() + const advancedGasTitle = await findElement(driver, By.xpath(`//span[contains(text(), 'Advanced gas controls')]`)) await driver.executeScript('arguments[0].scrollIntoView(true)', advancedGasTitle) - const advancedGasToggle = await findElement(driver, By.css('.settings-page__content-row:nth-of-type(11) .settings-page__content-item-col > div')) + const advancedGasToggle = await findElement(driver, By.css('.settings-page__content-row:nth-of-type(12) .settings-page__content-item-col > div')) await advancedGasToggle.click() windowHandles = await driver.getAllWindowHandles() extension = windowHandles[0] @@ -560,23 +563,37 @@ describe('MetaMask', function () { await delay(regularDelayMs) }) + let txValues + it('finds the transaction in the transactions list', async function () { const transactions = await findElements(driver, By.css('.transaction-list-item')) assert.equal(transactions.length, 4) - const txValues = await findElement(driver, By.css('.transaction-list-item__amount--primary')) - await driver.wait(until.elementTextMatches(txValues, /-3\s*ETH/), 10000) + txValues = await findElements(driver, By.css('.transaction-list-item__amount--primary')) + await driver.wait(until.elementTextMatches(txValues[0], /-3\s*ETH/), 10000) }) it('the transaction has the expected gas price', async function () { - const txValues = await findElement(driver, By.css('.transaction-list-item__amount--primary')) - await txValues.click() - await delay(tinyDelayMs) - await findElement(driver, By.xpath(`//div[contains(text(), 'Gas Price (GWEI)')]`)) - - await findElement(driver, By.xpath(`//span[contains(text(), '7')]`)) + await delay(largeDelayMs) + let txGasPriceLabels + let txGasPrices + try { + await txValues[0].click() + txGasPrices = await findElements(driver, By.css('.transaction-breakdown__value')) + txGasPriceLabels = await findElements(driver, By.css('.transaction-breakdown-row__title')) + txGasPrices = await findElements(driver, By.css('.transaction-breakdown__value')) + await driver.wait(until.elementTextMatches(txGasPrices[3], /^10$/), 10000) + } catch (e) { + console.log(e.message) + txValues = await findElements(driver, By.css('.transaction-list-item__amount--primary')) + await txValues[0].click() + txGasPriceLabels = await findElements(driver, By.css('.transaction-breakdown-row__title')) + txGasPrices = await findElements(driver, By.css('.transaction-breakdown__value')) + await driver.wait(until.elementTextMatches(txGasPrices[3], /^10$/), 10000) + } + assert(txGasPriceLabels[2]) - txValues.click() + await txValues[0].click() }) }) @@ -1125,6 +1142,7 @@ describe('MetaMask', function () { return confirmedTxes.length === 2 }, 10000) + await delay(regularDelayMs) const txValues = await findElements(driver, By.css('.transaction-list-item__amount--primary')) await driver.wait(until.elementTextMatches(txValues[0], /-7\s*TST/)) const txStatuses = await findElements(driver, By.css('.transaction-list-item__action')) diff --git a/ui/app/actions.js b/ui/app/actions.js index a9c3d2ccf..1d01a72ad 100644 --- a/ui/app/actions.js +++ b/ui/app/actions.js @@ -317,6 +317,7 @@ var actions = { updatePreferences, UPDATE_PREFERENCES: 'UPDATE_PREFERENCES', setUseNativeCurrencyAsPrimaryCurrencyPreference, + setShowFiatConversionOnTestnetsPreference, // Migration of users to new UI setCompletedUiMigration, @@ -2470,6 +2471,10 @@ function setUseNativeCurrencyAsPrimaryCurrencyPreference (value) { return setPreference('useNativeCurrencyAsPrimaryCurrency', value) } +function setShowFiatConversionOnTestnetsPreference (value) { + return setPreference('showFiatInTestnets', value) +} + function setCompletedOnboarding () { return dispatch => { dispatch(actions.showLoadingIndication()) diff --git a/ui/app/components/balance/balance.container.js b/ui/app/components/balance/balance.container.js index b8c3a04cc..1cd6df5ce 100644 --- a/ui/app/components/balance/balance.container.js +++ b/ui/app/components/balance/balance.container.js @@ -6,9 +6,13 @@ import { conversionRateSelector, getCurrentCurrency, getMetaMaskAccounts, + getIsMainnet, + preferencesSelector, } from '../../selectors' const mapStateToProps = state => { + const { showFiatInTestnets } = preferencesSelector(state) + const isMainnet = getIsMainnet(state) const accounts = getMetaMaskAccounts(state) const network = state.metamask.network const selectedAddress = state.metamask.selectedAddress || Object.keys(accounts)[0] @@ -21,6 +25,7 @@ const mapStateToProps = state => { conversionRate: conversionRateSelector(state), currentCurrency: getCurrentCurrency(state), assetImages: getAssetImages(state), + showFiat: (isMainnet || !!showFiatInTestnets), } } diff --git a/ui/app/components/currency-input/currency-input.component.js b/ui/app/components/currency-input/currency-input.component.js index 8fa3131ae..30e0e919b 100644 --- a/ui/app/components/currency-input/currency-input.component.js +++ b/ui/app/components/currency-input/currency-input.component.js @@ -11,6 +11,10 @@ import { ETH } from '../../constants/common' * gets converted into a decimal value depending on the currency (ETH or Fiat). */ export default class CurrencyInput extends PureComponent { + static contextTypes = { + t: PropTypes.func, + } + static propTypes = { conversionRate: PropTypes.number, currentCurrency: PropTypes.string, @@ -18,6 +22,7 @@ export default class CurrencyInput extends PureComponent { onChange: PropTypes.func, onBlur: PropTypes.func, useFiat: PropTypes.bool, + hideFiat: PropTypes.bool, value: PropTypes.string, fiatSuffix: PropTypes.string, nativeSuffix: PropTypes.string, @@ -61,8 +66,13 @@ export default class CurrencyInput extends PureComponent { } shouldUseFiat = () => { - const { useFiat } = this.props + const { useFiat, hideFiat } = this.props const { isSwapped } = this.state || {} + + if (hideFiat) { + return false + } + return isSwapped ? !useFiat : useFiat } @@ -93,10 +103,18 @@ export default class CurrencyInput extends PureComponent { } renderConversionComponent () { - const { currentCurrency, nativeCurrency } = this.props + const { currentCurrency, nativeCurrency, hideFiat } = this.props const { hexValue } = this.state let currency, numberOfDecimals + if (hideFiat) { + return ( + <div className="currency-input__conversion-component"> + { this.context.t('noConversionRateAvailable') } + </div> + ) + } + if (this.shouldUseFiat()) { // Display ETH currency = nativeCurrency || ETH diff --git a/ui/app/components/currency-input/currency-input.container.js b/ui/app/components/currency-input/currency-input.container.js index 4c89d6c1d..428be4557 100644 --- a/ui/app/components/currency-input/currency-input.container.js +++ b/ui/app/components/currency-input/currency-input.container.js @@ -1,14 +1,18 @@ import { connect } from 'react-redux' import CurrencyInput from './currency-input.component' import { ETH } from '../../constants/common' +import {getIsMainnet, preferencesSelector} from '../../selectors' const mapStateToProps = state => { const { metamask: { nativeCurrency, currentCurrency, conversionRate } } = state + const { showFiatInTestnets } = preferencesSelector(state) + const isMainnet = getIsMainnet(state) return { nativeCurrency, currentCurrency, conversionRate, + hideFiat: (!isMainnet && !showFiatInTestnets), } } 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 e1ab29187..6d4612e3c 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 @@ -1,4 +1,5 @@ import React from 'react' +import PropTypes from 'prop-types' import assert from 'assert' import { shallow, mount } from 'enzyme' import sinon from 'sinon' @@ -111,6 +112,45 @@ describe('CurrencyInput Component', () => { assert.equal(wrapper.find('.unit-input__input').props().value, '1') assert.equal(wrapper.find('.currency-display-component').text(), '0.004328ETH') }) + + it('should render properly with a native value when hideFiat is true', () => { + const mockStore = { + metamask: { + nativeCurrency: 'ETH', + currentCurrency: 'usd', + conversionRate: 231.06, + }, + } + const store = configureMockStore()(mockStore) + + const wrapper = mount( + <Provider store={store}> + <CurrencyInput + value="f602f2234d0ea" + fiatSuffix="USD" + nativeSuffix="ETH" + useFiat + hideFiat={true} + nativeCurrency="ETH" + currentCurrency="usd" + conversionRate={231.06} + /> + </Provider>, + { + context: { t: str => str + '_t' }, + childContextTypes: { t: PropTypes.func }, + } + ) + + assert.ok(wrapper) + const currencyInputInstance = wrapper.find(CurrencyInput).at(0).instance() + assert.equal(currencyInputInstance.state.decimalValue, 0.004328) + assert.equal(currencyInputInstance.state.hexValue, 'f602f2234d0ea') + 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, '0.004328') + assert.equal(wrapper.find('.currency-input__conversion-component').text(), 'noConversionRateAvailable_t') + }) }) describe('handling actions', () => { 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 10f530eff..6109d29b6 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 @@ -15,47 +15,155 @@ proxyquire('../currency-input.container.js', { describe('CurrencyInput container', () => { describe('mapStateToProps()', () => { - it('should return the correct props', () => { - const mockState = { - metamask: { + const tests = [ + // Test # 1 + { + comment: 'should return correct props in mainnet', + mockState: { + metamask: { + conversionRate: 280.45, + currentCurrency: 'usd', + nativeCurrency: 'ETH', + preferences: { + showFiatInTestnets: false, + }, + provider: { + type: 'mainnet', + }, + }, + }, + expected: { + conversionRate: 280.45, + currentCurrency: 'usd', + nativeCurrency: 'ETH', + hideFiat: false, + }, + }, + // Test # 2 + { + comment: 'should return correct props when not in mainnet and showFiatInTestnets is false', + mockState: { + metamask: { + conversionRate: 280.45, + currentCurrency: 'usd', + nativeCurrency: 'ETH', + preferences: { + showFiatInTestnets: false, + }, + provider: { + type: 'rinkeby', + }, + }, + }, + expected: { conversionRate: 280.45, currentCurrency: 'usd', nativeCurrency: 'ETH', + hideFiat: true, }, - } + }, + // Test # 3 + { + comment: 'should return correct props when not in mainnet and showFiatInTestnets is true', + mockState: { + metamask: { + conversionRate: 280.45, + currentCurrency: 'usd', + nativeCurrency: 'ETH', + preferences: { + showFiatInTestnets: true, + }, + provider: { + type: 'rinkeby', + }, + }, + }, + expected: { + conversionRate: 280.45, + currentCurrency: 'usd', + nativeCurrency: 'ETH', + hideFiat: false, + }, + }, + // Test # 4 + { + comment: 'should return correct props when in mainnet and showFiatInTestnets is true', + mockState: { + metamask: { + conversionRate: 280.45, + currentCurrency: 'usd', + nativeCurrency: 'ETH', + preferences: { + showFiatInTestnets: true, + }, + provider: { + type: 'mainnet', + }, + }, + }, + expected: { + conversionRate: 280.45, + currentCurrency: 'usd', + nativeCurrency: 'ETH', + hideFiat: false, + }, + }, + ] - assert.deepEqual(mapStateToProps(mockState), { - conversionRate: 280.45, - currentCurrency: 'usd', - nativeCurrency: 'ETH', - }) + tests.forEach(({ mockState, expected, comment }) => { + it(comment, () => assert.deepEqual(mapStateToProps(mockState), expected)) }) }) describe('mergeProps()', () => { - it('should return the correct props', () => { - 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, - nativeSuffix: 'ETH', - fiatSuffix: 'USD', - }) + const tests = [ + // Test # 1 + { + comment: 'should return the correct props', + mock: { + stateProps: { + conversionRate: 280.45, + currentCurrency: 'usd', + nativeCurrency: 'ETH', + }, + dispatchProps: {}, + ownProps: {}, + }, + expected: { + conversionRate: 280.45, + currentCurrency: 'usd', + nativeCurrency: 'ETH', + // useFiat: true, + nativeSuffix: 'ETH', + fiatSuffix: 'USD', + }, + }, + // Test # 1 + { + comment: 'should return the correct props when useFiat is true', + mock: { + stateProps: { + conversionRate: 280.45, + currentCurrency: 'usd', + nativeCurrency: 'ETH', + }, + dispatchProps: {}, + ownProps: { useFiat: true }, + }, + expected: { + conversionRate: 280.45, + currentCurrency: 'usd', + nativeCurrency: 'ETH', + useFiat: true, + nativeSuffix: 'ETH', + fiatSuffix: 'USD', + }, + }, + ] - assert.deepEqual(mergeProps(mockStateProps, mockDispatchProps, {}), { - conversionRate: 280.45, - currentCurrency: 'usd', - nativeCurrency: 'ETH', - nativeSuffix: 'ETH', - fiatSuffix: 'USD', + tests.forEach(({ mock: { stateProps, dispatchProps, ownProps }, expected, comment }) => { + it(comment, () => { + assert.deepEqual(mergeProps(stateProps, dispatchProps, ownProps), expected) }) }) }) diff --git a/ui/app/components/gas-customization/gas-modal-page-container/gas-modal-page-container.container.js b/ui/app/components/gas-customization/gas-modal-page-container/gas-modal-page-container.container.js index 1671f95fa..6692fb363 100644 --- a/ui/app/components/gas-customization/gas-modal-page-container/gas-modal-page-container.container.js +++ b/ui/app/components/gas-customization/gas-modal-page-container/gas-modal-page-container.container.js @@ -63,6 +63,7 @@ import { } from '../../send/send.utils' import { addHexPrefix } from 'ethereumjs-util' import { getAdjacentGasPrices, extrapolateY } from '../gas-price-chart/gas-price-chart.utils' +import {getIsMainnet, preferencesSelector} from '../../../selectors' const mapStateToProps = (state, ownProps) => { const { transaction = {} } = ownProps @@ -91,6 +92,10 @@ const mapStateToProps = (state, ownProps) => { const estimatedTimes = getEstimatedGasTimes(state) const balance = getCurrentEthBalance(state) + const { showFiatInTestnets } = preferencesSelector(state) + const isMainnet = getIsMainnet(state) + const showFiat = Boolean(isMainnet || showFiatInTestnets) + const insufficientBalance = !isBalanceSufficient({ amount: value, gasTotal, @@ -124,7 +129,7 @@ const mapStateToProps = (state, ownProps) => { infoRowProps: { originalTotalFiat: addHexWEIsToRenderableFiat(value, gasTotal, currentCurrency, conversionRate), originalTotalEth: addHexWEIsToRenderableEth(value, gasTotal), - newTotalFiat, + newTotalFiat: showFiat ? newTotalFiat : '', newTotalEth: addHexWEIsToRenderableEth(value, customGasTotal), transactionFee: addHexWEIsToRenderableEth('0x0', customGasTotal), sendAmount: addHexWEIsToRenderableEth(value, '0x0'), diff --git a/ui/app/components/gas-customization/gas-modal-page-container/tests/gas-modal-page-container-container.test.js b/ui/app/components/gas-customization/gas-modal-page-container/tests/gas-modal-page-container-container.test.js index 077ec471d..fb6a01fff 100644 --- a/ui/app/components/gas-customization/gas-modal-page-container/tests/gas-modal-page-container-container.test.js +++ b/ui/app/components/gas-customization/gas-modal-page-container/tests/gas-modal-page-container-container.test.js @@ -71,6 +71,12 @@ describe('gas-modal-page-container container', () => { }, currentCurrency: 'abc', conversionRate: 50, + preferences: { + showFiatInTestnets: false, + }, + provider: { + type: 'mainnet', + }, }, gas: { basicEstimates: { @@ -152,6 +158,63 @@ describe('gas-modal-page-container container', () => { }), expectedResult: Object.assign({}, baseExpectedResult, { isSpeedUp: true }), }, + { + mockState: Object.assign({}, baseMockState, { + metamask: { + ...baseMockState.metamask, + preferences: { + ...baseMockState.metamask.preferences, + showFiatInTestnets: false, + }, + provider: { + ...baseMockState.metamask.provider, + type: 'rinkeby', + }, + }, + }), + mockOwnProps: baseMockOwnProps, + expectedResult: { + ...baseExpectedResult, + infoRowProps: { + ...baseExpectedResult.infoRowProps, + newTotalFiat: '', + }, + }, + }, + { + mockState: Object.assign({}, baseMockState, { + metamask: { + ...baseMockState.metamask, + preferences: { + ...baseMockState.metamask.preferences, + showFiatInTestnets: true, + }, + provider: { + ...baseMockState.metamask.provider, + type: 'rinkeby', + }, + }, + }), + mockOwnProps: baseMockOwnProps, + expectedResult: baseExpectedResult, + }, + { + mockState: Object.assign({}, baseMockState, { + metamask: { + ...baseMockState.metamask, + preferences: { + ...baseMockState.metamask.preferences, + showFiatInTestnets: true, + }, + provider: { + ...baseMockState.metamask.provider, + type: 'mainnet', + }, + }, + }), + mockOwnProps: baseMockOwnProps, + expectedResult: baseExpectedResult, + }, ] let result 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 f3d17e105..3650dc869 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 @@ -86,6 +86,7 @@ export default class ConfirmTransactionBase extends Component { warning: PropTypes.string, advancedInlineGasShown: PropTypes.bool, insufficientBalance: PropTypes.bool, + hideFiatConversion: PropTypes.bool, } state = { @@ -174,6 +175,7 @@ export default class ConfirmTransactionBase extends Component { customGas, insufficientBalance, updateGasAndCalculate, + hideFiatConversion, } = this.props if (hideDetails) { @@ -190,6 +192,7 @@ export default class ConfirmTransactionBase extends Component { headerText="Edit" headerTextClassName="confirm-detail-row__header-text--edit" onHeaderClick={() => this.handleEditGas()} + secondaryText={hideFiatConversion ? this.context.t('noConversionRateAvailable') : ''} /> {advancedInlineGasShown ? <AdvancedGasInputs @@ -209,7 +212,7 @@ export default class ConfirmTransactionBase extends Component { label="Total" value={hexTransactionTotal} primaryText={primaryTotalTextOverride} - secondaryText={secondaryTotalTextOverride} + secondaryText={hideFiatConversion ? this.context.t('noConversionRateAvailable') : secondaryTotalTextOverride} headerText="Amount + Gas Fee" headerTextClassName="confirm-detail-row__header-text--total" primaryValueTextColor="#2f9ae0" diff --git a/ui/app/components/pages/confirm-transaction-base/confirm-transaction-base.container.js b/ui/app/components/pages/confirm-transaction-base/confirm-transaction-base.container.js index e11b35459..2a8033c8f 100644 --- a/ui/app/components/pages/confirm-transaction-base/confirm-transaction-base.container.js +++ b/ui/app/components/pages/confirm-transaction-base/confirm-transaction-base.container.js @@ -18,7 +18,7 @@ import { isBalanceSufficient, calcGasTotal } from '../../send/send.utils' import { conversionGreaterThan } from '../../../conversion-util' import { MIN_GAS_LIMIT_DEC } from '../../send/send.constants' import { checksumAddress, addressSlicer, valuesFor } from '../../../util' -import { getMetaMaskAccounts, getAdvancedInlineGasShown } from '../../../selectors' +import {getMetaMaskAccounts, getAdvancedInlineGasShown, preferencesSelector, getIsMainnet} from '../../../selectors' const casedContractMap = Object.keys(contractMap).reduce((acc, base) => { return { @@ -29,6 +29,8 @@ const casedContractMap = Object.keys(contractMap).reduce((acc, base) => { const mapStateToProps = (state, props) => { const { toAddress: propsToAddress } = props + const { showFiatInTestnets } = preferencesSelector(state) + const isMainnet = getIsMainnet(state) const { confirmTransaction, metamask, gas } = state const { ethTransactionAmount, @@ -135,6 +137,8 @@ const mapStateToProps = (state, props) => { }, advancedInlineGasShown: getAdvancedInlineGasShown(state), insufficientBalance, + hideSubtitle: (!isMainnet && !showFiatInTestnets), + hideFiatConversion: (!isMainnet && !showFiatInTestnets), } } 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 661cbd1dc..01621c354 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 @@ -63,6 +63,8 @@ export default class SettingsTab extends PureComponent { setAdvancedInlineGasFeatureFlag: PropTypes.func, advancedInlineGas: PropTypes.bool, mobileSync: PropTypes.bool, + showFiatInTestnets: PropTypes.bool, + setShowFiatConversionOnTestnetsPreference: PropTypes.func.isRequired, } state = { @@ -529,6 +531,35 @@ export default class SettingsTab extends PureComponent { ) } + renderShowConversionInTestnets () { + const { t } = this.context + const { + showFiatInTestnets, + setShowFiatConversionOnTestnetsPreference, + } = this.props + + return ( + <div className="settings-page__content-row"> + <div className="settings-page__content-item"> + <span>{ t('showFiatConversionInTestnets') }</span> + <div className="settings-page__content-description"> + { t('showFiatConversionInTestnetsDescription') } + </div> + </div> + <div className="settings-page__content-item"> + <div className="settings-page__content-item-col"> + <ToggleButton + value={showFiatInTestnets} + onToggle={value => setShowFiatConversionOnTestnetsPreference(!value)} + activeLabel="" + inactiveLabel="" + /> + </div> + </div> + </div> + ) + } + renderPrivacyOptIn () { const { t } = this.context const { privacyMode, setPrivacyMode } = this.props @@ -563,6 +594,7 @@ export default class SettingsTab extends PureComponent { { warning && <div className="settings-tab__error">{ warning }</div> } { this.renderCurrentConversion() } { this.renderUsePrimaryCurrencyOptions() } + { this.renderShowConversionInTestnets() } { this.renderCurrentLocale() } { this.renderNewRpcUrl() } { this.renderStateLogs() } 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 72458ba33..5cb9a9aae 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 @@ -12,6 +12,7 @@ import { setFeatureFlag, showModal, setUseNativeCurrencyAsPrimaryCurrencyPreference, + setShowFiatConversionOnTestnetsPreference, } from '../../../../actions' import { preferencesSelector } from '../../../../selectors' @@ -31,7 +32,7 @@ const mapStateToProps = state => { provider = {}, currentLocale, } = metamask - const { useNativeCurrencyAsPrimaryCurrency } = preferencesSelector(state) + const { useNativeCurrencyAsPrimaryCurrency, showFiatInTestnets } = preferencesSelector(state) return { warning, @@ -46,6 +47,7 @@ const mapStateToProps = state => { provider, useNativeCurrencyAsPrimaryCurrency, mobileSync, + showFiatInTestnets, } } @@ -64,6 +66,9 @@ const mapDispatchToProps = dispatch => { setUseNativeCurrencyAsPrimaryCurrencyPreference: value => { return dispatch(setUseNativeCurrencyAsPrimaryCurrencyPreference(value)) }, + setShowFiatConversionOnTestnetsPreference: value => { + return dispatch(setShowFiatConversionOnTestnetsPreference(value)) + }, showClearApprovalModal: () => dispatch(showModal({ name: 'CLEAR_APPROVED_ORIGINS' })), } } diff --git a/ui/app/components/send/account-list-item/account-list-item.component.js b/ui/app/components/send/account-list-item/account-list-item.component.js index 665383e58..0420af46b 100644 --- a/ui/app/components/send/account-list-item/account-list-item.component.js +++ b/ui/app/components/send/account-list-item/account-list-item.component.js @@ -19,8 +19,13 @@ export default class AccountListItem extends Component { handleClick: PropTypes.func, icon: PropTypes.node, balanceIsCached: PropTypes.bool, + showFiat: PropTypes.bool, }; + static defaultProps = { + showFiat: true, + } + static contextTypes = { t: PropTypes.func, }; @@ -34,6 +39,7 @@ export default class AccountListItem extends Component { handleClick, icon = null, balanceIsCached, + showFiat, } = this.props const { name, address, balance } = account || {} @@ -83,11 +89,15 @@ export default class AccountListItem extends Component { balanceIsCached ? <span className="account-list-item__cached-star">*</span> : null } </div> - <UserPreferencedCurrencyDisplay - type={SECONDARY} - value={balance} - hideTitle={true} - /> + { + showFiat && ( + <UserPreferencedCurrencyDisplay + type={SECONDARY} + value={balance} + hideTitle={true} + /> + ) + } </div> </Tooltip> ) 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 03a60be67..c045ef14f 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 @@ -5,17 +5,23 @@ import { getNativeCurrency, } from '../send.selectors.js' import { + getIsMainnet, isBalanceCached, + preferencesSelector, } from '../../../selectors' import AccountListItem from './account-list-item.component' export default connect(mapStateToProps)(AccountListItem) function mapStateToProps (state) { + const { showFiatInTestnets } = preferencesSelector(state) + const isMainnet = getIsMainnet(state) + return { conversionRate: getConversionRate(state), currentCurrency: getCurrentCurrency(state), nativeCurrency: getNativeCurrency(state), balanceIsCached: isBalanceCached(state), + showFiat: (isMainnet || !!showFiatInTestnets), } } 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 f2ddb73c0..2bd2ce0c5 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 @@ -126,9 +126,23 @@ describe('AccountListItem Component', function () { ) }) + it('should only render one CurrencyDisplay if showFiat is false', () => { + wrapper.setProps({ showFiat: false, displayBalance: true }) + assert.equal(wrapper.find(UserPreferencedCurrencyDisplay).length, 1) + assert.deepEqual( + wrapper.find(UserPreferencedCurrencyDisplay).at(0).props(), + { + type: 'PRIMARY', + value: 'mockBalance', + hideTitle: true, + } + ) + }) + it('should not render a CurrencyDisplay if displayBalance is false', () => { wrapper.setProps({ displayBalance: false }) assert.equal(wrapper.find(UserPreferencedCurrencyDisplay).length, 0) }) + }) }) 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 8c22bc8f8..662880aa0 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 @@ -11,12 +11,16 @@ proxyquire('../account-list-item.container.js', { }, }, '../send.selectors.js': { - getConversionRate: (s) => `mockConversionRate:${s}`, - getCurrentCurrency: (s) => `mockCurrentCurrency:${s}`, - getNativeCurrency: (s) => `mockNativeCurrency:${s}`, + getConversionRate: () => `mockConversionRate`, + getCurrentCurrency: () => `mockCurrentCurrency`, + getNativeCurrency: () => `mockNativeCurrency`, }, '../../../selectors.js': { - isBalanceCached: (s) => `mockBalanceIsCached:${s}`, + isBalanceCached: () => `mockBalanceIsCached`, + preferencesSelector: ({ showFiatInTestnets }) => ({ + showFiatInTestnets, + }), + getIsMainnet: ({ isMainnet }) => isMainnet, }, }) @@ -25,11 +29,42 @@ describe('account-list-item container', () => { describe('mapStateToProps()', () => { it('should map the correct properties to props', () => { - assert.deepEqual(mapStateToProps('mockState'), { - conversionRate: 'mockConversionRate:mockState', - currentCurrency: 'mockCurrentCurrency:mockState', - nativeCurrency: 'mockNativeCurrency:mockState', - balanceIsCached: 'mockBalanceIsCached:mockState', + assert.deepEqual(mapStateToProps({ isMainnet: true, showFiatInTestnets: false }), { + conversionRate: 'mockConversionRate', + currentCurrency: 'mockCurrentCurrency', + nativeCurrency: 'mockNativeCurrency', + balanceIsCached: 'mockBalanceIsCached', + showFiat: true, + }) + }) + + it('should map the correct properties to props when in mainnet and showFiatInTestnet is true', () => { + assert.deepEqual(mapStateToProps({ isMainnet: true, showFiatInTestnets: true }), { + conversionRate: 'mockConversionRate', + currentCurrency: 'mockCurrentCurrency', + nativeCurrency: 'mockNativeCurrency', + balanceIsCached: 'mockBalanceIsCached', + showFiat: true, + }) + }) + + it('should map the correct properties to props when not in mainnet and showFiatInTestnet is true', () => { + assert.deepEqual(mapStateToProps({ isMainnet: false, showFiatInTestnets: true }), { + conversionRate: 'mockConversionRate', + currentCurrency: 'mockCurrentCurrency', + nativeCurrency: 'mockNativeCurrency', + balanceIsCached: 'mockBalanceIsCached', + showFiat: true, + }) + }) + + it('should map the correct properties to props when not in mainnet and showFiatInTestnet is false', () => { + assert.deepEqual(mapStateToProps({ isMainnet: false, showFiatInTestnets: false }), { + conversionRate: 'mockConversionRate', + currentCurrency: 'mockCurrentCurrency', + nativeCurrency: 'mockNativeCurrency', + balanceIsCached: 'mockBalanceIsCached', + showFiat: false, }) }) 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 2dacb9bc4..881101880 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 @@ -159,6 +159,48 @@ describe('TokenInput Component', () => { assert.equal(wrapper.find('.unit-input__input').props().value, '1') assert.equal(wrapper.find('.currency-display-component').text(), '$462.12USD') }) + + it('should render properly with a token value for fiat, but hideConversion is true', () => { + const mockStore = { + metamask: { + currentCurrency: 'usd', + conversionRate: 231.06, + }, + } + const store = configureMockStore()(mockStore) + + const wrapper = mount( + <Provider store={store}> + <TokenInput + value="2710" + selectedToken={{ + address: '0x1', + decimals: '4', + symbol: 'ABC', + }} + suffix="ABC" + selectedTokenExchangeRate={2} + showFiat + hideConversion + /> + </Provider>, + { + context: { t }, + childContextTypes: { + t: PropTypes.func, + }, + }, + ) + + assert.ok(wrapper) + const tokenInputInstance = wrapper.find(TokenInput).at(0).instance() + assert.equal(tokenInputInstance.state.decimalValue, 1) + assert.equal(tokenInputInstance.state.hexValue, '2710') + 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-input__conversion-component').text(), 'translate noConversionRateAvailable') + }) }) describe('handling actions', () => { diff --git a/ui/app/components/token-input/tests/token-input.container.test.js b/ui/app/components/token-input/tests/token-input.container.test.js index d73bc9a94..2b1c102c8 100644 --- a/ui/app/components/token-input/tests/token-input.container.test.js +++ b/ui/app/components/token-input/tests/token-input.container.test.js @@ -29,6 +29,12 @@ describe('TokenInput container', () => { selectedTokenAddress: '0x1', contractExchangeRates: {}, send: {}, + preferences: { + showFiatInTestnets: false, + }, + provider: { + type: 'mainnet', + }, }, } @@ -40,6 +46,7 @@ describe('TokenInput container', () => { symbol: 'ABC', }, selectedTokenExchangeRate: 0, + hideConversion: false, }) }) @@ -59,6 +66,12 @@ describe('TokenInput container', () => { send: { token: { address: 'test' }, }, + preferences: { + showFiatInTestnets: false, + }, + provider: { + type: 'mainnet', + }, }, } @@ -68,6 +81,7 @@ describe('TokenInput container', () => { address: 'test', }, selectedTokenExchangeRate: 0, + hideConversion: false, }) }) @@ -87,6 +101,12 @@ describe('TokenInput container', () => { '0x1': 5, }, send: {}, + preferences: { + showFiatInTestnets: false, + }, + provider: { + type: 'mainnet', + }, }, } @@ -98,6 +118,112 @@ describe('TokenInput container', () => { symbol: 'ABC', }, selectedTokenExchangeRate: 5, + hideConversion: false, + }) + }) + + it('should return the correct props when not in mainnet and showFiatInTestnets is false', () => { + const mockState = { + metamask: { + currentCurrency: 'usd', + tokens: [ + { + address: '0x1', + decimals: '4', + symbol: 'ABC', + }, + ], + selectedTokenAddress: '0x1', + contractExchangeRates: {}, + send: {}, + preferences: { + showFiatInTestnets: false, + }, + provider: { + type: 'rinkeby', + }, + }, + } + + assert.deepEqual(mapStateToProps(mockState), { + currentCurrency: 'usd', + selectedToken: { + address: '0x1', + decimals: '4', + symbol: 'ABC', + }, + selectedTokenExchangeRate: 0, + hideConversion: true, + }) + }) + + it('should return the correct props when not in mainnet and showFiatInTestnets is true', () => { + const mockState = { + metamask: { + currentCurrency: 'usd', + tokens: [ + { + address: '0x1', + decimals: '4', + symbol: 'ABC', + }, + ], + selectedTokenAddress: '0x1', + contractExchangeRates: {}, + send: {}, + preferences: { + showFiatInTestnets: true, + }, + provider: { + type: 'rinkeby', + }, + }, + } + + assert.deepEqual(mapStateToProps(mockState), { + currentCurrency: 'usd', + selectedToken: { + address: '0x1', + decimals: '4', + symbol: 'ABC', + }, + selectedTokenExchangeRate: 0, + hideConversion: false, + }) + }) + + it('should return the correct props when in mainnet and showFiatInTestnets is true', () => { + const mockState = { + metamask: { + currentCurrency: 'usd', + tokens: [ + { + address: '0x1', + decimals: '4', + symbol: 'ABC', + }, + ], + selectedTokenAddress: '0x1', + contractExchangeRates: {}, + send: {}, + preferences: { + showFiatInTestnets: true, + }, + provider: { + type: 'mainnet', + }, + }, + } + + assert.deepEqual(mapStateToProps(mockState), { + currentCurrency: 'usd', + selectedToken: { + address: '0x1', + decimals: '4', + symbol: 'ABC', + }, + selectedTokenExchangeRate: 0, + hideConversion: false, }) }) }) diff --git a/ui/app/components/token-input/token-input.component.js b/ui/app/components/token-input/token-input.component.js index 10fa1151e..398b762ec 100644 --- a/ui/app/components/token-input/token-input.component.js +++ b/ui/app/components/token-input/token-input.component.js @@ -24,6 +24,7 @@ export default class TokenInput extends PureComponent { value: PropTypes.string, suffix: PropTypes.string, showFiat: PropTypes.bool, + hideConversion: PropTypes.bool, selectedToken: PropTypes.object, selectedTokenExchangeRate: PropTypes.number, } @@ -81,10 +82,18 @@ export default class TokenInput extends PureComponent { } renderConversionComponent () { - const { selectedTokenExchangeRate, showFiat, currentCurrency } = this.props + const { selectedTokenExchangeRate, showFiat, currentCurrency, hideConversion } = this.props const { decimalValue } = this.state let currency, numberOfDecimals + if (hideConversion) { + return ( + <div className="currency-input__conversion-component"> + { this.context.t('noConversionRateAvailable') } + </div> + ) + } + if (showFiat) { // Display Fiat currency = currentCurrency diff --git a/ui/app/components/token-input/token-input.container.js b/ui/app/components/token-input/token-input.container.js index ec233b1b8..a00d200f7 100644 --- a/ui/app/components/token-input/token-input.container.js +++ b/ui/app/components/token-input/token-input.container.js @@ -1,14 +1,17 @@ import { connect } from 'react-redux' import TokenInput from './token-input.component' -import { getSelectedToken, getSelectedTokenExchangeRate } from '../../selectors' +import {getIsMainnet, getSelectedToken, getSelectedTokenExchangeRate, preferencesSelector} from '../../selectors' const mapStateToProps = state => { const { metamask: { currentCurrency } } = state + const { showFiatInTestnets } = preferencesSelector(state) + const isMainnet = getIsMainnet(state) return { currentCurrency, selectedToken: getSelectedToken(state), selectedTokenExchangeRate: getSelectedTokenExchangeRate(state), + hideConversion: (!isMainnet && !showFiatInTestnets), } } diff --git a/ui/app/components/transaction-breakdown/transaction-breakdown.component.js b/ui/app/components/transaction-breakdown/transaction-breakdown.component.js index 141e16e17..26f52317d 100644 --- a/ui/app/components/transaction-breakdown/transaction-breakdown.component.js +++ b/ui/app/components/transaction-breakdown/transaction-breakdown.component.js @@ -18,15 +18,17 @@ export default class TransactionBreakdown extends PureComponent { transaction: PropTypes.object, className: PropTypes.string, nativeCurrency: PropTypes.string.isRequired, + showFiat: PropTypes.bool, } static defaultProps = { transaction: {}, + showFiat: true, } render () { const { t } = this.context - const { transaction, className, nativeCurrency } = this.props + const { transaction, className, nativeCurrency, showFiat } = this.props const { txParams: { gas, gasPrice, value } = {}, txReceipt: { gasUsed } = {} } = transaction const gasLimit = typeof gasUsed === 'string' ? gasUsed : gas @@ -84,11 +86,15 @@ export default class TransactionBreakdown extends PureComponent { type={PRIMARY} value={totalInHex} /> - <UserPreferencedCurrencyDisplay - className="transaction-breakdown__value" - type={SECONDARY} - value={totalInHex} - /> + { + showFiat && ( + <UserPreferencedCurrencyDisplay + className="transaction-breakdown__value" + type={SECONDARY} + value={totalInHex} + /> + ) + } </div> </TransactionBreakdownRow> </div> diff --git a/ui/app/components/transaction-breakdown/transaction-breakdown.container.js b/ui/app/components/transaction-breakdown/transaction-breakdown.container.js index ed2708e03..919187b6f 100644 --- a/ui/app/components/transaction-breakdown/transaction-breakdown.container.js +++ b/ui/app/components/transaction-breakdown/transaction-breakdown.container.js @@ -1,10 +1,14 @@ import { connect } from 'react-redux' import TransactionBreakdown from './transaction-breakdown.component' -import { getNativeCurrency } from '../../selectors' +import {getIsMainnet, getNativeCurrency, preferencesSelector} from '../../selectors' const mapStateToProps = (state) => { + const { showFiatInTestnets } = preferencesSelector(state) + const isMainnet = getIsMainnet(state) + return { nativeCurrency: getNativeCurrency(state), + showFiat: (isMainnet || !!showFiatInTestnets), } } 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 ecd8b4cef..29d3a7b1f 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 @@ -24,6 +24,7 @@ export default class TransactionListItem extends PureComponent { showCancelModal: PropTypes.func, showCancel: PropTypes.bool, showRetry: PropTypes.bool, + showFiat: PropTypes.bool, token: PropTypes.object, tokenData: PropTypes.object, transaction: PropTypes.object, @@ -33,6 +34,10 @@ export default class TransactionListItem extends PureComponent { fetchGasEstimates: PropTypes.func, } + static defaultProps = { + showFiat: true, + } + state = { showTransactionDetails: false, } @@ -115,9 +120,9 @@ export default class TransactionListItem extends PureComponent { } renderSecondaryCurrency () { - const { token, value } = this.props + const { token, value, showFiat } = this.props - return token + return token || !showFiat ? null : ( <UserPreferencedCurrencyDisplay 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 45777057c..93a82849e 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 @@ -14,11 +14,16 @@ import { setCustomGasPriceForRetry, setCustomGasLimit, } from '../../ducks/gas.duck' +import {getIsMainnet, preferencesSelector} from '../../selectors' const mapStateToProps = state => { const { metamask: { knownMethodData } } = state + const { showFiatInTestnets } = preferencesSelector(state) + const isMainnet = getIsMainnet(state) + return { knownMethodData, + showFiat: (isMainnet || !!showFiatInTestnets), } } diff --git a/ui/app/components/transaction-view-balance/transaction-view-balance.component.js b/ui/app/components/transaction-view-balance/transaction-view-balance.component.js index bd6b4bdb6..b16e04f4f 100644 --- a/ui/app/components/transaction-view-balance/transaction-view-balance.component.js +++ b/ui/app/components/transaction-view-balance/transaction-view-balance.component.js @@ -22,10 +22,15 @@ export default class TransactionViewBalance extends PureComponent { balance: PropTypes.string, assetImage: PropTypes.string, balanceIsCached: PropTypes.bool, + showFiat: PropTypes.bool, + } + + static defaultProps = { + showFiat: true, } renderBalance () { - const { selectedToken, balance, balanceIsCached } = this.props + const { selectedToken, balance, balanceIsCached, showFiat } = this.props return selectedToken ? ( @@ -53,16 +58,20 @@ export default class TransactionViewBalance extends PureComponent { balanceIsCached ? <span className="transaction-view-balance__cached-star">*</span> : null } </div> - <UserPreferencedCurrencyDisplay - className={classnames({ - 'transaction-view-balance__cached-secondary-balance': balanceIsCached, - 'transaction-view-balance__secondary-balance': !balanceIsCached, - })} - value={balance} - type={SECONDARY} - ethNumberOfDecimals={4} - hideTitle={true} - /> + { + showFiat && ( + <UserPreferencedCurrencyDisplay + className={classnames({ + 'transaction-view-balance__cached-secondary-balance': balanceIsCached, + 'transaction-view-balance__secondary-balance': !balanceIsCached, + })} + value={balance} + type={SECONDARY} + ethNumberOfDecimals={4} + hideTitle={true} + /> + ) + } </div> </Tooltip> ) 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 354db5ae1..df6d287eb 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 @@ -9,10 +9,14 @@ import { getSelectedTokenAssetImage, getMetaMaskAccounts, isBalanceCached, + preferencesSelector, + getIsMainnet, } from '../../selectors' import { showModal } from '../../actions' const mapStateToProps = state => { + const { showFiatInTestnets } = preferencesSelector(state) + const isMainnet = getIsMainnet(state) const selectedAddress = getSelectedAddress(state) const { metamask: { network } } = state const accounts = getMetaMaskAccounts(state) @@ -26,6 +30,7 @@ const mapStateToProps = state => { nativeCurrency: getNativeCurrency(state), assetImage: getSelectedTokenAssetImage(state), balanceIsCached: isBalanceCached(state), + showFiat: (isMainnet || !!showFiatInTestnets), } } 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 ba1c23d83..88d63baae 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 @@ -21,6 +21,10 @@ describe('UserPreferencedCurrencyDisplay container', () => { nativeCurrency: 'ETH', preferences: { useNativeCurrencyAsPrimaryCurrency: true, + showFiatInTestnets: false, + }, + provider: { + type: 'mainnet', }, }, } @@ -28,6 +32,30 @@ describe('UserPreferencedCurrencyDisplay container', () => { assert.deepEqual(mapStateToProps(mockState), { nativeCurrency: 'ETH', useNativeCurrencyAsPrimaryCurrency: true, + isMainnet: true, + showFiatInTestnets: false, + }) + }) + + it('should return the correct props when not in mainnet and showFiatInTestnets is true', () => { + const mockState = { + metamask: { + nativeCurrency: 'ETH', + preferences: { + useNativeCurrencyAsPrimaryCurrency: true, + showFiatInTestnets: true, + }, + provider: { + type: 'rinkeby', + }, + }, + } + + assert.deepEqual(mapStateToProps(mockState), { + nativeCurrency: 'ETH', + useNativeCurrencyAsPrimaryCurrency: true, + isMainnet: false, + showFiatInTestnets: true, }) }) }) @@ -41,6 +69,8 @@ describe('UserPreferencedCurrencyDisplay container', () => { stateProps: { useNativeCurrencyAsPrimaryCurrency: true, nativeCurrency: 'ETH', + isMainnet: true, + showFiatInTestnets: false, }, ownProps: { type: 'PRIMARY', @@ -56,6 +86,8 @@ describe('UserPreferencedCurrencyDisplay container', () => { stateProps: { useNativeCurrencyAsPrimaryCurrency: false, nativeCurrency: 'ETH', + isMainnet: true, + showFiatInTestnets: false, }, ownProps: { type: 'PRIMARY', @@ -71,6 +103,8 @@ describe('UserPreferencedCurrencyDisplay container', () => { stateProps: { useNativeCurrencyAsPrimaryCurrency: true, nativeCurrency: 'ETH', + isMainnet: true, + showFiatInTestnets: false, }, ownProps: { type: 'SECONDARY', @@ -88,6 +122,8 @@ describe('UserPreferencedCurrencyDisplay container', () => { stateProps: { useNativeCurrencyAsPrimaryCurrency: false, nativeCurrency: 'ETH', + isMainnet: true, + showFiatInTestnets: false, }, ownProps: { type: 'SECONDARY', @@ -103,6 +139,57 @@ describe('UserPreferencedCurrencyDisplay container', () => { prefix: 'b', }, }, + { + stateProps: { + useNativeCurrencyAsPrimaryCurrency: false, + nativeCurrency: 'ETH', + isMainnet: false, + showFiatInTestnets: false, + }, + ownProps: { + type: 'PRIMARY', + }, + result: { + currency: 'ETH', + nativeCurrency: 'ETH', + numberOfDecimals: 6, + prefix: undefined, + }, + }, + { + stateProps: { + useNativeCurrencyAsPrimaryCurrency: false, + nativeCurrency: 'ETH', + isMainnet: false, + showFiatInTestnets: true, + }, + ownProps: { + type: 'PRIMARY', + }, + result: { + currency: undefined, + nativeCurrency: 'ETH', + numberOfDecimals: 2, + prefix: undefined, + }, + }, + { + stateProps: { + useNativeCurrencyAsPrimaryCurrency: false, + nativeCurrency: 'ETH', + isMainnet: true, + showFiatInTestnets: true, + }, + ownProps: { + type: 'PRIMARY', + }, + result: { + currency: undefined, + nativeCurrency: 'ETH', + numberOfDecimals: 2, + prefix: undefined, + }, + }, ] tests.forEach(({ stateProps, ownProps, result }) => { 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 7999301ad..3c5bd0f21 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 @@ -1,19 +1,26 @@ import { connect } from 'react-redux' import UserPreferencedCurrencyDisplay from './user-preferenced-currency-display.component' -import { preferencesSelector } from '../../selectors' +import { preferencesSelector, getIsMainnet } from '../../selectors' import { ETH, PRIMARY, SECONDARY } from '../../constants/common' const mapStateToProps = (state, ownProps) => { - const { useNativeCurrencyAsPrimaryCurrency } = preferencesSelector(state) + const { + useNativeCurrencyAsPrimaryCurrency, + showFiatInTestnets, + } = preferencesSelector(state) + + const isMainnet = getIsMainnet(state) return { useNativeCurrencyAsPrimaryCurrency, + showFiatInTestnets, + isMainnet, nativeCurrency: state.metamask.nativeCurrency, } } const mergeProps = (stateProps, dispatchProps, ownProps) => { - const { useNativeCurrencyAsPrimaryCurrency, nativeCurrency, ...restStateProps } = stateProps + const { useNativeCurrencyAsPrimaryCurrency, showFiatInTestnets, isMainnet, nativeCurrency, ...restStateProps } = stateProps const { type, numberOfDecimals: propsNumberOfDecimals, @@ -40,6 +47,12 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => { prefix = propsPrefix || fiatPrefix } + if (!isMainnet && !showFiatInTestnets) { + currency = nativeCurrency || ETH + numberOfDecimals = propsNumberOfDecimals || ethNumberOfDecimals || 6 + prefix = propsPrefix || ethPrefix + } + return { ...restStateProps, ...dispatchProps, diff --git a/ui/app/constants/common.js b/ui/app/constants/common.js index 4ff4dc837..c6e566b8b 100644 --- a/ui/app/constants/common.js +++ b/ui/app/constants/common.js @@ -4,3 +4,7 @@ export const WEI = 'WEI' export const PRIMARY = 'PRIMARY' export const SECONDARY = 'SECONDARY' + +export const NETWORK_TYPES = { + MAINNET: 'mainnet', +} diff --git a/ui/app/reducers/metamask.js b/ui/app/reducers/metamask.js index 0de56ddbb..fb0fd7130 100644 --- a/ui/app/reducers/metamask.js +++ b/ui/app/reducers/metamask.js @@ -53,6 +53,7 @@ function reduceMetamask (state, action) { currentLocale: '', preferences: { useNativeCurrencyAsPrimaryCurrency: true, + showFiatInTestnets: false, }, completedOnboarding: false, knownMethodData: {}, @@ -375,7 +376,10 @@ function reduceMetamask (state, action) { case actions.UPDATE_PREFERENCES: { return extend(metamaskState, { - preferences: { ...action.payload }, + preferences: { + ...metamaskState.preferences, + ...action.payload, + }, }) } diff --git a/ui/app/selectors.js b/ui/app/selectors.js index 976342455..a36671b42 100644 --- a/ui/app/selectors.js +++ b/ui/app/selectors.js @@ -1,3 +1,5 @@ +import {NETWORK_TYPES} from './constants/common' + const abi = require('human-standard-token-abi') import { transactionsSelector, @@ -37,6 +39,7 @@ const selectors = { getNetworkIdentifier, isBalanceCached, getAdvancedInlineGasShown, + getIsMainnet, } module.exports = selectors @@ -228,6 +231,11 @@ function getTotalUnapprovedCount ({ metamask }) { unapprovedTypedMessagesCount } +function getIsMainnet (state) { + const networkType = getNetworkIdentifier(state) + return networkType === NETWORK_TYPES.MAINNET +} + function preferencesSelector ({ metamask }) { return metamask.preferences } diff --git a/ui/app/selectors/custom-gas.js b/ui/app/selectors/custom-gas.js index e4fba5b01..8039c0746 100644 --- a/ui/app/selectors/custom-gas.js +++ b/ui/app/selectors/custom-gas.js @@ -5,7 +5,7 @@ import { conversionGreaterThan, } from '../conversion-util' import { - getCurrentCurrency, + getCurrentCurrency, getIsMainnet, preferencesSelector, } from '../selectors' import { formatCurrency, @@ -225,6 +225,10 @@ function getRenderableBasicEstimateData (state, gasLimit) { if (getBasicGasEstimateLoadingStatus(state)) { return [] } + + const { showFiatInTestnets } = preferencesSelector(state) + const isMainnet = getIsMainnet(state) + const showFiat = (isMainnet || !!showFiatInTestnets) const conversionRate = state.metamask.conversionRate const currentCurrency = getCurrentCurrency(state) const { @@ -243,22 +247,28 @@ function getRenderableBasicEstimateData (state, gasLimit) { return [ { labelKey: 'slow', - feeInPrimaryCurrency: getRenderableConvertedCurrencyFee(safeLow, gasLimit, currentCurrency, conversionRate), - feeInSecondaryCurrency: getRenderableEthFee(safeLow, gasLimit), + feeInPrimaryCurrency: getRenderableEthFee(safeLow, gasLimit), + feeInSecondaryCurrency: showFiat + ? getRenderableConvertedCurrencyFee(safeLow, gasLimit, currentCurrency, conversionRate) + : '', timeEstimate: safeLowWait && getRenderableTimeEstimate(safeLowWait), priceInHexWei: getGasPriceInHexWei(safeLow), }, { labelKey: 'average', - feeInPrimaryCurrency: getRenderableConvertedCurrencyFee(fast, gasLimit, currentCurrency, conversionRate), - feeInSecondaryCurrency: getRenderableEthFee(fast, gasLimit), + feeInPrimaryCurrency: getRenderableEthFee(fast, gasLimit), + feeInSecondaryCurrency: showFiat + ? getRenderableConvertedCurrencyFee(fast, gasLimit, currentCurrency, conversionRate) + : '', timeEstimate: fastWait && getRenderableTimeEstimate(fastWait), priceInHexWei: getGasPriceInHexWei(fast), }, { labelKey: 'fast', - feeInPrimaryCurrency: getRenderableConvertedCurrencyFee(fastest, gasLimit, currentCurrency, conversionRate), - feeInSecondaryCurrency: getRenderableEthFee(fastest, gasLimit), + feeInPrimaryCurrency: getRenderableEthFee(fastest, gasLimit), + feeInSecondaryCurrency: showFiat + ? getRenderableConvertedCurrencyFee(fastest, gasLimit, currentCurrency, conversionRate) + : '', timeEstimate: fastestWait && getRenderableTimeEstimate(fastestWait), priceInHexWei: getGasPriceInHexWei(fastest), }, @@ -269,6 +279,10 @@ function getRenderableEstimateDataForSmallButtonsFromGWEI (state) { if (getBasicGasEstimateLoadingStatus(state)) { return [] } + + const { showFiatInTestnets } = preferencesSelector(state) + const isMainnet = getIsMainnet(state) + const showFiat = (isMainnet || !!showFiatInTestnets) const gasLimit = state.metamask.send.gasLimit || getCustomGasLimit(state) || '0x5208' const conversionRate = state.metamask.conversionRate const currentCurrency = getCurrentCurrency(state) @@ -285,19 +299,25 @@ function getRenderableEstimateDataForSmallButtonsFromGWEI (state) { return [ { labelKey: 'slow', - feeInSecondaryCurrency: getRenderableConvertedCurrencyFee(safeLow, gasLimit, currentCurrency, conversionRate), + feeInSecondaryCurrency: showFiat + ? getRenderableConvertedCurrencyFee(safeLow, gasLimit, currentCurrency, conversionRate) + : '', feeInPrimaryCurrency: getRenderableEthFee(safeLow, gasLimit, NUMBER_OF_DECIMALS_SM_BTNS, true), priceInHexWei: getGasPriceInHexWei(safeLow, true), }, { labelKey: 'average', - feeInSecondaryCurrency: getRenderableConvertedCurrencyFee(fast, gasLimit, currentCurrency, conversionRate), + feeInSecondaryCurrency: showFiat + ? getRenderableConvertedCurrencyFee(fast, gasLimit, currentCurrency, conversionRate) + : '', feeInPrimaryCurrency: getRenderableEthFee(fast, gasLimit, NUMBER_OF_DECIMALS_SM_BTNS, true), priceInHexWei: getGasPriceInHexWei(fast, true), }, { labelKey: 'fast', - feeInSecondaryCurrency: getRenderableConvertedCurrencyFee(fastest, gasLimit, currentCurrency, conversionRate), + feeInSecondaryCurrency: showFiat + ? getRenderableConvertedCurrencyFee(fastest, gasLimit, currentCurrency, conversionRate) + : '', feeInPrimaryCurrency: getRenderableEthFee(fastest, gasLimit, NUMBER_OF_DECIMALS_SM_BTNS, true), priceInHexWei: getGasPriceInHexWei(fastest, true), }, diff --git a/ui/app/selectors/tests/custom-gas.test.js b/ui/app/selectors/tests/custom-gas.test.js index 0776ac4d0..73240d997 100644 --- a/ui/app/selectors/tests/custom-gas.test.js +++ b/ui/app/selectors/tests/custom-gas.test.js @@ -78,22 +78,22 @@ describe('custom-gas selectors', () => { expectedResult: [ { labelKey: 'slow', - feeInPrimaryCurrency: '$0.01', - feeInSecondaryCurrency: '0.0000525 ETH', + feeInSecondaryCurrency: '$0.01', + feeInPrimaryCurrency: '0.0000525 ETH', timeEstimate: '~6 min 36 sec', priceInHexWei: '0x9502f900', }, { labelKey: 'average', - feeInPrimaryCurrency: '$0.03', - feeInSecondaryCurrency: '0.000105 ETH', + feeInSecondaryCurrency: '$0.03', + feeInPrimaryCurrency: '0.000105 ETH', timeEstimate: '~3 min 18 sec', priceInHexWei: '0x12a05f200', }, { labelKey: 'fast', - feeInPrimaryCurrency: '$0.05', - feeInSecondaryCurrency: '0.00021 ETH', + feeInSecondaryCurrency: '$0.05', + feeInPrimaryCurrency: '0.00021 ETH', timeEstimate: '~30 sec', priceInHexWei: '0x2540be400', }, @@ -102,6 +102,12 @@ describe('custom-gas selectors', () => { metamask: { conversionRate: 255.71, currentCurrency: 'usd', + preferences: { + showFiatInTestnets: false, + }, + provider: { + type: 'mainnet', + }, }, gas: { basicEstimates: { @@ -120,22 +126,22 @@ describe('custom-gas selectors', () => { expectedResult: [ { labelKey: 'slow', - feeInPrimaryCurrency: '$0.27', - feeInSecondaryCurrency: '0.000105 ETH', + feeInSecondaryCurrency: '$0.27', + feeInPrimaryCurrency: '0.000105 ETH', timeEstimate: '~13 min 12 sec', priceInHexWei: '0x12a05f200', }, { labelKey: 'average', - feeInPrimaryCurrency: '$0.54', - feeInSecondaryCurrency: '0.00021 ETH', + feeInSecondaryCurrency: '$0.54', + feeInPrimaryCurrency: '0.00021 ETH', timeEstimate: '~6 min 36 sec', priceInHexWei: '0x2540be400', }, { labelKey: 'fast', - feeInPrimaryCurrency: '$1.07', - feeInSecondaryCurrency: '0.00042 ETH', + feeInSecondaryCurrency: '$1.07', + feeInPrimaryCurrency: '0.00042 ETH', timeEstimate: '~1 min', priceInHexWei: '0x4a817c800', }, @@ -147,6 +153,165 @@ describe('custom-gas selectors', () => { send: { gasLimit: '0x5208', }, + preferences: { + showFiatInTestnets: false, + }, + provider: { + type: 'mainnet', + }, + }, + gas: { + basicEstimates: { + blockTime: 14.16326530612245, + safeLow: 5, + safeLowWait: 13.2, + fast: 10, + fastWait: 6.6, + fastest: 20, + fastestWait: 1.0, + }, + }, + }, + }, + { + expectedResult: [ + { + labelKey: 'slow', + feeInSecondaryCurrency: '', + feeInPrimaryCurrency: '0.000105 ETH', + timeEstimate: '~13 min 12 sec', + priceInHexWei: '0x12a05f200', + }, + { + labelKey: 'average', + feeInSecondaryCurrency: '', + feeInPrimaryCurrency: '0.00021 ETH', + timeEstimate: '~6 min 36 sec', + priceInHexWei: '0x2540be400', + }, + { + labelKey: 'fast', + feeInSecondaryCurrency: '', + feeInPrimaryCurrency: '0.00042 ETH', + timeEstimate: '~1 min', + priceInHexWei: '0x4a817c800', + }, + ], + mockState: { + metamask: { + conversionRate: 2557.1, + currentCurrency: 'usd', + send: { + gasLimit: '0x5208', + }, + preferences: { + showFiatInTestnets: false, + }, + provider: { + type: 'rinkeby', + }, + }, + gas: { + basicEstimates: { + blockTime: 14.16326530612245, + safeLow: 5, + safeLowWait: 13.2, + fast: 10, + fastWait: 6.6, + fastest: 20, + fastestWait: 1.0, + }, + }, + }, + }, + { + expectedResult: [ + { + labelKey: 'slow', + feeInSecondaryCurrency: '$0.27', + feeInPrimaryCurrency: '0.000105 ETH', + timeEstimate: '~13 min 12 sec', + priceInHexWei: '0x12a05f200', + }, + { + labelKey: 'average', + feeInSecondaryCurrency: '$0.54', + feeInPrimaryCurrency: '0.00021 ETH', + timeEstimate: '~6 min 36 sec', + priceInHexWei: '0x2540be400', + }, + { + labelKey: 'fast', + feeInSecondaryCurrency: '$1.07', + feeInPrimaryCurrency: '0.00042 ETH', + timeEstimate: '~1 min', + priceInHexWei: '0x4a817c800', + }, + ], + mockState: { + metamask: { + conversionRate: 2557.1, + currentCurrency: 'usd', + send: { + gasLimit: '0x5208', + }, + preferences: { + showFiatInTestnets: true, + }, + provider: { + type: 'rinkeby', + }, + }, + gas: { + basicEstimates: { + blockTime: 14.16326530612245, + safeLow: 5, + safeLowWait: 13.2, + fast: 10, + fastWait: 6.6, + fastest: 20, + fastestWait: 1.0, + }, + }, + }, + }, + { + expectedResult: [ + { + labelKey: 'slow', + feeInSecondaryCurrency: '$0.27', + feeInPrimaryCurrency: '0.000105 ETH', + timeEstimate: '~13 min 12 sec', + priceInHexWei: '0x12a05f200', + }, + { + labelKey: 'average', + feeInSecondaryCurrency: '$0.54', + feeInPrimaryCurrency: '0.00021 ETH', + timeEstimate: '~6 min 36 sec', + priceInHexWei: '0x2540be400', + }, + { + labelKey: 'fast', + feeInSecondaryCurrency: '$1.07', + feeInPrimaryCurrency: '0.00042 ETH', + timeEstimate: '~1 min', + priceInHexWei: '0x4a817c800', + }, + ], + mockState: { + metamask: { + conversionRate: 2557.1, + currentCurrency: 'usd', + send: { + gasLimit: '0x5208', + }, + preferences: { + showFiatInTestnets: true, + }, + provider: { + type: 'mainnet', + }, }, gas: { basicEstimates: { @@ -203,6 +368,12 @@ describe('custom-gas selectors', () => { send: { gasLimit: '0x5208', }, + preferences: { + showFiatInTestnets: false, + }, + provider: { + type: 'mainnet', + }, }, gas: { basicEstimates: { @@ -245,6 +416,156 @@ describe('custom-gas selectors', () => { send: { gasLimit: '0x5208', }, + preferences: { + showFiatInTestnets: false, + }, + provider: { + type: 'mainnet', + }, + }, + gas: { + basicEstimates: { + blockTime: 14.16326530612245, + safeLow: 50, + safeLowWait: 13.2, + fast: 100, + fastWait: 6.6, + fastest: 200, + fastestWait: 1.0, + }, + }, + }, + }, + { + expectedResult: [ + { + feeInSecondaryCurrency: '', + feeInPrimaryCurrency: '0.00105 ETH', + labelKey: 'slow', + priceInHexWei: '0xba43b7400', + }, + { + feeInSecondaryCurrency: '', + feeInPrimaryCurrency: '0.0021 ETH', + labelKey: 'average', + priceInHexWei: '0x174876e800', + }, + { + feeInSecondaryCurrency: '', + feeInPrimaryCurrency: '0.0042 ETH', + labelKey: 'fast', + priceInHexWei: '0x2e90edd000', + }, + ], + mockState: { + metamask: { + conversionRate: 2557.1, + currentCurrency: 'usd', + send: { + gasLimit: '0x5208', + }, + preferences: { + showFiatInTestnets: false, + }, + provider: { + type: 'rinkeby', + }, + }, + gas: { + basicEstimates: { + blockTime: 14.16326530612245, + safeLow: 50, + safeLowWait: 13.2, + fast: 100, + fastWait: 6.6, + fastest: 200, + fastestWait: 1.0, + }, + }, + }, + }, + { + expectedResult: [ + { + feeInSecondaryCurrency: '$2.68', + feeInPrimaryCurrency: '0.00105 ETH', + labelKey: 'slow', + priceInHexWei: '0xba43b7400', + }, + { + feeInSecondaryCurrency: '$5.37', + feeInPrimaryCurrency: '0.0021 ETH', + labelKey: 'average', + priceInHexWei: '0x174876e800', + }, + { + feeInSecondaryCurrency: '$10.74', + feeInPrimaryCurrency: '0.0042 ETH', + labelKey: 'fast', + priceInHexWei: '0x2e90edd000', + }, + ], + mockState: { + metamask: { + conversionRate: 2557.1, + currentCurrency: 'usd', + send: { + gasLimit: '0x5208', + }, + preferences: { + showFiatInTestnets: true, + }, + provider: { + type: 'rinkeby', + }, + }, + gas: { + basicEstimates: { + blockTime: 14.16326530612245, + safeLow: 50, + safeLowWait: 13.2, + fast: 100, + fastWait: 6.6, + fastest: 200, + fastestWait: 1.0, + }, + }, + }, + }, + { + expectedResult: [ + { + feeInSecondaryCurrency: '$2.68', + feeInPrimaryCurrency: '0.00105 ETH', + labelKey: 'slow', + priceInHexWei: '0xba43b7400', + }, + { + feeInSecondaryCurrency: '$5.37', + feeInPrimaryCurrency: '0.0021 ETH', + labelKey: 'average', + priceInHexWei: '0x174876e800', + }, + { + feeInSecondaryCurrency: '$10.74', + feeInPrimaryCurrency: '0.0042 ETH', + labelKey: 'fast', + priceInHexWei: '0x2e90edd000', + }, + ], + mockState: { + metamask: { + conversionRate: 2557.1, + currentCurrency: 'usd', + send: { + gasLimit: '0x5208', + }, + preferences: { + showFiatInTestnets: true, + }, + provider: { + type: 'mainnet', + }, }, gas: { basicEstimates: { |