diff options
author | Jacky Chan <jchan@uber.com> | 2017-08-29 15:52:59 +0800 |
---|---|---|
committer | Chi Kei Chan <chikeichan@gmail.com> | 2017-10-21 12:51:37 +0800 |
commit | 449bce5eea5a5ee049828121876340ae226351b7 (patch) | |
tree | 515443b226678d61b1a8c466ec814e358c26d107 /mascara/src | |
parent | 1a9b217558fd7a3a528a068c6820f2d905d62e9d (diff) | |
download | tangerine-wallet-browser-449bce5eea5a5ee049828121876340ae226351b7.tar tangerine-wallet-browser-449bce5eea5a5ee049828121876340ae226351b7.tar.gz tangerine-wallet-browser-449bce5eea5a5ee049828121876340ae226351b7.tar.bz2 tangerine-wallet-browser-449bce5eea5a5ee049828121876340ae226351b7.tar.lz tangerine-wallet-browser-449bce5eea5a5ee049828121876340ae226351b7.tar.xz tangerine-wallet-browser-449bce5eea5a5ee049828121876340ae226351b7.tar.zst tangerine-wallet-browser-449bce5eea5a5ee049828121876340ae226351b7.zip |
Implement Import Account Screen
Diffstat (limited to 'mascara/src')
-rw-r--r-- | mascara/src/app/first-time/create-password-screen.js | 8 | ||||
-rw-r--r-- | mascara/src/app/first-time/import-account-screen.js | 185 | ||||
-rw-r--r-- | mascara/src/app/first-time/index.css | 112 | ||||
-rw-r--r-- | mascara/src/app/first-time/index.js | 12 |
4 files changed, 311 insertions, 6 deletions
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 ? <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> 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' }) => ( + <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) 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 ( <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: |