New add account page with create and import options.

feature/default_network_editable
Dan 7 years ago committed by Chi Kei Chan
parent 89a8267fe7
commit 980e1bfcf8
  1. 34
      ui/app/accounts/import/index.js
  2. 40
      ui/app/accounts/import/json.js
  3. 61
      ui/app/accounts/import/private-key.js
  4. 96
      ui/app/accounts/new-account/create-form.js
  5. 82
      ui/app/accounts/new-account/index.js
  6. 18
      ui/app/actions.js
  7. 5
      ui/app/app.js
  8. 16
      ui/app/components/account-menu/index.js
  9. 10
      ui/app/components/dropdowns/components/account-dropdowns.js
  10. 2
      ui/app/css/itcss/components/index.scss
  11. 181
      ui/app/css/itcss/components/new-account.scss
  12. 19
      ui/app/reducers/app.js
  13. 6
      ui/app/selectors.js

@ -2,7 +2,6 @@ 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')
import Select from 'react-select'
// Subviews
@ -34,37 +33,14 @@ AccountImportSubview.prototype.render = function () {
const { type } = state
return (
h('div.flex-center', {
style: {
flexDirection: 'column',
marginTop: '32px',
},
}, [
h('.section-title.flex-row.flex-center', [
h('i.fa.fa-arrow-left.fa-lg.cursor-pointer', {
onClick: (event) => {
props.dispatch(actions.goHome())
},
}),
h('h2.page-subtitle', 'Import Accounts'),
]),
h('div', {
style: {
padding: '10px 0',
width: '260px',
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],
@ -75,10 +51,10 @@ AccountImportSubview.prototype.render = function () {
}
}),
onChange: (opt) => {
props.dispatch(actions.showImportPage())
this.setState({ type: opt.value })
},
}),
]),
this.renderImportView(),

@ -24,14 +24,7 @@ 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!'),
@ -40,28 +33,35 @@ JsonImportSubview.prototype.render = function () {
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,
])

@ -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,45 +12,49 @@ 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.componentWillUnmount = function () {
this.props.dispatch(actions.displayWarning(null))
}
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('div.new-account-import-form__private-key', [
h('span.new-account-create-form__instruction', 'Paste your private key string here:'),
h('input.large-input.letter-spacey', {
h('input.new-account-import-form__input-password', {
type: 'password',
id: 'private-key-box',
onKeyPress: this.createKeyringOnEnter.bind(this),
style: {
width: 260,
marginTop: 12,
},
onKeyPress: () => this.createKeyringOnEnter(),
}),
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: () => goHome(),
}, [
'CANCEL',
]),
h('button.new-account-create-form__button-create', {
onClick: () => this.createNewKeychain(),
}, [
'IMPORT',
]),
]),
error ? h('span.error', error) : null,
])
@ -67,5 +71,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 ])
}

@ -0,0 +1,96 @@
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: `Account ${newAccountNumber}`,
}
}
render () {
const { newAccountName } = 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: this.state.newAccountName,
placeholder: 'E.g. My new account',
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),
}, [
'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)

@ -0,0 +1,82 @@
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 {
// Is this supposed to be used somewhere?
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),
]),
])
}

