1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-11-24 11:01:41 +01:00
metamask-extension/ui/app/pages/home/home.component.js
Mark Stacey d1e8f5db86
Prevent redirecting to swaps in notification (#9497)
If a notification popup was opened while the user was partway through
the swaps flow, the notification would display the swaps flow instead
of whatever action triggered the popup (e.g. a connect request or a
confirmation). This is confusing and potentially dangerous, as the
user might mistakenly think the swap was triggered by a dapp.

The swap redirects are now prevented in the notification UI. The user
will still be redirected to an in-progress swap flow if they open the
browser action popup or the fullscreen UI, but not on the notification
popup that is triggered by dapp actions.
2020-10-07 13:49:47 -02:30

323 lines
9.9 KiB
JavaScript

import React, { PureComponent } from 'react'
import PropTypes from 'prop-types'
import { Redirect, Route } from 'react-router-dom'
import { formatDate } from '../../helpers/utils/util'
import AssetList from '../../components/app/asset-list'
import HomeNotification from '../../components/app/home-notification'
import MultipleNotifications from '../../components/app/multiple-notifications'
import TransactionList from '../../components/app/transaction-list'
import MenuBar from '../../components/app/menu-bar'
import Popover from '../../components/ui/popover'
import Button from '../../components/ui/button'
import ConnectedSites from '../connected-sites'
import ConnectedAccounts from '../connected-accounts'
import { Tabs, Tab } from '../../components/ui/tabs'
import { EthOverview } from '../../components/app/wallet-overview'
import SwapsIntroPopup from '../swaps/intro-popup'
import {
ASSET_ROUTE,
RESTORE_VAULT_ROUTE,
CONFIRM_TRANSACTION_ROUTE,
CONFIRM_ADD_SUGGESTED_TOKEN_ROUTE,
INITIALIZE_BACKUP_SEED_PHRASE_ROUTE,
CONNECT_ROUTE,
CONNECTED_ROUTE,
CONNECTED_ACCOUNTS_ROUTE,
AWAITING_SWAP_ROUTE,
BUILD_QUOTE_ROUTE,
VIEW_QUOTE_ROUTE,
} from '../../helpers/constants/routes'
const LEARN_MORE_URL = 'https://metamask.zendesk.com/hc/en-us/articles/360045129011-Intro-to-MetaMask-v8-extension'
export default class Home extends PureComponent {
static contextTypes = {
t: PropTypes.func,
}
static propTypes = {
history: PropTypes.object,
forgottenPassword: PropTypes.bool,
suggestedTokens: PropTypes.object,
unconfirmedTransactionsCount: PropTypes.number,
shouldShowSeedPhraseReminder: PropTypes.bool,
isPopup: PropTypes.bool,
isNotification: PropTypes.bool.isRequired,
threeBoxSynced: PropTypes.bool,
setupThreeBox: PropTypes.func,
turnThreeBoxSyncingOn: PropTypes.func,
showRestorePrompt: PropTypes.bool,
selectedAddress: PropTypes.string,
restoreFromThreeBox: PropTypes.func,
setShowRestorePromptToFalse: PropTypes.func,
threeBoxLastUpdated: PropTypes.number,
firstPermissionsRequestId: PropTypes.string,
totalUnapprovedCount: PropTypes.number.isRequired,
setConnectedStatusPopoverHasBeenShown: PropTypes.func,
connectedStatusPopoverHasBeenShown: PropTypes.bool,
defaultHomeActiveTabName: PropTypes.string,
onTabClick: PropTypes.func.isRequired,
setSwapsWelcomeMessageHasBeenShown: PropTypes.func.isRequired,
swapsWelcomeMessageHasBeenShown: PropTypes.bool.isRequired,
haveSwapsQuotes: PropTypes.bool.isRequired,
showAwaitingSwapScreen: PropTypes.bool.isRequired,
swapsFetchParams: PropTypes.object,
swapsEnabled: PropTypes.bool,
}
state = {
mounted: false,
}
componentDidMount () {
const {
firstPermissionsRequestId,
history,
isNotification,
suggestedTokens = {},
totalUnapprovedCount,
unconfirmedTransactionsCount,
haveSwapsQuotes,
showAwaitingSwapScreen,
swapsFetchParams,
} = this.props
this.setState({ mounted: true })
if (isNotification && totalUnapprovedCount === 0) {
global.platform.closeCurrentWindow()
} else if (!isNotification && showAwaitingSwapScreen) {
history.push(AWAITING_SWAP_ROUTE)
} else if (!isNotification && haveSwapsQuotes) {
history.push(VIEW_QUOTE_ROUTE)
} else if (!isNotification && swapsFetchParams) {
history.push(BUILD_QUOTE_ROUTE)
} else if (firstPermissionsRequestId) {
history.push(`${CONNECT_ROUTE}/${firstPermissionsRequestId}`)
} else if (unconfirmedTransactionsCount > 0) {
history.push(CONFIRM_TRANSACTION_ROUTE)
} else if (Object.keys(suggestedTokens).length > 0) {
history.push(CONFIRM_ADD_SUGGESTED_TOKEN_ROUTE)
}
}
static getDerivedStateFromProps (
{
firstPermissionsRequestId,
isNotification,
suggestedTokens,
totalUnapprovedCount,
unconfirmedTransactionsCount,
haveSwapsQuotes,
showAwaitingSwapScreen,
swapsFetchParams,
},
{ mounted },
) {
if (!mounted) {
if (isNotification && totalUnapprovedCount === 0) {
return { closing: true }
} else if (
firstPermissionsRequestId ||
unconfirmedTransactionsCount > 0 ||
Object.keys(suggestedTokens).length > 0 ||
(
!isNotification && (
showAwaitingSwapScreen ||
haveSwapsQuotes ||
swapsFetchParams
)
)
) {
return { redirecting: true }
}
}
return null
}
componentDidUpdate (_, prevState) {
const {
setupThreeBox,
showRestorePrompt,
threeBoxLastUpdated,
threeBoxSynced,
} = this.props
if (!prevState.closing && this.state.closing) {
global.platform.closeCurrentWindow()
}
if (threeBoxSynced && showRestorePrompt && threeBoxLastUpdated === null) {
setupThreeBox()
}
}
renderNotifications () {
const { t } = this.context
const {
history,
shouldShowSeedPhraseReminder,
isPopup,
selectedAddress,
restoreFromThreeBox,
turnThreeBoxSyncingOn,
setShowRestorePromptToFalse,
showRestorePrompt,
threeBoxLastUpdated,
} = this.props
return (
<MultipleNotifications>
{
shouldShowSeedPhraseReminder
? (
<HomeNotification
descriptionText={t('backupApprovalNotice')}
acceptText={t('backupNow')}
onAccept={() => {
if (isPopup) {
global.platform.openExtensionInBrowser(INITIALIZE_BACKUP_SEED_PHRASE_ROUTE)
} else {
history.push(INITIALIZE_BACKUP_SEED_PHRASE_ROUTE)
}
}}
infoText={t('backupApprovalInfo')}
key="home-backupApprovalNotice"
/>
)
: null
}
{
threeBoxLastUpdated && showRestorePrompt
? (
<HomeNotification
descriptionText={t('restoreWalletPreferences', [formatDate(threeBoxLastUpdated, 'M/d/y')])}
acceptText={t('restore')}
ignoreText={t('noThanks')}
infoText={t('dataBackupFoundInfo')}
onAccept={() => {
restoreFromThreeBox(selectedAddress)
.then(() => {
turnThreeBoxSyncingOn()
})
}}
onIgnore={() => {
setShowRestorePromptToFalse()
}}
key="home-privacyModeDefault"
/>
)
: null
}
</MultipleNotifications>
)
}
renderPopover = () => {
const { setConnectedStatusPopoverHasBeenShown } = this.props
const { t } = this.context
return (
<Popover
title={ t('whatsThis') }
onClose={setConnectedStatusPopoverHasBeenShown}
className="home__connected-status-popover"
showArrow
CustomBackground={({ onClose }) => {
return (
<div
className="home__connected-status-popover-bg-container"
onClick={onClose}
>
<div className="home__connected-status-popover-bg" />
</div>
)
}}
footer={(
<>
<a
href={LEARN_MORE_URL}
target="_blank"
rel="noopener noreferrer"
>
{ t('learnMore') }
</a>
<Button
type="primary"
onClick={setConnectedStatusPopoverHasBeenShown}
>
{ t('dismiss') }
</Button>
</>
)}
>
<main className="home__connect-status-text">
<div>{ t('metaMaskConnectStatusParagraphOne') }</div>
<div>{ t('metaMaskConnectStatusParagraphTwo') }</div>
<div>{ t('metaMaskConnectStatusParagraphThree') }</div>
</main>
</Popover>
)
}
render () {
const { t } = this.context
const {
defaultHomeActiveTabName,
onTabClick,
forgottenPassword,
history,
connectedStatusPopoverHasBeenShown,
isPopup,
swapsWelcomeMessageHasBeenShown,
setSwapsWelcomeMessageHasBeenShown,
swapsEnabled,
} = this.props
if (forgottenPassword) {
return <Redirect to={{ pathname: RESTORE_VAULT_ROUTE }} />
} else if (this.state.closing || this.state.redirecting) {
return null
}
return (
<div className="main-container">
<Route path={CONNECTED_ROUTE} component={ConnectedSites} exact />
<Route path={CONNECTED_ACCOUNTS_ROUTE} component={ConnectedAccounts} exact />
<div className="home__container">
{!swapsWelcomeMessageHasBeenShown && swapsEnabled ? (
<SwapsIntroPopup onClose={setSwapsWelcomeMessageHasBeenShown} />
) : null}
{ isPopup && !connectedStatusPopoverHasBeenShown ? this.renderPopover() : null }
<div className="home__main-view">
<MenuBar />
<div className="home__balance-wrapper">
<EthOverview />
</div>
<Tabs defaultActiveTabName={defaultHomeActiveTabName} onTabClick={onTabClick} tabsClassName="home__tabs">
<Tab
activeClassName="home__tab--active"
className="home__tab"
data-testid="home__asset-tab"
name={t('assets')}
>
<AssetList
onClickAsset={(asset) => history.push(`${ASSET_ROUTE}/${asset}`)}
/>
</Tab>
<Tab
activeClassName="home__tab--active"
className="home__tab"
data-testid="home__activity-tab"
name={t('activity')}
>
<TransactionList />
</Tab>
</Tabs>
</div>
{ this.renderNotifications() }
</div>
</div>
)
}
}