aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJacky Chan <jchan@uber.com>2017-08-21 19:56:09 +0800
committerChi Kei Chan <chikeichan@gmail.com>2017-10-21 12:51:37 +0800
commite1497fafa64b5f8e25407611709920dc5e0eaf77 (patch)
tree11f1b6eaddf131e4c733ec990ef67b476d6a86db
parent4e446410eb76c1bd5d27b6dd4f413bfbf3df0e2d (diff)
downloadtangerine-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.js1
-rw-r--r--mascara/src/app/first-time/create-password-screen.js112
-rw-r--r--mascara/src/app/first-time/index.css56
-rw-r--r--mascara/src/app/first-time/index.js51
-rw-r--r--mascara/src/app/first-time/loading-screen.js11
-rw-r--r--mascara/src/app/first-time/spinner.js70
-rw-r--r--mascara/src/app/first-time/unique-image-screen.js40
-rw-r--r--package.json1
-rw-r--r--ui/app/actions.js27
-rw-r--r--ui/app/app.js24
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