@ -50,12 +50,16 @@ var actions = {
SHOW_NEW_VAULT_SEED: 'SHOW_NEW_VAULT_SEED',
SHOW_INFO_PAGE: 'SHOW_INFO_PAGE',
SHOW_IMPORT_PAGE: 'SHOW_IMPORT_PAGE',
SHOW_NEW_ACCOUNT_PAGE: 'SHOW_NEW_ACCOUNT_PAGE',
SET_NEW_ACCOUNT_FORM: 'SET_NEW_ACCOUNT_FORM',
unlockMetamask: unlockMetamask,
unlockFailed: unlockFailed,
showCreateVault: showCreateVault,
showRestoreVault: showRestoreVault,
showInitializeMenu: showInitializeMenu,
showImportPage,
showNewAccountPage,
setNewAccountForm,
createNewVaultAndKeychain: createNewVaultAndKeychain,
createNewVaultAndRestore: createNewVaultAndRestore,
createNewVaultInProgress: createNewVaultInProgress,
@ -829,6 +833,20 @@ function showImportPage () {
}
}
function showNewAccountPage (formToSelect) {
return {
type: actions.SHOW_NEW_ACCOUNT_PAGE,
formToSelect,
}
}
function setNewAccountForm (formToSelect) {
return {
type: actions.SET_NEW_ACCOUNT_FORM,
formToSelect,
}
}
function createNewVaultInProgress () {
return {
type: actions.CREATE_NEW_VAULT_IN_PROGRESS,

@ -26,6 +26,7 @@ const WalletView = require('./components/wallet-view')
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')
const Identicon = require('./components/identicon')
@ -435,6 +436,10 @@ App.prototype.renderPrimary = function () {
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':
log.debug('rendering reveal seed confirmation screen')
return h(RevealSeedConfirmation, {key: 'reveal-seed-conf'})

@ -42,13 +42,8 @@ function mapDispatchToProps (dispatch) {
dispatch(actions.hideSidebar())
dispatch(actions.toggleAccountMenu())
},
showNewAccountModal: () => {
dispatch(actions.showModal({ name: 'NEW_ACCOUNT' }))
dispatch(actions.hideSidebar())
dispatch(actions.toggleAccountMenu())
},
showImportPage: () => {
dispatch(actions.showImportPage())
showNewAccountPage: (formToSelect) => {
dispatch(actions.showNewAccountPage(formToSelect))
dispatch(actions.hideSidebar())
dispatch(actions.toggleAccountMenu())
},
@ -64,8 +59,7 @@ AccountMenu.prototype.render = function () {
const {
isAccountMenuOpen,
toggleAccountMenu,
showNewAccountModal,
showImportPage,
showNewAccountPage,
lockMetamask,
showConfigPage,
showInfoPage,
@ -85,12 +79,12 @@ AccountMenu.prototype.render = function () {
h('div.account-menu__accounts', this.renderAccounts()),
h(Divider),
h(Item, {
onClick: showNewAccountModal,
onClick: () => showNewAccountPage('CREATE'),
icon: h('img', { src: 'images/plus-btn-white.svg' }),
text: 'Create Account',
}),
h(Item, {
onClick: showImportPage,
onClick: () => showNewAccountPage('IMPORT'),
icon: h('img', { src: 'images/import-account.svg' }),
text: 'Import Account',
}),

@ -199,7 +199,7 @@ class AccountDropdowns extends Component {
{},
menuItemStyles,
),
onClick: () => actions.showNewAccountModal(),
onClick: () => actions.showNewAccountPageCreateForm(),
},
[
h(
@ -228,7 +228,7 @@ class AccountDropdowns extends Component {
actions.hideSidebar()
}
},
onClick: () => actions.showImportPage(),
onClick: () => actions.showNewAccountPageImportForm(),
style: Object.assign(
{},
menuItemStyles,
@ -457,9 +457,7 @@ const mapDispatchToProps = (dispatch) => {
identity,
}))
},
showNewAccountModal: () => {
dispatch(actions.showModal({ name: 'NEW_ACCOUNT' }))
},
showNewAccountPageCreateForm: () => dispatch(actions.showNewAccountPage({ form: 'CREATE' })),
showExportPrivateKeyModal: () => {
dispatch(actions.showModal({ name: 'EXPORT_PRIVATE_KEY' }))
},
@ -467,7 +465,7 @@ const mapDispatchToProps = (dispatch) => {
dispatch(actions.showAddTokenPage())
},
addNewAccount: () => dispatch(actions.addNewAccount()),
showImportPage: () => dispatch(actions.showImportPage()),
showNewAccountPageImportForm: () => dispatch(actions.showNewAccountPage({ form: 'IMPORT' })),
showQrView: (selected, identity) => dispatch(actions.showQrView(selected, identity)),
},
}

@ -51,3 +51,5 @@
@import './account-dropdown-mini.scss';
@import './editable-label.scss';
@import './new-account.scss';

@ -0,0 +1,181 @@
.new-account {
width: 376px;
background-color: #FFFFFF;
box-shadow: 0 0 7px 0 rgba(0,0,0,0.08);
z-index: 25;
padding-bottom: 31px;
&__header {
display: flex;
flex-flow: column;
border-bottom: 1px solid $geyser;
}
&__title {
color: $tundora;
font-family: Roboto;
font-size: 32px;
font-weight: 500;
line-height: 43px;
margin-top: 22px;
margin-left: 29px;
}
&__tabs {
margin-left: 22px;
display: flex;
margin-top: 10px;
&__tab {
height: 54px;
width: 75px;
padding: 15px 10px;
color: $dusty-gray;
font-family: Roboto;
font-size: 18px;
line-height: 24px;
text-align: center;
}
&__tab:first-of-type {
margin-right: 20px;
}
&__unselected:hover {
color: $black;
border-bottom: none;
}
&__selected {
color: $curious-blue;
border-bottom: 3px solid $curious-blue;
}
}
}
.new-account-import-form {
&__select-section {
display: flex;
justify-content: space-evenly;
align-items: center;
margin-top: 29px;
}
&__select-label {
color: $scorpion;
font-family: Roboto;
font-size: 16px;
line-height: 21px;
}
&__select {
height: 54px;
width: 210px;
border: 1px solid #D2D8DD;
border-radius: 4px;
background-color: #FFFFFF;
display: flex;
align-items: center;
.Select-control,
.Select-control:hover {
border: none;
box-shadow: none;
}
}
&__instruction {
color: $scorpion;
font-family: Roboto;
font-size: 16px;
line-height: 21px;
align-self: flex-start;
margin-left: 30px;
}
&__private-key {
display: flex;
flex-flow: column;
align-items: center;
margin-top: 34px;
}
&__input-password {
height: 54px;
width: 315px;
border: 1px solid $geyser;
border-radius: 4px;
background-color: $white;
margin-top: 16px;
}
&__json {
display: flex;
flex-flow: column;
align-items: center;
margin-top: 29px;
}
}
.new-account-create-form {
display: flex;
flex-flow: column;
align-items: center;
&__input-label {
color: $scorpion;
font-family: Roboto;
font-size: 16px;
line-height: 21px;
margin-top: 29px;
align-self: flex-start;
margin-left: 30px;
}
&__input {
height: 54px;
width: 315.84px;
border: 1px solid $geyser;
border-radius: 4px;
background-color: $white;
color: $scorpion;
font-family: Roboto;
font-size: 16px;
line-height: 21px;
margin-top: 15px;
}
&__buttons {
margin-top: 39px;
display: flex;
width: 100%;
justify-content: space-evenly;
}
&__button-cancel,
&__button-create {
height: 55px;
width: 150px;
border-radius: 2px;
background-color: #FFFFFF;
}
&__button-cancel {
border: 1px solid $dusty-gray;
color: $dusty-gray;
font-family: Roboto;
font-size: 16px;
line-height: 21px;
text-align: center;
}
&__button-create {
border: 1px solid $curious-blue;
color: $curious-blue;
font-family: Roboto;
font-size: 16px;
line-height: 21px;
text-align: center;
}
}

@ -170,7 +170,6 @@ function reduceApp (state, action) {
})
case actions.SHOW_IMPORT_PAGE:
return extend(appState, {
currentView: {
name: 'import-menu',
@ -179,6 +178,24 @@ function reduceApp (state, action) {
warning: null,
})
case actions.SHOW_NEW_ACCOUNT_PAGE:
return extend(appState, {
currentView: {
name: 'new-account-page',
context: action.formToSelect,
},
transForward: true,
warning: null,
})
case actions.SET_NEW_ACCOUNT_FORM:
return extend(appState, {
currentView: {
name: appState.currentView.name,
context: action.formToSelect,
},
})
case actions.SHOW_INFO_PAGE:
return extend(appState, {
currentView: {

@ -26,6 +26,7 @@ const selectors = {
getSelectedTokenContract,
autoAddToBetaUI,
getSendMaxModeState,
getCurrentViewContext,
}
module.exports = selectors
@ -180,4 +181,9 @@ function autoAddToBetaUI (state) {
const userIsNotInBeta = !state.metamask.featureFlags.betaUI
return userIsNotInBeta && userPassesThreshold
}
function getCurrentViewContext (state) {
const { currentView = {} } = state.appState
return currentView.context
}
Loading…
Cancel
Save