Browse Source

Merge pull request #353 from NastiaS/onboarding

Onboarding
master
Gaëtan Renaudeau 7 years ago
committed by GitHub
parent
commit
34fbf85ee6
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      src/components/Breadcrumb/Step.js
  2. 13
      src/components/Onboarding/OnboardingBreadcrumb.js
  3. 60
      src/components/Onboarding/helperComponents.js
  4. 5
      src/components/Onboarding/index.js
  5. 4
      src/components/Onboarding/steps/Analytics.js
  6. 4
      src/components/Onboarding/steps/Finish.js
  7. 253
      src/components/Onboarding/steps/GenuineCheck.js
  8. 4
      src/components/Onboarding/steps/SelectDevice.js
  9. 46
      src/components/Onboarding/steps/SelectPIN.js
  10. 4
      src/components/Onboarding/steps/SetPassword.js
  11. 45
      src/components/Onboarding/steps/WriteSeed.js
  12. 78
      src/icons/onboarding/LedgerNanoError.js
  13. 24
      src/icons/onboarding/Warning.js
  14. 7
      src/reducers/onboarding.js
  15. 2
      static/i18n/en/common.yml
  16. 46
      static/i18n/en/onboarding.yml

5
src/components/Breadcrumb/Step.js

@ -27,12 +27,13 @@ const Wrapper = styled(Box).attrs({
const StepNumber = styled(Box).attrs({
alignItems: 'center',
justifyContent: 'center',
color: 'white',
color: 'fog',
bg: p =>
['active', 'valid'].includes(p.status) ? 'wallet' : p.status === 'error' ? 'alertRed' : 'fog',
['active', 'valid'].includes(p.status) ? 'wallet' : p.status === 'error' ? 'alertRed' : 'white',
ff: 'Rubik|Regular',
})`
border-radius: 50%;
border: 1px solid #d8d8d8;
font-size: 10px;
height: ${RADIUS}px;
line-height: 10px;

13
src/components/Onboarding/OnboardingBreadcrumb.js

@ -18,15 +18,22 @@ type Props = {
function OnboardingBreadcrumb(props: Props) {
const { onboarding } = props
const { stepName } = onboarding
const { stepName, isGenuineFail } = onboarding
const filteredSteps = onboarding.steps
.filter(step => !step.external)
.map(step => ({ ...step, label: step.label })) // TODO: translate
const stepIndex = findIndex(filteredSteps, s => s.name === stepName)
return <Breadcrumb currentStep={stepIndex} items={filteredSteps} />
const genuineStepIndex = findIndex(filteredSteps, s => s.name === 'genuineCheck')
return (
<Breadcrumb
stepsErrors={isGenuineFail ? [genuineStepIndex] : undefined}
currentStep={stepIndex}
items={filteredSteps}
/>
)
}
export default connect(mapStateToProps)(OnboardingBreadcrumb)

60
src/components/Onboarding/helperComponents.js

@ -4,6 +4,7 @@ import styled from 'styled-components'
import { radii } from 'styles/theme'
import Box from 'components/base/Box'
import IconWarning from 'icons/onboarding/Warning'
// GENERAL
export const Title = styled(Box).attrs({
@ -46,20 +47,18 @@ type StepType = {
icon: any,
desc: string,
}
export function InstructionStep({ step }: { step: StepType }) {
export function OptionRow({ step }: { step: StepType }) {
const { icon, desc } = step
return (
<Box horizontal>
<Box justify="center" color="grey" style={{ width: 26 }}>
{icon}
</Box>
<Box horizontal m={2}>
<Box justify="center">{icon}</Box>
<Box ff="Open Sans|Regular" justify="center" fontSize={4} style={{ paddingLeft: 10 }} shrink>
<InstructionStepDesc>{desc}</InstructionStepDesc>
<OptionRowDesc>{desc}</OptionRowDesc>
</Box>
</Box>
)
}
export const InstructionStepDesc = styled(Box).attrs({
export const OptionRowDesc = styled(Box).attrs({
ff: 'Open Sans|Regular',
fontSize: 4,
textAlign: 'left',
@ -68,15 +67,42 @@ export const InstructionStepDesc = styled(Box).attrs({
shrink: 1,
})``
export const IconInstructionStep = styled(Box).attrs({
width: 26,
height: 26,
export const IconOptionRow = styled(Box).attrs({
ff: 'Rubik|Regular',
textAlign: 'center',
fontSize: 3,
fontSize: 14,
color: 'wallet',
})`
border-radius: 100%;
background: #6490f126;
line-height: 2;
`
})``
export function DisclaimerBox({ disclaimerNotes, ...p }: { disclaimerNotes: any }) {
return (
<Box
shrink
grow
flow={4}
style={{
minWidth: 680,
backgroundColor: '#ea2e490c',
border: 'dashed 1px #ea2e49b3',
}}
{...p}
>
<Box
m={3}
style={{
position: 'relative',
}}
>
<Box
style={{
position: 'absolute',
top: '0px',
right: '0px',
}}
>
<IconWarning />
</Box>
{disclaimerNotes.map(note => <OptionRow key={note.key} step={note} />)}
</Box>
</Box>
)
}

5
src/components/Onboarding/index.js

@ -10,7 +10,7 @@ import type { T } from 'types/common'
import type { OnboardingState } from 'reducers/onboarding'
import { saveSettings } from 'actions/settings'
import { nextStep, prevStep, jumpStep } from 'reducers/onboarding'
import { nextStep, prevStep, jumpStep, setGenuineCheckFail } from 'reducers/onboarding'
import { getCurrentDevice } from 'reducers/devices'
// TODO: re-write it without auto lock, fixed width of the password modal, not dynamic titles
@ -69,12 +69,14 @@ type Props = {
export type StepProps = {
t: T,
onboarding: OnboardingState,
prevStep: Function,
nextStep: Function,
jumpStep: Function,
finish: Function,
savePassword: Function,
getDeviceInfo: Function,
setGenuineCheckFail: Function,
}
class Onboarding extends PureComponent<Props> {
@ -107,6 +109,7 @@ class Onboarding extends PureComponent<Props> {
const stepProps: StepProps = {
t,
onboarding,
setGenuineCheckFail,
prevStep,
nextStep,
jumpStep,

4
src/components/Onboarding/steps/Analytics.js

@ -14,8 +14,8 @@ import type { StepProps } from '..'
export default (props: StepProps) => {
const { nextStep, prevStep, t } = props
return (
<Box sticky>
<Box grow alignItems="center" justifyContent="center">
<Box sticky pt={150}>
<Box grow alignItems="center">
<Title>{t('onboarding:analytics.title')}</Title>
<Description style={{ maxWidth: 714 }}>{t('onboarding:analytics.desc')}</Description>
<DeviceIcon style={{ padding: 15 }}>

4
src/components/Onboarding/steps/Finish.js

@ -39,8 +39,8 @@ const socialMedia = [
export default (props: StepProps) => {
const { finish, t } = props
return (
<Box sticky alignItems="center" justifyContent="center">
<Box align="center" alignItems="center">
<Box sticky pt={150}>
<Box grow alignItems="center">
<Box color="positiveGreen">
<IconCheckCircle size={44} />
</Box>

253
src/components/Onboarding/steps/GenuineCheck.js

@ -1,71 +1,133 @@
// @flow
import React, { PureComponent } from 'react'
import { connect } from 'react-redux'
import styled from 'styled-components'
import { radii, colors } from 'styles/theme'
import Box from 'components/base/Box'
import type { T } from 'types/common'
import { setGenuineCheckFail } from 'reducers/onboarding'
import Box, { Card } from 'components/base/Box'
import Button from 'components/base/Button'
import IconCheck from 'icons/Check'
import IconLedgerNanoError from 'icons/onboarding/LedgerNanoError'
import { Title, Description } from '../helperComponents'
import { Title, Description, IconOptionRow } from '../helperComponents'
import type { StepProps } from '..'
import OnboardingFooter from '../OnboardingFooter'
const mapDispatchToProps = { setGenuineCheckFail }
type State = {
currentDevice: {
manufacturer: string,
release: number,
},
showDeviceInfo: boolean,
showError: boolean,
pinStepPass: boolean | null,
phraseStepPass: boolean | null,
}
// temp checking the release version of the device if connected
class GenuineCheck extends PureComponent<StepProps, State> {
state = {
showDeviceInfo: false,
currentDevice: { manufacturer: 'Unknown', release: 0 },
showError: false,
pinStepPass: null,
phraseStepPass: null,
}
handleCheckDevice = () => {
const currentDeviceInfo = this.props.getDeviceInfo()
if (currentDeviceInfo) {
this.setState({ showError: false, currentDevice: currentDeviceInfo, showDeviceInfo: true })
} else {
this.setState({ showError: true })
handleStepPass = (step: string, pass: boolean | null) => {
this.setState({ [`${step}`]: pass })
if (typeof pass === 'boolean' && !pass) {
this.props.setGenuineCheckFail(true)
}
}
redoGenuineCheck = () => {
this.props.setGenuineCheckFail(false)
}
contactSupport = () => {
console.log('contact support coming later')
}
renderGenuineFail = () => (
<GenuineCheckFail
redoGenuineCheck={this.redoGenuineCheck}
contactSupport={this.contactSupport}
t={this.props.t}
/>
)
render() {
const { nextStep, prevStep, t } = this.props
const { showDeviceInfo, currentDevice, showError } = this.state
const { nextStep, prevStep, t, onboarding } = this.props
const { pinStepPass, phraseStepPass } = this.state
if (onboarding.isGenuineFail) {
return this.renderGenuineFail()
}
return (
<Box sticky>
<Box grow alignItems="center" justifyContent="center">
<Box sticky pt={150}>
<Box grow alignItems="center">
<Title>{t('onboarding:genuineCheck.title')}</Title>
<Description>{t('onboarding:genuineCheck.desc')}</Description>
<Box alignItems="center" justifyContent="center">
<Title>Coming next week</Title>
<Box alignItems="center" justifyContent="center" style={{ padding: '15px' }}>
<Button big primary onClick={() => this.handleCheckDevice()}>
Check your device!
</Button>
{showDeviceInfo && (
<Box>
<Description>
The manufacturer is <b>{currentDevice.manufacturer}</b>
The release number is <b>{currentDevice.release}</b>
</Description>
<Box mt={5}>
<CardWrapper>
<Box justify="center">
<Box horizontal>
<IconOptionRow>1.</IconOptionRow>
<CardTitle>{t('onboarding:genuineCheck.steps.step1.title')}</CardTitle>
</Box>
<CardDescription>{t('onboarding:genuineCheck.steps.step2.desc')}</CardDescription>
</Box>
{!pinStepPass ? (
<ButtonCombo
handleStepPass={this.handleStepPass}
step="pinStepPass"
disabled={false}
t={this.props.t}
/>
) : (
<Box justify="center" color={colors.wallet} ml={8}>
<IconCheck size={16} />
</Box>
)}
{showError && (
<Box>
<Description color="red">Connect your device please</Description>
</CardWrapper>
</Box>
<Box mt={5}>
<CardWrapper disabled={!pinStepPass}>
<Box justify="center">
<Box horizontal>
<IconOptionRow>2.</IconOptionRow>
<CardTitle>{t('onboarding:genuineCheck.steps.step2.title')}</CardTitle>
</Box>
<CardDescription>{t('onboarding:genuineCheck.steps.step2.desc')}</CardDescription>
</Box>
{!phraseStepPass ? (
<ButtonCombo
handleStepPass={this.handleStepPass}
step="phraseStepPass"
disabled={!pinStepPass}
t={this.props.t}
/>
) : (
<Box justify="center" color={colors.wallet} ml={8}>
<IconCheck size={16} />
</Box>
)}
</Box>
</CardWrapper>
</Box>
<Box mt={5}>
<CardWrapper disabled={!phraseStepPass}>
<Box justify="center">
<Box horizontal>
<IconOptionRow>3.</IconOptionRow>
<CardTitle>{t('onboarding:genuineCheck.steps.step3.title')}</CardTitle>
</Box>
<CardDescription>{t('onboarding:genuineCheck.steps.step3.desc')}</CardDescription>
</Box>
<Box justify="center" horizontal mx={5}>
<Button big primary disabled={!phraseStepPass}>
{t('onboarding:genuineCheck.buttons.genuineCheck')}
</Button>
</Box>
</CardWrapper>
</Box>
</Box>
<OnboardingFooter
@ -81,4 +143,115 @@ class GenuineCheck extends PureComponent<StepProps, State> {
}
}
export default GenuineCheck
export default connect(null, mapDispatchToProps)(GenuineCheck)
export function ButtonCombo({
handleStepPass,
step,
disabled,
t,
}: {
handleStepPass: any,
step: string,
disabled: boolean,
t: T,
}) {
return (
<Box justify="center" horizontal style={{ margin: '0 20px' }}>
<Button
disabled={disabled}
style={{ padding: '0 20px' }}
outline
onClick={() => handleStepPass(step, true)}
>
{t('common:yes')}
</Button>
<Button
disabled={disabled}
style={{ padding: '0 20px' }}
outline
onClick={() => handleStepPass(step, false)}
>
{t('common:no')}
</Button>
</Box>
)
}
// TODO extract to a separate file
export function GenuineCheckFail({
redoGenuineCheck,
contactSupport,
t,
}: {
redoGenuineCheck: () => void,
contactSupport: () => void,
t: T,
}) {
return (
<Box sticky pt={150}>
<Box grow alignItems="center">
<Title>{t('onboarding:genuineCheck.errorPage.ledgerNano.title')}</Title>
<Description style={{ maxWidth: 527 }}>
{t('onboarding:genuineCheck.errorPage.ledgerNano.desc')}
</Description>
<Box style={{ minWidth: 527 }}>
<IconLedgerNanoError />
</Box>
</Box>
<Wrapper horizontal>
<Button
small
outline
onClick={() => {
redoGenuineCheck()
}}
>
{t('common:back')}
</Button>
<Button
small
danger
onClick={() => {
contactSupport()
}}
ml="auto"
>
{t('onboarding:genuineCheck.buttons.contactSupport')}
</Button>
</Wrapper>
</Box>
)
}
export const CardDescription = styled(Box).attrs({
ff: 'Open Sans|Regular',
fontSize: 4,
textAlign: 'left',
color: 'grey',
})`
max-width: 400px;
`
export const CardTitle = styled(Box).attrs({
ff: 'Open Sans|SemiBold',
fontSize: 4,
textAlign: 'left',
pl: 2,
})``
const Wrapper = styled(Box).attrs({
px: 5,
py: 3,
})`
border-top: 2px solid ${p => p.theme.colors.lightGrey};
border-bottom-left-radius: ${radii[1]}px;
border-bottom-right-radius: ${radii[1]}px;
`
const CardWrapper = styled(Card).attrs({
horizontal: true,
p: 5,
})`
border: ${props => (props.disabled ? '1px dashed #d8d8d8' : '1px solid #d8d8d8')};
max-height: 97px;
min-width: 620px;
background-color: ${props => (props.disabled ? colors.lightGrey : colors.white)};
opacity: ${props => (props.disabled ? 0.7 : 1)};
`

4
src/components/Onboarding/steps/SelectDevice.js

@ -14,8 +14,8 @@ export default (props: StepProps) => {
const { nextStep, t } = props
return (
<Box sticky alignItems="center" justifyContent="center">
<Box align="center">
<Box sticky pt={150}>
<Box grow alignItems="center">
<Title>{t('onboarding:selectDevice.title')}</Title>
<Description style={{ maxWidth: 714 }}>{t('onboarding:selectDevice.desc')}</Description>
<Box>

46
src/components/Onboarding/steps/SelectPIN.js

@ -3,14 +3,18 @@
import React from 'react'
import Box from 'components/base/Box'
import { colors } from 'styles/theme'
import IconSelectPIN from 'icons/onboarding/SelectPIN'
import IconChevronRight from 'icons/ChevronRight'
import {
Title,
Description,
Inner,
InstructionStep,
IconInstructionStep,
OptionRow,
IconOptionRow,
DisclaimerBox,
} from '../helperComponents'
import OnboardingFooter from '../OnboardingFooter'
@ -21,42 +25,60 @@ export default (props: StepProps) => {
const steps = [
{
key: 'step1',
icon: <IconInstructionStep>1</IconInstructionStep>,
icon: <IconOptionRow>1.</IconOptionRow>,
desc: t('onboarding:selectPIN.instructions.step1'),
},
{
key: 'step2',
icon: <IconInstructionStep>2</IconInstructionStep>,
icon: <IconOptionRow>2.</IconOptionRow>,
desc: t('onboarding:selectPIN.instructions.step2'),
},
{
key: 'step3',
icon: <IconInstructionStep>3</IconInstructionStep>,
icon: <IconOptionRow>3.</IconOptionRow>,
desc: t('onboarding:selectPIN.instructions.step3'),
},
{
key: 'step4',
icon: <IconInstructionStep>4</IconInstructionStep>,
icon: <IconOptionRow>4.</IconOptionRow>,
desc: t('onboarding:selectPIN.instructions.step4'),
},
]
const disclaimerNotes = [
{
key: 'note1',
icon: <IconChevronRight size={12} style={{ color: colors.smoke }} />,
desc: t('onboarding:selectPIN.disclaimer.note1'),
},
{
key: 'note2',
icon: <IconChevronRight size={12} style={{ color: colors.smoke }} />,
desc: t('onboarding:selectPIN.disclaimer.note2'),
},
{
key: 'note3',
icon: <IconChevronRight size={12} style={{ color: colors.smoke }} />,
desc: t('onboarding:selectPIN.disclaimer.note3'),
},
]
return (
<Box sticky>
<Box grow alignItems="center" justifyContent="center">
<Box sticky pt={150}>
<Box grow alignItems="center">
<Box align="center" mb={5}>
<Title>{t('onboarding:selectPIN.title')}</Title>
<Description style={{ maxWidth: 714 }}>{t('onboarding:selectPIN.desc')}</Description>
<Description style={{ maxWidth: 527 }}>{t('onboarding:selectPIN.desc')}</Description>
</Box>
<Box>
<Box align="center">
<Inner style={{ width: 760 }}>
<Box style={{ width: 260 }} mt={5}>
<IconSelectPIN />
</Box>
<Box shrink grow flow={5}>
{steps.map(step => <InstructionStep key={step.key} step={step} />)}
<Box shrink grow flow={4}>
{steps.map(step => <OptionRow key={step.key} step={step} />)}
</Box>
</Inner>
<DisclaimerBox mt={6} disclaimerNotes={disclaimerNotes} />
</Box>
</Box>
<OnboardingFooter

4
src/components/Onboarding/steps/SetPassword.js

@ -49,8 +49,8 @@ class SetPassword extends PureComponent<StepProps, State> {
const { nextStep, prevStep, t } = this.props
const { isPasswordModalOpened, isPasswordEnabled } = this.state
return (
<Box sticky>
<Box grow alignItems="center" justifyContent="center">
<Box sticky pt={150}>
<Box grow alignItems="center">
<Title>{t('onboarding:setPassword.title')}</Title>
<Description style={{ maxWidth: 714 }}>{t('onboarding:setPassword.desc')}</Description>
<IconSetPassword />

45
src/components/Onboarding/steps/WriteSeed.js

@ -4,13 +4,16 @@ import React from 'react'
import Box from 'components/base/Box'
import IconWriteSeed from 'icons/onboarding/WriteSeed'
import IconChevronRight from 'icons/ChevronRight'
import { colors } from 'styles/theme'
import {
Title,
Description,
Inner,
InstructionStep,
IconInstructionStep,
OptionRow,
IconOptionRow,
DisclaimerBox,
} from '../helperComponents'
import OnboardingFooter from '../OnboardingFooter'
@ -21,46 +24,54 @@ export default (props: StepProps) => {
const steps = [
{
key: 'step1',
icon: <IconInstructionStep>1</IconInstructionStep>,
icon: <IconOptionRow>1.</IconOptionRow>,
desc: t('onboarding:writeSeed.instructions.step1'),
},
{
key: 'step2',
icon: <IconInstructionStep>2</IconInstructionStep>,
icon: <IconOptionRow>2.</IconOptionRow>,
desc: t('onboarding:writeSeed.instructions.step2'),
},
{
key: 'step3',
icon: <IconInstructionStep>3</IconInstructionStep>,
icon: <IconOptionRow>3.</IconOptionRow>,
desc: t('onboarding:writeSeed.instructions.step3'),
},
]
const disclaimerNotes = [
{
key: 'note1',
icon: <IconChevronRight size={12} style={{ color: colors.smoke }} />,
desc: t('onboarding:writeSeed.disclaimer.note1'),
},
{
key: 'step4',
icon: <IconInstructionStep>4</IconInstructionStep>,
desc: t('onboarding:writeSeed.instructions.step4'),
key: 'note2',
icon: <IconChevronRight size={12} style={{ color: colors.smoke }} />,
desc: t('onboarding:writeSeed.disclaimer.note2'),
},
{
key: 'step5',
icon: <IconInstructionStep>5</IconInstructionStep>,
desc: t('onboarding:writeSeed.instructions.step5'),
key: 'note3',
icon: <IconChevronRight size={12} style={{ color: colors.smoke }} />,
desc: t('onboarding:writeSeed.disclaimer.note3'),
},
]
return (
<Box sticky>
<Box grow alignItems="center" justifyContent="center">
<Box sticky pt={150}>
<Box grow alignItems="center">
<Box align="center" mb={5}>
<Title>{t('onboarding:writeSeed.title')}</Title>
<Description style={{ maxWidth: 714 }}>{t('onboarding:writeSeed.desc')}</Description>
</Box>
<Box>
<Box align="center">
<Inner style={{ width: 760 }}>
<Box style={{ width: 260, alignItems: 'center' }} mt={4}>
<Box style={{ width: 260, alignItems: 'center' }}>
<IconWriteSeed />
</Box>
<Box shrink grow flow={5}>
{steps.map(step => <InstructionStep key={step.key} step={step} />)}
<Box shrink grow flow={4}>
{steps.map(step => <OptionRow key={step.key} step={step} />)}
</Box>
</Inner>
<DisclaimerBox mt={6} disclaimerNotes={disclaimerNotes} />
</Box>
</Box>
<OnboardingFooter

78
src/icons/onboarding/LedgerNanoError.js

@ -0,0 +1,78 @@
// @flow
import React from 'react'
export default () => (
<svg>
<defs>
<linearGradient id="a" x1="50%" x2="50%" y1="0%" y2="100%">
<stop offset="0%" />
<stop offset="100%" stopColor="#FFF" />
</linearGradient>
<rect id="b" width="41.711" height="238.384" rx="4" />
<path
id="c"
d="M5.773 5l2.541-2.541a.235.235 0 0 0 0-.332l-.441-.441a.235.235 0 0 0-.332 0L5 4.226l-2.541-2.54a.235.235 0 0 0-.332 0l-.441.441a.235.235 0 0 0 0 .332L4.226 5l-2.54 2.541a.235.235 0 0 0 0 .332l.441.441c.092.092.24.092.332 0L5 5.774l2.541 2.54c.092.092.24.092.332 0l.441-.441a.235.235 0 0 0 0-.332L5.774 5z"
/>
</defs>
<g fill="none" fillRule="evenodd">
<path
stroke="#1D2027"
strokeWidth="2"
d="M127.356 30a1 1 0 0 1-1 1H100.13a5 5 0 0 1-5-5v-8.486a5 5 0 0 1 5-5h26.225a1 1 0 0 1 1 1V30z"
/>
<path stroke="#142533" strokeWidth="2" d="M94.747 24.792H83.818v-6.436h10.93v6.436z" />
<path
stroke="#1D2027"
strokeWidth="2"
d="M127.423 27.287V16.032l6.977.082a1 1 0 0 1 .988 1v9.267a1 1 0 0 1-1.012.988l-6.953-.082z"
/>
<path
fill="url(#a)"
d="M6.836 53.925h1.616v82.65H6.836v-82.65zm5.657 0h1.616v82.65h-1.616v-82.65z"
transform="matrix(0 -1 -1 0 137 32)"
/>
<g transform="rotate(-90 85 -42)">
<rect width="4.492" height="17.12" x="38.336" y="15.505" fill="#142533" rx="2" />
<rect width="4.492" height="17.12" x="38.336" y="70.094" fill="#142533" rx="2" />
<use fill="#FFF" />
<rect
width="39.711"
height="236.384"
x="1"
y="1"
fill="#FCE0E4"
stroke="#142533"
strokeLinejoin="square"
strokeWidth="2"
rx="4"
/>
<rect
width="20.176"
height="61.019"
x="10.767"
y="21.173"
fill="#FFF"
stroke="#EA2E49"
rx="1.6"
/>
<path
fill="#FFF"
stroke="#142533"
strokeWidth="2"
d="M20.856 95.966C9.89 95.966 1 104.856 1 115.822v118.562a3 3 0 0 0 3 3h33.711a3 3 0 0 0 3-3V115.822c0-10.966-8.89-19.856-19.855-19.856z"
/>
<ellipse cx="21.016" cy="116.123" stroke="#EA2E49" rx="10.57" ry="10.644" />
<g transform="translate(16.066 26.884)">
<mask id="d" fill="#fff">
<use xlinkHref="#c" />
</mask>
<use fill="#000" fillRule="nonzero" xlinkHref="#c" />
<g fill="#EA2E49" mask="url(#d)">
<path d="M0 0h10v10H0z" />
</g>
</g>
</g>
</g>
</svg>
)

24
src/icons/onboarding/Warning.js

@ -0,0 +1,24 @@
// @flow
import React from 'react'
export default () => (
<svg width="28" height="31">
<g fill="none">
<path
fill="#EA2E49"
fillOpacity=".1"
d="M14.135 1C14.045 17.38 14 27.046 14 30h.271C21.448 28.36 27 21.552 27 14.24V6.295L14.135 1z"
/>
<path
stroke="#142533"
strokeWidth="1.5"
d="M14 1L1 6.296v7.943C1 21.552 6.474 28.361 13.863 30h.274C21.389 28.36 27 21.552 27 14.24V6.295L14 1z"
/>
<path
fill="#EA2E49"
d="M18.538 10.727c-.44 0-.799.338-.799.755l-.014 3.386s.001.237-.24.237c-.245 0-.238-.237-.238-.237V9.983c0-.417-.354-.74-.794-.74-.442 0-.756.323-.756.74v4.885s-.027.239-.264.239c-.235 0-.253-.239-.253-.239V9.17c0-.417-.333-.733-.774-.733-.44 0-.776.316-.776.733v5.7s-.012.228-.266.228c-.25 0-.25-.229-.25-.229v-4.233c0-.417-.346-.679-.787-.679-.442 0-.764.262-.764.679v6.187s-.044.275-.46-.18c-1.07-1.163-1.629-1.394-1.629-1.394s-1.015-.47-1.465.378c-.326.616.02.65.552 1.405.472.67 1.963 2.43 1.963 2.43s1.768 2.358 4.155 2.358c0 0 4.674.189 4.674-4.182l-.016-6.155c0-.417-.358-.755-.799-.755"
/>
</g>
</svg>
)

7
src/reducers/onboarding.js

@ -17,11 +17,13 @@ export type OnboardingState = {
stepIndex: number,
stepName: string, // TODO: specify that the string comes from Steps type
steps: Step[],
isGenuineFail: boolean,
}
const state: OnboardingState = {
stepIndex: 0,
stepName: 'start',
isGenuineFail: false,
steps: [
{
name: 'start',
@ -138,6 +140,10 @@ const handlers = {
const index = state.steps.indexOf(step)
return { ...state, stepName: step.name, stepIndex: index }
},
ONBOARDING_SET_GENUINE_CHECK_FAIL: (state, { payload: isGenuineFail }) => ({
...state,
isGenuineFail,
}),
}
export default handleActions(handlers, state)
@ -145,3 +151,4 @@ export default handleActions(handlers, state)
export const nextStep = createAction('ONBOARDING_NEXT_STEP')
export const prevStep = createAction('ONBOARDING_PREV_STEP')
export const jumpStep = createAction('ONBOARDING_JUMP_STEP')
export const setGenuineCheckFail = createAction('ONBOARDING_SET_GENUINE_CHECK_FAIL')

2
static/i18n/en/common.yml

@ -1,4 +1,6 @@
ok: Okay
yes: Yes
no: No
confirm: Confirm
cancel: Cancel
continue: Continue

46
static/i18n/en/onboarding.yml

@ -25,25 +25,49 @@ selectDevice:
title: Ledger Blue
desc: Please replace it with the final wording once it’s done.
selectPIN:
title: Select PIN code
title: Choose your PIN code
desc: This is a long text, please replace it with the final wording once it’s done. Lorem ipsum dolor amet ledger lorem dolor ipsum amet
instructions:
step1: Connect your Ledger Nano S to your computer using the supplied micro USB cable.
step2: Press both buttons simultaneously as instructed on your Ledger Nano S screen.
step3: Select Configure as new device on your Ledger Nano S by pressing the right button, located above the validation icon.
step1: Connect the Ledger Nano S to your computer.
step2: Press both buttons simultaneously as instructed on the screen.
step3: Press the right button to select Configure as new device.
step4: Choose a PIN code between 4 and 8 digits long.
disclaimer:
note1: Choose your own PIN code. This code unlocks your device.
note2: An 8-digit PIN code offers an optimum level of security.
note3: Never use a device supplied with a PIN code and/or a 24-word recovery phrase.
writeSeed:
title: 24-Word Recovery phrase
desc: The 24 words that constitute your recovery phrase will now be displayed one by one on the Ledger Nano S screen. These 24 words will be displayed only once during this initialization.
title: Save your recovery phrase
desc: Your recovery phrase is formed by 24 words. They will be displayed only once.
instructions:
step1: Copy the first word (Word \#1) in position 1 on the blank Recovery sheet included in the box.
step2: Move to Word \#2 by pressing the right button, copy it in position 2 on the Recovery sheet.
step3: Repeat the process until all 24 words are copied on the Recovery sheet.
step4: To confirm, use the right or left button to select each of the 24 words in the right order.
step5: Validate each word by simultaneously pressing both buttons.
step1: Copy the first word (Word \#1) in position 1 on the blank Recovery sheet.
step2: Press the right button to display Word \#2 and repeat the process until all 24 words are copied on the Recovery sheet.
step3: Confirm your recovery phrase press both buttons to validate each word displayed on the screen.
disclaimer:
note1: Carefully secure your 24 words out of sight.
note2: Ledger does not keep any backup of your 24 words.
note3: Make sure you are the sole holder of the 24-word recovery phrase.
note4: Never use a device supplied with a recovery phrase and/or a PIN code.
genuineCheck:
title: Check PIN / Seed / Authenticity
desc: Your Ledger Nano S should now display Your device is now ready. Before getting started, please confirm that
steps:
step1:
title: You alone have chosen your PIN code
desc: This is a long text, please replace it with the final wording once it’s done. Lorem ipsum dolor amet ledger lorem dolor
step2:
title: You alone have initialized your recovery phrase
desc: This is a long text, please replace it with the final wording once it’s done. Lorem ipsum dolor amet ledger lorem dolor
step3:
title: Your device is a genuine Ledger device
desc: This is a long text, please replace it with the final wording once it’s done. Lorem ipsum dolor amet ledger lorem dolor
buttons:
genuineCheck: Genuine check
contactSupport: Contact Support
errorPage:
ledgerNano:
title: Something is wrong with your Ledger Nano S
desc: Your Ledger Nano S should now display Your device is now ready. Before getting started, please confirm that
setPassword:
title: This is the title of the screen. 1 line is the maximum
desc: This is a long text, please replace it with the final wording once it’s done. Lorem ipsum dolor amet ledger lorem dolor ipsum amet

Loading…
Cancel
Save