diff options
author | Jacky Chan <jchan@uber.com> | 2017-08-21 19:56:09 +0800 |
---|---|---|
committer | Chi Kei Chan <chikeichan@gmail.com> | 2017-10-21 12:51:37 +0800 |
commit | e1497fafa64b5f8e25407611709920dc5e0eaf77 (patch) | |
tree | 11f1b6eaddf131e4c733ec990ef67b476d6a86db | |
parent | 4e446410eb76c1bd5d27b6dd4f413bfbf3df0e2d (diff) | |
download | tangerine-wallet-browser-e1497fafa64b5f8e25407611709920dc5e0eaf77.tar tangerine-wallet-browser-e1497fafa64b5f8e25407611709920dc5e0eaf77.tar.gz tangerine-wallet-browser-e1497fafa64b5f8e25407611709920dc5e0eaf77.tar.bz2 tangerine-wallet-browser-e1497fafa64b5f8e25407611709920dc5e0eaf77.tar.lz tangerine-wallet-browser-e1497fafa64b5f8e25407611709920dc5e0eaf77.tar.xz tangerine-wallet-browser-e1497fafa64b5f8e25407611709920dc5e0eaf77.tar.zst tangerine-wallet-browser-e1497fafa64b5f8e25407611709920dc5e0eaf77.zip |
Add UniqueImageScreen
-rw-r--r-- | mascara/src/app/first-time/breadcrumbs.js | 1 | ||||
-rw-r--r-- | mascara/src/app/first-time/create-password-screen.js | 112 | ||||
-rw-r--r-- | mascara/src/app/first-time/index.css | 56 | ||||
-rw-r--r-- | mascara/src/app/first-time/index.js | 51 | ||||
-rw-r--r-- | mascara/src/app/first-time/loading-screen.js | 11 | ||||
-rw-r--r-- | mascara/src/app/first-time/spinner.js | 70 | ||||
-rw-r--r-- | mascara/src/app/first-time/unique-image-screen.js | 40 | ||||
-rw-r--r-- | package.json | 1 | ||||
-rw-r--r-- | ui/app/actions.js | 27 | ||||
-rw-r--r-- | ui/app/app.js | 24 |
10 files changed, 329 insertions, 64 deletions
diff --git a/mascara/src/app/first-time/breadcrumbs.js b/mascara/src/app/first-time/breadcrumbs.js index cbd0da1a1..f8460d200 100644 --- a/mascara/src/app/first-time/breadcrumbs.js +++ b/mascara/src/app/first-time/breadcrumbs.js @@ -13,6 +13,7 @@ export default class Breadcrumbs extends Component { <div className="breadcrumbs"> {Array(total).fill().map((_, i) => ( <div + key={i} className="breadcrumb" style={{backgroundColor: i === currentIndex ? '#D8D8D8' : '#FFFFFF'}} /> diff --git a/mascara/src/app/first-time/create-password-screen.js b/mascara/src/app/first-time/create-password-screen.js index 4b4cea51d..e4b0425ce 100644 --- a/mascara/src/app/first-time/create-password-screen.js +++ b/mascara/src/app/first-time/create-password-screen.js @@ -1,46 +1,92 @@ import React, {Component, PropTypes} from 'react' +import {connect} from 'react-redux'; +import {createNewVaultAndKeychain} from '../../../../ui/app/actions' +import LoadingScreen from './loading-screen' import Breadcrumbs from './breadcrumbs' -export default class CreatePasswordScreen extends Component { +class CreatePasswordScreen extends Component { + static propTypes = { + isLoading: PropTypes.bool.isRequired, + createAccount: PropTypes.func.isRequired, + next: PropTypes.func.isRequired + } state = { password: '', confirmPassword: '' } + isValid() { + const {password, confirmPassword} = this.state; + + if (!password || !confirmPassword) { + return false; + } + + if (password.length < 8) { + return false; + } + + return password === confirmPassword; + } + + createAccount = () => { + if (!this.isValid()) { + return; + } + + const {password} = this.state; + const {createAccount, next} = this.props; + + createAccount(password) + .then(next); + } + render() { - return ( - <div className="create-password"> - <div className="create-password__title"> - Create Password + const { isLoading } = this.props + + return isLoading + ? <LoadingScreen loadingMessage="Creating your new account" /> + : ( + <div className="create-password"> + <div className="create-password__title"> + Create Password + </div> + <input + className="first-time-flow__input" + type="password" + placeholder="New Password (min 8 characters)" + onChange={e => this.setState({password: e.target.value})} + /> + <input + className="first-time-flow__input create-password__confirm-input" + type="password" + placeholder="Confirm Password" + onChange={e => this.setState({confirmPassword: e.target.value})} + /> + <button + className="first-time-flow__button" + disabled={!this.isValid()} + onClick={this.createAccount} + > + Create + </button> + <a + href="" + className="first-time-flow__link create-password__import-link" + onClick={e => e.preventDefault()} + > + Import an account + </a> + <Breadcrumbs total={3} currentIndex={0} /> </div> - <input - className="first-time-flow__input" - type="password" - placeholder="New Password (min 8 characters)" - onChange={e => this.setState({password: e.target.value})} - /> - <input - className="first-time-flow__input create-password__confirm-input" - type="password" - placeholder="Confirm Password" - onChange={e => this.setState({confirmPassword: e.target.value})} - /> - <button - className="first-time-flow__button" - > - Create - </button> - <a - href="" - className="first-time-flow__link create-password__import-link" - onClick={e => e.preventDefault()} - > - Import an account - </a> - <Breadcrumbs total={3} currentIndex={0} /> - </div> - ) + ) } +} -}
\ No newline at end of file +export default connect( + ({ appState: { isLoading } }) => ({ isLoading }), + dispatch => ({ + createAccount: password => dispatch(createNewVaultAndKeychain(password)) + }) +)(CreatePasswordScreen) diff --git a/mascara/src/app/first-time/index.css b/mascara/src/app/first-time/index.css index dbcde5004..5193b412f 100644 --- a/mascara/src/app/first-time/index.css +++ b/mascara/src/app/first-time/index.css @@ -1,18 +1,21 @@ +$primary + .first-time-flow { height: 100vh; width: 100vw; background-color: #FFF; } -.create-password { +.create-password, +.unique-image { display: flex; flex-flow: column nowrap; margin: 67px 0 0 146px; - max-width: 350px; + max-width: 35rem; } -.create-password__title { - height: 102px; +.create-password__title, +.unique-image__title { width: 280px; color: #1B344D; font-size: 40px; @@ -29,6 +32,23 @@ margin-bottom: 54px; } +.unique-image__title { + margin-top: 24px; +} + +.unique-image__body-text { + width: 335px; + color: #1B344D; + font-size: 16px; + line-height: 23px; + font-family: Montserrat UltraLight; +} + +.unique-image__body-text + +.unique-image__body-text { + margin-top: 24px; +} + .first-time-flow__input { width: 350px; font-size: 18px; @@ -57,6 +77,11 @@ transition: 200ms ease-in-out; } +button.first-time-flow__button[disabled] { + background-color: rgba(247, 134, 28, 0.9); + opacity: .6; +} + button.first-time-flow__button:hover { transform: scale(1); background-color: rgba(247, 134, 28, 0.9); @@ -82,4 +107,27 @@ button.first-time-flow__button:hover { .breadcrumb + .breadcrumb { margin-left: 10px; +} + +.loading-screen { + width: 100vw; + height: 100vh; + display: flex; + flex-flow: column nowrap; + align-items: center; + margin-top: 143px; +} + +.loading-screen .spinner { + margin-bottom: 25px; + width: 100px; + height: 100px; +} + +.loading-screen__message { + color: #1B344D; + font-size: 20px; + line-height: 26px; + text-align: center; + font-family: Montserrat UltraLight; }
\ No newline at end of file diff --git a/mascara/src/app/first-time/index.js b/mascara/src/app/first-time/index.js index 1a9a00eec..4bc03c09c 100644 --- a/mascara/src/app/first-time/index.js +++ b/mascara/src/app/first-time/index.js @@ -1,14 +1,20 @@ import React, {Component, PropTypes} from 'react' +import {connect} from 'react-redux'; import CreatePasswordScreen from './create-password-screen' +import UniqueImageScreen from './unique-image-screen' -export default class FirstTimeFlow extends Component { +class FirstTimeFlow extends Component { static propTypes = { - screenType: PropTypes.string + isInitialized: PropTypes.bool, + seedWords: PropTypes.string, + noActiveNotices: PropTypes.bool }; static defaultProps = { - screenType: FirstTimeFlow.CREATE_PASSWORD + isInitialized: false, + seedWords: '', + noActiveNotices: false }; static SCREEN_TYPE = { @@ -20,9 +26,23 @@ export default class FirstTimeFlow extends Component { BUY_ETHER: 'buy_ether' }; - static getScreenType = ({isInitialized, noActiveNotices, seedWords}) => { + constructor(props) { + super(props); + this.state = { + screenType: this.getScreenType() + } + } + + setScreenType(screenType) { + this.setState({ screenType }) + } + + getScreenType() { + const {isInitialized, seedWords, noActiveNotices} = this.props; const {SCREEN_TYPE} = FirstTimeFlow + return SCREEN_TYPE.UNIQUE_IMAGE + if (!isInitialized) { return SCREEN_TYPE.CREATE_PASSWORD } @@ -39,9 +59,19 @@ export default class FirstTimeFlow extends Component { renderScreen() { const {SCREEN_TYPE} = FirstTimeFlow - switch (this.props.screenType) { + switch (this.state.screenType) { case SCREEN_TYPE.CREATE_PASSWORD: - return <CreatePasswordScreen /> + return ( + <CreatePasswordScreen + next={() => this.setScreenType(SCREEN_TYPE.UNIQUE_IMAGE)} + /> + ) + case SCREEN_TYPE.UNIQUE_IMAGE: + return ( + <UniqueImageScreen + next={() => this.setScreenType(SCREEN_TYPE.TERM_OF_USE)} + /> + ) default: return <noscript /> } @@ -56,3 +86,12 @@ export default class FirstTimeFlow extends Component { } } + +export default connect( + ({ metamask: { isInitialized, seedWords, noActiveNotices } }) => ({ + isInitialized, + seedWords, + noActiveNotices + }) +)(FirstTimeFlow) + diff --git a/mascara/src/app/first-time/loading-screen.js b/mascara/src/app/first-time/loading-screen.js new file mode 100644 index 000000000..b90465e09 --- /dev/null +++ b/mascara/src/app/first-time/loading-screen.js @@ -0,0 +1,11 @@ +import React, {Component, PropTypes} from 'react' +import Spinner from './Spinner' + +export default function LoadingScreen({ className = '', loadingMessage }) { + return ( + <div className={`${className} loading-screen`}> + <Spinner color="#1B344D" /> + <div className="loading-screen__message">{loadingMessage}</div> + </div> + ); +} diff --git a/mascara/src/app/first-time/spinner.js b/mascara/src/app/first-time/spinner.js new file mode 100644 index 000000000..78dca9a88 --- /dev/null +++ b/mascara/src/app/first-time/spinner.js @@ -0,0 +1,70 @@ +import React from 'react'; + +export default function Spinner({ className = '', color = "#000000" }) { + return ( + <div className={`spinner ${className}`}> + <svg className="lds-spinner" width="100%" height="100%" xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid" style={{background: 'none'}}> + <g transform="rotate(0 50 50)"> + <rect x={47} y={16} rx={0} ry={0} width={6} height={20} fill={color}> + <animate attributeName="opacity" values="1;0" dur="1s" begin="-0.9166666666666666s" repeatCount="indefinite" /> + </rect> + </g> + <g transform="rotate(30 50 50)"> + <rect x={47} y={16} rx={0} ry={0} width={6} height={20} fill={color}> + <animate attributeName="opacity" values="1;0" dur="1s" begin="-0.8333333333333334s" repeatCount="indefinite" /> + </rect> + </g> + <g transform="rotate(60 50 50)"> + <rect x={47} y={16} rx={0} ry={0} width={6} height={20} fill={color}> + <animate attributeName="opacity" values="1;0" dur="1s" begin="-0.75s" repeatCount="indefinite" /> + </rect> + </g> + <g transform="rotate(90 50 50)"> + <rect x={47} y={16} rx={0} ry={0} width={6} height={20} fill={color}> + <animate attributeName="opacity" values="1;0" dur="1s" begin="-0.6666666666666666s" repeatCount="indefinite" /> + </rect> + </g> + <g transform="rotate(120 50 50)"> + <rect x={47} y={16} rx={0} ry={0} width={6} height={20} fill={color}> + <animate attributeName="opacity" values="1;0" dur="1s" begin="-0.5833333333333334s" repeatCount="indefinite" /> + </rect> + </g> + <g transform="rotate(150 50 50)"> + <rect x={47} y={16} rx={0} ry={0} width={6} height={20} fill={color}> + <animate attributeName="opacity" values="1;0" dur="1s" begin="-0.5s" repeatCount="indefinite" /> + </rect> + </g> + <g transform="rotate(180 50 50)"> + <rect x={47} y={16} rx={0} ry={0} width={6} height={20} fill={color}> + <animate attributeName="opacity" values="1;0" dur="1s" begin="-0.4166666666666667s" repeatCount="indefinite" /> + </rect> + </g> + <g transform="rotate(210 50 50)"> + <rect x={47} y={16} rx={0} ry={0} width={6} height={20} fill={color}> + <animate attributeName="opacity" values="1;0" dur="1s" begin="-0.3333333333333333s" repeatCount="indefinite" /> + </rect> + </g> + <g transform="rotate(240 50 50)"> + <rect x={47} y={16} rx={0} ry={0} width={6} height={20} fill={color}> + <animate attributeName="opacity" values="1;0" dur="1s" begin="-0.25s" repeatCount="indefinite" /> + </rect> + </g> + <g transform="rotate(270 50 50)"> + <rect x={47} y={16} rx={0} ry={0} width={6} height={20} fill={color}> + <animate attributeName="opacity" values="1;0" dur="1s" begin="-0.16666666666666666s" repeatCount="indefinite" /> + </rect> + </g> + <g transform="rotate(300 50 50)"> + <rect x={47} y={16} rx={0} ry={0} width={6} height={20} fill={color}> + <animate attributeName="opacity" values="1;0" dur="1s" begin="-0.08333333333333333s" repeatCount="indefinite" /> + </rect> + </g> + <g transform="rotate(330 50 50)"> + <rect x={47} y={16} rx={0} ry={0} width={6} height={20} fill={color}> + <animate attributeName="opacity" values="1;0" dur="1s" begin="0s" repeatCount="indefinite" /> + </rect> + </g> + </svg> + </div> + ); +} diff --git a/mascara/src/app/first-time/unique-image-screen.js b/mascara/src/app/first-time/unique-image-screen.js new file mode 100644 index 000000000..ae1512d47 --- /dev/null +++ b/mascara/src/app/first-time/unique-image-screen.js @@ -0,0 +1,40 @@ +import React, {Component, PropTypes} from 'react' +import {connect} from 'react-redux'; +import Identicon from '../../../../ui/app/components/identicon' +import Breadcrumbs from './breadcrumbs' + +class UniqueImageScreen extends Component { + static propTypes = { + address: PropTypes.string.isRequired, + next: PropTypes.func.isRequired + } + + render() { + return ( + <div className="unique-image"> + <Identicon address={this.props.address} diameter={70} /> + <div className="unique-image__title">You unique account image</div> + <div className="unique-image__body-text"> + This image was programmatically generated for you by your new account number. + </div> + <div className="unique-image__body-text"> + You’ll see this image everytime you need to confirm a transaction. + </div> + <button + className="first-time-flow__button" + onClick={this.props.next} + > + Next + </button> + <Breadcrumbs total={3} currentIndex={1} /> + </div> + ) + } +} + +export default connect( + ({ metamask: { identities } }) => ({ + address: Object.entries(identities) + .map(([key]) => key)[0] + }) +)(UniqueImageScreen) diff --git a/package.json b/package.json index 106851013..502b070cb 100644 --- a/package.json +++ b/package.json @@ -130,6 +130,7 @@ "react-simple-file-input": "^2.0.0", "react-tooltip-component": "^0.3.0", "readable-stream": "^2.3.3", + "recompose": "^0.25.0", "redux": "^3.0.5", "redux-logger": "^3.0.6", "redux-thunk": "^2.2.0", diff --git a/ui/app/actions.js b/ui/app/actions.js index 84990922e..11cd14c16 100644 --- a/ui/app/actions.js +++ b/ui/app/actions.js @@ -243,19 +243,26 @@ function createNewVaultAndKeychain (password) { return (dispatch) => { dispatch(actions.showLoadingIndication()) log.debug(`background.createNewVaultAndKeychain`) - background.createNewVaultAndKeychain(password, (err) => { - if (err) { - return dispatch(actions.displayWarning(err.message)) - } - log.debug(`background.placeSeedWords`) - background.placeSeedWords((err) => { + + return new Promise((resolve, reject) => { + background.createNewVaultAndKeychain(password, (err) => { if (err) { - return dispatch(actions.displayWarning(err.message)) + dispatch(actions.displayWarning(err.message)) + return reject(err) } - dispatch(actions.hideLoadingIndication()) - forceUpdateMetamaskState(dispatch) + log.debug(`background.placeSeedWords`) + background.placeSeedWords((err) => { + if (err) { + dispatch(actions.displayWarning(err.message)) + return reject(err) + } + dispatch(actions.hideLoadingIndication()) + forceUpdateMetamaskState(dispatch) + resolve() + }) }) - }) + }); + } } diff --git a/ui/app/app.js b/ui/app/app.js index 57e3d3366..ec36eb22e 100644 --- a/ui/app/app.js +++ b/ui/app/app.js @@ -106,10 +106,7 @@ App.prototype.render = function () { this.renderNetworkDropdown(), this.renderDropdown(), - h(Loading, { - isLoading: isLoading || isLoadingNetwork, - loadingMessage: loadMessage, - }), + this.renderLoadingIndicator({ isLoading, isLoadingNetwork, loadMessage }), // panel content h('.app-primary' + (transForward ? '.from-right' : '.from-left'), { @@ -401,6 +398,17 @@ App.prototype.renderDropdown = function () { ]) } +App.prototype.renderLoadingIndicator = function({ isLoading, isLoadingNetwork, loadMessage }) { + const { isMascara } = this.props; + + return isMascara + ? null + : h(Loading, { + isLoading: isLoading || isLoadingNetwork, + loadingMessage: loadMessage, + }) +} + App.prototype.renderBackButton = function (style, justArrow = false) { var props = this.props return ( @@ -420,19 +428,13 @@ App.prototype.renderBackButton = function (style, justArrow = false) { ) } -App.prototype.renderMascaraFirstTime = function () { - return 'hi' -} - App.prototype.renderPrimary = function () { log.debug('rendering primary') var props = this.props const {isMascara, isOnboarding} = props if (isMascara && isOnboarding) { - return h(MascaraFirstTime, { - screenType: MascaraFirstTime.getScreenType(props) - }) + return h(MascaraFirstTime) } // notices |