nifty-wallet/ui/app/app.js

500 lines
14 KiB
JavaScript
Raw Normal View History

const { Component } = require('react')
2017-12-04 00:06:09 -08:00
const PropTypes = require('prop-types')
const connect = require('react-redux').connect
2018-04-02 16:24:37 -07:00
const { Route, Switch, withRouter } = require('react-router-dom')
const { compose } = require('recompose')
const h = require('react-hyperscript')
const actions = require('./actions')
2018-01-09 16:45:39 -08:00
const classnames = require('classnames')
const log = require('loglevel')
2018-01-09 16:45:39 -08:00
// init
const InitializeScreen = require('../../mascara/src/app/first-time').default
// accounts
const SendTransactionScreen2 = require('./components/send/send-v2-container')
const ConfirmTxScreen = require('./conf-tx')
// slideout menu
const WalletView = require('./components/wallet-view')
// other views
const Home = require('./components/pages/home')
const Authenticated = require('./components/pages/authenticated')
const Initialized = require('./components/pages/initialized')
const Settings = require('./components/pages/settings')
const UnlockPage = require('./components/pages/unlock')
const RestoreVaultPage = require('./components/pages/keychains/restore-vault')
2018-04-03 17:43:42 -07:00
const RevealSeedConfirmation = require('./keychains/hd/recover-seed/confirmation')
const AddTokenPage = require('./components/pages/add-token')
const CreateAccountPage = require('./components/pages/create-account')
const NoticeScreen = require('./components/pages/notice')
const Loading = require('./components/loading')
const NetworkIndicator = require('./components/network')
2017-10-12 15:46:09 -07:00
const Identicon = require('./components/identicon')
2017-08-02 17:49:04 -07:00
const ReactCSSTransitionGroup = require('react-addons-css-transition-group')
const NetworkDropdown = require('./components/dropdowns/network-dropdown')
2017-10-12 23:10:58 -07:00
const AccountMenu = require('./components/account-menu')
// Global Modals
const Modal = require('./components/modals/index').Modal
// Routes
const {
DEFAULT_ROUTE,
UNLOCK_ROUTE,
SETTINGS_ROUTE,
REVEAL_SEED_ROUTE,
RESTORE_VAULT_ROUTE,
ADD_TOKEN_ROUTE,
NEW_ACCOUNT_ROUTE,
SEND_ROUTE,
CONFIRM_TRANSACTION_ROUTE,
2017-12-03 22:24:30 -08:00
INITIALIZE_ROUTE,
NOTICE_ROUTE,
} = require('./routes')
class App extends Component {
componentWillMount () {
2017-12-03 22:24:30 -08:00
const { currentCurrency, setCurrentCurrencyToUSD } = this.props
if (!currentCurrency) {
setCurrentCurrencyToUSD()
}
2017-08-06 19:03:38 -07:00
}
renderRoutes () {
const exact = true
return (
h(Switch, [
2018-04-02 02:59:49 -07:00
h(Route, { path: INITIALIZE_ROUTE, component: InitializeScreen }),
2018-04-03 17:43:42 -07:00
h(Initialized, { path: REVEAL_SEED_ROUTE, exact, component: RevealSeedConfirmation }),
h(Initialized, { path: UNLOCK_ROUTE, exact, component: UnlockPage }),
h(Initialized, { path: SETTINGS_ROUTE, component: Settings }),
h(Initialized, { path: RESTORE_VAULT_ROUTE, exact, component: RestoreVaultPage }),
2018-04-02 02:59:49 -07:00
h(Initialized, { path: NOTICE_ROUTE, exact, component: NoticeScreen }),
h(Authenticated, { path: CONFIRM_TRANSACTION_ROUTE, component: ConfirmTxScreen }),
h(Authenticated, { path: SEND_ROUTE, exact, component: SendTransactionScreen2 }),
h(Authenticated, { path: ADD_TOKEN_ROUTE, exact, component: AddTokenPage }),
h(Authenticated, { path: NEW_ACCOUNT_ROUTE, component: CreateAccountPage }),
h(Authenticated, { path: DEFAULT_ROUTE, exact, component: Home }),
])
)
}
render () {
2017-12-04 00:06:09 -08:00
const {
isLoading,
loadingMessage,
network,
2018-03-27 00:20:35 -07:00
isMouseUser,
2017-12-04 00:06:09 -08:00
provider,
frequentRpcList,
currentView,
2018-03-27 00:20:35 -07:00
setMouseUserState,
2017-12-04 00:06:09 -08:00
} = this.props
const isLoadingNetwork = network === 'loading' && currentView.name !== 'config'
const loadMessage = loadingMessage || isLoadingNetwork ?
2018-03-27 00:20:35 -07:00
this.getConnectingLabel() : null
log.debug('Main ui render function')
return (
h('.flex-column.full-height', {
2018-03-27 00:20:35 -07:00
className: classnames({ 'mouse-user-styles': isMouseUser }),
style: {
overflowX: 'hidden',
position: 'relative',
alignItems: 'center',
},
2018-03-27 00:20:35 -07:00
tabIndex: '0',
onClick: () => setMouseUserState(true),
onKeyDown: (e) => {
if (e.keyCode === 9) {
setMouseUserState(false)
}
},
}, [
2017-08-08 13:37:41 -07:00
// global modal
h(Modal, {}, []),
// app bar
this.renderAppBar(),
// sidebar
this.renderSidebar(),
// network dropdown
h(NetworkDropdown, {
2017-12-04 00:06:09 -08:00
provider,
frequentRpcList,
}, []),
2017-10-12 23:10:58 -07:00
h(AccountMenu),
2017-11-02 05:15:59 -07:00
(isLoading || isLoadingNetwork) && h(Loading, {
loadingMessage: loadMessage,
}),
// this.renderLoadingIndicator({ isLoading, isLoadingNetwork, loadMessage }),
// content
this.renderRoutes(),
])
)
}
2017-08-08 13:37:41 -07:00
renderGlobalModal () {
return h(Modal, {
ref: 'modalRef',
2017-08-02 17:49:04 -07:00
}, [
// h(BuyOptions, {}, []),
])
}
renderSidebar () {
return h('div', [
h('style', `
.sidebar-enter {
transition: transform 300ms ease-in-out;
transform: translateX(-100%);
}
.sidebar-enter.sidebar-enter-active {
transition: transform 300ms ease-in-out;
transform: translateX(0%);
}
.sidebar-leave {
transition: transform 200ms ease-out;
transform: translateX(0%);
}
.sidebar-leave.sidebar-leave-active {
transition: transform 200ms ease-out;
transform: translateX(-100%);
}
`),
h(ReactCSSTransitionGroup, {
transitionName: 'sidebar',
transitionEnterTimeout: 300,
transitionLeaveTimeout: 200,
}, [
// A second instance of Walletview is used for non-mobile viewports
this.props.sidebarOpen ? h(WalletView, {
responsiveDisplayClassname: '.sidebar',
style: {},
}) : undefined,
2017-08-18 04:11:26 -07:00
]),
2016-05-13 18:07:47 -07:00
// overlay
// TODO: add onClick for overlay to close sidebar
this.props.sidebarOpen ? h('div.sidebar-overlay', {
style: {},
onClick: () => {
this.props.hideSidebar()
},
}, []) : undefined,
])
}
renderAppBar () {
const {
isUnlocked,
network,
provider,
networkDropdownOpen,
showNetworkDropdown,
hideNetworkDropdown,
2018-03-27 00:20:35 -07:00
isInitialized,
welcomeScreenSeen,
isPopup,
betaUI,
} = this.props
if (window.METAMASK_UI_TYPE === 'notification') {
return null
}
const props = this.props
const {isMascara, isOnboarding} = props
2016-05-13 18:07:47 -07:00
// Do not render header if user is in mascara onboarding
if (isMascara && isOnboarding) {
return null
}
2016-05-13 18:07:47 -07:00
// Do not render header if user is in mascara buy ether
if (isMascara && props.currentView.name === 'buyEth') {
return null
}
return (
2016-05-13 18:07:47 -07:00
h('.full-width', {
style: {},
2017-02-15 15:35:56 -08:00
}, [
2018-03-27 00:20:35 -07:00
(isInitialized || welcomeScreenSeen || isPopup || !betaUI) && h('.app-header.flex-row.flex-space-between', {
className: classnames({
'app-header--initialized': !isOnboarding,
}),
}, [
h('div.app-header-contents', {}, [
h('div.left-menu-wrapper', {
2018-02-09 12:05:27 -08:00
onClick: () => props.history.push(DEFAULT_ROUTE),
2017-10-12 15:46:09 -07:00
}, [
// mini logo
h('img.metafox-icon', {
height: 42,
width: 42,
src: '/images/metamask-fox.svg',
2017-10-12 15:46:09 -07:00
}),
// metamask name
2018-03-29 09:37:29 -07:00
h('.flex-row', [
2018-03-30 16:18:48 -07:00
h('h1', this.context.t('appName')),
h('div.beta-label', this.context.t('beta')),
2018-03-29 09:37:29 -07:00
]),
2017-10-12 15:46:09 -07:00
]),
2018-03-27 00:20:35 -07:00
betaUI && isInitialized && h('div.header__right-actions', [
h('div.network-component-wrapper', {
style: {},
}, [
// Network Indicator
h(NetworkIndicator, {
network,
provider,
disabled: this.props.location.pathname === CONFIRM_TRANSACTION_ROUTE,
onClick: (event) => {
event.preventDefault()
event.stopPropagation()
return networkDropdownOpen === false
? showNetworkDropdown()
: hideNetworkDropdown()
},
}),
]),
2018-04-02 02:59:49 -07:00
isUnlocked && h('div.account-menu__icon', { onClick: this.props.toggleAccountMenu }, [
h(Identicon, {
address: this.props.selectedAddress,
diameter: 32,
}),
]),
2017-10-15 22:28:25 -07:00
]),
]),
2017-09-04 15:55:14 -07:00
]),
2017-08-21 04:56:09 -07:00
2018-03-27 00:20:35 -07:00
!isInitialized && !isPopup && betaUI && h('.alpha-warning__container', {}, [
h('h2', {
className: classnames({
'alpha-warning': welcomeScreenSeen,
'alpha-warning-welcome-screen': !welcomeScreenSeen,
}),
}, 'Please be aware that this version is still under development'),
]),
2016-06-16 16:55:32 -07:00
])
)
}
2016-11-01 15:00:28 -07:00
renderLoadingIndicator ({ isLoading, isLoadingNetwork, loadMessage }) {
const { isMascara } = this.props
2017-08-18 04:11:26 -07:00
return isMascara
? null
: h(Loading, {
isLoading: isLoading || isLoadingNetwork,
loadingMessage: loadMessage,
})
2017-08-30 02:05:45 -07:00
}
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))
}
}
2018-03-27 00:20:35 -07:00
getConnectingLabel = function () {
const { provider } = this.props
const providerName = provider.type
let name
if (providerName === 'mainnet') {
2018-03-30 16:18:48 -07:00
name = this.context.t('connectingToMainnet')
2018-03-27 00:20:35 -07:00
} else if (providerName === 'ropsten') {
2018-03-30 16:18:48 -07:00
name = this.context.t('connectingToRopsten')
2018-03-27 00:20:35 -07:00
} else if (providerName === 'kovan') {
2018-03-30 16:18:48 -07:00
name = this.context.t('connectingToRopsten')
2018-03-27 00:20:35 -07:00
} else if (providerName === 'rinkeby') {
2018-03-30 16:18:48 -07:00
name = this.context.t('connectingToRinkeby')
2018-03-27 00:20:35 -07:00
} else {
2018-03-30 16:18:48 -07:00
name = this.context.t('connectingToUnknown')
2018-03-27 00:20:35 -07:00
}
return name
2018-03-19 11:28:02 -07:00
}
getNetworkName () {
const { provider } = this.props
const providerName = provider.type
let name
if (providerName === 'mainnet') {
2018-03-30 16:18:48 -07:00
name = this.context.t('mainnet')
} else if (providerName === 'ropsten') {
2018-03-30 16:18:48 -07:00
name = this.context.t('ropsten')
} else if (providerName === 'kovan') {
2018-03-30 16:18:48 -07:00
name = this.context.t('kovan')
} else if (providerName === 'rinkeby') {
2018-03-30 16:18:48 -07:00
name = this.context.t('rinkeby')
} else {
2018-03-30 16:18:48 -07:00
name = this.context.t('unknownNetwork')
}
return name
2016-05-13 18:07:47 -07:00
}
}
2016-06-28 17:57:53 -07:00
2017-12-04 00:06:09 -08:00
App.propTypes = {
currentCurrency: PropTypes.string,
setCurrentCurrencyToUSD: PropTypes.func,
isLoading: PropTypes.bool,
loadingMessage: PropTypes.string,
network: PropTypes.string,
provider: PropTypes.object,
frequentRpcList: PropTypes.array,
currentView: PropTypes.object,
sidebarOpen: 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,
2017-12-04 00:06:09 -08:00
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,
2017-12-04 19:27:42 -08:00
unapprovedMsgCount: PropTypes.number,
unapprovedPersonalMsgCount: PropTypes.number,
unapprovedTypedMessagesCount: PropTypes.number,
2018-03-27 00:20:35 -07:00
welcomeScreenSeen: PropTypes.bool,
isPopup: PropTypes.bool,
betaUI: PropTypes.bool,
isMouseUser: PropTypes.bool,
setMouseUserState: PropTypes.func,
t: PropTypes.func,
2017-12-04 00:06:09 -08:00
}
function mapStateToProps (state) {
const { appState, metamask } = state
const {
networkDropdownOpen,
sidebarOpen,
isLoading,
loadingMessage,
} = appState
const {
identities,
accounts,
address,
keyrings,
isInitialized,
noActiveNotices,
seedWords,
unapprovedTxs,
lastUnreadNotice,
lostAccounts,
2017-12-04 19:27:42 -08:00
unapprovedMsgCount,
unapprovedPersonalMsgCount,
unapprovedTypedMessagesCount,
} = metamask
const selected = address || Object.keys(accounts)[0]
return {
// state from plugin
networkDropdownOpen,
sidebarOpen,
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),
2018-03-27 00:20:35 -07:00
isPopup: state.metamask.isPopup,
seedWords: state.metamask.seedWords,
unapprovedTxs,
2018-03-27 00:20:35 -07:00
unapprovedMsgs: state.metamask.unapprovedMsgs,
2017-12-04 19:27:42 -08:00
unapprovedMsgCount,
unapprovedPersonalMsgCount,
unapprovedTypedMessagesCount,
menuOpen: state.appState.menuOpen,
network: state.metamask.network,
provider: state.metamask.provider,
forgottenPassword: state.appState.forgottenPassword,
lastUnreadNotice,
lostAccounts,
frequentRpcList: state.metamask.frequentRpcList || [],
currentCurrency: state.metamask.currentCurrency,
2018-03-27 00:20:35 -07:00
isMouseUser: state.appState.isMouseUser,
betaUI: state.metamask.featureFlags.betaUI,
isRevealingSeedWords: state.metamask.isRevealingSeedWords,
Qr: state.appState.Qr,
welcomeScreenSeen: state.metamask.welcomeScreenSeen,
// state needed to get account dropdown temporarily rendering from app bar
identities,
selected,
keyrings,
}
}
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()),
2018-03-27 00:20:35 -07:00
setMouseUserState: (isMouseUser) => dispatch(actions.setMouseUserState(isMouseUser)),
}
}
2018-03-30 17:37:24 -07:00
App.contextTypes = {
t: PropTypes.func,
}
module.exports = compose(
withRouter,
connect(mapStateToProps, mapDispatchToProps)
)(App)