diff options
Diffstat (limited to 'ui')
27 files changed, 433 insertions, 274 deletions
diff --git a/ui/app/account-detail.js b/ui/app/account-detail.js index 486a1a633..ebb15cd05 100644 --- a/ui/app/account-detail.js +++ b/ui/app/account-detail.js @@ -10,7 +10,7 @@ const ReactCSSTransitionGroup = require('react-addons-css-transition-group') const valuesFor = require('./util').valuesFor const Identicon = require('./components/identicon') -const AccountEtherBalance = require('./components/account-eth-balance') +const EthBalance = require('./components/eth-balance') const TransactionList = require('./components/transaction-list') const ExportAccountView = require('./components/account-export') const ethUtil = require('ethereumjs-util') @@ -168,7 +168,7 @@ AccountDetailScreen.prototype.render = function () { }, }, [ - h(AccountEtherBalance, { + h(EthBalance, { value: account && account.balance, style: { lineHeight: '7px', @@ -235,7 +235,7 @@ AccountDetailScreen.prototype.subview = function () { AccountDetailScreen.prototype.transactionList = function () { const { transactions, unconfTxs, unconfMsgs, address, network, shapeShiftTxList } = this.props - var txsToRender = transactions + var txsToRender = transactions.concat(unconfTxs) // only transactions that are from the current address .filter(tx => tx.txParams.from === address) // only transactions that are on the current network diff --git a/ui/app/accounts/account-list-item.js b/ui/app/accounts/account-list-item.js index aa80e0e23..0b4acdfec 100644 --- a/ui/app/accounts/account-list-item.js +++ b/ui/app/accounts/account-list-item.js @@ -3,7 +3,7 @@ const h = require('react-hyperscript') const inherits = require('util').inherits const ethUtil = require('ethereumjs-util') -const AccountEtherBalance = require('../components/account-eth-balance') +const EthBalance = require('../components/eth-balance') const CopyButton = require('../components/copyButton') const Identicon = require('../components/identicon') @@ -50,7 +50,7 @@ NewComponent.prototype.render = function () { textOverflow: 'ellipsis', }, }, ethUtil.toChecksumAddress(identity.address)), - h(AccountEtherBalance, { + h(EthBalance, { value: account.balance, style: { lineHeight: '7px', diff --git a/ui/app/accounts/index.js b/ui/app/accounts/index.js index 6e12accc7..c20900c1e 100644 --- a/ui/app/accounts/index.js +++ b/ui/app/accounts/index.js @@ -11,6 +11,7 @@ module.exports = connect(mapStateToProps)(AccountsScreen) function mapStateToProps (state) { const pendingTxs = valuesFor(state.metamask.unconfTxs) + .filter(tx => tx.txParams.metamaskNetworkId === state.metamask.network) const pendingMsgs = valuesFor(state.metamask.unconfMsgs) const pending = pendingTxs.concat(pendingMsgs) diff --git a/ui/app/actions.js b/ui/app/actions.js index c6c932296..57c2bf3e8 100644 --- a/ui/app/actions.js +++ b/ui/app/actions.js @@ -137,6 +137,12 @@ var actions = { getQr: getQr, reshowQrCode: reshowQrCode, SHOW_QR_VIEW: 'SHOW_QR_VIEW', +// FORGOT PASSWORD: + BACK_TO_INIT_MENU: 'BACK_TO_INIT_MENU', + goBackToInitView: goBackToInitView, + RECOVERY_IN_PROGRESS: 'RECOVERY_IN_PROGRESS', + BACK_TO_UNLOCK_VIEW: 'BACK_TO_UNLOCK_VIEW', + backToUnlockView: backToUnlockView, } module.exports = actions @@ -156,8 +162,10 @@ function goHome () { function tryUnlockMetamask (password) { return (dispatch) => { + dispatch(actions.showLoadingIndication()) dispatch(actions.unlockInProgress()) _accountManager.submitPassword(password, (err, selectedAccount) => { + dispatch(actions.hideLoadingIndication()) if (err) { dispatch(actions.unlockFailed()) } else { @@ -270,8 +278,6 @@ function signMsg (msgData) { function signTx (txData) { return (dispatch) => { - dispatch(actions.showLoadingIndication()) - web3.eth.sendTransaction(txData, (err, data) => { dispatch(actions.hideLoadingIndication()) @@ -279,6 +285,7 @@ function signTx (txData) { dispatch(actions.hideWarning()) dispatch(actions.goHome()) }) + dispatch(this.showConfTxPage()) } } @@ -370,6 +377,12 @@ function showNewVaultSeed (seed) { } } +function backToUnlockView () { + return { + type: actions.BACK_TO_UNLOCK_VIEW, + } +} + // // unlock screen // @@ -498,6 +511,12 @@ function showConfigPage (transitionForward = true) { } } +function goBackToInitView () { + return { + type: actions.BACK_TO_INIT_MENU, + } +} + // // config // diff --git a/ui/app/app.js b/ui/app/app.js index 84ff16ec8..2e05f0038 100644 --- a/ui/app/app.js +++ b/ui/app/app.js @@ -51,6 +51,7 @@ function mapStateToProps (state) { menuOpen: state.appState.menuOpen, network: state.metamask.network, provider: state.metamask.provider, + forgottenPassword: state.appState.forgottenPassword, } } @@ -89,6 +90,7 @@ App.prototype.render = function () { transitionLeaveTimeout: 300, }, [ this.renderPrimary(), + this.renderBackToInitButton(), ]), ]), ]) @@ -96,6 +98,11 @@ App.prototype.render = function () { } 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 @@ -238,10 +245,17 @@ App.prototype.renderNetworkDropdown = function () { label: 'Localhost 8545', closeMenu: () => this.setState({ isNetworkMenuOpen: false }), action: () => props.dispatch(actions.setRpcTarget('http://localhost:8545')), - icon: h('i.fa.fa-question-circle.fa-lg', { ariaHidden: true }), + icon: h('i.fa.fa-question-circle.fa-lg'), activeNetworkRender: props.provider.rpcTarget, }), + 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'), + }), + this.renderCustomOption(props.provider.rpcTarget), ]) } @@ -275,24 +289,109 @@ App.prototype.renderDropdown = function () { label: 'Settings', closeMenu: () => this.setState({ isMainMenuOpen: !isOpen }), action: () => this.props.dispatch(actions.showConfigPage()), - icon: h('i.fa.fa-gear.fa-lg', { ariaHidden: true }), + icon: h('i.fa.fa-gear.fa-lg'), }), h(DropMenuItem, { label: 'Lock', closeMenu: () => this.setState({ isMainMenuOpen: !isOpen }), action: () => this.props.dispatch(actions.lockMetamask()), - icon: h('i.fa.fa-lock.fa-lg', { ariaHidden: true }), + icon: h('i.fa.fa-lock.fa-lg'), }), h(DropMenuItem, { label: 'Help', closeMenu: () => this.setState({ isMainMenuOpen: !isOpen }), action: () => this.props.dispatch(actions.showInfoPage()), - icon: h('i.fa.fa-question.fa-lg', { ariaHidden: true }), + icon: h('i.fa.fa-question.fa-lg'), }), ]) } +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'), + ]) + ) + +} +App.prototype.renderBackToInitButton = function () { + var props = this.props + var button = null + if (!props.isUnlocked) { + if (props.currentView.name === 'InitMenu') { + button = props.forgottenPassword ? h('.flex-row', { + key: 'rightArrow', + style: { + position: 'absolute', + bottom: '10px', + right: '15px', + fontSize: '21px', + fontFamily: 'Montserrat Light', + color: '#7F8082', + width: '77.578px', + alignItems: 'flex-end', + }, + }, [ + h('div.cursor-pointer', { + style: { + marginRight: '3px', + }, + onClick: () => props.dispatch(actions.backToUnlockView()), + }, 'LOGIN'), + h('i.fa.fa-arrow-right.cursor-pointer'), + ]) : null + } else if (props.isInitialized) { + var style + switch (props.currentView.name) { + case 'createVault': + style = { + position: 'absolute', + top: '41px', + left: '80px', + fontSize: '21px', + fontFamily: 'Montserrat Bold', + color: 'rgb(174, 174, 174)', + } + return this.renderBackButton(style, true) + case 'restoreVault': + style = { + position: 'absolute', + top: '41px', + left: '70px', + fontSize: '21px', + fontFamily: 'Montserrat Bold', + color: 'rgb(174, 174, 174)', + } + return this.renderBackButton(style, true) + default: + style = { + position: 'absolute', + bottom: '10px', + left: '15px', + fontSize: '21px', + fontFamily: 'Montserrat Light', + color: '#7F8082', + width: '71.969px', + alignItems: 'flex-end', + } + return this.renderBackButton(style) + } + } + } + return button +} App.prototype.renderPrimary = function () { var props = this.props @@ -306,7 +405,7 @@ App.prototype.renderPrimary = function () { } // show initialize screen - if (!props.isInitialized) { + if (!props.isInitialized || props.forgottenPassword) { // show current view switch (props.currentView.name) { @@ -408,25 +507,21 @@ App.prototype.toggleMetamaskActive = function () { App.prototype.renderCustomOption = function (rpcTarget) { switch (rpcTarget) { case undefined: - return 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', { ariaHidden: true }), - }) + return null + case 'http://localhost:8545': return 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', { ariaHidden: true }), + icon: h('i.fa.fa-question-circle.fa-lg'), }) default: return h(DropMenuItem, { label: `${rpcTarget}`, closeMenu: () => this.setState({ isNetworkMenuOpen: false }), - icon: h('i.fa.fa-question-circle.fa-lg', { ariaHidden: true }), + icon: h('i.fa.fa-question-circle.fa-lg'), activeNetworkRender: 'custom', }) } diff --git a/ui/app/components/account-eth-balance.js b/ui/app/components/account-eth-balance.js deleted file mode 100644 index 8d693685f..000000000 --- a/ui/app/components/account-eth-balance.js +++ /dev/null @@ -1,140 +0,0 @@ -const Component = require('react').Component -const h = require('react-hyperscript') -const inherits = require('util').inherits -const connect = require('react-redux').connect -const formatBalance = require('../util').formatBalance -const generateBalanceObject = require('../util').generateBalanceObject -const Tooltip = require('./tooltip.js') - -module.exports = connect(mapStateToProps)(EthBalanceComponent) - -function mapStateToProps (state) { - return { - conversionRate: state.metamask.conversionRate, - conversionDate: state.metamask.conversionDate, - currentFiat: state.metamask.currentFiat, - } -} - -inherits(EthBalanceComponent, Component) -function EthBalanceComponent () { - Component.call(this) -} - -EthBalanceComponent.prototype.render = function () { - var state = this.props - var style = state.style - - const value = formatBalance(state.value, 6) - var width = state.width - - return ( - - h('.ether-balance', { - style: style, - }, [ - h('.ether-balance-amount', { - style: { - display: 'inline', - width: width, - }, - }, this.renderBalance(value, state)), - ]) - - ) -} -EthBalanceComponent.prototype.renderBalance = function (value, state) { - if (value === 'None') return value - var balanceObj = generateBalanceObject(value, state.shorten ? 1 : 3) - var balance, fiatDisplayNumber, fiatTooltipNumber - var splitBalance = value.split(' ') - var ethNumber = splitBalance[0] - var ethSuffix = splitBalance[1] - - - if (state.conversionRate !== 0) { - fiatTooltipNumber = Number(splitBalance[0]) * state.conversionRate - fiatDisplayNumber = fiatTooltipNumber.toFixed(2) - } else { - fiatDisplayNumber = 'N/A' - } - - var fiatSuffix = state.currentFiat - - if (state.shorten) { - balance = balanceObj.shortBalance - } else { - balance = balanceObj.balance - } - - var label = balanceObj.label - - return ( - h('.flex-column', [ - h(Tooltip, { - position: 'bottom', - title: `${ethNumber} ${ethSuffix}`, - }, [ - h('.flex-row', { - style: { - alignItems: 'flex-end', - lineHeight: '13px', - fontFamily: 'Montserrat Light', - textRendering: 'geometricPrecision', - marginBottom: '5px', - }, - }, [ - h('div', { - style: { - width: '100%', - textAlign: 'right', - }, - }, balance), - h('div', { - style: { - color: '#AEAEAE', - marginLeft: '5px', - }, - }, label), - ]), - ]), - h(Tooltip, { - position: 'bottom', - title: `${fiatTooltipNumber} ${fiatSuffix}`, - }, [ - fiatDisplay(fiatDisplayNumber, fiatSuffix), - ]), - ]) - ) -} - -function fiatDisplay (fiatDisplayNumber, fiatSuffix) { - if (fiatDisplayNumber !== 'N/A') { - return h('.flex-row', { - style: { - alignItems: 'flex-end', - lineHeight: '13px', - fontFamily: 'Montserrat Light', - textRendering: 'geometricPrecision', - }, - }, [ - h('div', { - style: { - width: '100%', - textAlign: 'right', - fontSize: '12px', - color: '#333333', - }, - }, fiatDisplayNumber), - h('div', { - style: { - color: '#AEAEAE', - marginLeft: '5px', - fontSize: '12px', - }, - }, fiatSuffix), - ]) - } else { - return h('div') - } -} diff --git a/ui/app/components/account-info-link.js b/ui/app/components/account-info-link.js index 4fe3b8b5d..49c42e9ec 100644 --- a/ui/app/components/account-info-link.js +++ b/ui/app/components/account-info-link.js @@ -14,7 +14,7 @@ function AccountInfoLink () { AccountInfoLink.prototype.render = function () { const { selected, network } = this.props - const title = 'View account on etherscan' + const title = 'View account on Etherscan' const url = genAccountLink(selected, network) if (!url) { diff --git a/ui/app/components/buy-button-subview.js b/ui/app/components/buy-button-subview.js index 742241e5b..c3e9e5d7b 100644 --- a/ui/app/components/buy-button-subview.js +++ b/ui/app/components/buy-button-subview.js @@ -106,7 +106,7 @@ BuyButtonSubview.prototype.formVersionSubview = function () { style: { width: '225px', }, - }, 'In order to access this feature please switch too the Main Network'), + }, 'In order to access this feature please switch to the Main Network'), h('h3.text-transform-uppercase', 'or:'), this.props.network === '2' ? h('button.text-transform-uppercase', { onClick: () => this.props.dispatch(actions.buyEth()), diff --git a/ui/app/components/eth-balance.js b/ui/app/components/eth-balance.js index 498873faa..46127bed5 100644 --- a/ui/app/components/eth-balance.js +++ b/ui/app/components/eth-balance.js @@ -4,6 +4,7 @@ const inherits = require('util').inherits const formatBalance = require('../util').formatBalance const generateBalanceObject = require('../util').generateBalanceObject const Tooltip = require('./tooltip.js') +const FiatValue = require('./fiat-value.js') module.exports = EthBalanceComponent @@ -13,11 +14,11 @@ function EthBalanceComponent () { } EthBalanceComponent.prototype.render = function () { - var state = this.props - var style = state.style + var props = this.props + var style = props.style var needsParse = this.props.needsParse !== undefined ? this.props.needsParse : true - const value = formatBalance(state.value, 6, needsParse) - var width = state.width + const value = formatBalance(props.value, 6, needsParse) + var width = props.width return ( @@ -35,15 +36,16 @@ EthBalanceComponent.prototype.render = function () { ) } EthBalanceComponent.prototype.renderBalance = function (value) { - var state = this.props + var props = this.props if (value === 'None') return value - var balanceObj = generateBalanceObject(value, state.shorten ? 1 : 3) + var balanceObj = generateBalanceObject(value, props.shorten ? 1 : 3) var balance var splitBalance = value.split(' ') var ethNumber = splitBalance[0] var ethSuffix = splitBalance[1] + const showFiat = 'showFiat' in props ? props.showFiat : true - if (state.shorten) { + if (props.shorten) { balance = balanceObj.shortBalance } else { balance = balanceObj.balance @@ -55,8 +57,8 @@ EthBalanceComponent.prototype.renderBalance = function (value) { h(Tooltip, { position: 'bottom', title: `${ethNumber} ${ethSuffix}`, - }, [ - h('.flex-column', { + }, h('div.flex-column', [ + h('.flex-row', { style: { alignItems: 'flex-end', lineHeight: '13px', @@ -74,9 +76,12 @@ EthBalanceComponent.prototype.renderBalance = function (value) { style: { color: ' #AEAEAE', fontSize: '12px', + marginLeft: '5px', }, }, label), ]), - ]) + + showFiat ? h(FiatValue, { value: props.value }) : null, + ])) ) } diff --git a/ui/app/components/fiat-value.js b/ui/app/components/fiat-value.js new file mode 100644 index 000000000..13ee48245 --- /dev/null +++ b/ui/app/components/fiat-value.js @@ -0,0 +1,71 @@ +const Component = require('react').Component +const h = require('react-hyperscript') +const inherits = require('util').inherits +const connect = require('react-redux').connect +const formatBalance = require('../util').formatBalance + +module.exports = connect(mapStateToProps)(FiatValue) + +function mapStateToProps (state) { + return { + conversionRate: state.metamask.conversionRate, + currentFiat: state.metamask.currentFiat, + } +} + +inherits(FiatValue, Component) +function FiatValue () { + Component.call(this) +} + +FiatValue.prototype.render = function () { + const props = this.props + const value = formatBalance(props.value, 6) + + if (value === 'None') return value + var fiatDisplayNumber, fiatTooltipNumber + var splitBalance = value.split(' ') + + if (props.conversionRate !== 0) { + fiatTooltipNumber = Number(splitBalance[0]) * props.conversionRate + fiatDisplayNumber = fiatTooltipNumber.toFixed(2) + } else { + fiatDisplayNumber = 'N/A' + fiatTooltipNumber = 'Unknown' + } + + var fiatSuffix = props.currentFiat + + return fiatDisplay(fiatDisplayNumber, fiatSuffix) +} + +function fiatDisplay (fiatDisplayNumber, fiatSuffix) { + if (fiatDisplayNumber !== 'N/A') { + return h('.flex-row', { + style: { + alignItems: 'flex-end', + lineHeight: '13px', + fontFamily: 'Montserrat Light', + textRendering: 'geometricPrecision', + }, + }, [ + h('div', { + style: { + width: '100%', + textAlign: 'right', + fontSize: '12px', + color: '#333333', + }, + }, fiatDisplayNumber), + h('div', { + style: { + color: '#AEAEAE', + marginLeft: '5px', + fontSize: '12px', + }, + }, fiatSuffix), + ]) + } else { + return h('div') + } +} diff --git a/ui/app/components/mascot.js b/ui/app/components/mascot.js index f2b00262b..f015d0c4d 100644 --- a/ui/app/components/mascot.js +++ b/ui/app/components/mascot.js @@ -14,9 +14,8 @@ function Mascot () { pxNotRatio: true, width: 200, height: 200, - staticImage: './images/icon-512.png', }) - if (!this.logo.webGLSupport) return + this.refollowMouse = debounce(this.logo.setFollowMouse.bind(this.logo, true), 1000) this.unfollowMouse = this.logo.setFollowMouse.bind(this.logo, false) } @@ -27,32 +26,25 @@ Mascot.prototype.render = function () { // and we dont get that until render this.handleAnimationEvents() - return ( - - h('#metamask-mascot-container') - - ) + return h('#metamask-mascot-container', { + style: { zIndex: 2 }, + }) } Mascot.prototype.componentDidMount = function () { var targetDivId = 'metamask-mascot-container' var container = document.getElementById(targetDivId) - if (!this.logo.webGLSupport) { - var staticLogo = this.logo.staticLogo - staticLogo.style.marginBottom = '40px' - container.appendChild(staticLogo) - } else { - container.appendChild(this.logo.canvas) - } + container.appendChild(this.logo.container) } Mascot.prototype.componentWillUnmount = function () { - if (!this.logo.webGLSupport) return - this.logo.canvas.remove() + this.animations = this.props.animationEventEmitter + this.animations.removeAllListeners() + this.logo.container.remove() + this.logo.stopAnimation() } Mascot.prototype.handleAnimationEvents = function () { - if (!this.logo.webGLSupport) return // only setup listeners once if (this.animations) return this.animations = this.props.animationEventEmitter @@ -61,7 +53,6 @@ Mascot.prototype.handleAnimationEvents = function () { } Mascot.prototype.lookAt = function (target) { - if (!this.logo.webGLSupport) return this.unfollowMouse() this.logo.lookAt(target) this.refollowMouse() diff --git a/ui/app/components/network.js b/ui/app/components/network.js index 2f1bf639a..845861396 100644 --- a/ui/app/components/network.js +++ b/ui/app/components/network.js @@ -61,7 +61,7 @@ Network.prototype.render = function () { style: { color: '#039396', }}, - 'Etherum Main Net'), + 'Ethereum Main Net'), ]) case 'morden-test-network': return h('.network-indicator', [ @@ -75,7 +75,6 @@ Network.prototype.render = function () { default: return h('.network-indicator', [ h('i.fa.fa-question-circle.fa-lg', { - ariaHidden: true, style: { margin: '10px', color: 'rgb(125, 128, 130)', diff --git a/ui/app/components/pending-tx-details.js b/ui/app/components/pending-tx-details.js index 2fb0eae3f..c2e39a1ca 100644 --- a/ui/app/components/pending-tx-details.js +++ b/ui/app/components/pending-tx-details.js @@ -4,9 +4,8 @@ const inherits = require('util').inherits const carratInline = require('fs').readFileSync('./images/forward-carrat.svg', 'utf8') const MiniAccountPanel = require('./mini-account-panel') -const EtherBalance = require('./eth-balance') +const EthBalance = require('./eth-balance') const addressSummary = require('../util').addressSummary -const formatBalance = require('../util').formatBalance const nameForAddress = require('../../lib/contract-namer') const ethUtil = require('ethereumjs-util') const BN = ethUtil.BN @@ -70,7 +69,7 @@ PTXP.render = function () { fontFamily: 'Montserrat Light, Montserrat, sans-serif', }, }, [ - h(EtherBalance, { + h(EthBalance, { value: balance, inline: true, labelColor: '#F7861C', @@ -107,12 +106,12 @@ PTXP.render = function () { h('.row', [ h('.cell.label', 'Amount'), - h('.cell.value', formatBalance(txParams.value)), + h(EthBalance, { value: txParams.value }), ]), h('.cell.row', [ h('.cell.label', 'Max Transaction Fee'), - h('.cell.value', formatBalance(txFee.toString(16))), + h(EthBalance, { value: txFee.toString(16) }), ]), h('.cell.row', { @@ -129,7 +128,7 @@ PTXP.render = function () { alignItems: 'center', }, }, [ - h(EtherBalance, { + h(EthBalance, { value: maxCost.toString(16), inline: true, labelColor: 'black', diff --git a/ui/app/components/shapeshift-form.js b/ui/app/components/shapeshift-form.js index b8650f7d5..58b7942c3 100644 --- a/ui/app/components/shapeshift-form.js +++ b/ui/app/components/shapeshift-form.js @@ -1,4 +1,4 @@ -const Component = require('react').Component +const PersistentForm = require('../../lib/persistent-form') const h = require('react-hyperscript') const inherits = require('util').inherits const connect = require('react-redux').connect @@ -17,12 +17,15 @@ function mapStateToProps(state) { } } -inherits(ShapeshiftForm, Component) +inherits(ShapeshiftForm, PersistentForm) function ShapeshiftForm () { - Component.call(this) + PersistentForm.call(this) + this.persistentFormParentId = 'shapeshift-buy-form' } + ShapeshiftForm.prototype.render = function () { + return h(ReactCSSTransitionGroup, { className: 'css-transition-group', transitionName: 'main', @@ -66,6 +69,9 @@ ShapeshiftForm.prototype.renderMain = function () { h('input#fromCoin.buy-inputs.ex-coins', { type: 'text', list: 'coinList', + dataset: { + persistentFormId: 'input-coin', + }, style: { boxSizing: 'border-box', }, @@ -159,6 +165,9 @@ ShapeshiftForm.prototype.renderMain = function () { h('input#fromCoinAddress.buy-inputs', { type: 'text', placeholder: `Your ${coin} Refund Address`, + dataset: { + persistentFormId: 'refund-address', + }, style: { boxSizing: 'border-box', width: '278px', diff --git a/ui/app/components/tooltip.js b/ui/app/components/tooltip.js index fb67c717e..757ad0cd6 100644 --- a/ui/app/components/tooltip.js +++ b/ui/app/components/tooltip.js @@ -11,12 +11,14 @@ function Tooltip () { } Tooltip.prototype.render = function () { + const props = this.props + const { position, title, children } = props return h(ReactTooltip, { - position: props.position ? props.position : 'left', - title: props.title, + position: position || 'left', + title, fixed: false, - }, props.children) + }, children) } diff --git a/ui/app/components/transaction-list-item.js b/ui/app/components/transaction-list-item.js index 1b85464e1..66a232981 100644 --- a/ui/app/components/transaction-list-item.js +++ b/ui/app/components/transaction-list-item.js @@ -77,6 +77,7 @@ TransactionListItem.prototype.render = function () { value: txParams.value, width: '55px', shorten: true, + showFiat: false, style: {fontSize: '15px'}, }) : h('.flex-column'), ]) diff --git a/ui/app/conf-tx.js b/ui/app/conf-tx.js index 43604e8cf..99b4bc9f1 100644 --- a/ui/app/conf-tx.js +++ b/ui/app/conf-tx.js @@ -5,6 +5,7 @@ const h = require('react-hyperscript') const connect = require('react-redux').connect const actions = require('./actions') const txHelper = require('../lib/tx-helper') +const isPopupOrNotification = require('../../app/scripts/lib/is-popup-or-notification') const PendingTx = require('./components/pending-tx') const PendingMsg = require('./components/pending-msg') @@ -20,6 +21,7 @@ function mapStateToProps (state) { unconfMsgs: state.metamask.unconfMsgs, index: state.appState.currentView.context, warning: state.appState.warning, + network: state.metamask.network, } } @@ -31,11 +33,13 @@ function ConfirmTxScreen () { ConfirmTxScreen.prototype.render = function () { var state = this.props + var network = state.network var unconfTxs = state.unconfTxs var unconfMsgs = state.unconfMsgs - var unconfTxList = txHelper(unconfTxs, unconfMsgs) + var unconfTxList = txHelper(unconfTxs, unconfMsgs, network) var index = state.index !== undefined ? state.index : 0 var txData = unconfTxList[index] || unconfTxList[0] || {} + var isNotification = isPopupOrNotification() === 'notification' return ( @@ -43,9 +47,9 @@ ConfirmTxScreen.prototype.render = function () { // subtitle and nav h('.section-title.flex-row.flex-center', [ - h('i.fa.fa-arrow-left.fa-lg.cursor-pointer', { + !isNotification ? h('i.fa.fa-arrow-left.fa-lg.cursor-pointer', { onClick: this.goHome.bind(this), - }), + }) : null, h('h2.page-subtitle', 'Confirm Transaction'), ]), diff --git a/ui/app/eth-store-warning.js b/ui/app/eth-store-warning.js index 55274996b..fe3c7ce5d 100644 --- a/ui/app/eth-store-warning.js +++ b/ui/app/eth-store-warning.js @@ -35,10 +35,9 @@ EthStoreWarning.prototype.render = function () { margin: '10px 10px 10px 10px', }, }, - `The MetaMask team would like to - remind you that MetaMask is currently in beta - so - don't store large - amounts of ether in MetaMask. + `MetaMask is currently in beta; use + caution in storing large + amounts of ether. `), h('i.fa.fa-exclamation-triangle.fa-4', { diff --git a/ui/app/first-time/init-menu.js b/ui/app/first-time/init-menu.js index e63c30fc5..94a9d3df6 100644 --- a/ui/app/first-time/init-menu.js +++ b/ui/app/first-time/init-menu.js @@ -73,9 +73,7 @@ InitializeMenuScreen.prototype.renderMenu = function () { margin: 12, }, }, 'Restore Existing Vault'), - ]) - ) } diff --git a/ui/app/first-time/restore-vault.js b/ui/app/first-time/restore-vault.js index 684781e50..4c1f21008 100644 --- a/ui/app/first-time/restore-vault.js +++ b/ui/app/first-time/restore-vault.js @@ -1,14 +1,14 @@ const inherits = require('util').inherits -const Component = require('react').Component +const PersistentForm = require('../../lib/persistent-form') const connect = require('react-redux').connect const h = require('react-hyperscript') const actions = require('../actions') module.exports = connect(mapStateToProps)(RestoreVaultScreen) -inherits(RestoreVaultScreen, Component) +inherits(RestoreVaultScreen, PersistentForm) function RestoreVaultScreen () { - Component.call(this) + PersistentForm.call(this) } function mapStateToProps (state) { @@ -19,6 +19,8 @@ function mapStateToProps (state) { RestoreVaultScreen.prototype.render = function () { var state = this.props + this.persistentFormParentId = 'restore-vault-form' + return ( h('.initialize-screen.flex-column.flex-center.flex-grow', [ @@ -39,6 +41,9 @@ RestoreVaultScreen.prototype.render = function () { // wallet seed entry h('h3', 'Wallet Seed'), h('textarea.twelve-word-phrase.letter-spacey', { + dataset: { + persistentFormId: 'wallet-seed', + }, placeholder: 'Enter your secret twelve word phrase here to restore your vault.', }), @@ -47,6 +52,9 @@ RestoreVaultScreen.prototype.render = function () { type: 'password', id: 'password-box', placeholder: 'New Password (min 8 chars)', + dataset: { + persistentFormId: 'password', + }, style: { width: 260, marginTop: 12, @@ -59,6 +67,9 @@ RestoreVaultScreen.prototype.render = function () { id: 'password-box-confirm', placeholder: 'Confirm Password', onKeyPress: this.onMaybeCreate.bind(this), + dataset: { + persistentFormId: 'password-confirmation', + }, style: { width: 260, marginTop: 16, diff --git a/ui/app/info.js b/ui/app/info.js index 4e540bd03..5c06409bd 100644 --- a/ui/app/info.js +++ b/ui/app/info.js @@ -67,7 +67,7 @@ InfoScreen.prototype.render = function () { `For more information on MetaMask you can visit our web site. If you want to contact us with questions or just - say 'Hi', you can find us on theise platforms:`), + say 'Hi', you can find us on these platforms:`), h('div', { style: { diff --git a/ui/app/reducers/app.js b/ui/app/reducers/app.js index 8c2696877..a6cd9ca1b 100644 --- a/ui/app/reducers/app.js +++ b/ui/app/reducers/app.js @@ -1,6 +1,7 @@ const extend = require('xtend') const actions = require('../actions') const txHelper = require('../../lib/tx-helper') +const notification = require('../../../app/scripts/lib/notifications') module.exports = reduceApp @@ -123,6 +124,7 @@ function reduceApp (state, action) { case actions.UNLOCK_METAMASK: return extend(appState, { + forgottenPassword: appState.forgottenPassword ? !appState.forgottenPassword : null, detailView: {}, transForward: true, isLoading: false, @@ -136,6 +138,25 @@ function reduceApp (state, action) { warning: null, }) + case actions.BACK_TO_INIT_MENU: + return extend(appState, { + warning: null, + transForward: false, + forgottenPassword: true, + currentView: { + name: 'InitMenu', + }, + }) + + case actions.BACK_TO_UNLOCK_VIEW: + return extend(appState, { + warning: null, + transForward: true, + forgottenPassword: !appState.forgottenPassword, + currentView: { + name: 'UnlockScreen', + }, + }) // reveal seed words case actions.REVEAL_SEED_CONFIRMATION: @@ -170,6 +191,7 @@ function reduceApp (state, action) { case actions.SHOW_ACCOUNT_DETAIL: return extend(appState, { + forgottenPassword: appState.forgottenPassword ? !appState.forgottenPassword : null, currentView: { name: 'accountDetail', context: action.value, @@ -236,8 +258,9 @@ function reduceApp (state, action) { case actions.COMPLETED_TX: var unconfTxs = state.metamask.unconfTxs var unconfMsgs = state.metamask.unconfMsgs + var network = state.metamask.network - var unconfTxList = txHelper(unconfTxs, unconfMsgs) + var unconfTxList = txHelper(unconfTxs, unconfMsgs, network) .filter(tx => tx !== tx.id) if (unconfTxList && unconfTxList.length > 0) { @@ -250,6 +273,9 @@ function reduceApp (state, action) { warning: null, }) } else { + + notification.closePopup() + return extend(appState, { transForward: false, warning: null, @@ -498,14 +524,16 @@ function reduceApp (state, action) { function hasPendingTxs (state) { var unconfTxs = state.metamask.unconfTxs var unconfMsgs = state.metamask.unconfMsgs - var unconfTxList = txHelper(unconfTxs, unconfMsgs) + var network = state.metamask.network + var unconfTxList = txHelper(unconfTxs, unconfMsgs, network) return unconfTxList.length > 0 } function indexForPending (state, txId) { var unconfTxs = state.metamask.unconfTxs var unconfMsgs = state.metamask.unconfMsgs - var unconfTxList = txHelper(unconfTxs, unconfMsgs) + var network = state.metamask.network + var unconfTxList = txHelper(unconfTxs, unconfMsgs, network) let idx unconfTxList.forEach((tx, i) => { if (tx.id === txId) { @@ -515,4 +543,3 @@ function indexForPending (state, txId) { return idx } - diff --git a/ui/app/send.js b/ui/app/send.js index 06ea199f4..009866cf7 100644 --- a/ui/app/send.js +++ b/ui/app/send.js @@ -1,5 +1,5 @@ const inherits = require('util').inherits -const Component = require('react').Component +const PersistentForm = require('../lib/persistent-form') const h = require('react-hyperscript') const connect = require('react-redux').connect const Identicon = require('./components/identicon') @@ -7,7 +7,7 @@ const actions = require('./actions') const util = require('./util') const numericBalance = require('./util').numericBalance const addressSummary = require('./util').addressSummary -const EtherBalance = require('./components/eth-balance') +const EthBalance = require('./components/eth-balance') const ethUtil = require('ethereumjs-util') module.exports = connect(mapStateToProps)(SendTransactionScreen) @@ -29,12 +29,14 @@ function mapStateToProps (state) { return result } -inherits(SendTransactionScreen, Component) +inherits(SendTransactionScreen, PersistentForm) function SendTransactionScreen () { - Component.call(this) + PersistentForm.call(this) } SendTransactionScreen.prototype.render = function () { + this.persistentFormParentId = 'send-tx-form' + var state = this.props var address = state.address var account = state.account @@ -105,8 +107,7 @@ SendTransactionScreen.prototype.render = function () { // balance h('.flex-row.flex-center', [ - // h('div', formatBalance(account && account.balance)), - h(EtherBalance, { + h(EthBalance, { value: account && account.balance, }), @@ -137,6 +138,9 @@ SendTransactionScreen.prototype.render = function () { h('input.large-input', { name: 'address', placeholder: 'Recipient Address', + dataset: { + persistentFormId: 'recipient-address', + }, }), ]), @@ -150,6 +154,9 @@ SendTransactionScreen.prototype.render = function () { style: { marginRight: 6, }, + dataset: { + persistentFormId: 'tx-amount', + }, }), h('button.primary', { @@ -185,11 +192,12 @@ SendTransactionScreen.prototype.render = function () { width: '100%', resize: 'none', }, + dataset: { + persistentFormId: 'tx-data', + }, }), ]), - ]) - ) } @@ -227,7 +235,6 @@ SendTransactionScreen.prototype.onSubmit = function () { } this.props.dispatch(actions.hideWarning()) - this.props.dispatch(actions.showLoadingIndication()) var txParams = { from: this.props.address, diff --git a/ui/app/unlock.js b/ui/app/unlock.js index 7cc1699c3..b82e46d02 100644 --- a/ui/app/unlock.js +++ b/ui/app/unlock.js @@ -26,47 +26,46 @@ UnlockScreen.prototype.render = function () { const state = this.props const warning = state.warning return ( - - h('.unlock-screen.flex-column.flex-center.flex-grow', [ - - h(Mascot, { - animationEventEmitter: this.animationEventEmitter, - }), - - h('h1', { - style: { - fontSize: '1.4em', - textTransform: 'uppercase', - color: '#7F8082', - }, - }, 'MetaMask'), - - h('input.large-input', { - type: 'password', - id: 'password-box', - placeholder: 'enter password', - style: { - - }, - onKeyPress: this.onKeyPress.bind(this), - onInput: this.inputChanged.bind(this), - }), - - h('.error', { - style: { - display: warning ? 'block' : 'none', - }, - }, warning), - - h('button.primary.cursor-pointer', { - onClick: this.onSubmit.bind(this), - style: { - margin: 10, - }, - }, 'Unlock'), - + h('.flex-column.hey-im-here', [ + h('.unlock-screen.flex-column.flex-center.flex-grow', [ + + h(Mascot, { + animationEventEmitter: this.animationEventEmitter, + }), + + h('h1', { + style: { + fontSize: '1.4em', + textTransform: 'uppercase', + color: '#7F8082', + }, + }, 'MetaMask'), + + h('input.large-input', { + type: 'password', + id: 'password-box', + placeholder: 'enter password', + style: { + + }, + onKeyPress: this.onKeyPress.bind(this), + onInput: this.inputChanged.bind(this), + }), + + h('.error', { + style: { + display: warning ? 'block' : 'none', + }, + }, warning), + + h('button.primary.cursor-pointer', { + onClick: this.onSubmit.bind(this), + style: { + margin: 10, + }, + }, 'Unlock'), + ]), ]) - ) } diff --git a/ui/index.js b/ui/index.js index 8cf74f6ee..0e69b00d6 100644 --- a/ui/index.js +++ b/ui/index.js @@ -3,7 +3,7 @@ const h = require('react-hyperscript') const Root = require('./app/root') const actions = require('./app/actions') const configureStore = require('./app/store') - +const txHelper = require('./lib/tx-helper') module.exports = launchApp function launchApp (opts) { @@ -34,7 +34,8 @@ function startApp (metamaskState, accountManager, opts) { }) // if unconfirmed txs, start on txConf page - if (Object.keys(metamaskState.unconfTxs || {}).length) { + var unconfirmedTxsAll = txHelper(metamaskState.unconfTxs, metamaskState.unconfMsgs, metamaskState.network) + if (unconfirmedTxsAll.length > 0) { store.dispatch(actions.showConfTxPage()) } diff --git a/ui/lib/persistent-form.js b/ui/lib/persistent-form.js new file mode 100644 index 000000000..d4dc20b03 --- /dev/null +++ b/ui/lib/persistent-form.js @@ -0,0 +1,61 @@ +const inherits = require('util').inherits +const Component = require('react').Component +const defaultKey = 'persistent-form-default' +const eventName = 'keyup' + +module.exports = PersistentForm + +function PersistentForm () { + Component.call(this) +} + +inherits(PersistentForm, Component) + +PersistentForm.prototype.componentDidMount = function () { + const fields = document.querySelectorAll('[data-persistent-formid]') + const store = this.getPersistentStore() + + for (var i = 0; i < fields.length; i++) { + const field = fields[i] + const key = field.getAttribute('data-persistent-formid') + const cached = store[key] + if (cached !== undefined) { + field.value = cached + } + + field.addEventListener(eventName, this.persistentFieldDidUpdate.bind(this)) + } +} + +PersistentForm.prototype.getPersistentStore = function () { + let store = window.localStorage[this.persistentFormParentId || defaultKey] + if (store && store !== 'null') { + store = JSON.parse(store) + } else { + store = {} + } + return store +} + +PersistentForm.prototype.setPersistentStore = function (newStore) { + window.localStorage[this.persistentFormParentId || defaultKey] = JSON.stringify(newStore) +} + +PersistentForm.prototype.persistentFieldDidUpdate = function (event) { + const field = event.target + const store = this.getPersistentStore() + const key = field.getAttribute('data-persistent-formid') + const val = field.value + store[key] = val + this.setPersistentStore(store) +} + +PersistentForm.prototype.componentWillUnmount = function () { + const fields = document.querySelectorAll('[data-persistent-formid]') + for (var i = 0; i < fields.length; i++) { + const field = fields[i] + field.removeEventListener(eventName, this.persistentFieldDidUpdate.bind(this)) + } + this.setPersistentStore({}) +} + diff --git a/ui/lib/tx-helper.js b/ui/lib/tx-helper.js index 8f15cd3cc..c984bc9af 100644 --- a/ui/lib/tx-helper.js +++ b/ui/lib/tx-helper.js @@ -1,7 +1,7 @@ const valuesFor = require('../app/util').valuesFor -module.exports = function (unconfTxs, unconfMsgs) { - var txValues = valuesFor(unconfTxs) +module.exports = function (unconfTxs, unconfMsgs, network) { + var txValues = network ? valuesFor(unconfTxs).filter(tx => tx.txParams.metamaskNetworkId === network) : valuesFor(unconfTxs) var msgValues = valuesFor(unconfMsgs) var allValues = txValues.concat(msgValues) return allValues.sort(tx => tx.time) |