mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-26 12:29:06 +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 = {
|
||||
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
|
||||
? <LoadingScreen loadingMessage="Creating your new account" />
|
||||
@ -74,7 +75,10 @@ class CreatePasswordScreen extends Component {
|
||||
<a
|
||||
href=""
|
||||
className="first-time-flow__link create-password__import-link"
|
||||
onClick={e => e.preventDefault()}
|
||||
onClick={e => {
|
||||
e.preventDefault()
|
||||
goToImportAccount()
|
||||
}}
|
||||
>
|
||||
Import an account
|
||||
</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,
|
||||
.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;
|
||||
|
@ -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 (
|
||||
<CreatePasswordScreen
|
||||
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:
|
||||
|
@ -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)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user