-
-
-
-
{descriptionText}
-
- {infoText ? (
-
{infoText}
- }
- offset={-36}
- distance={36}
- animation="none"
- position="top"
- arrow
- theme="tippy-tooltip-home"
- >
-
-
- ) : null}
+ return (
+
+
+
-
- {onAccept && acceptText ? (
-
- ) : null}
- {onIgnore && ignoreText ? (
-
+
+ {onAccept && acceptText ? (
+
+ ) : null}
+ {onIgnore && ignoreText ? (
+
+ ) : null}
+ {checkboxText ? (
+
+ {checkboxTooltipText ? (
+
+ {checkboxElement}
+
+ ) : (
+ checkboxElement
+ )}
+
+ {checkboxText}
+
+
+ ) : null}
- )
- }
+
+ )
}
+
+HomeNotification.propTypes = {
+ acceptText: PropTypes.node,
+ checkboxText: PropTypes.node,
+ checkboxTooltipText: PropTypes.node,
+ classNames: PropTypes.array,
+ descriptionText: PropTypes.node.isRequired,
+ ignoreText: PropTypes.node,
+ infoText: PropTypes.node,
+ onAccept: PropTypes.func,
+ onIgnore: PropTypes.func,
+}
+
+export default HomeNotification
diff --git a/ui/app/components/app/home-notification/index.scss b/ui/app/components/app/home-notification/index.scss
index 452681ac2..38173145b 100644
--- a/ui/app/components/app/home-notification/index.scss
+++ b/ui/app/components/app/home-notification/index.scss
@@ -1,14 +1,7 @@
-.tippy-tooltip {
- // This looks weird, but its repeating the class name
- // using interpolation for higher specificity.
- {&}-home-theme {
- background: rgba(36, 41, 46, 0.9);
- color: $white;
- border-radius: 8px;
- }
-}
-
.home-notification {
+ display: flex;
+ flex-flow: column;
+ justify-content: space-between;
background: rgba(36, 41, 46, 0.9);
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.12);
border-radius: 8px;
@@ -19,37 +12,62 @@
min-width: 472px;
}
- display: flex;
- flex-flow: column;
- justify-content: space-between;
-
- &__header-container {
+ &__content-container {
display: flex;
}
- &__header {
+ &__content {
display: flex;
align-items: center;
justify-content: space-between;
}
- &__icon {
- height: 16px;
- align-self: center;
- }
-
&__text {
@include H7;
color: $white;
- margin-left: 10px;
- margin-right: 8px;
+ }
+
+ &__text-link {
+ @include H7;
+
+ color: $primary-blue;
+ cursor: pointer;
}
.fa-info-circle {
color: #6a737d;
}
+ & &__checkbox-wrapper {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+
+ @media screen and (max-width: 575px) {
+ width: 160px;
+ }
+ }
+
+ & &__checkbox {
+ height: 13px;
+ width: 13px;
+ font-size: 16px;
+ cursor: pointer;
+ }
+
+ & &__checkbox-label {
+ @include H7;
+
+ color: $white;
+ margin-left: 10px;
+ margin-top: 1px;
+ user-select: none;
+ -moz-user-select: none;
+ -webkit-user-select: none;
+ cursor: pointer;
+ }
+
& &__ignore-button {
border-color: #6a737d;
box-sizing: border-box;
@@ -102,23 +120,14 @@
&__buttons {
display: flex;
width: 100%;
- margin-top: 10px;
- justify-content: flex-start;
+ padding-top: 10px;
+ align-items: center;
+ justify-content: space-between;
flex-direction: row-reverse;
}
-}
-.home-notification-tooltip {
- &__tooltip-container {
+ &__tooltip-wrapper {
display: flex;
- }
-
- &__content {
- @include H7;
-
- color: $white;
- text-align: left;
- display: inline-block;
- width: 200px;
+ margin-left: 10px;
}
}
diff --git a/ui/app/components/app/loading-network-screen/loading-network-screen.component.js b/ui/app/components/app/loading-network-screen/loading-network-screen.component.js
index 1eb95eedf..3f3def230 100644
--- a/ui/app/components/app/loading-network-screen/loading-network-screen.component.js
+++ b/ui/app/components/app/loading-network-screen/loading-network-screen.component.js
@@ -19,11 +19,8 @@ export default class LoadingNetworkScreen extends PureComponent {
providerId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
showNetworkDropdown: PropTypes.func,
setProviderArgs: PropTypes.array,
- lastSelectedProvider: PropTypes.oneOfType([
- PropTypes.string,
- PropTypes.object,
- ]),
setProviderType: PropTypes.func,
+ rollbackToPreviousProvider: PropTypes.func,
isLoadingNetwork: PropTypes.bool,
}
@@ -123,14 +120,14 @@ export default class LoadingNetworkScreen extends PureComponent {
}
render() {
- const { lastSelectedProvider, setProviderType } = this.props
+ const { rollbackToPreviousProvider } = this.props
return (
setProviderType(lastSelectedProvider || 'ropsten')}
+ onClick={rollbackToPreviousProvider}
/>
}
showLoadingSpinner={!this.state.showErrorScreen}
diff --git a/ui/app/components/app/loading-network-screen/loading-network-screen.container.js b/ui/app/components/app/loading-network-screen/loading-network-screen.container.js
index 994edd175..59a97a3cc 100644
--- a/ui/app/components/app/loading-network-screen/loading-network-screen.container.js
+++ b/ui/app/components/app/loading-network-screen/loading-network-screen.container.js
@@ -4,7 +4,7 @@ import { getNetworkIdentifier } from '../../../selectors'
import LoadingNetworkScreen from './loading-network-screen.component'
const mapStateToProps = (state) => {
- const { loadingMessage, lastSelectedProvider } = state.appState
+ const { loadingMessage } = state.appState
const { provider, network } = state.metamask
const { rpcUrl, chainId, ticker, nickname, type } = provider
@@ -14,7 +14,6 @@ const mapStateToProps = (state) => {
return {
isLoadingNetwork: network === 'loading',
loadingMessage,
- lastSelectedProvider,
setProviderArgs,
provider,
providerId: getNetworkIdentifier(state),
@@ -26,6 +25,8 @@ const mapDispatchToProps = (dispatch) => {
setProviderType: (type) => {
dispatch(actions.setProviderType(type))
},
+ rollbackToPreviousProvider: () =>
+ dispatch(actions.rollbackToPreviousProvider()),
showNetworkDropdown: () => dispatch(actions.showNetworkDropdown()),
}
}
diff --git a/ui/app/components/app/multiple-notifications/multiple-notifications.component.js b/ui/app/components/app/multiple-notifications/multiple-notifications.component.js
index e8dcebf13..9d16d1dfe 100644
--- a/ui/app/components/app/multiple-notifications/multiple-notifications.component.js
+++ b/ui/app/components/app/multiple-notifications/multiple-notifications.component.js
@@ -21,7 +21,7 @@ export default class MultipleNotifications extends PureComponent {
const { showAll } = this.state
const { children, classNames } = this.props
- const childrenToRender = children.filter((child) => child)
+ const childrenToRender = children.filter(Boolean)
if (childrenToRender.length === 0) {
return null
}
diff --git a/ui/app/components/ui/page-container/page-container.component.js b/ui/app/components/ui/page-container/page-container.component.js
index eb1280b3b..79126e454 100644
--- a/ui/app/components/ui/page-container/page-container.component.js
+++ b/ui/app/components/ui/page-container/page-container.component.js
@@ -67,7 +67,7 @@ export default class PageContainer extends PureComponent {
renderActiveTabContent() {
const { tabsComponent } = this.props
let { children } = tabsComponent.props
- children = children.filter((child) => child)
+ children = children.filter(Boolean)
const { activeTabIndex } = this.state
return children[activeTabIndex]
diff --git a/ui/app/components/ui/url-icon/index.scss b/ui/app/components/ui/url-icon/index.scss
index 482f327d3..14d5524cb 100644
--- a/ui/app/components/ui/url-icon/index.scss
+++ b/ui/app/components/ui/url-icon/index.scss
@@ -20,9 +20,9 @@
border-radius: 50%;
background: #bbc0c5;
flex: 0 1 auto;
+ display: flex;
justify-content: center;
align-items: center;
- text-align: center;
padding-top: 2px;
}
}
diff --git a/ui/app/components/ui/url-icon/url-icon.js b/ui/app/components/ui/url-icon/url-icon.js
index 6dcfcc06f..115eb5fe1 100644
--- a/ui/app/components/ui/url-icon/url-icon.js
+++ b/ui/app/components/ui/url-icon/url-icon.js
@@ -3,13 +3,13 @@ import PropTypes from 'prop-types'
import classnames from 'classnames'
import IconWithFallback from '../icon-with-fallback'
-export default function UrlIcon({ url, className, name }) {
+export default function UrlIcon({ url, className, name, fallbackClassName }) {
return (
)
}
@@ -18,4 +18,5 @@ UrlIcon.propTypes = {
url: PropTypes.string,
className: PropTypes.string,
name: PropTypes.string,
+ fallbackClassName: PropTypes.string,
}
diff --git a/ui/app/ducks/alerts/invalid-custom-network.js b/ui/app/ducks/alerts/invalid-custom-network.js
index aa7ba3cdb..be2aa20e1 100644
--- a/ui/app/ducks/alerts/invalid-custom-network.js
+++ b/ui/app/ducks/alerts/invalid-custom-network.js
@@ -1,6 +1,6 @@
import { createSlice } from '@reduxjs/toolkit'
-import { ALERT_TYPES } from '../../../../app/scripts/controllers/alert'
+import { ALERT_TYPES } from '../../../../shared/constants/alerts'
import { ALERT_STATE } from './enums'
// Constants
diff --git a/ui/app/ducks/alerts/unconnected-account.js b/ui/app/ducks/alerts/unconnected-account.js
index 58ca9a569..563af43d2 100644
--- a/ui/app/ducks/alerts/unconnected-account.js
+++ b/ui/app/ducks/alerts/unconnected-account.js
@@ -1,7 +1,7 @@
import { createSlice } from '@reduxjs/toolkit'
import { captureException } from '@sentry/browser'
-import { ALERT_TYPES } from '../../../../app/scripts/controllers/alert'
+import { ALERT_TYPES } from '../../../../shared/constants/alerts'
import * as actionConstants from '../../store/actionConstants'
import {
addPermittedAccount,
@@ -101,7 +101,7 @@ export const dismissAndDisableAlert = () => {
return async (dispatch) => {
try {
await dispatch(disableAlertRequested())
- await dispatch(setAlertEnabledness(name, false))
+ await setAlertEnabledness(name, false)
await dispatch(disableAlertSucceeded())
} catch (error) {
console.error(error)
diff --git a/ui/app/ducks/app/app.js b/ui/app/ducks/app/app.js
index 913142761..06f194a17 100644
--- a/ui/app/ducks/app/app.js
+++ b/ui/app/ducks/app/app.js
@@ -42,7 +42,6 @@ export default function reduceApp(state = {}, action) {
trezor: `m/44'/60'/0'/0`,
ledger: `m/44'/60'/0'/0/0`,
},
- lastSelectedProvider: null,
networksTabSelectedRpcUrl: '',
networksTabIsInAddMode: false,
loadingMethodData: false,
@@ -305,15 +304,6 @@ export default function reduceApp(state = {}, action) {
gasIsLoading: false,
}
- case actionConstants.SET_PREVIOUS_PROVIDER:
- if (action.value === 'loading') {
- return appState
- }
- return {
- ...appState,
- lastSelectedProvider: action.value,
- }
-
case actionConstants.SET_SELECTED_SETTINGS_RPC_URL:
return {
...appState,
diff --git a/ui/app/ducks/index.js b/ui/app/ducks/index.js
index f414a0892..42fccab06 100644
--- a/ui/app/ducks/index.js
+++ b/ui/app/ducks/index.js
@@ -1,5 +1,5 @@
import { combineReducers } from 'redux'
-import { ALERT_TYPES } from '../../../app/scripts/controllers/alert'
+import { ALERT_TYPES } from '../../../shared/constants/alerts'
import metamaskReducer from './metamask/metamask'
import localeMessagesReducer from './locale/locale'
import sendReducer from './send/send.duck'
diff --git a/ui/app/ducks/metamask/metamask.js b/ui/app/ducks/metamask/metamask.js
index 61abf2559..24180f0d3 100644
--- a/ui/app/ducks/metamask/metamask.js
+++ b/ui/app/ducks/metamask/metamask.js
@@ -1,5 +1,5 @@
import * as actionConstants from '../../store/actionConstants'
-import { ALERT_TYPES } from '../../../../app/scripts/controllers/alert'
+import { ALERT_TYPES } from '../../../../shared/constants/alerts'
export default function reduceMetamask(state = {}, action) {
const metamaskState = {
@@ -375,12 +375,12 @@ export const getCurrentLocale = (state) => state.metamask.currentLocale
export const getAlertEnabledness = (state) => state.metamask.alertEnabledness
-export const getInvalidCustomNetworkAlertEnabledness = (state) =>
- getAlertEnabledness(state)[ALERT_TYPES.invalidCustomNetwork]
-
export const getUnconnectedAccountAlertEnabledness = (state) =>
getAlertEnabledness(state)[ALERT_TYPES.unconnectedAccount]
+export const getWeb3ShimUsageAlertEnabledness = (state) =>
+ getAlertEnabledness(state)[ALERT_TYPES.web3ShimUsage]
+
export const getUnconnectedAccountAlertShown = (state) =>
state.metamask.unconnectedAccountAlertShownOrigins
diff --git a/ui/app/ducks/swaps/swaps.js b/ui/app/ducks/swaps/swaps.js
index f6d36c204..876bd50ec 100644
--- a/ui/app/ducks/swaps/swaps.js
+++ b/ui/app/ducks/swaps/swaps.js
@@ -606,7 +606,7 @@ export const signAndSendTransactions = (history, metaMetricsEvent) => {
customSwapsGas ||
(usedQuote?.gasEstimate
? estimatedGasLimitWithMultiplier
- : usedQuote?.maxGas)
+ : `0x${decimalToHex(usedQuote?.maxGas || 0)}`)
const usedGasPrice = getUsedSwapsGasPrice(state)
usedTradeTxParams.gas = maxGasLimit
diff --git a/ui/app/helpers/utils/conversions.util.js b/ui/app/helpers/utils/conversions.util.js
index e4cf21717..9e0f97681 100644
--- a/ui/app/helpers/utils/conversions.util.js
+++ b/ui/app/helpers/utils/conversions.util.js
@@ -172,7 +172,7 @@ export function addHexes(aHexWEI, bHexWEI) {
}
export function sumHexWEIs(hexWEIs) {
- return hexWEIs.filter((n) => n).reduce(addHexes)
+ return hexWEIs.filter(Boolean).reduce(addHexes)
}
export function sumHexWEIsToUnformattedFiat(
diff --git a/ui/app/hooks/useTokensToSearch.js b/ui/app/hooks/useTokensToSearch.js
index 8df33e55e..a4e0e94f4 100644
--- a/ui/app/hooks/useTokensToSearch.js
+++ b/ui/app/hooks/useTokensToSearch.js
@@ -145,9 +145,7 @@ export function useTokensToSearch({
return new BigNumber(rawFiat || 0).gt(secondRawFiat || 0) ? -1 : 1
},
)
- tokensToSearchBuckets.top = tokensToSearchBuckets.top.filter(
- (token) => token,
- )
+ tokensToSearchBuckets.top = tokensToSearchBuckets.top.filter(Boolean)
return [
...tokensToSearchBuckets.owned,
...tokensToSearchBuckets.top,
diff --git a/ui/app/pages/add-token/token-list/token-list.component.js b/ui/app/pages/add-token/token-list/token-list.component.js
index 83e9c352d..54006136e 100644
--- a/ui/app/pages/add-token/token-list/token-list.component.js
+++ b/ui/app/pages/add-token/token-list/token-list.component.js
@@ -4,7 +4,7 @@ import classnames from 'classnames'
import { checkExistingAddresses } from '../../../helpers/utils/util'
import TokenListPlaceholder from './token-list-placeholder'
-export default class InfoBox extends Component {
+export default class TokenList extends Component {
static contextTypes = {
t: PropTypes.func,
}
diff --git a/ui/app/pages/confirm-transaction-base/confirm-transaction-base.component.js b/ui/app/pages/confirm-transaction-base/confirm-transaction-base.component.js
index f25380795..24f452182 100644
--- a/ui/app/pages/confirm-transaction-base/confirm-transaction-base.component.js
+++ b/ui/app/pages/confirm-transaction-base/confirm-transaction-base.component.js
@@ -141,7 +141,7 @@ export default class ConfirmTransactionBase extends Component {
nextNonce !== prevNextNonce ||
customNonceValue !== prevCustomNonceValue
) {
- if (customNonceValue > nextNonce) {
+ if (nextNonce !== null && customNonceValue > nextNonce) {
this.setState({
submitWarning: this.context.t('nextNonceWarning', [nextNonce]),
})
diff --git a/ui/app/pages/home/home.component.js b/ui/app/pages/home/home.component.js
index 17ddeb3cf..92c8d685d 100644
--- a/ui/app/pages/home/home.component.js
+++ b/ui/app/pages/home/home.component.js
@@ -31,6 +31,8 @@ import {
const LEARN_MORE_URL =
'https://metamask.zendesk.com/hc/en-us/articles/360045129011-Intro-to-MetaMask-v8-extension'
+const LEGACY_WEB3_URL =
+ 'https://metamask.zendesk.com/hc/en-us/articles/360053147012'
export default class Home extends PureComponent {
static contextTypes = {
@@ -42,7 +44,7 @@ export default class Home extends PureComponent {
forgottenPassword: PropTypes.bool,
suggestedTokens: PropTypes.object,
unconfirmedTransactionsCount: PropTypes.number,
- shouldShowSeedPhraseReminder: PropTypes.bool,
+ shouldShowSeedPhraseReminder: PropTypes.bool.isRequired,
isPopup: PropTypes.bool,
isNotification: PropTypes.bool.isRequired,
threeBoxSynced: PropTypes.bool,
@@ -66,6 +68,10 @@ export default class Home extends PureComponent {
swapsFetchParams: PropTypes.object,
swapsEnabled: PropTypes.bool,
isMainnet: PropTypes.bool,
+ shouldShowWeb3ShimUsageNotification: PropTypes.bool.isRequired,
+ setWeb3ShimUsageAlertDismissed: PropTypes.func.isRequired,
+ originOfCurrentTab: PropTypes.string,
+ disableWeb3ShimUsageAlert: PropTypes.func.isRequired,
}
state = {
@@ -161,10 +167,39 @@ export default class Home extends PureComponent {
setShowRestorePromptToFalse,
showRestorePrompt,
threeBoxLastUpdated,
+ shouldShowWeb3ShimUsageNotification,
+ setWeb3ShimUsageAlertDismissed,
+ originOfCurrentTab,
+ disableWeb3ShimUsageAlert,
} = this.props
return (
+ {shouldShowWeb3ShimUsageNotification ? (
+
+ global.platform.openTab({ url: LEGACY_WEB3_URL })
+ }
+ >
+ {t('here')}
+ ,
+ ])}
+ ignoreText={t('dismiss')}
+ onIgnore={(disable) => {
+ setWeb3ShimUsageAlertDismissed(originOfCurrentTab)
+ if (disable) {
+ disableWeb3ShimUsageAlert()
+ }
+ }}
+ checkboxText={t('dontShowThisAgain')}
+ checkboxTooltipText={t('canToggleInSettings')}
+ key="home-web3ShimUsageNotification"
+ />
+ ) : null}
{shouldShowSeedPhraseReminder ? (
{
@@ -58,6 +68,14 @@ const mapStateToProps = (state) => {
? firstPermissionsRequest.metadata.id
: null
+ const originOfCurrentTab = getOriginOfCurrentTab(state)
+ const shouldShowWeb3ShimUsageNotification =
+ isPopup &&
+ getWeb3ShimUsageAlertEnabledness(state) &&
+ activeTabHasPermissions(state) &&
+ getWeb3ShimUsageStateForOrigin(state, originOfCurrentTab) ===
+ WEB3_SHIM_USAGE_ALERT_STATES.RECORDED
+
return {
forgottenPassword,
suggestedTokens,
@@ -81,6 +99,8 @@ const mapStateToProps = (state) => {
swapsFetchParams: swapsState.fetchParams,
showAwaitingSwapScreen: swapsState.routeState === 'awaiting',
isMainnet: getIsMainnet(state),
+ originOfCurrentTab,
+ shouldShowWeb3ShimUsageNotification,
}
}
@@ -103,6 +123,10 @@ const mapDispatchToProps = (dispatch) => ({
onTabClick: (name) => dispatch(setDefaultHomeActiveTabName(name)),
setSwapsWelcomeMessageHasBeenShown: () =>
dispatch(setSwapsWelcomeMessageHasBeenShown()),
+ setWeb3ShimUsageAlertDismissed: (origin) =>
+ setWeb3ShimUsageAlertDismissed(origin),
+ disableWeb3ShimUsageAlert: () =>
+ setAlertEnabledness(ALERT_TYPES.web3ShimUsage, false),
})
export default compose(
diff --git a/ui/app/pages/send/send.scss b/ui/app/pages/send/send.scss
index decf358cf..e416b41b2 100644
--- a/ui/app/pages/send/send.scss
+++ b/ui/app/pages/send/send.scss
@@ -21,6 +21,7 @@
position: absolute;
right: 1rem;
width: min-content;
+ font-size: 0.75rem;
}
}
diff --git a/ui/app/pages/settings/alerts-tab/alerts-tab.js b/ui/app/pages/settings/alerts-tab/alerts-tab.js
index 6bc6b5327..832a4ec17 100644
--- a/ui/app/pages/settings/alerts-tab/alerts-tab.js
+++ b/ui/app/pages/settings/alerts-tab/alerts-tab.js
@@ -1,8 +1,8 @@
import React from 'react'
import PropTypes from 'prop-types'
-import { useDispatch, useSelector } from 'react-redux'
+import { useSelector } from 'react-redux'
-import { ALERT_TYPES } from '../../../../../app/scripts/controllers/alert'
+import { ALERT_TYPES } from '../../../../../shared/constants/alerts'
import Tooltip from '../../../components/ui/tooltip'
import ToggleButton from '../../../components/ui/toggle-button'
import { setAlertEnabledness } from '../../../store/actions'
@@ -11,7 +11,6 @@ import { useI18nContext } from '../../../hooks/useI18nContext'
const AlertSettingsEntry = ({ alertId, description, title }) => {
const t = useI18nContext()
- const dispatch = useDispatch()
const isEnabled = useSelector((state) => getAlertEnabledness(state)[alertId])
return (
@@ -27,7 +26,7 @@ const AlertSettingsEntry = ({ alertId, description, title }) => {
dispatch(setAlertEnabledness(alertId, !isEnabled))}
+ onToggle={() => setAlertEnabledness(alertId, !isEnabled)}
value={isEnabled}
/>
>
@@ -48,6 +47,10 @@ const AlertsTab = () => {
title: t('alertSettingsUnconnectedAccount'),
description: t('alertSettingsUnconnectedAccountDescription'),
},
+ [ALERT_TYPES.web3ShimUsage]: {
+ title: t('alertSettingsWeb3ShimUsage'),
+ description: t('alertSettingsWeb3ShimUsageDescription'),
+ },
}
return (
diff --git a/ui/app/pages/settings/networks-tab/networks-tab.constants.js b/ui/app/pages/settings/networks-tab/networks-tab.constants.js
index 4c0ab5462..0271abcfc 100644
--- a/ui/app/pages/settings/networks-tab/networks-tab.constants.js
+++ b/ui/app/pages/settings/networks-tab/networks-tab.constants.js
@@ -55,7 +55,7 @@ const defaultNetworksData = [
rpcUrl: `https://kovan.infura.io/v3/${process.env.INFURA_PROJECT_ID}`,
chainId: KOVAN_CHAIN_ID,
ticker: 'ETH',
- blockExplorerUrl: 'https://etherscan.io',
+ blockExplorerUrl: 'https://kovan.etherscan.io',
},
]
diff --git a/ui/app/pages/swaps/build-quote/build-quote.js b/ui/app/pages/swaps/build-quote/build-quote.js
index 13d873b30..4714f79ad 100644
--- a/ui/app/pages/swaps/build-quote/build-quote.js
+++ b/ui/app/pages/swaps/build-quote/build-quote.js
@@ -418,6 +418,7 @@ export default function BuildQuote({
Number(maxSlippage) > MAX_ALLOWED_SLIPPAGE
}
hideCancel
+ showTermsOfService
/>
)
diff --git a/ui/app/pages/swaps/exchange-rate-display/exchange-rate-display.js b/ui/app/pages/swaps/exchange-rate-display/exchange-rate-display.js
index fbb268d65..e70ffd8e7 100644
--- a/ui/app/pages/swaps/exchange-rate-display/exchange-rate-display.js
+++ b/ui/app/pages/swaps/exchange-rate-display/exchange-rate-display.js
@@ -3,7 +3,7 @@ import PropTypes from 'prop-types'
import BigNumber from 'bignumber.js'
import classnames from 'classnames'
import { calcTokenAmount } from '../../../helpers/utils/token-util'
-import { toPrecisionWithoutTrailingZeros } from '../../../helpers/utils/util'
+import { formatSwapsValueForDisplay } from '../swaps.util'
export default function ExchangeRateDisplay({
primaryTokenValue,
@@ -13,6 +13,7 @@ export default function ExchangeRateDisplay({
secondaryTokenDecimals = 18,
secondaryTokenSymbol,
arrowColor = 'black',
+ boldSymbols = true,
className,
}) {
const [showPrimaryToSecondary, setShowPrimaryToSecondary] = useState(true)
@@ -57,16 +58,24 @@ export default function ExchangeRateDisplay({
} else if (new BigNumber(rate, 10).lt('0.000001', 10)) {
rateToDisplay = rate
} else {
- rateToDisplay = toPrecisionWithoutTrailingZeros(rate, 9)
+ rateToDisplay = formatSwapsValueForDisplay(rate)
}
return (
1
-
{baseSymbol}
+
+ {baseSymbol}
+
{comparisonSymbol}
{rateToDisplay}
-
{ratiodSymbol}
+
+ {ratiodSymbol}
+
+
+
+ {bestQuoteText && (
+
{bestQuoteText}
+ )}
+
+
+ {t('swapNQuotes', [numberOfQuotes])}
+
+
+
+
+
+
+
@@ -83,26 +113,39 @@ export default function FeeCard({
{!hideTokenApprovalRow && (
-
+
{t('swapThisWillAllowApprove', [tokenApprovalTextComponent])}
-
onTokenApprovalClick()}
- >
- {t('swapEditLimit')}
-
+
onTokenApprovalClick()}
+ >
+ {t('swapEditLimit')}
+
)}
+
+
+
+ {t('swapQuoteIncludesRate', [metaMaskFee])}
+
+
+
+
)
@@ -122,4 +165,9 @@ FeeCard.propTypes = {
tokenApprovalTextComponent: PropTypes.node,
tokenApprovalSourceTokenSymbol: PropTypes.string,
onTokenApprovalClick: PropTypes.func,
+ metaMaskFee: PropTypes.string.isRequired,
+ isBestQuote: PropTypes.bool,
+ onQuotesClick: PropTypes.func.isRequired,
+ numberOfQuotes: PropTypes.number.isRequired,
+ tokenConversionRate: PropTypes.number,
}
diff --git a/ui/app/pages/swaps/fee-card/fee-card.stories.js b/ui/app/pages/swaps/fee-card/fee-card.stories.js
index af57849f3..aaaf43d0f 100644
--- a/ui/app/pages/swaps/fee-card/fee-card.stories.js
+++ b/ui/app/pages/swaps/fee-card/fee-card.stories.js
@@ -1,6 +1,6 @@
import React from 'react'
import { action } from '@storybook/addon-actions'
-import { text } from '@storybook/addon-knobs'
+import { text, boolean, number, object } from '@storybook/addon-knobs'
import FeeCard from './fee-card'
const tokenApprovalTextComponent = (
@@ -35,6 +35,13 @@ export const WithAllProps = () => {
tokenApprovalSourceTokenSymbol="ABC"
onTokenApprovalClick={action('Clicked third row link')}
hideTokenApprovalRow={false}
+ metaMaskFee="0.875"
+ savings={object('savings 1', { total: '8.55' })}
+ onQuotesClick={action('Clicked quotes link')}
+ numberOfQuotes={number('numberOfQuotes', 6)}
+ isBestQuote={boolean('isBestQuote', true)}
+ conversionRate={300}
+ currentCurrency="usd"
/>
)
@@ -55,6 +62,11 @@ export const WithoutThirdRow = () => {
}}
onFeeCardMaxRowClick={action('Clicked max fee row link')}
hideTokenApprovalRow
+ onQuotesClick={action('Clicked quotes link')}
+ numberOfQuotes={number('numberOfQuotes', 1)}
+ isBestQuote={boolean('isBestQuote', true)}
+ savings={object('savings 1', { total: '8.55' })}
+ metaMaskFee="0.875"
/>
)
@@ -70,6 +82,9 @@ export const WithOnlyRequiredProps = () => {
}}
onFeeCardMaxRowClick={action('Clicked max fee row link')}
hideTokenApprovalRow
+ metaMaskFee="0.875"
+ onQuotesClick={action('Clicked quotes link')}
+ numberOfQuotes={2}
/>
)
diff --git a/ui/app/pages/swaps/fee-card/index.scss b/ui/app/pages/swaps/fee-card/index.scss
index 0a86f6ab7..d1807fc0d 100644
--- a/ui/app/pages/swaps/fee-card/index.scss
+++ b/ui/app/pages/swaps/fee-card/index.scss
@@ -1,11 +1,69 @@
.fee-card {
- border-radius: 8px;
- border: 1px solid $Grey-100;
width: 100%;
@include H7;
+ &__savings-and-quotes-header {
+ display: flex;
+ position: relative;
+ align-items: center;
+ }
+
+ &__savings-and-quotes-row {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ width: 100%;
+ left: 58px;
+ height: 39px;
+ background: $Blue-000;
+ border: 1px solid $Blue-500;
+ border-top-right-radius: 8px;
+ border-top-left-radius: 8px;
+ border-bottom: 0;
+ padding-left: 8px;
+ padding-right: 8px;
+ }
+
+ &__savings-text {
+ @include H6;
+
+ font-weight: bold;
+ color: $Blue-500;
+ }
+
+ &__quote-link-container {
+ display: flex;
+ align-items: center;
+ cursor: pointer;
+ }
+
+ &__quote-link-text {
+ @include H7;
+
+ color: $Blue-500;
+ }
+
+ &__caret-right {
+ color: $Blue-500;
+ width: 6px;
+ height: 6px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ margin-left: 6px;
+
+ i {
+ transform: rotate(90deg);
+ }
+ }
+
&__main {
+ border: 1px solid $Blue-500;
+ border-bottom-left-radius: 8px;
+ border-bottom-right-radius: 8px;
+ width: 100%;
+ max-width: 311px;
padding: 16px 16px 12px 16px;
}
@@ -31,6 +89,10 @@
cursor: pointer;
}
+ &__row-header-text--bold {
+ color: $Black-100;
+ }
+
&__row,
&__top-bordered-row {
display: flex;
@@ -51,7 +113,6 @@
img {
height: 10px;
width: 10px;
- margin-left: 4px;
cursor: pointer;
}
}
@@ -60,7 +121,12 @@
height: 10px;
width: 10px;
justify-content: center;
- margin-top: 2px;
+
+ div {
+ // Needed to override the style property added by the react-tippy library
+ display: flex !important;
+ height: 10px;
+ }
}
&__info-tooltip-paragraph {
@@ -111,11 +177,14 @@
margin-right: 12px;
}
- &__row-header-primary,
- &__row-header-primary--bold {
+ &__row-header-primary {
color: $Grey-500;
}
+ &__row-header-primary--bold {
+ color: $Black-100;
+ }
+
&__row-header-text--bold,
&__row-header-secondary--bold,
&__row-header-primary--bold {
@@ -125,6 +194,11 @@
&__bold {
font-weight: bold;
}
+
+ &__tilde {
+ font-family: Roboto, Helvetica, Arial, sans-serif;
+ margin-right: -3.5px;
+ }
}
.info-tooltip {
diff --git a/ui/app/pages/swaps/fee-card/pig-icon.js b/ui/app/pages/swaps/fee-card/pig-icon.js
new file mode 100644
index 000000000..ef7677ae4
--- /dev/null
+++ b/ui/app/pages/swaps/fee-card/pig-icon.js
@@ -0,0 +1,54 @@
+import React from 'react'
+
+export default function PigIcon() {
+ return (
+
-
-
-
-
- {isBestQuote && }
- {isBestQuote && t('swapsBestQuote')}
-
-
- {t('swapsConvertToAbout', [
-
- {`${sourceAmount} ${sourceSymbol}`}
- ,
- ])}
-
+
+
+ {formatSwapsValueForDisplay(sourceAmount)}
+
+
+
+ {sourceSymbol}
+
+
+
+
+
+
+ {destinationSymbol}
+
+
-
- {`${destinationSymbol}`}
-
@@ -141,8 +115,9 @@ export default function MainQuoteSummary({
secondaryTokenValue={destinationValue}
secondaryTokenDecimals={destinationDecimals}
secondaryTokenSymbol={destinationSymbol}
- className="exchange-rate-display--white"
- arrowColor="white"
+ arrowColor="#037DD6"
+ boldSymbols={false}
+ className="main-quote-summary__exchange-rate-display"
/>
@@ -151,7 +126,6 @@ export default function MainQuoteSummary({
}
MainQuoteSummary.propTypes = {
- isBestQuote: PropTypes.bool,
sourceValue: PropTypes.oneOfType([
PropTypes.string,
PropTypes.instanceOf(BigNumber),
@@ -167,4 +141,6 @@ MainQuoteSummary.propTypes = {
PropTypes.number,
]),
destinationSymbol: PropTypes.string.isRequired,
+ sourceIconUrl: PropTypes.string,
+ destinationIconUrl: PropTypes.string,
}
diff --git a/ui/app/pages/swaps/main-quote-summary/main-quote-summary.stories.js b/ui/app/pages/swaps/main-quote-summary/main-quote-summary.stories.js
index ea267eb17..0f5ca998d 100644
--- a/ui/app/pages/swaps/main-quote-summary/main-quote-summary.stories.js
+++ b/ui/app/pages/swaps/main-quote-summary/main-quote-summary.stories.js
@@ -1,5 +1,5 @@
import React from 'react'
-import { text, number, boolean } from '@storybook/addon-knobs'
+import { text, number } from '@storybook/addon-knobs'
import MainQuoteSummary from './main-quote-summary'
export default {
@@ -8,28 +8,24 @@ export default {
export const BestQuote = () => {
return (
-
- )
-}
-
-export const NotBestQuote = () => {
- return (
-
+
+
+
)
}
diff --git a/ui/app/pages/swaps/swaps-footer/swaps-footer.js b/ui/app/pages/swaps/swaps-footer/swaps-footer.js
index 870ce028b..62a3e2a70 100644
--- a/ui/app/pages/swaps/swaps-footer/swaps-footer.js
+++ b/ui/app/pages/swaps/swaps-footer/swaps-footer.js
@@ -31,7 +31,10 @@ export default function SwapsFooter({
onSubmit={onSubmit}
submitText={submitText}
submitButtonType="confirm"
- footerClassName="swaps-footer__custom-page-container-footer-class"
+ footerClassName={classnames(
+ 'swaps-footer__custom-page-container-footer-class',
+ className,
+ )}
footerButtonClassName={classnames(
'swaps-footer__custom-page-container-footer-button-class',
{
diff --git a/ui/app/pages/swaps/swaps-gas-customization-modal/swaps-gas-customization-modal.container.js b/ui/app/pages/swaps/swaps-gas-customization-modal/swaps-gas-customization-modal.container.js
index d123eb3d6..deea5c4b0 100644
--- a/ui/app/pages/swaps/swaps-gas-customization-modal/swaps-gas-customization-modal.container.js
+++ b/ui/app/pages/swaps/swaps-gas-customization-modal/swaps-gas-customization-modal.container.js
@@ -159,7 +159,7 @@ export default connect(
)(SwapsGasCustomizationModalComponent)
function sumHexWEIsToRenderableEth(hexWEIs) {
- const hexWEIsSum = hexWEIs.filter((n) => n).reduce(addHexes)
+ const hexWEIsSum = hexWEIs.filter(Boolean).reduce(addHexes)
return formatETHFee(
getValueFromWeiHex({
value: hexWEIsSum,
diff --git a/ui/app/pages/swaps/swaps.util.js b/ui/app/pages/swaps/swaps.util.js
index e64cbf8fd..81876d947 100644
--- a/ui/app/pages/swaps/swaps.util.js
+++ b/ui/app/pages/swaps/swaps.util.js
@@ -533,6 +533,7 @@ export function quotesToRenderableData(
destinationTokenDecimals: destinationTokenInfo.decimals,
destinationTokenSymbol: destinationTokenInfo.symbol,
destinationTokenValue: formatSwapsValueForDisplay(destinationValue),
+ destinationIconUrl: destinationTokenInfo.iconUrl,
isBestQuote: quote.isBestQuote,
liquiditySourceKey,
feeInEth,
@@ -544,6 +545,7 @@ export function quotesToRenderableData(
sourceTokenDecimals: sourceTokenInfo.decimals,
sourceTokenSymbol: sourceTokenInfo.symbol,
sourceTokenValue: sourceValue,
+ sourceTokenIconUrl: sourceTokenInfo.iconUrl,
ethValueOfTrade,
minimumAmountReceived,
metaMaskFee: fee,
diff --git a/ui/app/pages/swaps/view-quote/index.scss b/ui/app/pages/swaps/view-quote/index.scss
index 474eced60..397748c2a 100644
--- a/ui/app/pages/swaps/view-quote/index.scss
+++ b/ui/app/pages/swaps/view-quote/index.scss
@@ -17,7 +17,7 @@
@media screen and (max-width: 576px) {
overflow-y: auto;
- max-height: 388px;
+ max-height: 428px;
}
}
@@ -44,13 +44,13 @@
display: flex;
align-items: center;
justify-content: center;
+ min-height: 46px;
}
&__view-other-button,
&__view-other-button-fade {
display: flex;
align-items: center;
- margin-bottom: 16px;
position: absolute;
@include H7;
@@ -133,6 +133,9 @@
width: 100%;
align-items: center;
justify-content: center;
+ width: intrinsic; /* Safari/WebKit uses a non-standard name */
+ width: max-content;
+ max-width: 340px;
margin-top: 8px;
@media screen and (min-width: 576px) {
@@ -149,57 +152,29 @@
}
&__countdown-timer-container {
- @media screen and (max-width: 576px) {
- margin-top: 12px;
- margin-bottom: 16px;
-
- &--thin {
- margin-top: 8px;
- margin-bottom: 8px;
-
- > div {
- margin-top: 0;
- margin-bottom: 0;
- }
- }
- }
-
- @media screen and (min-width: 576px) {
- &--thin {
- margin-top: 6px;
- }
- }
+ width: 152px;
+ min-height: 32px;
+ display: flex;
+ justify-content: center;
+ border-radius: 42px;
+ background: #f2f3f4;
+ margin-top: 8px;
}
&__fee-card-container {
+ display: flex;
+ align-items: center;
width: 100%;
- margin-top: 8px;
+ max-width: 311px;
margin-bottom: 8px;
@media screen and (min-width: 576px) {
margin-bottom: 0;
-
- &--three-rows {
- margin-bottom: -16px;
- }
- }
- }
-
- &__main-quote-summary-container {
- margin-top: 24px;
-
- @media screen and (max-width: 576px) {
- margin-top: 0;
- }
-
- &--thin {
- margin-top: 8px;
}
}
&__metamask-rate {
display: flex;
- margin-top: 8%;
}
&__metamask-rate-text {
@@ -214,5 +189,9 @@
&__thin-swaps-footer {
max-height: 82px;
+
+ @media screen and (min-width: 576px) {
+ height: 72px;
+ }
}
}
diff --git a/ui/app/pages/swaps/view-quote/view-quote.js b/ui/app/pages/swaps/view-quote/view-quote.js
index ea004a9f0..bdceee91f 100644
--- a/ui/app/pages/swaps/view-quote/view-quote.js
+++ b/ui/app/pages/swaps/view-quote/view-quote.js
@@ -74,7 +74,6 @@ import { useTokenTracker } from '../../../hooks/useTokenTracker'
import { QUOTES_EXPIRED_ERROR } from '../../../helpers/constants/swaps'
import CountdownTimer from '../countdown-timer'
import SwapsFooter from '../swaps-footer'
-import InfoTooltip from '../../../components/ui/info-tooltip'
import ViewQuotePriceDifference from './view-quote-price-difference'
export default function ViewQuote() {
@@ -119,6 +118,7 @@ export default function ViewQuote() {
const swapsQuoteRefreshTime = useSelector(getSwapsQuoteRefreshTime)
const { isBestQuote } = usedQuote
+
const fetchParamsSourceToken = fetchParams?.sourceToken
const usedGasLimit =
@@ -191,9 +191,11 @@ export default function ViewQuote() {
destinationTokenDecimals,
destinationTokenSymbol,
destinationTokenValue,
+ destinationIconUrl,
sourceTokenDecimals,
sourceTokenSymbol,
sourceTokenValue,
+ sourceTokenIconUrl,
} = renderableDataForUsedQuote
const { feeInFiat, feeInEth } = getRenderableNetworkFeesForQuote(
@@ -508,11 +510,7 @@ export default function ViewQuote() {
/>
)}
-