diff --git a/mascara/src/app/first-time/create-password-screen.js b/mascara/src/app/first-time/create-password-screen.js
index e4b0425ce..8dd7d44af 100644
--- a/mascara/src/app/first-time/create-password-screen.js
+++ b/mascara/src/app/first-time/create-password-screen.js
@@ -8,6 +8,7 @@ class CreatePasswordScreen extends Component {
static propTypes = {
isLoading: PropTypes.bool.isRequired,
createAccount: PropTypes.func.isRequired,
+ goToImportAccount: PropTypes.func.isRequired,
next: PropTypes.func.isRequired
}
@@ -43,7 +44,7 @@ class CreatePasswordScreen extends Component {
}
render() {
- const { isLoading } = this.props
+ const { isLoading, goToImportAccount } = this.props
return isLoading
?
@@ -74,7 +75,10 @@ class CreatePasswordScreen extends Component {
e.preventDefault()}
+ onClick={e => {
+ e.preventDefault()
+ goToImportAccount()
+ }}
>
Import an account
diff --git a/mascara/src/app/first-time/import-account-screen.js b/mascara/src/app/first-time/import-account-screen.js
new file mode 100644
index 000000000..17be90c2a
--- /dev/null
+++ b/mascara/src/app/first-time/import-account-screen.js
@@ -0,0 +1,185 @@
+import React, {Component, PropTypes} from 'react'
+import classnames from 'classnames'
+import {importNewAccount} from '../../../../ui/app/actions'
+import {connect} from 'react-redux';
+
+const Input = ({ label, placeholder, onChange, errorMessage, type = 'text' }) => (
+
+
{label}
+
+
{errorMessage}
+
+)
+
+class ImportAccountScreen extends Component {
+ static OPTIONS = {
+ PRIVATE_KEY: 'private_key',
+ JSON_FILE: 'json_file',
+ };
+
+ static propTypes = {
+ warning: PropTypes.string,
+ back: PropTypes.func.isRequired,
+ next: PropTypes.func.isRequired,
+ importNewAccount: PropTypes.func.isRequired,
+ };
+
+ state = {
+ selectedOption: ImportAccountScreen.OPTIONS.PRIVATE_KEY,
+ privateKey: '',
+ jsonFile: {},
+ }
+
+ isValid() {
+ const { OPTIONS } = ImportAccountScreen;
+ const { privateKey, jsonFile, password } = this.state;
+
+ switch (this.state.selectedOption) {
+ case OPTIONS.JSON_FILE:
+ return Boolean(jsonFile && password)
+ case OPTIONS.PRIVATE_KEY:
+ default:
+ return Boolean(privateKey)
+ }
+ }
+
+ onClick = () => {
+ const { OPTIONS } = ImportAccountScreen;
+ const { importNewAccount, next } = this.props;
+ const { privateKey, jsonFile, password } = this.state;
+
+ switch (this.state.selectedOption) {
+ case OPTIONS.JSON_FILE:
+ return importNewAccount('JSON File', [ jsonFile, password ])
+ .then(next)
+ case OPTIONS.PRIVATE_KEY:
+ default:
+ return importNewAccount('Private Key', [ privateKey ])
+ .then(next)
+ }
+ }
+
+ renderPrivateKey() {
+ return Input({
+ label: 'Add Private Key String',
+ placeholder: 'Enter private key',
+ onChange: e => this.setState({ privateKey: e.target.value }),
+ errorMessage: this.props.warning && 'Something went wrong. Please make sure your private key is correct.'
+ })
+ }
+
+ renderJsonFile() {
+ const { jsonFile: { name } } = this.state;
+ const { warning } = this.props;
+
+ return (
+
+
+
Upload File
+
+
this.setState({ jsonFile: e.target.files[0] })}
+ />
+
+
{name}
+
+
+ {warning && 'Something went wrong. Please make sure your JSON file is properly formatted.'}
+
+
+ {Input({
+ label: 'Enter Password',
+ placeholder: 'Enter Password',
+ type: 'password',
+ onChange: e => this.setState({ password: e.target.value }),
+ errorMessage: warning && 'Please make sure your password is correct.'
+ })}
+
+ )
+ }
+
+ renderContent() {
+ const { OPTIONS } = ImportAccountScreen;
+
+ switch (this.state.selectedOption) {
+ case OPTIONS.JSON_FILE:
+ return this.renderJsonFile()
+ case OPTIONS.PRIVATE_KEY:
+ default:
+ return this.renderPrivateKey()
+ }
+ }
+
+ render() {
+ const { OPTIONS } = ImportAccountScreen;
+ const { selectedOption } = this.state;
+
+ return (
+
+ )
+ }
+}
+
+export default connect(
+ ({ appState: { isLoading, warning } }) => ({ isLoading, warning }),
+ dispatch => ({
+ importNewAccount: (strategy, args) => dispatch(importNewAccount(strategy, args))
+ })
+)(ImportAccountScreen)
diff --git a/mascara/src/app/first-time/index.css b/mascara/src/app/first-time/index.css
index e9951059b..da8f801e8 100644
--- a/mascara/src/app/first-time/index.css
+++ b/mascara/src/app/first-time/index.css
@@ -9,7 +9,8 @@ $primary
.create-password,
.unique-image,
.tou,
-.backup-phrase {
+.backup-phrase,
+.import-account {
display: flex;
flex-flow: column nowrap;
margin: 67px 0 0 146px;
@@ -27,7 +28,8 @@ $primary
.create-password__title,
.unique-image__title,
.tou__title,
-.backup-phrase__title {
+.backup-phrase__title,
+.import-account__title {
width: 280px;
color: #1B344D;
font-size: 40px;
@@ -166,7 +168,9 @@ $primary
}
.backup-phrase__back-button,
-.backup-phrase__back-button:hover {
+.backup-phrase__back-button:hover,
+.import-account__back-button,
+.import-account__back-button:hover {
position: absolute;
top: 24px;
color: #22232C;
@@ -219,6 +223,108 @@ button.backup-phrase__confirm-seed-option:hover {
transform: scale(1);
}
+.import-account__faq-link {
+ font-size: 18px;
+ line-height: 23px;
+ font-family: Montserrat Light;
+}
+
+.import-account__selector-label {
+ color: #1B344D;
+ font-family: Montserrat Light;
+ font-size: 18px;
+ line-height: 23px;
+}
+
+.import-account__dropdown {
+ width: 325px;
+ border: 1px solid #CDCDCD;
+ border-radius: 4px;
+ background-color: #FFFFFF;
+ margin-top: 14px;
+ color: #5B5D67;
+ font-family: Montserrat Light;
+ font-size: 18px;
+ line-height: 23px;
+ padding: 14px 21px;
+ appearance: none;
+ -webkit-appearance: none;
+ -moz-appearance: none;
+ cursor: pointer;
+}
+
+.import-account__description-text {
+ color: #757575;
+ font-size: 18px;
+ line-height: 23px;
+ margin-top: 21px;
+ font-family: Montserrat UltraLight;
+}
+
+.import-account__input-wrapper {
+ display: flex;
+ flex-flow: column nowrap;
+ margin-top: 30px;
+}
+
+.first-time-flow__input--error {
+ border: 1px solid #FF001F !important;
+}
+
+.import-account__input-error-message {
+ margin-top: 10px;
+ width: 422px;
+ color: #FF001F;
+ font-family: Montserrat Light;
+ font-size: 16px;
+ line-height: 21px;
+}
+
+.import-account__input-label {
+ margin-bottom: 9px;
+ color: #1B344D;
+ font-family: Montserrat Light;
+ font-size: 18px;
+ line-height: 23px;
+ text-transform: uppercase;
+}
+
+.import-account__input {
+ width: 325px !important;
+}
+
+.import-account__file-input {
+ display: none;
+}
+
+.import-account__file-input-label {
+ height: 53px;
+ width: 148px;
+ border: 1px solid #1B344D;
+ border-radius: 4px;
+ color: #1B344D;
+ font-family: Montserrat Light;
+ font-size: 18px;
+ display: flex;
+ flex-flow: column nowrap;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
+}
+
+.import-account__file-picker-wrapper {
+ display: flex;
+ flex-flow: row nowrap;
+ align-items: center;
+}
+
+.import-account__file-name {
+ color: #000000;
+ font-family: Montserrat Light;
+ font-size: 18px;
+ line-height: 23px;
+ margin-left: 22px;
+}
.first-time-flow__input {
width: 350px;
font-size: 18px;
diff --git a/mascara/src/app/first-time/index.js b/mascara/src/app/first-time/index.js
index d15bb3ce1..1ba6ed28c 100644
--- a/mascara/src/app/first-time/index.js
+++ b/mascara/src/app/first-time/index.js
@@ -4,6 +4,7 @@ import CreatePasswordScreen from './create-password-screen'
import UniqueImageScreen from './unique-image-screen'
import NoticeScreen from './notice-screen'
import BackupPhraseScreen from './backup-phrase-screen'
+import ImportAccountScreen from './import-account-screen'
class FirstTimeFlow extends Component {
@@ -21,6 +22,7 @@ class FirstTimeFlow extends Component {
static SCREEN_TYPE = {
CREATE_PASSWORD: 'create_password',
+ IMPORT_ACCOUNT: 'import_account',
UNIQUE_IMAGE: 'unique_image',
NOTICE: 'notice',
BACK_UP_PHRASE: 'back_up_phrase',
@@ -43,7 +45,7 @@ class FirstTimeFlow extends Component {
const {isInitialized, seedWords, noActiveNotices} = this.props;
const {SCREEN_TYPE} = FirstTimeFlow
- // return SCREEN_TYPE.UNIQUE_IMAGE
+ // return SCREEN_TYPE.IMPORT_ACCOUNT
if (!isInitialized) {
return SCREEN_TYPE.CREATE_PASSWORD
@@ -66,6 +68,14 @@ class FirstTimeFlow extends Component {
return (
this.setScreenType(SCREEN_TYPE.UNIQUE_IMAGE)}
+ goToImportAccount={() => this.setScreenType(SCREEN_TYPE.IMPORT_ACCOUNT)}
+ />
+ )
+ case SCREEN_TYPE.IMPORT_ACCOUNT:
+ return (
+ this.setScreenType(SCREEN_TYPE.CREATE_PASSWORD)}
+ next={() => this.setScreenType(SCREEN_TYPE.NOTICE)}
/>
)
case SCREEN_TYPE.UNIQUE_IMAGE:
diff --git a/ui/app/actions.js b/ui/app/actions.js
index ec18f099e..04bba6bf2 100644
--- a/ui/app/actions.js
+++ b/ui/app/actions.js
@@ -310,18 +310,25 @@ function importNewAccount (strategy, args) {
return (dispatch) => {
dispatch(actions.showLoadingIndication('This may take a while, be patient.'))
log.debug(`background.importAccountWithStrategy`)
- background.importAccountWithStrategy(strategy, args, (err) => {
- if (err) return dispatch(actions.displayWarning(err.message))
- log.debug(`background.getState`)
- background.getState((err, newState) => {
- dispatch(actions.hideLoadingIndication())
+ return new Promise((resolve, reject) => {
+ background.importAccountWithStrategy(strategy, args, (err) => {
if (err) {
- return dispatch(actions.displayWarning(err.message))
+ dispatch(actions.displayWarning(err.message))
+ return reject(err)
}
- dispatch(actions.updateMetamaskState(newState))
- dispatch({
- type: actions.SHOW_ACCOUNT_DETAIL,
- value: newState.selectedAddress,
+ log.debug(`background.getState`)
+ background.getState((err, newState) => {
+ dispatch(actions.hideLoadingIndication())
+ if (err) {
+ dispatch(actions.displayWarning(err.message))
+ return reject(err)
+ }
+ dispatch(actions.updateMetamaskState(newState))
+ dispatch({
+ type: actions.SHOW_ACCOUNT_DETAIL,
+ value: newState.selectedAddress,
+ })
+ resolve(newState)
})
})
})