Add UniqueImageScreen

feature/default_network_editable
Jacky Chan 7 years ago committed by Chi Kei Chan
parent 4e446410eb
commit e1497fafa6
  1. 1
      mascara/src/app/first-time/breadcrumbs.js
  2. 112
      mascara/src/app/first-time/create-password-screen.js
  3. 56
      mascara/src/app/first-time/index.css
  4. 51
      mascara/src/app/first-time/index.js
  5. 11
      mascara/src/app/first-time/loading-screen.js
  6. 70
      mascara/src/app/first-time/spinner.js
  7. 40
      mascara/src/app/first-time/unique-image-screen.js
  8. 1
      package.json
  9. 27
      ui/app/actions.js
  10. 24
      ui/app/app.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'}}
/>

@ -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>
)
)
}
}
export default connect(
({ appState: { isLoading } }) => ({ isLoading }),
dispatch => ({
createAccount: password => dispatch(createNewVaultAndKeychain(password))
})
)(CreatePasswordScreen)

@ -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);
@ -83,3 +108,26 @@ 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;
}

@ -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)

@ -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>
);
}

@ -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>
);
}

@ -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">
Youll 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)

@ -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",

@ -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()
})
})
})
});
}
}

@ -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

Loading…
Cancel
Save