diff options
author | Dan Finlay <dan@danfinlay.com> | 2017-02-16 08:09:16 +0800 |
---|---|---|
committer | Dan Finlay <dan@danfinlay.com> | 2017-02-16 08:09:16 +0800 |
commit | 6d103dc1e7eeb9a1f55e4387fcc1fe194c7eb4cf (patch) | |
tree | 47906b974dee789ffd544b4c85da67e6f2323016 /ui/app/accounts | |
parent | 245e779f37763ce0633119c257877706d0bf3554 (diff) | |
parent | 943bcec0d702b2c70b323000ed25d3c425e2a44f (diff) | |
download | tangerine-wallet-browser-6d103dc1e7eeb9a1f55e4387fcc1fe194c7eb4cf.tar.gz tangerine-wallet-browser-6d103dc1e7eeb9a1f55e4387fcc1fe194c7eb4cf.tar.zst tangerine-wallet-browser-6d103dc1e7eeb9a1f55e4387fcc1fe194c7eb4cf.zip |
Merge branch 'kumavis-patch-1' of github.com:MetaMask/metamask-plugin into kumavis-patch-1
Diffstat (limited to 'ui/app/accounts')
-rw-r--r-- | ui/app/accounts/account-list-item.js | 42 | ||||
-rw-r--r-- | ui/app/accounts/import/index.js | 91 | ||||
-rw-r--r-- | ui/app/accounts/import/json.js | 98 | ||||
-rw-r--r-- | ui/app/accounts/import/private-key.js | 68 | ||||
-rw-r--r-- | ui/app/accounts/import/seed.js | 30 | ||||
-rw-r--r-- | ui/app/accounts/index.js | 59 |
6 files changed, 344 insertions, 44 deletions
diff --git a/ui/app/accounts/account-list-item.js b/ui/app/accounts/account-list-item.js index aa80e0e23..2a3c13d05 100644 --- a/ui/app/accounts/account-list-item.js +++ b/ui/app/accounts/account-list-item.js @@ -3,34 +3,34 @@ 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') -module.exports = NewComponent +module.exports = AccountListItem -inherits(NewComponent, Component) -function NewComponent () { +inherits(AccountListItem, Component) +function AccountListItem () { Component.call(this) } -NewComponent.prototype.render = function () { - const identity = this.props.identity - var isSelected = this.props.selectedAddress === identity.address - var account = this.props.accounts[identity.address] +AccountListItem.prototype.render = function () { + const { identity, selectedAddress, accounts, onShowDetail } = this.props + + const checksumAddress = identity && identity.address && ethUtil.toChecksumAddress(identity.address) + const isSelected = selectedAddress === identity.address + const account = accounts[identity.address] const selectedClass = isSelected ? '.selected' : '' return ( h(`.accounts-list-option.flex-row.flex-space-between.pointer.hover-white${selectedClass}`, { key: `account-panel-${identity.address}`, - style: { - flex: '1 0 auto', - }, - onClick: (event) => this.props.onShowDetail(identity.address, event), + onClick: (event) => onShowDetail(identity.address, event), }, [ h('.identicon-wrapper.flex-column.flex-center.select-none', [ this.pendingOrNot(), + this.indicateIfLoose(), h(Identicon, { address: identity.address, imageify: true, @@ -49,9 +49,9 @@ NewComponent.prototype.render = function () { overflow: 'hidden', textOverflow: 'ellipsis', }, - }, ethUtil.toChecksumAddress(identity.address)), - h(AccountEtherBalance, { - value: account.balance, + }, checksumAddress), + h(EthBalance, { + value: account && account.balance, style: { lineHeight: '7px', marginTop: '10px', @@ -66,14 +66,22 @@ NewComponent.prototype.render = function () { }, }, [ h(CopyButton, { - value: ethUtil.toChecksumAddress(identity.address), + value: checksumAddress, }), ]), ]) ) } -NewComponent.prototype.pendingOrNot = function () { +AccountListItem.prototype.indicateIfLoose = function () { + try { // Sometimes keyrings aren't loaded yet: + const type = this.props.keyring.type + const isLoose = type !== 'HD Key Tree' + return isLoose ? h('.keyring-label', 'LOOSE') : null + } catch (e) { return } +} + +AccountListItem.prototype.pendingOrNot = function () { const pending = this.props.pending if (pending.length === 0) return null return h('.pending-dot', pending.length) diff --git a/ui/app/accounts/import/index.js b/ui/app/accounts/import/index.js new file mode 100644 index 000000000..96350852a --- /dev/null +++ b/ui/app/accounts/import/index.js @@ -0,0 +1,91 @@ +const inherits = require('util').inherits +const Component = require('react').Component +const h = require('react-hyperscript') +const connect = require('react-redux').connect +import Select from 'react-select' + +// Subviews +const JsonImportView = require('./json.js') +const PrivateKeyImportView = require('./private-key.js') + +const menuItems = [ + 'Private Key', + 'JSON File', +] + +module.exports = connect(mapStateToProps)(AccountImportSubview) + +function mapStateToProps (state) { + return { + menuItems, + } +} + +inherits(AccountImportSubview, Component) +function AccountImportSubview () { + Component.call(this) +} + +AccountImportSubview.prototype.render = function () { + const props = this.props + const state = this.state || {} + const { menuItems } = props + const { type } = state + + return ( + h('div', { + style: { + }, + }, [ + h('div', { + style: { + padding: '10px', + color: 'rgb(174, 174, 174)', + }, + }, [ + + h('h3', { style: { padding: '3px' } }, 'SELECT TYPE'), + + h('style', ` + .has-value.Select--single > .Select-control .Select-value .Select-value-label, .Select-value-label { + color: rgb(174,174,174); + } + `), + + h(Select, { + name: 'import-type-select', + clearable: false, + value: type || menuItems[0], + options: menuItems.map((type) => { + return { + value: type, + label: type, + } + }), + onChange: (opt) => { + this.setState({ type: opt.value }) + }, + }), + ]), + + this.renderImportView(), + ]) + ) +} + +AccountImportSubview.prototype.renderImportView = function() { + const props = this.props + const state = this.state || {} + const { type } = state + const { menuItems } = props + const current = type || menuItems[0] + + switch (current) { + case 'Private Key': + return h(PrivateKeyImportView) + case 'JSON File': + return h(JsonImportView) + default: + return h(JsonImportView) + } +} diff --git a/ui/app/accounts/import/json.js b/ui/app/accounts/import/json.js new file mode 100644 index 000000000..1c2b331d4 --- /dev/null +++ b/ui/app/accounts/import/json.js @@ -0,0 +1,98 @@ +const inherits = require('util').inherits +const Component = require('react').Component +const h = require('react-hyperscript') +const connect = require('react-redux').connect +const actions = require('../../actions') +const FileInput = require('react-simple-file-input').default + +module.exports = connect(mapStateToProps)(JsonImportSubview) + +function mapStateToProps (state) { + return { + error: state.appState.warning, + } +} + +inherits(JsonImportSubview, Component) +function JsonImportSubview () { + Component.call(this) +} + +JsonImportSubview.prototype.render = function () { + const { error } = this.props + + return ( + h('div', { + style: { + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + padding: '5px 15px 0px 15px', + }, + }, [ + + h('p', 'Used by a variety of different clients'), + + h(FileInput, { + readAs: 'text', + onLoad: this.onLoad.bind(this), + style: { + margin: '20px 0px 12px 20px', + fontSize: '15px', + }, + }), + + h('input.large-input.letter-spacey', { + type: 'password', + placeholder: 'Enter password', + id: 'json-password-box', + onKeyPress: this.createKeyringOnEnter.bind(this), + style: { + width: 260, + marginTop: 12, + }, + }), + + h('button.primary', { + onClick: this.createNewKeychain.bind(this), + style: { + margin: 12, + }, + }, 'Import'), + + error ? h('span.warning', error) : null, + ]) + ) +} + +JsonImportSubview.prototype.onLoad = function (event, file) { + this.setState({file: file, fileContents: event.target.result}) +} + +JsonImportSubview.prototype.createKeyringOnEnter = function (event) { + if (event.key === 'Enter') { + event.preventDefault() + this.createNewKeychain() + } +} + +JsonImportSubview.prototype.createNewKeychain = function () { + const state = this.state + const { fileContents } = state + + if (!fileContents) { + const message = 'You must select a file to import.' + return this.props.dispatch(actions.displayWarning(message)) + } + + const passwordInput = document.getElementById('json-password-box') + const password = passwordInput.value + + if (!password) { + const message = 'You must enter a password for the selected file.' + return this.props.dispatch(actions.displayWarning(message)) + } + + this.props.dispatch(actions.importNewAccount('JSON File', [ fileContents, password ])) +} + diff --git a/ui/app/accounts/import/private-key.js b/ui/app/accounts/import/private-key.js new file mode 100644 index 000000000..b139a0374 --- /dev/null +++ b/ui/app/accounts/import/private-key.js @@ -0,0 +1,68 @@ +const inherits = require('util').inherits +const Component = require('react').Component +const h = require('react-hyperscript') +const connect = require('react-redux').connect +const actions = require('../../actions') + +module.exports = connect(mapStateToProps)(PrivateKeyImportView) + +function mapStateToProps (state) { + return { + error: state.appState.warning, + } +} + +inherits(PrivateKeyImportView, Component) +function PrivateKeyImportView () { + Component.call(this) +} + +PrivateKeyImportView.prototype.render = function () { + const { error } = this.props + + return ( + h('div', { + style: { + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + padding: '5px 15px 0px 15px', + }, + }, [ + h('span', 'Paste your private key string here'), + + h('input.large-input.letter-spacey', { + type: 'password', + id: 'private-key-box', + onKeyPress: this.createKeyringOnEnter.bind(this), + style: { + width: 260, + marginTop: 12, + }, + }), + + h('button.primary', { + onClick: this.createNewKeychain.bind(this), + style: { + margin: 12, + }, + }, 'Import'), + + error ? h('span.warning', error) : null, + ]) + ) +} + +PrivateKeyImportView.prototype.createKeyringOnEnter = function (event) { + if (event.key === 'Enter') { + event.preventDefault() + this.createNewKeychain() + } +} + +PrivateKeyImportView.prototype.createNewKeychain = function () { + const input = document.getElementById('private-key-box') + const privateKey = input.value + this.props.dispatch(actions.importNewAccount('Private Key', [ privateKey ])) +} + diff --git a/ui/app/accounts/import/seed.js b/ui/app/accounts/import/seed.js new file mode 100644 index 000000000..b4a7c0afa --- /dev/null +++ b/ui/app/accounts/import/seed.js @@ -0,0 +1,30 @@ +const inherits = require('util').inherits +const Component = require('react').Component +const h = require('react-hyperscript') +const connect = require('react-redux').connect + +module.exports = connect(mapStateToProps)(SeedImportSubview) + +function mapStateToProps (state) { + return {} +} + +inherits(SeedImportSubview, Component) +function SeedImportSubview () { + Component.call(this) +} + +SeedImportSubview.prototype.render = function () { + return ( + h('div', { + style: { + }, + }, [ + `Paste your seed phrase here!`, + h('textarea'), + h('br'), + h('button', 'Submit'), + ]) + ) +} + diff --git a/ui/app/accounts/index.js b/ui/app/accounts/index.js index 6e12accc7..e236a4e85 100644 --- a/ui/app/accounts/index.js +++ b/ui/app/accounts/index.js @@ -10,18 +10,19 @@ const AccountListItem = require('./account-list-item') module.exports = connect(mapStateToProps)(AccountsScreen) function mapStateToProps (state) { - const pendingTxs = valuesFor(state.metamask.unconfTxs) - const pendingMsgs = valuesFor(state.metamask.unconfMsgs) + const pendingTxs = valuesFor(state.metamask.unapprovedTxs) + .filter(tx => tx.txParams.metamaskNetworkId === state.metamask.network) + const pendingMsgs = valuesFor(state.metamask.unapprovedMsgs) const pending = pendingTxs.concat(pendingMsgs) return { accounts: state.metamask.accounts, identities: state.metamask.identities, - unconfTxs: state.metamask.unconfTxs, + unapprovedTxs: state.metamask.unapprovedTxs, selectedAddress: state.metamask.selectedAddress, - currentDomain: state.appState.currentDomain, scrollToBottom: state.appState.scrollToBottom, pending, + keyrings: state.metamask.keyrings, } } @@ -31,14 +32,11 @@ function AccountsScreen () { } AccountsScreen.prototype.render = function () { - var state = this.props - var identityList = valuesFor(state.identities) - var unconfTxList = valuesFor(state.unconfTxs) - var actions = { - onSelect: this.onSelect.bind(this), - onShowDetail: this.onShowDetail.bind(this), - goHome: this.goHome.bind(this), - } + const props = this.props + const { keyrings } = props + const identityList = valuesFor(props.identities) + const unapprovedTxList = valuesFor(props.unapprovedTxs) + return ( h('.accounts-section.flex-grow', [ @@ -46,7 +44,7 @@ AccountsScreen.prototype.render = function () { // subtitle and nav h('.section-title.flex-center', [ h('i.fa.fa-arrow-left.fa-lg.cursor-pointer', { - onClick: actions.goHome, + onClick: this.goHome.bind(this), }), h('h2.page-subtitle', 'Select Account'), ]), @@ -54,7 +52,7 @@ AccountsScreen.prototype.render = function () { h('hr.horizontal-line'), // identity selection - h('section.identity-section.flex-column', { + h('section.identity-section', { style: { height: '418px', overflowY: 'auto', @@ -73,6 +71,12 @@ AccountsScreen.prototype.render = function () { } }) + const simpleAddress = identity.address.substring(2).toLowerCase() + const keyring = keyrings.find((kr) => { + return kr.accounts.includes(simpleAddress) || + kr.accounts.includes(identity.address) + }) + return h(AccountListItem, { key: `acct-panel-${identity.address}`, identity, @@ -80,18 +84,18 @@ AccountsScreen.prototype.render = function () { accounts: this.props.accounts, onShowDetail: this.onShowDetail.bind(this), pending, + keyring, }) }), - h('hr.horizontal-line', {key: 'horizontal-line1'}), + h('hr.horizontal-line'), h('div.footer.hover-white.pointer', { key: 'reveal-account-bar', onClick: () => { - this.onRevealAccount() + this.addNewAccount() }, style: { display: 'flex', - flex: '1 0 auto', height: '40px', paddint: '10px', justifyContent: 'center', @@ -100,9 +104,10 @@ AccountsScreen.prototype.render = function () { }, [ h('i.fa.fa-plus.fa-lg', {key: ''}), ]), + h('hr.horizontal-line'), ]), - unconfTxList.length ? ( + unapprovedTxList.length ? ( h('.unconftx-link.flex-row.flex-center', { onClick: this.navigateToConfTx.bind(this), @@ -134,21 +139,21 @@ AccountsScreen.prototype.navigateToConfTx = function () { this.props.dispatch(actions.showConfTxPage()) } -AccountsScreen.prototype.onSelect = function (address, event) { - event.stopPropagation() - // if already selected, deselect - if (this.props.selectedAddress === address) address = null - this.props.dispatch(actions.setSelectedAddress(address)) -} - AccountsScreen.prototype.onShowDetail = function (address, event) { event.stopPropagation() this.props.dispatch(actions.showAccountDetail(address)) } -AccountsScreen.prototype.onRevealAccount = function () { - this.props.dispatch(actions.revealAccount()) +AccountsScreen.prototype.addNewAccount = function () { + this.props.dispatch(actions.addNewAccount(0)) +} + +/* An optional view proposed in this design: + * https://consensys.quip.com/zZVrAysM5znY +AccountsScreen.prototype.addNewAccount = function () { + this.props.dispatch(actions.navigateToNewAccountScreen()) } +*/ AccountsScreen.prototype.goHome = function () { this.props.dispatch(actions.goHome()) |