Browse Source

Merge pull request #386 from NastiaS/polishBranch

Polish branch
master
Gaëtan Renaudeau 7 years ago
committed by GitHub
parent
commit
ea7521147e
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      src/components/Onboarding/OnboardingBreadcrumb.js
  2. 5
      src/components/Onboarding/OnboardingFooter.js
  3. 14
      src/components/Onboarding/index.js
  4. 51
      src/components/Onboarding/steps/Analytics.js
  5. 72
      src/components/Onboarding/steps/GenuineCheck.js
  6. 15
      src/components/Onboarding/steps/Init.js
  7. 16
      src/icons/Recover.js
  8. 21
      src/reducers/onboarding.js
  9. 2
      src/reducers/settings.js
  10. 8
      static/i18n/en/onboarding.yml

4
src/components/Onboarding/OnboardingBreadcrumb.js

@ -18,7 +18,7 @@ type Props = {
function OnboardingBreadcrumb(props: Props) { function OnboardingBreadcrumb(props: Props) {
const { onboarding } = props const { onboarding } = props
const { stepName, isGenuineFail } = onboarding const { stepName, genuine } = onboarding
const filteredSteps = onboarding.steps const filteredSteps = onboarding.steps
.filter(step => !step.external) .filter(step => !step.external)
@ -29,7 +29,7 @@ function OnboardingBreadcrumb(props: Props) {
return ( return (
<Breadcrumb <Breadcrumb
stepsErrors={isGenuineFail ? [genuineStepIndex] : undefined} stepsErrors={genuine.isGenuineFail ? [genuineStepIndex] : undefined}
currentStep={stepIndex} currentStep={stepIndex}
items={filteredSteps} items={filteredSteps}
/> />

5
src/components/Onboarding/OnboardingFooter.js

@ -22,14 +22,15 @@ type Props = {
t: T, t: T,
nextStep: () => void, nextStep: () => void,
prevStep: () => void, prevStep: () => void,
isContinueDisabled?: boolean,
} }
const OnboardingFooter = ({ t, nextStep, prevStep, ...props }: Props) => ( const OnboardingFooter = ({ t, nextStep, prevStep, isContinueDisabled, ...props }: Props) => (
<Wrapper {...props}> <Wrapper {...props}>
<Button small outline onClick={() => prevStep()}> <Button small outline onClick={() => prevStep()}>
{t('common:back')} {t('common:back')}
</Button> </Button>
<Button small primary onClick={() => nextStep()} ml="auto"> <Button disabled={isContinueDisabled} small primary onClick={() => nextStep()} ml="auto">
{t('common:continue')} {t('common:continue')}
</Button> </Button>
</Wrapper> </Wrapper>

14
src/components/Onboarding/index.js

@ -10,13 +10,7 @@ import type { T } from 'types/common'
import type { OnboardingState } from 'reducers/onboarding' import type { OnboardingState } from 'reducers/onboarding'
import { saveSettings } from 'actions/settings' import { saveSettings } from 'actions/settings'
import { import { nextStep, prevStep, jumpStep, updateGenuineCheck, isLedgerNano } from 'reducers/onboarding'
nextStep,
prevStep,
jumpStep,
setGenuineCheckFail,
isLedgerNano,
} from 'reducers/onboarding'
import { getCurrentDevice } from 'reducers/devices' import { getCurrentDevice } from 'reducers/devices'
// import { unlock } from 'reducers/application' // import { unlock } from 'reducers/application'
@ -80,9 +74,10 @@ export type StepProps = {
nextStep: Function, nextStep: Function,
jumpStep: Function, jumpStep: Function,
finish: Function, finish: Function,
saveSettings: Function,
// savePassword: Function, // savePassword: Function,
getDeviceInfo: Function, getDeviceInfo: Function,
setGenuineCheckFail: Function, updateGenuineCheck: Function,
isLedgerNano: Function, isLedgerNano: Function,
} }
@ -116,7 +111,7 @@ class Onboarding extends PureComponent<Props> {
const stepProps: StepProps = { const stepProps: StepProps = {
t, t,
onboarding, onboarding,
setGenuineCheckFail, updateGenuineCheck,
isLedgerNano, isLedgerNano,
prevStep, prevStep,
nextStep, nextStep,
@ -124,6 +119,7 @@ class Onboarding extends PureComponent<Props> {
finish: this.finish, finish: this.finish,
// savePassword: this.savePassword, // savePassword: this.savePassword,
getDeviceInfo: this.getDeviceInfo, getDeviceInfo: this.getDeviceInfo,
saveSettings,
} }
return ( return (

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

@ -1,7 +1,9 @@
// @flow // @flow
import React from 'react' import React, { PureComponent } from 'react'
import styled from 'styled-components' import styled from 'styled-components'
import { connect } from 'react-redux'
import { saveSettings } from 'actions/settings'
import Box from 'components/base/Box' import Box from 'components/base/Box'
import CheckBox from 'components/base/CheckBox' import CheckBox from 'components/base/CheckBox'
@ -10,8 +12,31 @@ import OnboardingFooter from '../OnboardingFooter'
import type { StepProps } from '..' import type { StepProps } from '..'
export default (props: StepProps) => { const mapDispatchToProps = { saveSettings }
const { nextStep, prevStep, t } = props
type State = {
analyticsToggle: boolean,
termsConditionsToggle: boolean,
}
class Analytics extends PureComponent<StepProps, State> {
state = {
analyticsToggle: false,
termsConditionsToggle: false,
}
handleAnalyticsToggle = (isChecked: boolean) => {
this.setState({ analyticsToggle: !this.state.analyticsToggle })
this.props.saveSettings({
shareAnalytics: isChecked,
})
}
handleTermsToggle = () => {
this.setState({ termsConditionsToggle: !this.state.termsConditionsToggle })
}
render() {
const { nextStep, prevStep, t } = this.props
const { analyticsToggle, termsConditionsToggle } = this.state
return ( return (
<Box sticky pt={150}> <Box sticky pt={150}>
<Box grow alignItems="center"> <Box grow alignItems="center">
@ -20,25 +45,25 @@ export default (props: StepProps) => {
<Box mt={5}> <Box mt={5}>
<Container> <Container>
<Box justify="center"> <Box justify="center" style={{ width: 450 }}>
<Box horizontal> <Box horizontal>
<AnalyticsTitle>{t('onboarding:analytics.shareDiagnostics.title')}</AnalyticsTitle> <AnalyticsTitle>{t('onboarding:analytics.shareAnalytics.title')}</AnalyticsTitle>
</Box> </Box>
<AnalyticsText>{t('onboarding:analytics.shareDiagnostics.desc')}</AnalyticsText> <AnalyticsText>{t('onboarding:analytics.shareAnalytics.desc')}</AnalyticsText>
</Box> </Box>
<Box alignItems="center" horizontal mx={5}> <Box alignItems="center" horizontal mx={5}>
<CheckBox isChecked={false} /> <CheckBox isChecked={analyticsToggle} onChange={this.handleAnalyticsToggle} />
</Box> </Box>
</Container> </Container>
<Container> <Container>
<Box justify="center"> <Box justify="center" style={{ width: 450 }}>
<Box horizontal> <Box horizontal>
<AnalyticsTitle>{t('onboarding:analytics.shareDiagnostics.title')}</AnalyticsTitle> <AnalyticsTitle>{t('onboarding:analytics.termsConditions.title')}</AnalyticsTitle>
</Box> </Box>
<AnalyticsText>{t('onboarding:analytics.shareDiagnostics.desc')}</AnalyticsText> <AnalyticsText>{t('onboarding:analytics.termsConditions.desc')}</AnalyticsText>
</Box> </Box>
<Box alignItems="center" horizontal mx={5}> <Box alignItems="center" horizontal mx={5}>
<CheckBox isChecked={false} /> <CheckBox isChecked={termsConditionsToggle} onChange={this.handleTermsToggle} />
</Box> </Box>
</Container> </Container>
</Box> </Box>
@ -50,10 +75,14 @@ export default (props: StepProps) => {
t={t} t={t}
nextStep={nextStep} nextStep={nextStep}
prevStep={prevStep} prevStep={prevStep}
isContinueDisabled={!termsConditionsToggle}
/> />
</Box> </Box>
) )
} }
}
export default connect(null, mapDispatchToProps)(Analytics)
export const AnalyticsText = styled(Box).attrs({ export const AnalyticsText = styled(Box).attrs({
ff: 'Open Sans|Regular', ff: 'Open Sans|Regular',

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

@ -1,13 +1,14 @@
// @flow // @flow
import React, { PureComponent, Fragment } from 'react' import React, { PureComponent, Fragment } from 'react'
import { shell } from 'electron'
import { connect } from 'react-redux' import { connect } from 'react-redux'
import styled from 'styled-components' import styled from 'styled-components'
import { radii } from 'styles/theme' import { radii } from 'styles/theme'
import type { T } from 'types/common' import type { T } from 'types/common'
import { setGenuineCheckFail } from 'reducers/onboarding' import { updateGenuineCheck } from 'reducers/onboarding'
import Box, { Card } from 'components/base/Box' import Box, { Card } from 'components/base/Box'
import Button from 'components/base/Button' import Button from 'components/base/Button'
@ -23,25 +24,25 @@ import { Title, Description, IconOptionRow } from '../helperComponents'
import type { StepProps } from '..' import type { StepProps } from '..'
import OnboardingFooter from '../OnboardingFooter' import OnboardingFooter from '../OnboardingFooter'
const mapDispatchToProps = { setGenuineCheckFail } const mapDispatchToProps = { updateGenuineCheck }
type State = { type State = {
pinStepPass: boolean | null,
phraseStepPass: boolean | null,
cachedPinStepButton: string, cachedPinStepButton: string,
cachedPhraseStepButton: string, cachedRecoveryStepButton: string,
isGenuineCheckModalOpened: boolean, isGenuineCheckModalOpened: boolean,
isDeviceGenuine: boolean,
} }
class GenuineCheck extends PureComponent<StepProps, State> { const INITIAL_STATE = {
state = {
pinStepPass: null,
phraseStepPass: null,
cachedPinStepButton: '', cachedPinStepButton: '',
cachedPhraseStepButton: '', cachedRecoveryStepButton: '',
isGenuineCheckModalOpened: false, isGenuineCheckModalOpened: false,
isDeviceGenuine: false, }
class GenuineCheck extends PureComponent<StepProps, State> {
state = {
...INITIAL_STATE,
cachedPinStepButton: this.props.onboarding.genuine.pinStepPass ? 'yes' : '',
cachedRecoveryStepButton: this.props.onboarding.genuine.recoveryStepPass ? 'yes' : '',
} }
getButtonLabel() { getButtonLabel() {
@ -61,15 +62,21 @@ class GenuineCheck extends PureComponent<StepProps, State> {
} }
handleButtonPass = (item: Object, step: string) => { handleButtonPass = (item: Object, step: string) => {
this.setState({ [`${step}`]: item.pass }) this.props.updateGenuineCheck({ [`${step}`]: item.pass })
if (step === 'pinStepPass') { if (step === 'pinStepPass') {
this.setState({ cachedPinStepButton: item.key }) this.setState({ cachedPinStepButton: item.key })
} else { } else {
this.setState({ cachedPhraseStepButton: item.key }) this.setState({ cachedRecoveryStepButton: item.key })
} }
if (!item.pass) { if (!item.pass) {
this.props.setGenuineCheckFail(true) this.setState(INITIAL_STATE)
this.props.updateGenuineCheck({
isGenuineFail: true,
recoveryStepPass: false,
pinStepPass: false,
isDeviceGenuine: false,
})
} }
} }
@ -79,15 +86,19 @@ class GenuineCheck extends PureComponent<StepProps, State> {
handleGenuineCheck = async isGenuine => { handleGenuineCheck = async isGenuine => {
await new Promise(r => setTimeout(r, 1e3)) // let's wait a bit before closing modal await new Promise(r => setTimeout(r, 1e3)) // let's wait a bit before closing modal
this.handleCloseGenuineCheckModal() this.handleCloseGenuineCheckModal()
this.setState({ isDeviceGenuine: isGenuine }) this.props.updateGenuineCheck({
isDeviceGenuine: isGenuine,
})
} }
redoGenuineCheck = () => { redoGenuineCheck = () => {
this.props.setGenuineCheckFail(false) this.props.updateGenuineCheck({ isGenuineFail: false })
} }
contactSupport = () => { contactSupport = () => {
console.log('contact support coming later') const contactSupportUrl =
'https://support.ledgerwallet.com/hc/en-us/requests/new?ticket_form_id=248165'
shell.openExternal(contactSupportUrl)
} }
renderGenuineFail = () => ( renderGenuineFail = () => (
@ -101,16 +112,10 @@ class GenuineCheck extends PureComponent<StepProps, State> {
render() { render() {
const { nextStep, prevStep, t, onboarding } = this.props const { nextStep, prevStep, t, onboarding } = this.props
const { const { genuine } = onboarding
pinStepPass, const { cachedPinStepButton, cachedRecoveryStepButton, isGenuineCheckModalOpened } = this.state
phraseStepPass,
cachedPinStepButton,
cachedPhraseStepButton,
isGenuineCheckModalOpened,
isDeviceGenuine,
} = this.state
if (onboarding.isGenuineFail) { if (genuine.isGenuineFail) {
return this.renderGenuineFail() return this.renderGenuineFail()
} }
@ -137,7 +142,7 @@ class GenuineCheck extends PureComponent<StepProps, State> {
</CardWrapper> </CardWrapper>
</Box> </Box>
<Box mt={5}> <Box mt={5}>
<CardWrapper isDisabled={!pinStepPass}> <CardWrapper isDisabled={!genuine.pinStepPass}>
<Box justify="center"> <Box justify="center">
<Box horizontal> <Box horizontal>
<IconOptionRow>2.</IconOptionRow> <IconOptionRow>2.</IconOptionRow>
@ -148,13 +153,13 @@ class GenuineCheck extends PureComponent<StepProps, State> {
<RadioGroup <RadioGroup
style={{ margin: '0 30px' }} style={{ margin: '0 30px' }}
items={this.getButtonLabel()} items={this.getButtonLabel()}
activeKey={cachedPhraseStepButton} activeKey={cachedRecoveryStepButton}
onChange={item => this.handleButtonPass(item, 'phraseStepPass')} onChange={item => this.handleButtonPass(item, 'recoveryStepPass')}
/> />
</CardWrapper> </CardWrapper>
</Box> </Box>
<Box mt={5}> <Box mt={5}>
<CardWrapper isDisabled={!phraseStepPass}> <CardWrapper isDisabled={!genuine.recoveryStepPass}>
<Box justify="center"> <Box justify="center">
<Box horizontal> <Box horizontal>
<IconOptionRow>3.</IconOptionRow> <IconOptionRow>3.</IconOptionRow>
@ -166,10 +171,10 @@ class GenuineCheck extends PureComponent<StepProps, State> {
<Button <Button
big big
primary primary
disabled={!phraseStepPass} disabled={!genuine.recoveryStepPass}
onClick={this.handleOpenGenuineCheckModal} onClick={this.handleOpenGenuineCheckModal}
> >
{isDeviceGenuine ? ( {genuine.isDeviceGenuine ? (
<Box horizontal align="center" flow={1}> <Box horizontal align="center" flow={1}>
<IconCheck size={16} /> <IconCheck size={16} />
<span>{t('onboarding:genuineCheck.buttons.tryAgain')}</span> <span>{t('onboarding:genuineCheck.buttons.tryAgain')}</span>
@ -189,6 +194,7 @@ class GenuineCheck extends PureComponent<StepProps, State> {
t={t} t={t}
nextStep={nextStep} nextStep={nextStep}
prevStep={prevStep} prevStep={prevStep}
isContinueDisabled={!genuine.isDeviceGenuine}
/> />
<GenuineCheckModal <GenuineCheckModal
isOpened={isGenuineCheckModalOpened} isOpened={isGenuineCheckModalOpened}

15
src/components/Onboarding/steps/Init.js

@ -2,10 +2,15 @@
import React from 'react' import React from 'react'
import { shell } from 'electron' import { shell } from 'electron'
import { colors } from 'styles/theme'
import styled from 'styled-components' import styled from 'styled-components'
import Box, { Card } from 'components/base/Box' import Box, { Card } from 'components/base/Box'
import IconUser from 'icons/User' import IconUser from 'icons/User'
import IconAdd from 'icons/Plus'
import IconRecover from 'icons/Recover'
import IconCheck from 'icons/Check'
import IconExternalLink from 'icons/ExternalLink'
import IconChevronRight from 'icons/ChevronRight' import IconChevronRight from 'icons/ChevronRight'
import { Title } from '../helperComponents' import { Title } from '../helperComponents'
@ -16,28 +21,28 @@ export default (props: StepProps) => {
const optionCards = [ const optionCards = [
{ {
key: 'newDevice', key: 'newDevice',
icon: <IconUser size={22} />, icon: <IconAdd size={16} />,
title: t('onboarding:init.newDevice.title'), title: t('onboarding:init.newDevice.title'),
desc: t('onboarding:init.newDevice.desc'), desc: t('onboarding:init.newDevice.desc'),
onClick: () => nextStep(), onClick: () => nextStep(),
}, },
{ {
key: 'restoreDevice', key: 'restoreDevice',
icon: <IconUser size={22} />, icon: <IconRecover size={16} />,
title: t('onboarding:init.restoreDevice.title'), title: t('onboarding:init.restoreDevice.title'),
desc: t('onboarding:init.restoreDevice.desc'), desc: t('onboarding:init.restoreDevice.desc'),
onClick: () => jumpStep('choosePIN'), onClick: () => jumpStep('choosePIN'),
}, },
{ {
key: 'initializedDevice', key: 'initializedDevice',
icon: <IconUser size={22} />, icon: <IconCheck size={16} />,
title: t('onboarding:init.initializedDevice.title'), title: t('onboarding:init.initializedDevice.title'),
desc: t('onboarding:init.initializedDevice.desc'), desc: t('onboarding:init.initializedDevice.desc'),
onClick: () => jumpStep('choosePIN'), onClick: () => jumpStep('choosePIN'),
}, },
{ {
key: 'noDevice', key: 'noDevice',
icon: <IconUser size={22} />, icon: <IconExternalLink size={16} />,
title: t('onboarding:init.noDevice.title'), title: t('onboarding:init.noDevice.title'),
desc: t('onboarding:init.noDevice.desc'), desc: t('onboarding:init.noDevice.desc'),
onClick: () => shell.openExternal('https://www.ledger.fr/'), onClick: () => shell.openExternal('https://www.ledger.fr/'),
@ -82,7 +87,7 @@ export function OptionFlowCard({ card }: { card: CardType }) {
}} }}
onClick={onClick} onClick={onClick}
> >
<Box justify="center" color="grey" style={{ width: 50 }}> <Box justify="center" style={{ width: 50, color: colors.wallet }}>
{icon} {icon}
</Box> </Box>
<Box ff="Open Sans|Regular" justify="center" fontSize={4} grow> <Box ff="Open Sans|Regular" justify="center" fontSize={4} grow>

16
src/icons/Recover.js

@ -0,0 +1,16 @@
// @flow
import React from 'react'
const path = (
<path
fill="currentColor"
d="M15.65 7.985c.008 4.27-3.475 7.762-7.745 7.765a7.722 7.722 0 0 1-5.2-1.998.375.375 0 0 1-.013-.544l.53-.53a.376.376 0 0 1 .517-.012A6.228 6.228 0 0 0 7.9 14.25 6.247 6.247 0 0 0 14.15 8 6.247 6.247 0 0 0 7.9 1.75a6.23 6.23 0 0 0-4.434 1.845L5 5.108a.375.375 0 0 1-.264.642H.725a.375.375 0 0 1-.375-.375V1.42c0-.333.402-.5.638-.267l1.41 1.39A7.75 7.75 0 0 1 15.65 7.985zm-5.22 2.818l.44-.606a.375.375 0 0 0-.082-.524L8.65 8.118V3.625a.375.375 0 0 0-.375-.375h-.75a.375.375 0 0 0-.375.375v5.257l2.755 2.004a.375.375 0 0 0 .524-.083z"
/>
)
export default ({ size, ...p }: { size: number }) => (
<svg viewBox="0 0 16 16" height={size} width={size} {...p}>
{path}
</svg>
)

21
src/reducers/onboarding.js

@ -17,14 +17,24 @@ export type OnboardingState = {
stepIndex: number, stepIndex: number,
stepName: string, // TODO: specify that the string comes from Steps type stepName: string, // TODO: specify that the string comes from Steps type
steps: Step[], steps: Step[],
genuine: {
pinStepPass: boolean,
recoveryStepPass: boolean,
isGenuineFail: boolean, isGenuineFail: boolean,
isDeviceGenuine: boolean,
},
isLedgerNano: boolean, isLedgerNano: boolean,
} }
const state: OnboardingState = { const state: OnboardingState = {
stepIndex: 0, stepIndex: 0,
stepName: 'start', stepName: 'start',
genuine: {
pinStepPass: false,
recoveryStepPass: false,
isGenuineFail: false, isGenuineFail: false,
isDeviceGenuine: false,
},
isLedgerNano: true, isLedgerNano: true,
steps: [ steps: [
{ {
@ -143,10 +153,15 @@ const handlers = {
const index = state.steps.indexOf(step) const index = state.steps.indexOf(step)
return { ...state, stepName: step.name, stepIndex: index } return { ...state, stepName: step.name, stepIndex: index }
}, },
ONBOARDING_SET_GENUINE_CHECK_FAIL: (state, { payload: isGenuineFail }) => ({
UPDATE_GENUINE_CHECK: (state, { payload: obj }) => ({
...state, ...state,
isGenuineFail, genuine: {
...state.genuine,
...obj,
},
}), }),
ONBOARDING_SET_DEVICE_TYPE: (state, { payload: isLedgerNano }) => ({ ONBOARDING_SET_DEVICE_TYPE: (state, { payload: isLedgerNano }) => ({
...state, ...state,
isLedgerNano, isLedgerNano,
@ -158,5 +173,5 @@ export default handleActions(handlers, state)
export const nextStep = createAction('ONBOARDING_NEXT_STEP') export const nextStep = createAction('ONBOARDING_NEXT_STEP')
export const prevStep = createAction('ONBOARDING_PREV_STEP') export const prevStep = createAction('ONBOARDING_PREV_STEP')
export const jumpStep = createAction('ONBOARDING_JUMP_STEP') export const jumpStep = createAction('ONBOARDING_JUMP_STEP')
export const setGenuineCheckFail = createAction('ONBOARDING_SET_GENUINE_CHECK_FAIL') export const updateGenuineCheck = createAction('UPDATE_GENUINE_CHECK')
export const isLedgerNano = createAction('ONBOARDING_SET_DEVICE_TYPE') export const isLedgerNano = createAction('ONBOARDING_SET_DEVICE_TYPE')

2
src/reducers/settings.js

@ -30,6 +30,7 @@ export type SettingsState = {
}, },
region: string, region: string,
developerMode: boolean, developerMode: boolean,
shareAnalytics: boolean,
} }
/* have to check if available for all OS */ /* have to check if available for all OS */
@ -69,6 +70,7 @@ const state: SettingsState = {
region, region,
developerMode: false, developerMode: false,
loaded: false, loaded: false,
shareAnalytics: false,
} }
function asCryptoCurrency(c: Currency): ?CryptoCurrency { function asCryptoCurrency(c: Currency): ?CryptoCurrency {

8
static/i18n/en/onboarding.yml

@ -82,12 +82,12 @@ setPassword:
analytics: analytics:
title: Help Ledger to improve its products and services title: Help Ledger to improve its products and services
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 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
shareDiagnostics: shareAnalytics:
title: Share analytics
desc: Help Ledger improve its products and services by automatically sending diagnostics and usage data.
shareData:
title: Share analytics title: Share analytics
desc: Help Ledger improve its products and services by automatically sending diagnostics and usage data. desc: Help Ledger improve its products and services by automatically sending diagnostics and usage data.
termsConditions:
title: Terms and Conditions
desc: Please accept terms and conditions to proceed
finish: finish:
title: This is the title of the screen. 1 line is the maximum 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 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