mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
b98b0b6d01
The app header would sometimes mistakenly not get rendered while on the Home screen. This could happen when a permission request was made while the UI was open, to either the browser action popup or the fullscreen UI. This was caused by faulty logic in the top-level router component. It would hide the app header if there was a pending permission request, presumably because the author assumed that a redirect to the permission flow would shortly follow. This redirect only happens on mount though, not if the UI was already open when the permission request was submitted. The intent of this logic was to hide a brief flash of the app header prior to rendering the permission flow. This brief flash has now been restored, which is unfortunate, but is better than the missing app header bug. We can revisit a solution to removing this flash in the future, hopefully in a manner that avoids this bug and works for all notification UI cases.
340 lines
10 KiB
JavaScript
340 lines
10 KiB
JavaScript
import classnames from 'classnames'
|
|
import PropTypes from 'prop-types'
|
|
import React, { Component } from 'react'
|
|
import { matchPath, Route, Switch } from 'react-router-dom'
|
|
import IdleTimer from 'react-idle-timer'
|
|
|
|
import FirstTimeFlow from '../first-time-flow'
|
|
import SendTransactionScreen from '../send'
|
|
import Swaps from '../swaps'
|
|
import ConfirmTransaction from '../confirm-transaction'
|
|
import Sidebar from '../../components/app/sidebars'
|
|
import Home from '../home'
|
|
import Settings from '../settings'
|
|
import Authenticated from '../../helpers/higher-order-components/authenticated'
|
|
import Initialized from '../../helpers/higher-order-components/initialized'
|
|
import Lock from '../lock'
|
|
import PermissionsConnect from '../permissions-connect'
|
|
import RestoreVaultPage from '../keychains/restore-vault'
|
|
import RevealSeedConfirmation from '../keychains/reveal-seed'
|
|
import MobileSyncPage from '../mobile-sync'
|
|
import AddTokenPage from '../add-token'
|
|
import ConfirmAddTokenPage from '../confirm-add-token'
|
|
import ConfirmAddSuggestedTokenPage from '../confirm-add-suggested-token'
|
|
import CreateAccountPage from '../create-account'
|
|
import Loading from '../../components/ui/loading-screen'
|
|
import LoadingNetwork from '../../components/app/loading-network-screen'
|
|
import NetworkDropdown from '../../components/app/dropdowns/network-dropdown'
|
|
import AccountMenu from '../../components/app/account-menu'
|
|
import { Modal } from '../../components/app/modals'
|
|
import Alert from '../../components/ui/alert'
|
|
import AppHeader from '../../components/app/app-header'
|
|
import UnlockPage from '../unlock-page'
|
|
import Alerts from '../../components/app/alerts'
|
|
import Asset from '../asset'
|
|
|
|
import {
|
|
ADD_TOKEN_ROUTE,
|
|
ASSET_ROUTE,
|
|
CONFIRM_ADD_SUGGESTED_TOKEN_ROUTE,
|
|
CONFIRM_ADD_TOKEN_ROUTE,
|
|
CONFIRM_TRANSACTION_ROUTE,
|
|
CONNECT_ROUTE,
|
|
DEFAULT_ROUTE,
|
|
INITIALIZE_ROUTE,
|
|
INITIALIZE_UNLOCK_ROUTE,
|
|
LOCK_ROUTE,
|
|
MOBILE_SYNC_ROUTE,
|
|
NEW_ACCOUNT_ROUTE,
|
|
RESTORE_VAULT_ROUTE,
|
|
REVEAL_SEED_ROUTE,
|
|
SEND_ROUTE,
|
|
SWAPS_ROUTE,
|
|
SETTINGS_ROUTE,
|
|
UNLOCK_ROUTE,
|
|
} from '../../helpers/constants/routes'
|
|
|
|
import { ENVIRONMENT_TYPE_NOTIFICATION, ENVIRONMENT_TYPE_POPUP } from '../../../../app/scripts/lib/enums'
|
|
import { getEnvironmentType } from '../../../../app/scripts/lib/util'
|
|
|
|
export default class Routes extends Component {
|
|
static propTypes = {
|
|
currentCurrency: PropTypes.string,
|
|
setCurrentCurrencyToUSD: PropTypes.func,
|
|
isLoading: PropTypes.bool,
|
|
loadingMessage: PropTypes.string,
|
|
alertMessage: PropTypes.string,
|
|
textDirection: PropTypes.string,
|
|
network: PropTypes.string,
|
|
provider: PropTypes.object,
|
|
frequentRpcListDetail: PropTypes.array,
|
|
sidebar: PropTypes.object,
|
|
alertOpen: PropTypes.bool,
|
|
hideSidebar: PropTypes.func,
|
|
isUnlocked: PropTypes.bool,
|
|
setLastActiveTime: PropTypes.func,
|
|
history: PropTypes.object,
|
|
location: PropTypes.object,
|
|
lockMetaMask: PropTypes.func,
|
|
submittedPendingTransactions: PropTypes.array,
|
|
isMouseUser: PropTypes.bool,
|
|
setMouseUserState: PropTypes.func,
|
|
providerId: PropTypes.string,
|
|
autoLockTimeLimit: PropTypes.number,
|
|
pageChanged: PropTypes.func.isRequired,
|
|
prepareToLeaveSwaps: PropTypes.func,
|
|
}
|
|
|
|
static contextTypes = {
|
|
t: PropTypes.func,
|
|
metricsEvent: PropTypes.func,
|
|
}
|
|
|
|
UNSAFE_componentWillMount () {
|
|
const { currentCurrency, pageChanged, setCurrentCurrencyToUSD } = this.props
|
|
|
|
if (!currentCurrency) {
|
|
setCurrentCurrencyToUSD()
|
|
}
|
|
|
|
this.props.history.listen((locationObj, action) => {
|
|
if (action === 'PUSH') {
|
|
pageChanged(locationObj.pathname)
|
|
this.context.metricsEvent({}, {
|
|
currentPath: locationObj.pathname,
|
|
pageOpts: {
|
|
hideDimensions: true,
|
|
},
|
|
})
|
|
}
|
|
})
|
|
}
|
|
|
|
renderRoutes () {
|
|
const { autoLockTimeLimit, setLastActiveTime } = this.props
|
|
|
|
const routes = (
|
|
<Switch>
|
|
<Route path={LOCK_ROUTE} component={Lock} exact />
|
|
<Route path={INITIALIZE_ROUTE} component={FirstTimeFlow} />
|
|
<Initialized path={UNLOCK_ROUTE} component={UnlockPage} exact />
|
|
<Initialized path={RESTORE_VAULT_ROUTE} component={RestoreVaultPage} exact />
|
|
<Authenticated path={REVEAL_SEED_ROUTE} component={RevealSeedConfirmation} exact />
|
|
<Authenticated path={MOBILE_SYNC_ROUTE} component={MobileSyncPage} exact />
|
|
<Authenticated path={SETTINGS_ROUTE} component={Settings} />
|
|
<Authenticated path={`${CONFIRM_TRANSACTION_ROUTE}/:id?`} component={ConfirmTransaction} />
|
|
<Authenticated path={SEND_ROUTE} component={SendTransactionScreen} exact />
|
|
<Authenticated path={SWAPS_ROUTE} component={Swaps} />
|
|
<Authenticated path={ADD_TOKEN_ROUTE} component={AddTokenPage} exact />
|
|
<Authenticated path={CONFIRM_ADD_TOKEN_ROUTE} component={ConfirmAddTokenPage} exact />
|
|
<Authenticated path={CONFIRM_ADD_SUGGESTED_TOKEN_ROUTE} component={ConfirmAddSuggestedTokenPage} exact />
|
|
<Authenticated path={NEW_ACCOUNT_ROUTE} component={CreateAccountPage} />
|
|
<Authenticated path={`${CONNECT_ROUTE}/:id`} component={PermissionsConnect} />
|
|
<Authenticated path={`${ASSET_ROUTE}/:asset`} component={Asset} />
|
|
<Authenticated path={DEFAULT_ROUTE} component={Home} />
|
|
</Switch>
|
|
)
|
|
|
|
if (autoLockTimeLimit > 0) {
|
|
return (
|
|
<IdleTimer onAction={setLastActiveTime} throttle={1000}>
|
|
{routes}
|
|
</IdleTimer>
|
|
)
|
|
}
|
|
|
|
return routes
|
|
}
|
|
|
|
onInitializationUnlockPage () {
|
|
const { location } = this.props
|
|
return Boolean(matchPath(location.pathname, { path: INITIALIZE_UNLOCK_ROUTE, exact: true }))
|
|
}
|
|
|
|
onConfirmPage () {
|
|
const { location } = this.props
|
|
return Boolean(matchPath(location.pathname, { path: CONFIRM_TRANSACTION_ROUTE, exact: false }))
|
|
}
|
|
|
|
onSwapsPage () {
|
|
const { location } = this.props
|
|
return Boolean(matchPath(location.pathname, { path: SWAPS_ROUTE, exact: false }))
|
|
}
|
|
|
|
hideAppHeader () {
|
|
const { location } = this.props
|
|
|
|
const isInitializing = Boolean(matchPath(location.pathname, {
|
|
path: INITIALIZE_ROUTE, exact: false,
|
|
}))
|
|
|
|
if (isInitializing && !this.onInitializationUnlockPage()) {
|
|
return true
|
|
}
|
|
|
|
const windowType = getEnvironmentType()
|
|
|
|
if (windowType === ENVIRONMENT_TYPE_NOTIFICATION) {
|
|
return true
|
|
}
|
|
|
|
if (windowType === ENVIRONMENT_TYPE_POPUP && this.onConfirmPage()) {
|
|
return true
|
|
}
|
|
|
|
const isHandlingPermissionsRequest = Boolean(matchPath(location.pathname, {
|
|
path: CONNECT_ROUTE, exact: false,
|
|
}))
|
|
|
|
return isHandlingPermissionsRequest
|
|
}
|
|
|
|
render () {
|
|
const {
|
|
isLoading,
|
|
isUnlocked,
|
|
alertMessage,
|
|
textDirection,
|
|
loadingMessage,
|
|
network,
|
|
provider,
|
|
frequentRpcListDetail,
|
|
setMouseUserState,
|
|
sidebar,
|
|
submittedPendingTransactions,
|
|
isMouseUser,
|
|
prepareToLeaveSwaps,
|
|
} = this.props
|
|
const isLoadingNetwork = network === 'loading'
|
|
const loadMessage = (loadingMessage || isLoadingNetwork)
|
|
? this.getConnectingLabel(loadingMessage)
|
|
: null
|
|
|
|
const {
|
|
isOpen: sidebarIsOpen,
|
|
transitionName: sidebarTransitionName,
|
|
type: sidebarType,
|
|
props,
|
|
} = sidebar
|
|
const { transaction: sidebarTransaction } = props || {}
|
|
|
|
const sidebarShouldClose = sidebarTransaction &&
|
|
!sidebarTransaction.status === 'failed' &&
|
|
!submittedPendingTransactions.find(({ id }) => id === sidebarTransaction.id)
|
|
|
|
return (
|
|
<div
|
|
className={classnames('app', { 'mouse-user-styles': isMouseUser })}
|
|
dir={textDirection}
|
|
onClick={() => setMouseUserState(true)}
|
|
onKeyDown={(e) => {
|
|
if (e.keyCode === 9) {
|
|
setMouseUserState(false)
|
|
}
|
|
}}
|
|
>
|
|
<Modal />
|
|
<Alert
|
|
visible={this.props.alertOpen}
|
|
msg={alertMessage}
|
|
/>
|
|
{ !this.hideAppHeader() && (
|
|
<AppHeader
|
|
hideNetworkIndicator={this.onInitializationUnlockPage()}
|
|
disableNetworkIndicator={this.onSwapsPage()}
|
|
onClick={async () => {
|
|
if (this.onSwapsPage()) {
|
|
await prepareToLeaveSwaps()
|
|
|
|
}
|
|
}}
|
|
disabled={this.onConfirmPage()}
|
|
/>
|
|
) }
|
|
<Sidebar
|
|
sidebarOpen={sidebarIsOpen}
|
|
sidebarShouldClose={sidebarShouldClose}
|
|
hideSidebar={this.props.hideSidebar}
|
|
transitionName={sidebarTransitionName}
|
|
type={sidebarType}
|
|
sidebarProps={sidebar.props}
|
|
/>
|
|
<NetworkDropdown
|
|
provider={provider}
|
|
frequentRpcListDetail={frequentRpcListDetail}
|
|
/>
|
|
<AccountMenu />
|
|
<div className="main-container-wrapper">
|
|
{ isLoading && <Loading loadingMessage={loadMessage} /> }
|
|
{ !isLoading && isLoadingNetwork && <LoadingNetwork /> }
|
|
{ this.renderRoutes() }
|
|
</div>
|
|
{
|
|
isUnlocked
|
|
? (
|
|
<Alerts history={this.props.history} />
|
|
)
|
|
: null
|
|
}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
toggleMetamaskActive () {
|
|
if (this.props.isUnlocked) {
|
|
// currently active: deactivate
|
|
this.props.lockMetaMask()
|
|
} else {
|
|
// currently inactive: redirect to password box
|
|
const passwordBox = document.querySelector('input[type=password]')
|
|
if (!passwordBox) {
|
|
return
|
|
}
|
|
passwordBox.focus()
|
|
}
|
|
}
|
|
|
|
getConnectingLabel (loadingMessage) {
|
|
if (loadingMessage) {
|
|
return loadingMessage
|
|
}
|
|
const { provider, providerId } = this.props
|
|
|
|
switch (provider.type) {
|
|
case 'mainnet':
|
|
return this.context.t('connectingToMainnet')
|
|
case 'ropsten':
|
|
return this.context.t('connectingToRopsten')
|
|
case 'kovan':
|
|
return this.context.t('connectingToKovan')
|
|
case 'rinkeby':
|
|
return this.context.t('connectingToRinkeby')
|
|
case 'localhost':
|
|
return this.context.t('connectingToLocalhost')
|
|
case 'goerli':
|
|
return this.context.t('connectingToGoerli')
|
|
default:
|
|
return this.context.t('connectingTo', [providerId])
|
|
}
|
|
}
|
|
|
|
getNetworkName () {
|
|
switch (this.props.provider.type) {
|
|
case 'mainnet':
|
|
return this.context.t('mainnet')
|
|
case 'ropsten':
|
|
return this.context.t('ropsten')
|
|
case 'kovan':
|
|
return this.context.t('kovan')
|
|
case 'rinkeby':
|
|
return this.context.t('rinkeby')
|
|
case 'localhost':
|
|
return this.context.t('localhost')
|
|
case 'goerli':
|
|
return this.context.t('goerli')
|
|
default:
|
|
return this.context.t('unknownNetwork')
|
|
}
|
|
}
|
|
}
|