nifty-wallet/ui/app/app.js

524 lines
15 KiB
JavaScript
Raw Normal View History

const inherits = require('util').inherits
const Component = require('react').Component
const connect = require('react-redux').connect
const h = require('react-hyperscript')
const actions = require('./actions')
2018-01-09 16:45:39 -08:00
const classnames = require('classnames')
2017-08-18 04:11:26 -07:00
// mascara
const MascaraFirstTime = require('../../mascara/src/app/first-time').default
const MascaraBuyEtherScreen = require('../../mascara/src/app/first-time/buy-ether-screen').default
// init
const InitializeMenuScreen = require('./first-time/init-menu')
2016-10-20 11:33:18 -07:00
const NewKeyChainScreen = require('./new-keychain')
// accounts
2017-07-30 20:42:12 -07:00
const MainContainer = require('./main-container')
const SendTransactionScreen2 = require('./components/send/send-v2-container')
const ConfirmTxScreen = require('./conf-tx')
// notice
2016-12-20 15:54:59 -08:00
const NoticeScreen = require('./components/notice')
2016-12-22 14:09:10 -08:00
const generateLostAccountsNotice = require('../lib/lost-accounts-notice')
// slideout menu
const WalletView = require('./components/wallet-view')
// other views
2017-10-22 22:40:03 -07:00
const Settings = require('./settings')
const AddTokenScreen = require('./add-token')
const Import = require('./accounts/import')
const NewAccount = require('./accounts/new-account')
const Loading = require('./components/loading')
const NetworkIndicator = require('./components/network')
2017-10-12 15:46:09 -07:00
const Identicon = require('./components/identicon')
const BuyView = require('./components/buy-button-subview')
const HDCreateVaultComplete = require('./keychains/hd/create-vault-complete')
const HDRestoreVaultScreen = require('./keychains/hd/restore-vault')
2016-11-01 22:19:04 -07:00
const RevealSeedConfirmation = require('./keychains/hd/recover-seed/confirmation')
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')
2017-11-01 19:30:33 -07:00
const QrView = require('./components/qr-code')
// Global Modals
const Modal = require('./components/modals/index').Modal
2017-08-06 19:03:38 -07:00
module.exports = connect(mapStateToProps, mapDispatchToProps)(App)
inherits(App, Component)
2016-06-21 13:18:32 -07:00
function App () { Component.call(this) }
2016-06-21 13:18:32 -07:00
function mapStateToProps (state) {
const {
identities,
accounts,
address,
2017-08-09 17:40:01 -07:00
keyrings,
2017-08-18 04:11:26 -07:00
isInitialized,
noActiveNotices,
seedWords,
} = state.metamask
2017-08-03 19:17:46 -07:00
const selected = address || Object.keys(accounts)[0]
return {
// state from plugin
networkDropdownOpen: state.appState.networkDropdownOpen,
2017-08-02 13:32:02 -07:00
sidebarOpen: state.appState.sidebarOpen,
isLoading: state.appState.isLoading,
loadingMessage: state.appState.loadingMessage,
noActiveNotices: state.metamask.noActiveNotices,
isInitialized: state.metamask.isInitialized,
isUnlocked: state.metamask.isUnlocked,
2017-10-12 15:46:09 -07:00
selectedAddress: state.metamask.selectedAddress,
currentView: state.appState.currentView,
activeAddress: state.appState.activeAddress,
transForward: state.appState.transForward,
2017-08-18 04:11:26 -07:00
isMascara: state.metamask.isMascara,
isOnboarding: Boolean(!noActiveNotices || seedWords || !isInitialized),
seedWords: state.metamask.seedWords,
unapprovedTxs: state.metamask.unapprovedTxs,
unapprovedMsgs: state.metamask.unapprovedMsgs,
2016-05-18 12:30:03 -07:00
menuOpen: state.appState.menuOpen,
network: state.metamask.network,
2016-06-28 18:06:10 -07:00
provider: state.metamask.provider,
forgottenPassword: state.appState.forgottenPassword,
lastUnreadNotice: state.metamask.lastUnreadNotice,
lostAccounts: state.metamask.lostAccounts,
frequentRpcList: state.metamask.frequentRpcList || [],
currentCurrency: state.metamask.currentCurrency,
// state needed to get account dropdown temporarily rendering from app bar
identities,
selected,
2017-08-09 17:40:01 -07:00
keyrings,
}
}
function mapDispatchToProps (dispatch, ownProps) {
2017-08-06 19:03:38 -07:00
return {
2017-08-31 04:08:11 -07:00
dispatch,
hideSidebar: () => dispatch(actions.hideSidebar()),
showNetworkDropdown: () => dispatch(actions.showNetworkDropdown()),
hideNetworkDropdown: () => dispatch(actions.hideNetworkDropdown()),
setCurrentCurrencyToUSD: () => dispatch(actions.setCurrentCurrency('usd')),
2017-10-15 22:28:25 -07:00
toggleAccountMenu: () => dispatch(actions.toggleAccountMenu()),
2017-08-06 19:03:38 -07:00
}
}
App.prototype.componentWillMount = function () {
if (!this.props.currentCurrency) {
this.props.setCurrentCurrencyToUSD()
}
}
2016-06-21 13:18:32 -07:00
App.prototype.render = function () {
var props = this.props
2017-08-29 07:50:48 -07:00
const { isLoading, loadingMessage, network } = props
const isLoadingNetwork = network === 'loading' && props.currentView.name !== 'config'
2017-06-14 19:36:37 -07:00
const loadMessage = loadingMessage || isLoadingNetwork ?
`Connecting to ${this.getNetworkName()}` : null
log.debug('Main ui render function')
return (
h('.flex-column.full-height', {
style: {
2017-12-08 08:06:32 -08:00
overflowX: 'hidden',
2016-06-30 23:50:20 -07:00
position: 'relative',
2017-07-24 17:04:13 -07:00
alignItems: 'center',
2016-06-21 13:18:32 -07:00
},
2016-05-13 18:07:47 -07:00
}, [
2017-12-08 08:06:32 -08:00
// global modal
h(Modal, {}, []),
2017-08-08 13:37:41 -07:00
2016-05-13 14:56:29 -07:00
// app bar
2016-05-13 18:07:47 -07:00
this.renderAppBar(),
2017-12-08 08:06:32 -08:00
// sidebar
this.renderSidebar(),
2017-12-08 08:06:32 -08:00
// network dropdown
h(NetworkDropdown, {
provider: this.props.provider,
frequentRpcList: this.props.frequentRpcList,
}, []),
2017-12-08 08:06:32 -08:00
h(AccountMenu),
2017-10-12 23:10:58 -07:00
2017-12-08 08:06:32 -08:00
(isLoading || isLoadingNetwork) && h(Loading, {
loadingMessage: loadMessage,
}),
2017-11-02 05:15:59 -07:00
2017-12-08 08:06:32 -08:00
// this.renderLoadingIndicator({ isLoading, isLoadingNetwork, loadMessage }),
2017-12-08 08:06:32 -08:00
// content
this.renderPrimary(),
])
)
}
2017-08-29 07:50:48 -07:00
App.prototype.renderGlobalModal = function () {
2017-08-08 13:37:41 -07:00
return h(Modal, {
2017-08-29 07:50:48 -07:00
ref: 'modalRef',
}, [
2017-08-29 07:50:48 -07:00
// h(BuyOptions, {}, []),
])
2017-08-08 13:37:41 -07:00
}
2017-08-29 07:50:48 -07:00
App.prototype.renderSidebar = function () {
2017-08-02 17:49:04 -07:00
return h('div', {
}, [
h('style', `
.sidebar-enter {
transition: transform 300ms ease-in-out;
2017-08-02 17:49:04 -07:00
transform: translateX(-100%);
}
.sidebar-enter.sidebar-enter-active {
transition: transform 300ms ease-in-out;
2017-08-02 17:49:04 -07:00
transform: translateX(0%);
}
.sidebar-leave {
transition: transform 200ms ease-out;
2017-08-02 17:49:04 -07:00
transform: translateX(0%);
}
.sidebar-leave.sidebar-leave-active {
transition: transform 200ms ease-out;
2017-08-02 17:49:04 -07:00
transform: translateX(-100%);
}
`),
2017-08-02 17:49:04 -07:00
h(ReactCSSTransitionGroup, {
transitionName: 'sidebar',
transitionEnterTimeout: 300,
transitionLeaveTimeout: 200,
2017-08-02 17:49:04 -07:00
}, [
// A second instance of Walletview is used for non-mobile viewports
2017-08-02 17:49:04 -07:00
this.props.sidebarOpen ? h(WalletView, {
responsiveDisplayClassname: '.sidebar',
2017-08-02 23:14:53 -07:00
style: {},
2017-08-02 17:49:04 -07:00
}) : undefined,
2017-08-02 17:50:27 -07:00
]),
// overlay
// TODO: add onClick for overlay to close sidebar
2017-08-02 23:14:53 -07:00
this.props.sidebarOpen ? h('div.sidebar-overlay', {
2017-08-06 19:03:38 -07:00
style: {},
onClick: () => {
this.props.hideSidebar()
},
2017-08-02 17:50:27 -07:00
}, []) : undefined,
])
}
2016-06-21 13:18:32 -07:00
App.prototype.renderAppBar = function () {
const {
isUnlocked,
network,
provider,
networkDropdownOpen,
showNetworkDropdown,
hideNetworkDropdown,
currentView,
} = this.props
if (window.METAMASK_UI_TYPE === 'notification') {
return null
}
const props = this.props
2017-08-18 04:11:26 -07:00
const {isMascara, isOnboarding} = props
// Do not render header if user is in mascara onboarding
2017-08-18 04:11:26 -07:00
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
}
2016-05-13 18:07:47 -07:00
return (
h('.full-width', {
2017-08-29 07:50:48 -07:00
style: {},
2017-07-24 17:04:13 -07:00
}, [
2016-05-13 18:07:47 -07:00
2016-05-18 14:36:35 -07:00
h('.app-header.flex-row.flex-space-between', {
2018-01-09 16:45:39 -08:00
className: classnames({
'app-header--initialized': !isOnboarding,
}),
2017-02-15 15:35:56 -08:00
}, [
h('div.app-header-contents', {}, [
h('div.left-menu-wrapper', {
onClick: () => {
2017-10-25 00:34:12 -07:00
props.dispatch(actions.backToAccountDetail(props.activeAddress))
},
}, [
// mini logo
2017-10-25 00:34:12 -07:00
h('img.metafox-icon', {
2018-01-10 21:09:09 -08:00
height: 42,
width: 42,
src: '/images/metamask-fox.svg',
}),
// metamask name
2018-01-10 21:09:09 -08:00
h('h1', 'MetaMask'),
]),
2017-10-12 15:46:09 -07:00
h('div.header__right-actions', [
h('div.network-component-wrapper', {
style: {},
}, [
// Network Indicator
h(NetworkIndicator, {
network,
provider,
disabled: currentView.name === 'confTx',
2017-10-12 15:46:09 -07:00
onClick: (event) => {
event.preventDefault()
event.stopPropagation()
return networkDropdownOpen === false
? showNetworkDropdown()
: hideNetworkDropdown()
2017-10-12 15:46:09 -07:00
},
}),
]),
isUnlocked && h('div.account-menu__icon', { onClick: this.props.toggleAccountMenu }, [
2017-10-15 22:28:25 -07:00
h(Identicon, {
address: this.props.selectedAddress,
diameter: 32,
}),
]),
]),
]),
2016-05-18 14:36:35 -07:00
]),
2017-07-29 13:31:53 -07:00
2016-05-18 14:36:35 -07:00
])
2016-05-13 18:07:47 -07:00
)
}
App.prototype.renderLoadingIndicator = function ({ isLoading, isLoadingNetwork, loadMessage }) {
const { isMascara } = this.props
2017-08-21 04:56:09 -07:00
return isMascara
? null
: h(Loading, {
isLoading: isLoading || isLoadingNetwork,
loadingMessage: loadMessage,
})
}
2016-08-26 11:30:07 -07:00
App.prototype.renderBackButton = function (style, justArrow = false) {
var props = this.props
return (
h('.flex-row', {
key: 'leftArrow',
style: style,
onClick: () => props.dispatch(actions.goBackToInitView()),
}, [
h('i.fa.fa-arrow-left.cursor-pointer'),
justArrow ? null : h('div.cursor-pointer', {
style: {
marginLeft: '3px',
},
onClick: () => props.dispatch(actions.goBackToInitView()),
}, 'BACK'),
])
)
}
2016-11-01 15:00:28 -07:00
2016-06-21 13:18:32 -07:00
App.prototype.renderPrimary = function () {
log.debug('rendering primary')
var props = this.props
2017-08-18 04:11:26 -07:00
const {isMascara, isOnboarding} = props
2017-08-30 02:05:45 -07:00
if (isMascara && isOnboarding) {
return h(MascaraFirstTime)
}
// notices
2017-02-20 12:59:12 -08:00
if (!props.noActiveNotices) {
2017-02-20 12:59:44 -08:00
log.debug('rendering notice screen for unread notices.')
return h(NoticeScreen, {
notice: props.lastUnreadNotice,
key: 'NoticeScreen',
onConfirm: () => props.dispatch(actions.markNoticeRead(props.lastUnreadNotice)),
})
} else if (props.lostAccounts && props.lostAccounts.length > 0) {
2017-02-20 12:59:44 -08:00
log.debug('rendering notice screen for lost accounts view.')
return h(NoticeScreen, {
notice: generateLostAccountsNotice(props.lostAccounts),
key: 'LostAccountsNotice',
onConfirm: () => props.dispatch(actions.markAccountsFound()),
})
2016-06-16 16:55:32 -07:00
}
// show initialize screen
if (!props.isInitialized || props.forgottenPassword) {
// show current view
2017-02-20 12:59:44 -08:00
log.debug('rendering an initialize screen')
switch (props.currentView.name) {
case 'restoreVault':
2017-02-20 12:59:44 -08:00
log.debug('rendering restore vault screen')
return h(HDRestoreVaultScreen, {key: 'HDRestoreVaultScreen'})
2016-05-11 22:21:10 -07:00
default:
2017-02-20 12:59:44 -08:00
log.debug('rendering menu screen')
return h(InitializeMenuScreen, {key: 'menuScreenInit'})
}
}
// show unlock screen
if (!props.isUnlocked) {
return h(MainContainer, {
currentViewName: props.currentView.name,
isUnlocked: props.isUnlocked,
})
}
2018-01-02 11:29:26 -08:00
// show seed words screen
2018-01-02 11:07:48 -08:00
if (props.seedWords) {
log.debug('rendering seed words')
return h(HDCreateVaultComplete, {key: 'HDCreateVaultComplete'})
}
// show current view
switch (props.currentView.name) {
case 'accountDetail':
2017-07-30 20:42:12 -07:00
log.debug('rendering main container')
return h(MainContainer, {key: 'account-detail'})
case 'sendTransaction':
2017-02-20 12:59:44 -08:00
log.debug('rendering send tx screen')
2017-10-12 23:10:58 -07:00
2017-10-18 19:23:57 -07:00
// Going to leave this here until we are ready to delete SendTransactionScreen v1
// const SendComponentToRender = checkFeatureToggle('send-v2')
// ? SendTransactionScreen2
// : SendTransactionScreen
2017-10-18 19:23:57 -07:00
return h(SendTransactionScreen2, {key: 'send-transaction'})
2017-09-07 04:24:03 -07:00
case 'sendToken':
log.debug('rendering send token screen')
2017-10-18 19:23:57 -07:00
// Going to leave this here until we are ready to delete SendTransactionScreen v1
// const SendTokenComponentToRender = checkFeatureToggle('send-v2')
// ? SendTransactionScreen2
// : SendTokenScreen
2017-10-18 19:23:57 -07:00
return h(SendTransactionScreen2, {key: 'sendToken'})
2016-10-15 10:48:12 -07:00
case 'newKeychain':
2017-02-20 12:59:44 -08:00
log.debug('rendering new keychain screen')
2016-10-15 10:48:12 -07:00
return h(NewKeyChainScreen, {key: 'new-keychain'})
case 'confTx':
2017-02-20 12:59:44 -08:00
log.debug('rendering confirm tx screen')
return h(ConfirmTxScreen, {key: 'confirm-tx'})
case 'add-token':
log.debug('rendering add-token screen from unlock screen.')
return h(AddTokenScreen, {key: 'add-token'})
case 'config':
2017-02-20 12:59:44 -08:00
log.debug('rendering config screen')
2017-10-22 22:40:03 -07:00
return h(Settings, {key: 'config'})
case 'import-menu':
2017-02-20 12:59:44 -08:00
log.debug('rendering import screen')
return h(Import, {key: 'import-menu'})
case 'new-account-page':
log.debug('rendering new account screen')
return h(NewAccount, {key: 'new-account'})
case 'reveal-seed-conf':
2017-02-20 12:59:44 -08:00
log.debug('rendering reveal seed confirmation screen')
return h(RevealSeedConfirmation, {key: 'reveal-seed-conf'})
case 'info':
2017-02-20 12:59:44 -08:00
log.debug('rendering info screen')
2017-10-25 09:26:05 -07:00
return h(Settings, {key: 'info', tab: 'info'})
case 'buyEth':
2017-02-20 12:59:44 -08:00
log.debug('rendering buy ether screen')
return h(BuyView, {key: 'buyEthView'})
case 'onboardingBuyEth':
log.debug('rendering onboarding buy ether screen')
return h(MascaraBuyEtherScreen, {key: 'buyEthView'})
2016-08-18 10:40:35 -07:00
case 'qr':
2017-02-20 12:59:44 -08:00
log.debug('rendering show qr screen')
2016-08-18 10:40:35 -07:00
return h('div', {
style: {
position: 'absolute',
height: '100%',
top: '0px',
left: '0px',
},
}, [
h('i.fa.fa-arrow-left.fa-lg.cursor-pointer.color-orange', {
onClick: () => props.dispatch(actions.backToAccountDetail(props.activeAddress)),
style: {
marginLeft: '10px',
marginTop: '50px',
},
}),
h('div', {
style: {
position: 'absolute',
left: '44px',
width: '285px',
},
}, [
h(QrView, {key: 'qr'}),
]),
])
default:
2017-02-20 12:59:44 -08:00
log.debug('rendering default, account detail screen')
2017-07-30 20:42:12 -07:00
return h(MainContainer, {key: 'account-detail'})
2016-06-21 13:18:32 -07:00
}
}
2016-06-21 13:18:32 -07:00
App.prototype.toggleMetamaskActive = function () {
2016-05-13 18:07:47 -07:00
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))
}
}
2016-06-28 17:57:53 -07:00
App.prototype.getNetworkName = function () {
const { provider } = this.props
const providerName = provider.type
let name
if (providerName === 'mainnet') {
name = 'Main Ethereum Network'
} else if (providerName === 'ropsten') {
name = 'Ropsten Test Network'
} else if (providerName === 'kovan') {
name = 'Kovan Test Network'
} else if (providerName === 'rinkeby') {
name = 'Rinkeby Test Network'
} else {
name = 'Unknown Private Network'
}
return name
}