nifty-wallet/ui/app/app.js

479 lines
13 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')
const ReactCSSTransitionGroup = require('react-addons-css-transition-group')
// init
2016-06-16 16:55:32 -07:00
const DisclaimerScreen = require('./first-time/disclaimer')
const InitializeMenuScreen = require('./first-time/init-menu')
2016-10-20 11:33:18 -07:00
const NewKeyChainScreen = require('./new-keychain')
// unlock
const UnlockScreen = require('./unlock')
// accounts
const AccountsScreen = require('./accounts')
const AccountDetailScreen = require('./account-detail')
const SendTransactionScreen = require('./send')
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')
// other views
const ConfigScreen = require('./config')
const InfoScreen = require('./info')
const LoadingIndicator = require('./components/loading')
2016-05-18 12:30:03 -07:00
const SandwichExpando = require('sandwich-expando')
const MenuDroppo = require('menu-droppo')
2016-05-18 14:36:35 -07:00
const DropMenuItem = require('./components/drop-menu-item')
const NetworkIndicator = require('./components/network')
const Tooltip = require('./components/tooltip')
const BuyView = require('./components/buy-button-subview')
2016-08-18 10:40:35 -07:00
const QrView = require('./components/qr-code')
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')
module.exports = connect(mapStateToProps)(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) {
return {
// state from plugin
isLoading: state.appState.isLoading,
isDisclaimerConfirmed: state.metamask.isDisclaimerConfirmed,
noActiveNotices: state.metamask.noActiveNotices,
isInitialized: state.metamask.isInitialized,
isUnlocked: state.metamask.isUnlocked,
currentView: state.appState.currentView,
activeAddress: state.appState.activeAddress,
transForward: state.appState.transForward,
seedWords: state.metamask.seedWords,
unconfTxs: state.metamask.unconfTxs,
unconfMsgs: state.metamask.unconfMsgs,
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,
}
}
2016-06-21 13:18:32 -07:00
App.prototype.render = function () {
var props = this.props
const { isLoading, transForward } = props
return (
h('.flex-column.flex-grow.full-height', {
style: {
// Windows was showing a vertical scroll bar:
overflow: 'hidden',
2016-06-30 23:50:20 -07:00
position: 'relative',
2016-06-21 13:18:32 -07:00
},
2016-05-13 18:07:47 -07:00
}, [
h(LoadingIndicator, { isLoading }),
2016-05-13 14:56:29 -07:00
// app bar
2016-05-13 18:07:47 -07:00
this.renderAppBar(),
this.renderNetworkDropdown(),
this.renderDropdown(),
// panel content
h('.app-primary.flex-grow' + (transForward ? '.from-right' : '.from-left'), {
style: {
height: '380px',
width: '360px',
2016-06-21 13:18:32 -07:00
},
}, [
h(ReactCSSTransitionGroup, {
className: 'css-transition-group',
transitionName: 'main',
transitionEnterTimeout: 300,
transitionLeaveTimeout: 300,
}, [
this.renderPrimary(),
]),
]),
])
)
}
2016-06-21 13:18:32 -07:00
App.prototype.renderAppBar = function () {
if (window.METAMASK_UI_TYPE === 'notification') {
return null
}
const props = this.props
const state = this.state || {}
const isNetworkMenuOpen = state.isNetworkMenuOpen || false
2016-05-13 18:07:47 -07:00
return (
2016-05-18 14:36:35 -07:00
h('div', [
2016-05-13 18:07:47 -07:00
2016-05-18 14:36:35 -07:00
h('.app-header.flex-row.flex-space-between', {
style: {
alignItems: 'center',
visibility: props.isUnlocked ? 'visible' : 'none',
background: props.isUnlocked ? 'white' : 'none',
2016-12-16 10:04:57 -08:00
height: '36px',
position: 'relative',
2016-07-06 20:11:49 -07:00
zIndex: 2,
2016-05-18 14:36:35 -07:00
},
}, props.isUnlocked && [
2016-05-18 14:36:35 -07:00
h('div', {
style: {
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
2016-06-21 13:18:32 -07:00
},
}, [
2016-12-16 10:04:57 -08:00
// mini logo
h('img', {
height: 24,
width: 24,
src: '/images/icon-128.png',
}),
h(NetworkIndicator, {
network: this.props.network,
2016-07-28 11:10:54 -07:00
provider: this.props.provider,
onClick: (event) => {
event.preventDefault()
event.stopPropagation()
this.setState({ isNetworkMenuOpen: !isNetworkMenuOpen })
},
}),
]),
2016-05-18 14:36:35 -07:00
2016-05-18 17:47:30 -07:00
// metamask name
h('h1', {
style: {
2016-12-16 10:04:57 -08:00
position: 'relative',
left: '9px',
},
}, 'MetaMask'),
h('div', {
style: {
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
2016-05-18 14:36:35 -07:00
},
}, [
// small accounts nav
h(Tooltip, { title: 'Switch Accounts' }, [
h('img.cursor-pointer.color-orange', {
src: 'images/switch_acc.svg',
style: {
width: '23.5px',
marginRight: '8px',
},
onClick: (event) => {
event.stopPropagation()
this.props.dispatch(actions.showAccountsPage())
},
}),
]),
// hamburger
h(SandwichExpando, {
width: 16,
barHeight: 2,
padding: 0,
2016-07-11 11:33:12 -07:00
isOpen: state.isMainMenuOpen,
color: 'rgb(247,146,30)',
onClick: (event) => {
event.preventDefault()
event.stopPropagation()
2016-07-11 11:33:12 -07:00
this.setState({ isMainMenuOpen: !state.isMainMenuOpen })
},
}),
]),
2016-05-18 14:36:35 -07:00
]),
])
2016-05-13 18:07:47 -07:00
)
}
2016-06-21 13:18:32 -07:00
App.prototype.renderNetworkDropdown = function () {
const props = this.props
const state = this.state || {}
const isOpen = state.isNetworkMenuOpen
return h(MenuDroppo, {
isOpen,
2016-06-21 13:18:32 -07:00
onClickOutside: (event) => {
this.setState({ isNetworkMenuOpen: !isOpen })
},
2016-07-06 20:11:49 -07:00
zIndex: 1,
style: {
2016-06-30 23:50:20 -07:00
position: 'absolute',
left: 0,
top: '36px',
},
innerStyle: {
background: 'white',
boxShadow: '1px 1px 2px rgba(0,0,0,0.1)',
},
}, [ // DROP MENU ITEMS
h('style', `
.drop-menu-item:hover { background:rgb(235, 235, 235); }
.drop-menu-item i { margin: 11px; }
`),
h(DropMenuItem, {
label: 'Main Ethereum Network',
2016-06-21 13:18:32 -07:00
closeMenu: () => this.setState({ isNetworkMenuOpen: false }),
action: () => props.dispatch(actions.setProviderType('mainnet')),
icon: h('.menu-icon.diamond'),
activeNetworkRender: props.network,
2016-07-28 10:53:51 -07:00
provider: props.provider,
}),
h(DropMenuItem, {
label: 'Ropsten Test Network',
2016-06-21 13:18:32 -07:00
closeMenu: () => this.setState({ isNetworkMenuOpen: false }),
action: () => props.dispatch(actions.setProviderType('testnet')),
icon: h('.menu-icon.red-dot'),
activeNetworkRender: props.network,
2016-10-25 14:16:04 -07:00
provider: props.provider,
}),
h(DropMenuItem, {
label: 'Localhost 8545',
2016-06-21 13:18:32 -07:00
closeMenu: () => this.setState({ isNetworkMenuOpen: false }),
action: () => props.dispatch(actions.setRpcTarget('http://localhost:8545')),
icon: h('i.fa.fa-question-circle.fa-lg'),
activeNetworkRender: props.provider.rpcTarget,
}),
2016-07-28 10:53:51 -07:00
2016-11-22 16:34:02 -08:00
this.renderCustomOption(props.provider),
h(DropMenuItem, {
label: 'Custom RPC',
closeMenu: () => this.setState({ isNetworkMenuOpen: false }),
action: () => this.props.dispatch(actions.showConfigPage()),
icon: h('i.fa.fa-question-circle.fa-lg'),
}),
])
}
2016-06-21 13:18:32 -07:00
App.prototype.renderDropdown = function () {
const state = this.state || {}
const isOpen = state.isMainMenuOpen
return h(MenuDroppo, {
isOpen: isOpen,
2016-07-06 20:11:49 -07:00
zIndex: 1,
onClickOutside: (event) => {
this.setState({ isMainMenuOpen: !isOpen })
},
style: {
2016-06-30 23:50:20 -07:00
position: 'absolute',
right: 0,
2016-08-03 19:43:54 -07:00
top: '36px',
},
innerStyle: {
background: 'white',
boxShadow: '1px 1px 2px rgba(0,0,0,0.1)',
},
}, [ // DROP MENU ITEMS
h('style', `
.drop-menu-item:hover { background:rgb(235, 235, 235); }
.drop-menu-item i { margin: 11px; }
`),
h(DropMenuItem, {
label: 'Settings',
closeMenu: () => this.setState({ isMainMenuOpen: !isOpen }),
2016-06-21 13:18:32 -07:00
action: () => this.props.dispatch(actions.showConfigPage()),
icon: h('i.fa.fa-gear.fa-lg'),
}),
h(DropMenuItem, {
2016-06-14 12:59:34 -07:00
label: 'Lock',
closeMenu: () => this.setState({ isMainMenuOpen: !isOpen }),
2016-06-21 13:18:32 -07:00
action: () => this.props.dispatch(actions.lockMetamask()),
icon: h('i.fa.fa-lock.fa-lg'),
}),
h(DropMenuItem, {
label: 'Info',
closeMenu: () => this.setState({ isMainMenuOpen: !isOpen }),
2016-06-21 13:18:32 -07:00
action: () => this.props.dispatch(actions.showInfoPage()),
icon: h('i.fa.fa-question.fa-lg'),
}),
])
}
2016-11-01 15:00:28 -07:00
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 () {
var props = this.props
if (!props.isDisclaimerConfirmed) {
2016-06-16 16:55:32 -07:00
return h(DisclaimerScreen, {key: 'disclaimerScreen'})
}
if (props.seedWords) {
return h(HDCreateVaultComplete, {key: 'HDCreateVaultComplete'})
}
// show initialize screen
if (!props.isInitialized || props.forgottenPassword) {
// show current view
switch (props.currentView.name) {
case 'restoreVault':
return h(HDRestoreVaultScreen, {key: 'HDRestoreVaultScreen'})
2016-05-11 22:21:10 -07:00
default:
return h(InitializeMenuScreen, {key: 'menuScreenInit'})
}
}
// show unlock screen
if (!props.isUnlocked) {
2016-11-01 15:00:28 -07:00
switch (props.currentView.name) {
case 'restoreVault':
return h(HDRestoreVaultScreen, {key: 'HDRestoreVaultScreen'})
default:
return h(UnlockScreen, {key: 'locked'})
}
}
// notices
if (!props.noActiveNotices) {
return h(NoticeScreen, {
notice: props.lastUnreadNotice,
key: 'NoticeScreen',
2016-12-20 15:54:59 -08:00
onConfirm: () => props.dispatch(actions.markNoticeRead(props.lastUnreadNotice)),
})
} else if (props.lostAccounts && props.lostAccounts.length > 0) {
return h(NoticeScreen, {
2016-12-22 14:09:10 -08:00
notice: generateLostAccountsNotice(props.lostAccounts),
key: 'LostAccountsNotice',
onConfirm: () => props.dispatch(actions.markAccountsFound()),
})
}
// show current view
switch (props.currentView.name) {
case 'accounts':
return h(AccountsScreen, {key: 'accounts'})
case 'accountDetail':
return h(AccountDetailScreen, {key: 'account-detail'})
case 'sendTransaction':
return h(SendTransactionScreen, {key: 'send-transaction'})
2016-10-15 10:48:12 -07:00
case 'newKeychain':
return h(NewKeyChainScreen, {key: 'new-keychain'})
case 'confTx':
return h(ConfirmTxScreen, {key: 'confirm-tx'})
case 'config':
return h(ConfigScreen, {key: 'config'})
case 'reveal-seed-conf':
return h(RevealSeedConfirmation, {key: 'reveal-seed-conf'})
case 'info':
return h(InfoScreen, {key: 'info'})
case 'buyEth':
return h(BuyView, {key: 'buyEthView'})
2016-08-18 10:40:35 -07:00
case 'qr':
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:
return h(AccountDetailScreen, {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
2016-11-22 16:34:02 -08:00
App.prototype.renderCustomOption = function (provider) {
const { rpcTarget, type } = provider
if (type !== 'rpc') return null
2016-06-28 18:06:10 -07:00
switch (rpcTarget) {
2016-08-29 12:36:47 -07:00
case 'http://localhost:8545':
2016-10-21 15:19:16 -07:00
return null
2016-06-28 17:57:53 -07:00
default:
return h(DropMenuItem, {
label: `${rpcTarget}`,
closeMenu: () => this.setState({ isNetworkMenuOpen: false }),
icon: h('i.fa.fa-question-circle.fa-lg'),
2016-06-29 16:31:27 -07:00
activeNetworkRender: 'custom',
2016-06-28 18:06:10 -07:00
})
2016-06-28 17:57:53 -07:00
}
}