|
|
|
import React, { Component } from 'react'
|
|
|
|
import PropTypes from 'prop-types'
|
|
|
|
import { connect } from 'react-redux'
|
|
|
|
import { Route, Switch, withRouter, matchPath } from 'react-router-dom'
|
|
|
|
import { compose } from 'recompose'
|
|
|
|
import actions from './actions'
|
|
|
|
import log from 'loglevel'
|
|
|
|
import { getMetaMaskAccounts, getNetworkIdentifier } from './selectors'
|
|
|
|
|
|
|
|
// init
|
|
|
|
import FirstTimeFlow from './components/pages/first-time-flow'
|
|
|
|
// accounts
|
|
|
|
const SendTransactionScreen = require('./components/send/send.container')
|
|
|
|
const ConfirmTransaction = require('./components/pages/confirm-transaction')
|
|
|
|
|
|
|
|
// slideout menu
|
|
|
|
const Sidebar = require('./components/sidebars').default
|
|
|
|
|
|
|
|
// other views
|
|
|
|
import Home from './components/pages/home'
|
|
|
|
import Settings from './components/pages/settings'
|
|
|
|
import Authenticated from './higher-order-components/authenticated'
|
|
|
|
import Initialized from './higher-order-components/initialized'
|
|
|
|
import Lock from './components/pages/lock'
|
|
|
|
const RestoreVaultPage = require('./components/pages/keychains/restore-vault').default
|
|
|
|
const RevealSeedConfirmation = require('./components/pages/keychains/reveal-seed')
|
|
|
|
const AddTokenPage = require('./components/pages/add-token')
|
|
|
|
const ConfirmAddTokenPage = require('./components/pages/confirm-add-token')
|
|
|
|
const ConfirmAddSuggestedTokenPage = require('./components/pages/confirm-add-suggested-token')
|
|
|
|
const CreateAccountPage = require('./components/pages/create-account')
|
|
|
|
const NoticeScreen = require('./components/pages/notice')
|
|
|
|
|
|
|
|
const Loading = require('./components/loading-screen')
|
|
|
|
const LoadingNetwork = require('./components/loading-network-screen').default
|
|
|
|
const NetworkDropdown = require('./components/dropdowns/network-dropdown')
|
|
|
|
import AccountMenu from './components/account-menu'
|
|
|
|
|
|
|
|
// Global Modals
|
|
|
|
const Modal = require('./components/modals/index').Modal
|
|
|
|
// Global Alert
|
|
|
|
const Alert = require('./components/alert')
|
|
|
|
|
|
|
|
import AppHeader from './components/app-header'
|
|
|
|
import UnlockPage from './components/pages/unlock-page'
|
|
|
|
|
|
|
|
import {
|
|
|
|
submittedPendingTransactionsSelector,
|
|
|
|
} from './selectors/transactions'
|
|
|
|
|
|
|
|
// Routes
|
|
|
|
import {
|
|
|
|
DEFAULT_ROUTE,
|
|
|
|
LOCK_ROUTE,
|
|
|
|
UNLOCK_ROUTE,
|
|
|
|
SETTINGS_ROUTE,
|
|
|
|
REVEAL_SEED_ROUTE,
|
|
|
|
RESTORE_VAULT_ROUTE,
|
|
|
|
ADD_TOKEN_ROUTE,
|
|
|
|
CONFIRM_ADD_TOKEN_ROUTE,
|
|
|
|
CONFIRM_ADD_SUGGESTED_TOKEN_ROUTE,
|
|
|
|
NEW_ACCOUNT_ROUTE,
|
|
|
|
SEND_ROUTE,
|
|
|
|
CONFIRM_TRANSACTION_ROUTE,
|
|
|
|
INITIALIZE_ROUTE,
|
|
|
|
INITIALIZE_UNLOCK_ROUTE,
|
|
|
|
NOTICE_ROUTE,
|
|
|
|
} from './routes'
|
|
|
|
|
|
|
|
// enums
|
|
|
|
import {
|
|
|
|
ENVIRONMENT_TYPE_NOTIFICATION,
|
|
|
|
ENVIRONMENT_TYPE_POPUP,
|
|
|
|
} from '../../app/scripts/lib/enums'
|
|
|
|
|
|
|
|
class App extends Component {
|
|
|
|
componentWillMount () {
|
|
|
|
const { currentCurrency, setCurrentCurrencyToUSD } = this.props
|
|
|
|
|
|
|
|
if (!currentCurrency) {
|
|
|
|
setCurrentCurrencyToUSD()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
renderRoutes () {
|
|
|
|
return (
|
|
|
|
<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={SETTINGS_ROUTE} component={Settings} />
|
|
|
|
<Authenticated path={NOTICE_ROUTE} component={NoticeScreen} exact />
|
|
|
|
<Authenticated path={`${CONFIRM_TRANSACTION_ROUTE}/:id?`} component={ConfirmTransaction} />
|
|
|
|
<Authenticated path={SEND_ROUTE} component={SendTransactionScreen} exact />
|
|
|
|
<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={DEFAULT_ROUTE} component={Home} exact />
|
|
|
|
</Switch>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
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 }))
|
|
|
|
}
|
|
|
|
|
|
|
|
hasProviderRequests () {
|
|
|
|
const { providerRequests } = this.props
|
|
|
|
return Array.isArray(providerRequests) && providerRequests.length > 0
|
|
|
|
}
|
|
|
|
|
|
|
|
hideAppHeader () {
|
|
|
|
const { location } = this.props
|
|
|
|
|
|
|
|
const isInitializing = Boolean(matchPath(location.pathname, {
|
|
|
|
path: INITIALIZE_ROUTE, exact: false,
|
|
|
|
}))
|
|
|
|
|
|
|
|
if (isInitializing && !this.onInitializationUnlockPage()) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
if (window.METAMASK_UI_TYPE === ENVIRONMENT_TYPE_NOTIFICATION) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
if (window.METAMASK_UI_TYPE === ENVIRONMENT_TYPE_POPUP) {
|
|
|
|
return this.onConfirmPage() || this.hasProviderRequests()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
render () {
|
|
|
|
const {
|
|
|
|
isLoading,
|
|
|
|
alertMessage,
|
|
|
|
loadingMessage,
|
|
|
|
network,
|
|
|
|
provider,
|
|
|
|
frequentRpcListDetail,
|
|
|
|
currentView,
|
|
|
|
setMouseUserState,
|
|
|
|
sidebar,
|
|
|
|
submittedPendingTransactions,
|
|
|
|
} = this.props
|
|
|
|
const isLoadingNetwork = network === 'loading' && currentView.name !== 'config'
|
|
|
|
const loadMessage = loadingMessage || isLoadingNetwork ?
|
|
|
|
this.getConnectingLabel(loadingMessage) : null
|
|
|
|
log.debug('Main ui render function')
|
|
|
|
|
|
|
|
const {
|
|
|
|
isOpen: sidebarIsOpen,
|
|
|
|
transitionName: sidebarTransitionName,
|
|
|
|
type: sidebarType,
|
|
|
|
props,
|
|
|
|
} = sidebar
|
|
|
|
const { transaction: sidebarTransaction } = props || {}
|
|
|
|
|
|
|
|
return (
|
|
|
|
<div
|
|
|
|
className="app"
|
|
|
|
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()}
|
|
|
|
disabled={this.onConfirmPage()}
|
|
|
|
/>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
<Sidebar
|
|
|
|
sidebarOpen={sidebarIsOpen}
|
|
|
|
sidebarShouldClose={sidebarTransaction && !submittedPendingTransactions.find(({ id }) => id === sidebarTransaction.id)}
|
|
|
|
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>
|
|
|
|
</div>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
toggleMetamaskActive () {
|
|
|
|
if (!this.props.isUnlocked) {
|
|
|
|
// currently inactive: redirect to password box
|
|
|
|
var passwordBox = document.querySelector('input[type=password]')
|
|
|
|
if (!passwordBox) return
|
|
|
|
passwordBox.focus()
|
|
|
|
} else {
|
|
|
|
// currently active: deactivate
|
|
|
|
this.props.dispatch(actions.lockMetamask(false))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
getConnectingLabel = function (loadingMessage) {
|
|
|
|
if (loadingMessage) {
|
|
|
|
return loadingMessage
|
|
|
|
}
|
|
|
|
const { provider, providerId } = this.props
|
|
|
|
const providerName = provider.type
|
|
|
|
|
|
|
|
let name
|
|
|
|
|
|
|
|
if (providerName === 'mainnet') {
|
|
|
|
name = this.context.t('connectingToMainnet')
|
|
|
|
} else if (providerName === 'ropsten') {
|
|
|
|
name = this.context.t('connectingToRopsten')
|
|
|
|
} else if (providerName === 'kovan') {
|
|
|
|
name = this.context.t('connectingToKovan')
|
|
|
|
} else if (providerName === 'rinkeby') {
|
|
|
|
name = this.context.t('connectingToRinkeby')
|
|
|
|
} else {
|
|
|
|
name = this.context.t('connectingTo', [providerId])
|
|
|
|
}
|
|
|
|
|
|
|
|
return name
|
|
|
|
}
|
|
|
|
|
|
|
|
getNetworkName () {
|
|
|
|
const { provider } = this.props
|
|
|
|
const providerName = provider.type
|
|
|
|
|
|
|
|
let name
|
|
|
|
|
|
|
|
if (providerName === 'mainnet') {
|
|
|
|
name = this.context.t('mainnet')
|
|
|
|
} else if (providerName === 'ropsten') {
|
|
|
|
name = this.context.t('ropsten')
|
|
|
|
} else if (providerName === 'kovan') {
|
|
|
|
name = this.context.t('kovan')
|
|
|
|
} else if (providerName === 'rinkeby') {
|
|
|
|
name = this.context.t('rinkeby')
|
|
|
|
} else {
|
|
|
|
name = this.context.t('unknownNetwork')
|
|
|
|
}
|
|
|
|
|
|
|
|
return name
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
App.propTypes = {
|
|
|
|
currentCurrency: PropTypes.string,
|
|
|
|
setCurrentCurrencyToUSD: PropTypes.func,
|
|
|
|
isLoading: PropTypes.bool,
|
|
|
|
loadingMessage: PropTypes.string,
|
|
|
|
alertMessage: PropTypes.string,
|
|
|
|
network: PropTypes.string,
|
|
|
|
provider: PropTypes.object,
|
|
|
|
frequentRpcListDetail: PropTypes.array,
|
|
|
|
currentView: PropTypes.object,
|
|
|
|
sidebar: PropTypes.object,
|
|
|
|
alertOpen: PropTypes.bool,
|
|
|
|
hideSidebar: PropTypes.func,
|
|
|
|
isMascara: PropTypes.bool,
|
|
|
|
isOnboarding: PropTypes.bool,
|
|
|
|
isUnlocked: PropTypes.bool,
|
|
|
|
networkDropdownOpen: PropTypes.bool,
|
|
|
|
showNetworkDropdown: PropTypes.func,
|
|
|
|
hideNetworkDropdown: PropTypes.func,
|
|
|
|
history: PropTypes.object,
|
|
|
|
location: PropTypes.object,
|
|
|
|
dispatch: PropTypes.func,
|
|
|
|
toggleAccountMenu: PropTypes.func,
|
|
|
|
selectedAddress: PropTypes.string,
|
|
|
|
noActiveNotices: PropTypes.bool,
|
|
|
|
lostAccounts: PropTypes.array,
|
|
|
|
isInitialized: PropTypes.bool,
|
|
|
|
forgottenPassword: PropTypes.bool,
|
|
|
|
activeAddress: PropTypes.string,
|
|
|
|
unapprovedTxs: PropTypes.object,
|
|
|
|
seedWords: PropTypes.string,
|
|
|
|
submittedPendingTransactions: PropTypes.array,
|
|
|
|
unapprovedMsgCount: PropTypes.number,
|
|
|
|
unapprovedPersonalMsgCount: PropTypes.number,
|
|
|
|
unapprovedTypedMessagesCount: PropTypes.number,
|
|
|
|
welcomeScreenSeen: PropTypes.bool,
|
|
|
|
isPopup: PropTypes.bool,
|
|
|
|
isMouseUser: PropTypes.bool,
|
|
|
|
setMouseUserState: PropTypes.func,
|
|
|
|
t: PropTypes.func,
|
|
|
|
providerId: PropTypes.string,
|
|
|
|
providerRequests: PropTypes.array,
|
|
|
|
}
|
|
|
|
|
|
|
|
function mapStateToProps (state) {
|
|
|
|
const { appState, metamask } = state
|
|
|
|
const {
|
|
|
|
networkDropdownOpen,
|
|
|
|
sidebar,
|
|
|
|
alertOpen,
|
|
|
|
alertMessage,
|
|
|
|
isLoading,
|
|
|
|
loadingMessage,
|
|
|
|
} = appState
|
|
|
|
|
|
|
|
const accounts = getMetaMaskAccounts(state)
|
|
|
|
|
|
|
|
const {
|
|
|
|
identities,
|
|
|
|
address,
|
|
|
|
keyrings,
|
|
|
|
isInitialized,
|
|
|
|
noActiveNotices,
|
|
|
|
seedWords,
|
|
|
|
unapprovedTxs,
|
|
|
|
nextUnreadNotice,
|
|
|
|
lostAccounts,
|
|
|
|
unapprovedMsgCount,
|
|
|
|
unapprovedPersonalMsgCount,
|
|
|
|
unapprovedTypedMessagesCount,
|
|
|
|
providerRequests,
|
|
|
|
} = metamask
|
|
|
|
const selected = address || Object.keys(accounts)[0]
|
|
|
|
|
|
|
|
return {
|
|
|
|
// state from plugin
|
|
|
|
networkDropdownOpen,
|
|
|
|
sidebar,
|
|
|
|
alertOpen,
|
|
|
|
alertMessage,
|
|
|
|
isLoading,
|
|
|
|
loadingMessage,
|
|
|
|
noActiveNotices,
|
|
|
|
isInitialized,
|
|
|
|
isUnlocked: state.metamask.isUnlocked,
|
|
|
|
selectedAddress: state.metamask.selectedAddress,
|
|
|
|
currentView: state.appState.currentView,
|
|
|
|
activeAddress: state.appState.activeAddress,
|
|
|
|
transForward: state.appState.transForward,
|
|
|
|
isMascara: state.metamask.isMascara,
|
|
|
|
isOnboarding: Boolean(!noActiveNotices || seedWords || !isInitialized),
|
|
|
|
isPopup: state.metamask.isPopup,
|
|
|
|
seedWords: state.metamask.seedWords,
|
|
|
|
submittedPendingTransactions: submittedPendingTransactionsSelector(state),
|
|
|
|
unapprovedTxs,
|
|
|
|
unapprovedMsgs: state.metamask.unapprovedMsgs,
|
|
|
|
unapprovedMsgCount,
|
|
|
|
unapprovedPersonalMsgCount,
|
|
|
|
unapprovedTypedMessagesCount,
|
|
|
|
menuOpen: state.appState.menuOpen,
|
|
|
|
network: state.metamask.network,
|
|
|
|
provider: state.metamask.provider,
|
|
|
|
forgottenPassword: state.appState.forgottenPassword,
|
|
|
|
nextUnreadNotice,
|
|
|
|
lostAccounts,
|
|
|
|
frequentRpcListDetail: state.metamask.frequentRpcListDetail || [],
|
|
|
|
currentCurrency: state.metamask.currentCurrency,
|
|
|
|
isMouseUser: state.appState.isMouseUser,
|
|
|
|
isRevealingSeedWords: state.metamask.isRevealingSeedWords,
|
|
|
|
Qr: state.appState.Qr,
|
|
|
|
welcomeScreenSeen: state.metamask.welcomeScreenSeen,
|
|
|
|
providerId: getNetworkIdentifier(state),
|
|
|
|
|
|
|
|
// state needed to get account dropdown temporarily rendering from app bar
|
|
|
|
identities,
|
|
|
|
selected,
|
|
|
|
keyrings,
|
|
|
|
providerRequests,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function mapDispatchToProps (dispatch, ownProps) {
|
|
|
|
return {
|
|
|
|
dispatch,
|
|
|
|
hideSidebar: () => dispatch(actions.hideSidebar()),
|
|
|
|
showNetworkDropdown: () => dispatch(actions.showNetworkDropdown()),
|
|
|
|
hideNetworkDropdown: () => dispatch(actions.hideNetworkDropdown()),
|
|
|
|
setCurrentCurrencyToUSD: () => dispatch(actions.setCurrentCurrency('usd')),
|
|
|
|
toggleAccountMenu: () => dispatch(actions.toggleAccountMenu()),
|
|
|
|
setMouseUserState: (isMouseUser) => dispatch(actions.setMouseUserState(isMouseUser)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
App.contextTypes = {
|
|
|
|
t: PropTypes.func,
|
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = compose(
|
|
|
|
withRouter,
|
|
|
|
connect(mapStateToProps, mapDispatchToProps)
|
|
|
|
)(App)
|