aboutsummaryrefslogtreecommitdiffstats
path: root/ui/app/accounts
diff options
context:
space:
mode:
authorkumavis <kumavis@users.noreply.github.com>2018-02-28 03:25:29 +0800
committerGitHub <noreply@github.com>2018-02-28 03:25:29 +0800
commit3fefccd37219c9b4b513fc8d929723e07022b9c4 (patch)
tree22865ecd672570a6162ac3c9402ec9d63ad3f7ef /ui/app/accounts
parent6a7ea00cd34f83b257f6b4280a5f4e20aa5d34ee (diff)
parentced62ac551a095c8f94f550f0c01a9d4fd04ce5b (diff)
downloadtangerine-wallet-browser-3fefccd37219c9b4b513fc8d929723e07022b9c4.tar.gz
tangerine-wallet-browser-3fefccd37219c9b4b513fc8d929723e07022b9c4.tar.zst
tangerine-wallet-browser-3fefccd37219c9b4b513fc8d929723e07022b9c4.zip
Merge branch 'master' into mascara-deploy
Diffstat (limited to 'ui/app/accounts')
-rw-r--r--ui/app/accounts/account-list-item.js88
-rw-r--r--ui/app/accounts/import/index.js23
-rw-r--r--ui/app/accounts/import/json.js49
-rw-r--r--ui/app/accounts/import/private-key.js72
-rw-r--r--ui/app/accounts/index.js160
-rw-r--r--ui/app/accounts/new-account/create-form.js99
-rw-r--r--ui/app/accounts/new-account/index.js81
7 files changed, 258 insertions, 314 deletions
diff --git a/ui/app/accounts/account-list-item.js b/ui/app/accounts/account-list-item.js
deleted file mode 100644
index 2a3c13d05..000000000
--- a/ui/app/accounts/account-list-item.js
+++ /dev/null
@@ -1,88 +0,0 @@
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const inherits = require('util').inherits
-const ethUtil = require('ethereumjs-util')
-
-const EthBalance = require('../components/eth-balance')
-const CopyButton = require('../components/copyButton')
-const Identicon = require('../components/identicon')
-
-module.exports = AccountListItem
-
-inherits(AccountListItem, Component)
-function AccountListItem () {
- Component.call(this)
-}
-
-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}`,
- 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,
- }),
- ]),
-
- // account address, balance
- h('.identity-data.flex-column.flex-justify-center.flex-grow.select-none', {
- style: {
- width: '200px',
- },
- }, [
- h('span', identity.name),
- h('span.font-small', {
- style: {
- overflow: 'hidden',
- textOverflow: 'ellipsis',
- },
- }, checksumAddress),
- h(EthBalance, {
- value: account && account.balance,
- style: {
- lineHeight: '7px',
- marginTop: '10px',
- },
- }),
- ]),
-
- // copy button
- h('.identity-copy.flex-column', {
- style: {
- margin: '0 20px',
- },
- }, [
- h(CopyButton, {
- value: checksumAddress,
- }),
- ]),
- ])
- )
-}
-
-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
index 96350852a..71eb9ae23 100644
--- a/ui/app/accounts/import/index.js
+++ b/ui/app/accounts/import/index.js
@@ -33,26 +33,14 @@ AccountImportSubview.prototype.render = function () {
const { type } = state
return (
- h('div', {
- style: {
- },
- }, [
- h('div', {
- style: {
- padding: '10px',
- color: 'rgb(174, 174, 174)',
- },
- }, [
+ h('div.new-account-import-form', [
- h('h3', { style: { padding: '3px' } }, 'SELECT TYPE'),
+ h('div.new-account-import-form__select-section', [
- h('style', `
- .has-value.Select--single > .Select-control .Select-value .Select-value-label, .Select-value-label {
- color: rgb(174,174,174);
- }
- `),
+ h('div.new-account-import-form__select-label', 'Select Type'),
h(Select, {
+ className: 'new-account-import-form__select',
name: 'import-type-select',
clearable: false,
value: type || menuItems[0],
@@ -66,6 +54,7 @@ AccountImportSubview.prototype.render = function () {
this.setState({ type: opt.value })
},
}),
+
]),
this.renderImportView(),
@@ -73,7 +62,7 @@ AccountImportSubview.prototype.render = function () {
)
}
-AccountImportSubview.prototype.renderImportView = function() {
+AccountImportSubview.prototype.renderImportView = function () {
const props = this.props
const state = this.state || {}
const { type } = state
diff --git a/ui/app/accounts/import/json.js b/ui/app/accounts/import/json.js
index 5ed31ab0a..dd5dfad22 100644
--- a/ui/app/accounts/import/json.js
+++ b/ui/app/accounts/import/json.js
@@ -5,6 +5,8 @@ const connect = require('react-redux').connect
const actions = require('../../actions')
const FileInput = require('react-simple-file-input').default
+const HELP_LINK = 'https://support.metamask.io/kb/article/7-importing-accounts'
+
module.exports = connect(mapStateToProps)(JsonImportSubview)
function mapStateToProps (state) {
@@ -22,43 +24,44 @@ JsonImportSubview.prototype.render = function () {
const { error } = this.props
return (
- h('div', {
- style: {
- display: 'flex',
- flexDirection: 'column',
- alignItems: 'center',
- padding: '5px 15px 0px 15px',
- },
- }, [
+ h('div.new-account-import-form__json', [
h('p', 'Used by a variety of different clients'),
+ h('a.warning', { href: HELP_LINK, target: '_blank' }, 'File import not working? Click here!'),
h(FileInput, {
readAs: 'text',
onLoad: this.onLoad.bind(this),
style: {
- margin: '20px 0px 12px 20px',
+ margin: '20px 0px 12px 34%',
fontSize: '15px',
+ display: 'flex',
+ justifyContent: 'center',
},
}),
- h('input.large-input.letter-spacey', {
+ h('input.new-account-import-form__input-password', {
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'),
+ h('div.new-account-create-form__buttons', {}, [
+
+ h('button.new-account-create-form__button-cancel', {
+ onClick: () => this.props.goHome(),
+ }, [
+ 'CANCEL',
+ ]),
+
+ h('button.new-account-create-form__button-create', {
+ onClick: () => this.createNewKeychain.bind(this),
+ }, [
+ 'IMPORT',
+ ]),
+
+ ]),
error ? h('span.error', error) : null,
])
@@ -78,6 +81,12 @@ JsonImportSubview.prototype.createKeyringOnEnter = function (event) {
JsonImportSubview.prototype.createNewKeychain = function () {
const state = this.state
+
+ if (!state) {
+ const message = 'You must select a valid file to import.'
+ return this.props.dispatch(actions.displayWarning(message))
+ }
+
const { fileContents } = state
if (!fileContents) {
diff --git a/ui/app/accounts/import/private-key.js b/ui/app/accounts/import/private-key.js
index 68ccee58e..12f3a6430 100644
--- a/ui/app/accounts/import/private-key.js
+++ b/ui/app/accounts/import/private-key.js
@@ -4,7 +4,7 @@ const h = require('react-hyperscript')
const connect = require('react-redux').connect
const actions = require('../../actions')
-module.exports = connect(mapStateToProps)(PrivateKeyImportView)
+module.exports = connect(mapStateToProps, mapDispatchToProps)(PrivateKeyImportView)
function mapStateToProps (state) {
return {
@@ -12,41 +12,54 @@ function mapStateToProps (state) {
}
}
+function mapDispatchToProps (dispatch) {
+ return {
+ goHome: () => dispatch(actions.goHome()),
+ importNewAccount: (strategy, [ privateKey ]) => {
+ dispatch(actions.importNewAccount(strategy, [ privateKey ]))
+ },
+ displayWarning: () => dispatch(actions.displayWarning(null)),
+ }
+}
+
inherits(PrivateKeyImportView, Component)
function PrivateKeyImportView () {
Component.call(this)
}
PrivateKeyImportView.prototype.render = function () {
- const { error } = this.props
+ const { error, goHome } = 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'),
+ h('div.new-account-import-form__private-key', [
+
+ h('div.new-account-import-form__private-key-password-container', [
+
+ h('span.new-account-import-form__instruction', 'Paste your private key string here:'),
+
+ h('input.new-account-import-form__input-password', {
+ type: 'password',
+ id: 'private-key-box',
+ onKeyPress: () => this.createKeyringOnEnter(),
+ }),
+
+ ]),
+
+ h('div.new-account-import-form__buttons', {}, [
+
+ h('button.new-account-create-form__button-cancel', {
+ onClick: () => goHome(),
+ }, [
+ 'CANCEL',
+ ]),
+
+ h('button.new-account-create-form__button-create', {
+ onClick: () => this.createNewKeychain(),
+ }, [
+ 'IMPORT',
+ ]),
+
+ ]),
error ? h('span.error', error) : null,
])
@@ -63,5 +76,6 @@ PrivateKeyImportView.prototype.createKeyringOnEnter = function (event) {
PrivateKeyImportView.prototype.createNewKeychain = function () {
const input = document.getElementById('private-key-box')
const privateKey = input.value
- this.props.dispatch(actions.importNewAccount('Private Key', [ privateKey ]))
+
+ this.props.importNewAccount('Private Key', [ privateKey ])
}
diff --git a/ui/app/accounts/index.js b/ui/app/accounts/index.js
deleted file mode 100644
index 9584ebad9..000000000
--- a/ui/app/accounts/index.js
+++ /dev/null
@@ -1,160 +0,0 @@
-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 valuesFor = require('../util').valuesFor
-const findDOMNode = require('react-dom').findDOMNode
-const AccountListItem = require('./account-list-item')
-
-module.exports = connect(mapStateToProps)(AccountsScreen)
-
-function mapStateToProps (state) {
- const pendingTxs = valuesFor(state.metamask.unapprovedTxs)
- .filter(txMeta => txMeta.metamaskNetworkId === state.metamask.network)
- const pendingMsgs = valuesFor(state.metamask.unapprovedMsgs)
- const pending = pendingTxs.concat(pendingMsgs)
-
- return {
- accounts: state.metamask.accounts,
- identities: state.metamask.identities,
- unapprovedTxs: state.metamask.unapprovedTxs,
- selectedAddress: state.metamask.selectedAddress,
- scrollToBottom: state.appState.scrollToBottom,
- pending,
- keyrings: state.metamask.keyrings,
- }
-}
-
-inherits(AccountsScreen, Component)
-function AccountsScreen () {
- Component.call(this)
-}
-
-AccountsScreen.prototype.render = function () {
- const props = this.props
- const { keyrings } = props
- const identityList = valuesFor(props.identities)
- const unapprovedTxList = valuesFor(props.unapprovedTxs)
-
- return (
-
- h('.accounts-section.flex-grow', [
-
- // subtitle and nav
- h('.section-title.flex-center', [
- h('i.fa.fa-arrow-left.fa-lg.cursor-pointer', {
- onClick: this.goHome.bind(this),
- }),
- h('h2.page-subtitle', 'Select Account'),
- ]),
-
- h('hr.horizontal-line'),
-
- // identity selection
- h('section.identity-section', {
- style: {
- height: '418px',
- overflowY: 'auto',
- overflowX: 'hidden',
- },
- },
- [
- identityList.map((identity) => {
- const pending = this.props.pending.filter((txOrMsg) => {
- if ('txParams' in txOrMsg) {
- return txOrMsg.txParams.from === identity.address
- } else if ('msgParams' in txOrMsg) {
- return txOrMsg.msgParams.from === identity.address
- } else {
- return false
- }
- })
-
- 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,
- selectedAddress: this.props.selectedAddress,
- accounts: this.props.accounts,
- onShowDetail: this.onShowDetail.bind(this),
- pending,
- keyring,
- })
- }),
-
- h('hr.horizontal-line'),
- h('div.footer.hover-white.pointer', {
- key: 'reveal-account-bar',
- onClick: () => {
- this.addNewAccount()
- },
- style: {
- display: 'flex',
- height: '40px',
- padding: '10px',
- justifyContent: 'center',
- alignItems: 'center',
- },
- }, [
- h('i.fa.fa-plus.fa-lg', {key: ''}),
- ]),
- h('hr.horizontal-line'),
- ]),
-
- unapprovedTxList.length ? (
-
- h('.unconftx-link.flex-row.flex-center', {
- onClick: this.navigateToConfTx.bind(this),
- }, [
- h('span', 'Unconfirmed Txs'),
- h('i.fa.fa-arrow-right.fa-lg'),
- ])
-
- ) : (
- null
- ),
- ])
- )
-}
-
-// If a new account was revealed, scroll to the bottom
-AccountsScreen.prototype.componentDidUpdate = function () {
- const scrollToBottom = this.props.scrollToBottom
-
- if (scrollToBottom) {
- var container = findDOMNode(this)
- var scrollable = container.querySelector('.identity-section')
- scrollable.scrollTop = scrollable.scrollHeight
- }
-}
-
-AccountsScreen.prototype.navigateToConfTx = function () {
- event.stopPropagation()
- this.props.dispatch(actions.showConfTxPage())
-}
-
-AccountsScreen.prototype.onShowDetail = function (address, event) {
- event.stopPropagation()
- this.props.dispatch(actions.showAccountDetail(address))
-}
-
-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())
-}
diff --git a/ui/app/accounts/new-account/create-form.js b/ui/app/accounts/new-account/create-form.js
new file mode 100644
index 000000000..a6b3bba4b
--- /dev/null
+++ b/ui/app/accounts/new-account/create-form.js
@@ -0,0 +1,99 @@
+const { Component } = require('react')
+const PropTypes = require('prop-types')
+const h = require('react-hyperscript')
+const { connect } = require('react-redux')
+const actions = require('../../actions')
+
+class NewAccountCreateForm extends Component {
+ constructor (props) {
+ super(props)
+
+ const { numberOfExistingAccounts = 0 } = props
+ const newAccountNumber = numberOfExistingAccounts + 1
+
+ this.state = {
+ newAccountName: '',
+ defaultAccountName: `Account ${newAccountNumber}`,
+ }
+ }
+
+ render () {
+ const { newAccountName, defaultAccountName } = this.state
+
+
+ return h('div.new-account-create-form', [
+
+ h('div.new-account-create-form__input-label', {}, [
+ 'Account Name',
+ ]),
+
+ h('div.new-account-create-form__input-wrapper', {}, [
+ h('input.new-account-create-form__input', {
+ value: newAccountName,
+ placeholder: defaultAccountName,
+ onChange: event => this.setState({ newAccountName: event.target.value }),
+ }, []),
+ ]),
+
+ h('div.new-account-create-form__buttons', {}, [
+
+ h('button.new-account-create-form__button-cancel', {
+ onClick: () => this.props.goHome(),
+ }, [
+ 'CANCEL',
+ ]),
+
+ h('button.new-account-create-form__button-create', {
+ onClick: () => this.props.createAccount(newAccountName || defaultAccountName),
+ }, [
+ 'CREATE',
+ ]),
+
+ ]),
+
+ ])
+ }
+}
+
+NewAccountCreateForm.propTypes = {
+ hideModal: PropTypes.func,
+ showImportPage: PropTypes.func,
+ createAccount: PropTypes.func,
+ goHome: PropTypes.func,
+ numberOfExistingAccounts: PropTypes.number,
+}
+
+const mapStateToProps = state => {
+ const { metamask: { network, selectedAddress, identities = {} } } = state
+ const numberOfExistingAccounts = Object.keys(identities).length
+
+ return {
+ network,
+ address: selectedAddress,
+ numberOfExistingAccounts,
+ }
+}
+
+const mapDispatchToProps = dispatch => {
+ return {
+ toCoinbase: (address) => {
+ dispatch(actions.buyEth({ network: '1', address, amount: 0 }))
+ },
+ hideModal: () => {
+ dispatch(actions.hideModal())
+ },
+ createAccount: (newAccountName) => {
+ dispatch(actions.addNewAccount())
+ .then((newAccountAddress) => {
+ if (newAccountName) {
+ dispatch(actions.saveAccountLabel(newAccountAddress, newAccountName))
+ }
+ dispatch(actions.goHome())
+ })
+ },
+ showImportPage: () => dispatch(actions.showImportPage()),
+ goHome: () => dispatch(actions.goHome()),
+ }
+}
+
+module.exports = connect(mapStateToProps, mapDispatchToProps)(NewAccountCreateForm)
diff --git a/ui/app/accounts/new-account/index.js b/ui/app/accounts/new-account/index.js
new file mode 100644
index 000000000..acf0dc6e4
--- /dev/null
+++ b/ui/app/accounts/new-account/index.js
@@ -0,0 +1,81 @@
+const Component = require('react').Component
+const h = require('react-hyperscript')
+const inherits = require('util').inherits
+const connect = require('react-redux').connect
+const actions = require('../../actions')
+const { getCurrentViewContext } = require('../../selectors')
+const classnames = require('classnames')
+
+const NewAccountCreateForm = require('./create-form')
+const NewAccountImportForm = require('../import')
+
+function mapStateToProps (state) {
+ return {
+ displayedForm: getCurrentViewContext(state),
+ }
+}
+
+function mapDispatchToProps (dispatch) {
+ return {
+ displayForm: form => dispatch(actions.setNewAccountForm(form)),
+ showQrView: (selected, identity) => dispatch(actions.showQrView(selected, identity)),
+ showExportPrivateKeyModal: () => {
+ dispatch(actions.showModal({ name: 'EXPORT_PRIVATE_KEY' }))
+ },
+ hideModal: () => dispatch(actions.hideModal()),
+ saveAccountLabel: (address, label) => dispatch(actions.saveAccountLabel(address, label)),
+ }
+}
+
+inherits(AccountDetailsModal, Component)
+function AccountDetailsModal (props) {
+ Component.call(this)
+
+ this.state = {
+ displayedForm: props.displayedForm,
+ }
+}
+
+module.exports = connect(mapStateToProps, mapDispatchToProps)(AccountDetailsModal)
+
+AccountDetailsModal.prototype.render = function () {
+ const { displayedForm, displayForm } = this.props
+
+ return h('div.new-account', {}, [
+
+ h('div.new-account__header', [
+
+ h('div.new-account__title', 'New Account'),
+
+ h('div.new-account__tabs', [
+
+ h('div.new-account__tabs__tab', {
+ className: classnames('new-account__tabs__tab', {
+ 'new-account__tabs__selected': displayedForm === 'CREATE',
+ 'new-account__tabs__unselected cursor-pointer': displayedForm !== 'CREATE',
+ }),
+ onClick: () => displayForm('CREATE'),
+ }, 'Create'),
+
+ h('div.new-account__tabs__tab', {
+ className: classnames('new-account__tabs__tab', {
+ 'new-account__tabs__selected': displayedForm === 'IMPORT',
+ 'new-account__tabs__unselected cursor-pointer': displayedForm !== 'IMPORT',
+ }),
+ onClick: () => displayForm('IMPORT'),
+ }, 'Import'),
+
+ ]),
+
+ ]),
+
+ h('div.new-account__form', [
+
+ displayedForm === 'CREATE'
+ ? h(NewAccountCreateForm)
+ : h(NewAccountImportForm),
+
+ ]),
+
+ ])
+}