mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
Implement Import Account Screen
This commit is contained in:
parent
1a9b217558
commit
449bce5eea
@ -8,6 +8,7 @@ class CreatePasswordScreen extends Component {
|
|||||||
static propTypes = {
|
static propTypes = {
|
||||||
isLoading: PropTypes.bool.isRequired,
|
isLoading: PropTypes.bool.isRequired,
|
||||||
createAccount: PropTypes.func.isRequired,
|
createAccount: PropTypes.func.isRequired,
|
||||||
|
goToImportAccount: PropTypes.func.isRequired,
|
||||||
next: PropTypes.func.isRequired
|
next: PropTypes.func.isRequired
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,7 +44,7 @@ class CreatePasswordScreen extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { isLoading } = this.props
|
const { isLoading, goToImportAccount } = this.props
|
||||||
|
|
||||||
return isLoading
|
return isLoading
|
||||||
? <LoadingScreen loadingMessage="Creating your new account" />
|
? <LoadingScreen loadingMessage="Creating your new account" />
|
||||||
@ -74,7 +75,10 @@ class CreatePasswordScreen extends Component {
|
|||||||
<a
|
<a
|
||||||
href=""
|
href=""
|
||||||
className="first-time-flow__link create-password__import-link"
|
className="first-time-flow__link create-password__import-link"
|
||||||
onClick={e => e.preventDefault()}
|
onClick={e => {
|
||||||
|
e.preventDefault()
|
||||||
|
goToImportAccount()
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
Import an account
|
Import an account
|
||||||
</a>
|
</a>
|
||||||
|
185
mascara/src/app/first-time/import-account-screen.js
Normal file
185
mascara/src/app/first-time/import-account-screen.js
Normal file
@ -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' }) => (
|
||||||
|
<div className="import-account__input-wrapper">
|
||||||
|
<div className="import-account__input-label">{label}</div>
|
||||||
|
<input
|
||||||
|
type={type}
|
||||||
|
placeholder={placeholder}
|
||||||
|
className={classnames('first-time-flow__input import-account__input', {
|
||||||
|
'first-time-flow__input--error': errorMessage
|
||||||
|
})}
|
||||||
|
onChange={onChange}
|
||||||
|
/>
|
||||||
|
<div className="import-account__input-error-message">{errorMessage}</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
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 (
|
||||||
|
<div className="">
|
||||||
|
<div className="import-account__input-wrapper">
|
||||||
|
<div className="import-account__input-label">Upload File</div>
|
||||||
|
<div className="import-account__file-picker-wrapper">
|
||||||
|
<input
|
||||||
|
type="file"
|
||||||
|
id="file"
|
||||||
|
className="import-account__file-input"
|
||||||
|
onChange={e => this.setState({ jsonFile: e.target.files[0] })}
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
htmlFor="file"
|
||||||
|
className={classnames('import-account__file-input-label', {
|
||||||
|
'import-account__file-input-label--error': warning
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
Choose File
|
||||||
|
</label>
|
||||||
|
<div className="import-account__file-name">{name}</div>
|
||||||
|
</div>
|
||||||
|
<div className="import-account__input-error-message">
|
||||||
|
{warning && 'Something went wrong. Please make sure your JSON file is properly formatted.'}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{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.'
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (
|
||||||
|
<div className="import-account">
|
||||||
|
<a
|
||||||
|
className="import-account__back-button"
|
||||||
|
onClick={e => {
|
||||||
|
e.preventDefault()
|
||||||
|
this.props.back()
|
||||||
|
}}
|
||||||
|
href="#"
|
||||||
|
>
|
||||||
|
{`< Back`}
|
||||||
|
</a>
|
||||||
|
<div className="import-account__title">
|
||||||
|
Import an Account
|
||||||
|
</div>
|
||||||
|
<div className="import-account__selector-label">
|
||||||
|
How would you like to import your account?
|
||||||
|
</div>
|
||||||
|
<select
|
||||||
|
className="import-account__dropdown"
|
||||||
|
value={selectedOption}
|
||||||
|
onChange={e => this.setState({ selectedOption: e.target.value })}
|
||||||
|
>
|
||||||
|
<option value={OPTIONS.PRIVATE_KEY}>Private Key</option>
|
||||||
|
<option value={OPTIONS.JSON_FILE}>JSON File</option>
|
||||||
|
</select>
|
||||||
|
{this.renderContent()}
|
||||||
|
<button
|
||||||
|
className="first-time-flow__button"
|
||||||
|
disabled={!this.isValid()}
|
||||||
|
onClick={this.onClick}
|
||||||
|
>
|
||||||
|
Import
|
||||||
|
</button>
|
||||||
|
<a
|
||||||
|
href="https://github.com/MetaMask/faq/blob/master/README.md#q-i-cant-use-the-import-feature-for-uploading-a-json-file-the-window-keeps-closing-when-i-try-to-select-a-file"
|
||||||
|
className="first-time-flow__link import-account__faq-link"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
File import not working?
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
({ appState: { isLoading, warning } }) => ({ isLoading, warning }),
|
||||||
|
dispatch => ({
|
||||||
|
importNewAccount: (strategy, args) => dispatch(importNewAccount(strategy, args))
|
||||||
|
})
|
||||||
|
)(ImportAccountScreen)
|
@ -9,7 +9,8 @@ $primary
|
|||||||
.create-password,
|
.create-password,
|
||||||
.unique-image,
|
.unique-image,
|
||||||
.tou,
|
.tou,
|
||||||
.backup-phrase {
|
.backup-phrase,
|
||||||
|
.import-account {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-flow: column nowrap;
|
flex-flow: column nowrap;
|
||||||
margin: 67px 0 0 146px;
|
margin: 67px 0 0 146px;
|
||||||
@ -27,7 +28,8 @@ $primary
|
|||||||
.create-password__title,
|
.create-password__title,
|
||||||
.unique-image__title,
|
.unique-image__title,
|
||||||
.tou__title,
|
.tou__title,
|
||||||
.backup-phrase__title {
|
.backup-phrase__title,
|
||||||
|
.import-account__title {
|
||||||
width: 280px;
|
width: 280px;
|
||||||
color: #1B344D;
|
color: #1B344D;
|
||||||
font-size: 40px;
|
font-size: 40px;
|
||||||
@ -166,7 +168,9 @@ $primary
|
|||||||
}
|
}
|
||||||
|
|
||||||
.backup-phrase__back-button,
|
.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;
|
position: absolute;
|
||||||
top: 24px;
|
top: 24px;
|
||||||
color: #22232C;
|
color: #22232C;
|
||||||
@ -219,6 +223,108 @@ button.backup-phrase__confirm-seed-option:hover {
|
|||||||
transform: scale(1);
|
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 {
|
.first-time-flow__input {
|
||||||
width: 350px;
|
width: 350px;
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
|
@ -4,6 +4,7 @@ import CreatePasswordScreen from './create-password-screen'
|
|||||||
import UniqueImageScreen from './unique-image-screen'
|
import UniqueImageScreen from './unique-image-screen'
|
||||||
import NoticeScreen from './notice-screen'
|
import NoticeScreen from './notice-screen'
|
||||||
import BackupPhraseScreen from './backup-phrase-screen'
|
import BackupPhraseScreen from './backup-phrase-screen'
|
||||||
|
import ImportAccountScreen from './import-account-screen'
|
||||||
|
|
||||||
class FirstTimeFlow extends Component {
|
class FirstTimeFlow extends Component {
|
||||||
|
|
||||||
@ -21,6 +22,7 @@ class FirstTimeFlow extends Component {
|
|||||||
|
|
||||||
static SCREEN_TYPE = {
|
static SCREEN_TYPE = {
|
||||||
CREATE_PASSWORD: 'create_password',
|
CREATE_PASSWORD: 'create_password',
|
||||||
|
IMPORT_ACCOUNT: 'import_account',
|
||||||
UNIQUE_IMAGE: 'unique_image',
|
UNIQUE_IMAGE: 'unique_image',
|
||||||
NOTICE: 'notice',
|
NOTICE: 'notice',
|
||||||
BACK_UP_PHRASE: 'back_up_phrase',
|
BACK_UP_PHRASE: 'back_up_phrase',
|
||||||
@ -43,7 +45,7 @@ class FirstTimeFlow extends Component {
|
|||||||
const {isInitialized, seedWords, noActiveNotices} = this.props;
|
const {isInitialized, seedWords, noActiveNotices} = this.props;
|
||||||
const {SCREEN_TYPE} = FirstTimeFlow
|
const {SCREEN_TYPE} = FirstTimeFlow
|
||||||
|
|
||||||
// return SCREEN_TYPE.UNIQUE_IMAGE
|
// return SCREEN_TYPE.IMPORT_ACCOUNT
|
||||||
|
|
||||||
if (!isInitialized) {
|
if (!isInitialized) {
|
||||||
return SCREEN_TYPE.CREATE_PASSWORD
|
return SCREEN_TYPE.CREATE_PASSWORD
|
||||||
@ -66,6 +68,14 @@ class FirstTimeFlow extends Component {
|
|||||||
return (
|
return (
|
||||||
<CreatePasswordScreen
|
<CreatePasswordScreen
|
||||||
next={() => this.setScreenType(SCREEN_TYPE.UNIQUE_IMAGE)}
|
next={() => this.setScreenType(SCREEN_TYPE.UNIQUE_IMAGE)}
|
||||||
|
goToImportAccount={() => this.setScreenType(SCREEN_TYPE.IMPORT_ACCOUNT)}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
case SCREEN_TYPE.IMPORT_ACCOUNT:
|
||||||
|
return (
|
||||||
|
<ImportAccountScreen
|
||||||
|
back={() => this.setScreenType(SCREEN_TYPE.CREATE_PASSWORD)}
|
||||||
|
next={() => this.setScreenType(SCREEN_TYPE.NOTICE)}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
case SCREEN_TYPE.UNIQUE_IMAGE:
|
case SCREEN_TYPE.UNIQUE_IMAGE:
|
||||||
|
@ -310,18 +310,25 @@ function importNewAccount (strategy, args) {
|
|||||||
return (dispatch) => {
|
return (dispatch) => {
|
||||||
dispatch(actions.showLoadingIndication('This may take a while, be patient.'))
|
dispatch(actions.showLoadingIndication('This may take a while, be patient.'))
|
||||||
log.debug(`background.importAccountWithStrategy`)
|
log.debug(`background.importAccountWithStrategy`)
|
||||||
background.importAccountWithStrategy(strategy, args, (err) => {
|
return new Promise((resolve, reject) => {
|
||||||
if (err) return dispatch(actions.displayWarning(err.message))
|
background.importAccountWithStrategy(strategy, args, (err) => {
|
||||||
log.debug(`background.getState`)
|
|
||||||
background.getState((err, newState) => {
|
|
||||||
dispatch(actions.hideLoadingIndication())
|
|
||||||
if (err) {
|
if (err) {
|
||||||
return dispatch(actions.displayWarning(err.message))
|
dispatch(actions.displayWarning(err.message))
|
||||||
|
return reject(err)
|
||||||
}
|
}
|
||||||
dispatch(actions.updateMetamaskState(newState))
|
log.debug(`background.getState`)
|
||||||
dispatch({
|
background.getState((err, newState) => {
|
||||||
type: actions.SHOW_ACCOUNT_DETAIL,
|
dispatch(actions.hideLoadingIndication())
|
||||||
value: newState.selectedAddress,
|
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)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
Loading…
x
Reference in New Issue
Block a user