diff --git a/src/components/Onboarding/index.js b/src/components/Onboarding/index.js index 557567bc..e511db45 100644 --- a/src/components/Onboarding/index.js +++ b/src/components/Onboarding/index.js @@ -19,7 +19,7 @@ import { prevStep, jumpStep, updateGenuineCheck, - isLedgerNano, + deviceModelId, flowType, relaunchOnboarding, onboardingRelaunchedSelector, @@ -102,7 +102,7 @@ export type StepProps = { getDeviceInfo: Function, updateGenuineCheck: Function, openModal: Function, - isLedgerNano: Function, + deviceModelId: Function, flowType: Function, } @@ -171,7 +171,7 @@ class Onboarding extends PureComponent { settings, updateGenuineCheck, openModal, - isLedgerNano, + deviceModelId, flowType, prevStep, nextStep, diff --git a/src/components/Onboarding/steps/Analytics.js b/src/components/Onboarding/steps/Analytics.js index 6e1a01d0..ca032261 100644 --- a/src/components/Onboarding/steps/Analytics.js +++ b/src/components/Onboarding/steps/Analytics.js @@ -3,6 +3,8 @@ import React, { PureComponent } from 'react' import styled from 'styled-components' import { connect } from 'react-redux' +import { getDeviceModel } from '@ledgerhq/devices' + import { saveSettings } from 'actions/settings' import Box from 'components/base/Box' import Switch from 'components/base/Switch' @@ -67,13 +69,15 @@ class Analytics extends PureComponent { const { nextStep, t, onboarding } = this.props const { analyticsToggle, sentryLogsToggle } = this.state + const model = getDeviceModel(onboarding.deviceModelId) + return ( {t('onboarding.analytics.title')} diff --git a/src/components/Onboarding/steps/Finish.js b/src/components/Onboarding/steps/Finish.js index cde25867..925317cf 100644 --- a/src/components/Onboarding/steps/Finish.js +++ b/src/components/Onboarding/steps/Finish.js @@ -3,6 +3,8 @@ import React, { Component } from 'react' import { openURL } from 'helpers/linking' import styled from 'styled-components' +import { getDeviceModel } from '@ledgerhq/devices' + import { i } from 'helpers/staticPath' import { urls } from 'config/urls' @@ -66,13 +68,16 @@ export default class Finish extends Component { render() { const { finish, t, onboarding } = this.props const { emit } = this.state + + const model = getDeviceModel(onboarding.deviceModelId) + return ( diff --git a/src/components/Onboarding/steps/GenuineCheck/GenuineCheckErrorPage.js b/src/components/Onboarding/steps/GenuineCheck/GenuineCheckErrorPage.js index 65518f46..9afa0f64 100644 --- a/src/components/Onboarding/steps/GenuineCheck/GenuineCheckErrorPage.js +++ b/src/components/Onboarding/steps/GenuineCheck/GenuineCheckErrorPage.js @@ -11,9 +11,21 @@ import Box from 'components/base/Box' import Button from 'components/base/Button' import ExternalLinkButton from 'components/base/ExternalLinkButton' import TrackPage from 'analytics/TrackPage' +import { getDeviceModel } from '@ledgerhq/devices' import { Title, Description, OnboardingFooterWrapper } from '../../helperComponents' +const Img = ({ type }: { type: string }) => { + switch (type) { + case 'blue': + return + case 'nanoX': + return + default: + return + } +} + type Props = { t: T, redoGenuineCheck: () => void, @@ -23,12 +35,15 @@ type Props = { class GenuineCheckErrorPage extends PureComponent { trackErrorPage = (page: string) => { const { onboarding } = this.props + + const model = getDeviceModel(onboarding.deviceModelId) + return ( ) } @@ -59,11 +74,7 @@ class GenuineCheckErrorPage extends PureComponent { )} - {onboarding.isLedgerNano ? ( - - ) : ( - - )} + ) diff --git a/src/components/Onboarding/steps/GenuineCheck/GenuineCheckUnavailable.js b/src/components/Onboarding/steps/GenuineCheck/GenuineCheckUnavailable.js index 727d59f3..0af9fe65 100644 --- a/src/components/Onboarding/steps/GenuineCheck/GenuineCheckUnavailable.js +++ b/src/components/Onboarding/steps/GenuineCheck/GenuineCheckUnavailable.js @@ -1,8 +1,9 @@ // @flow import React from 'react' -import { colors } from 'styles/theme' +import { getDeviceModel } from '@ledgerhq/devices' +import { colors } from 'styles/theme' import type { T } from 'types/common' import type { OnboardingState } from 'reducers/onboarding' @@ -54,6 +55,8 @@ export function GenuineCheckUnavailableMessage({ t: T, onboarding: OnboardingState, }) { + const model = getDeviceModel(onboarding.deviceModelId) + return ( diff --git a/src/components/Onboarding/steps/GenuineCheck/index.js b/src/components/Onboarding/steps/GenuineCheck/index.js index b8ab0de8..c1754965 100644 --- a/src/components/Onboarding/steps/GenuineCheck/index.js +++ b/src/components/Onboarding/steps/GenuineCheck/index.js @@ -3,8 +3,9 @@ import React, { PureComponent } from 'react' import { connect } from 'react-redux' import styled from 'styled-components' -import { colors } from 'styles/theme' +import { getDeviceModel } from '@ledgerhq/devices' +import { colors } from 'styles/theme' import { updateGenuineCheck } from 'reducers/onboarding' import Box from 'components/base/Box' @@ -165,13 +166,15 @@ class GenuineCheck extends PureComponent { return this.renderGenuineFail() } + const model = getDeviceModel(onboarding.deviceModelId) + return ( {t('onboarding.genuineCheck.title')} diff --git a/src/components/Onboarding/steps/SelectDevice.js b/src/components/Onboarding/steps/SelectDevice.js index c65e253b..c34b84ea 100644 --- a/src/components/Onboarding/steps/SelectDevice.js +++ b/src/components/Onboarding/steps/SelectDevice.js @@ -3,11 +3,14 @@ import React, { PureComponent } from 'react' import styled from 'styled-components' import { connect } from 'react-redux' +import { getDeviceModel } from '@ledgerhq/devices' + import { i } from 'helpers/staticPath' import { rgba } from 'styles/helpers' -import { isLedgerNano } from 'reducers/onboarding' +import { deviceModelId } from 'reducers/onboarding' +import type { DeviceModelId } from 'reducers/onboarding' import Box from 'components/base/Box' import TrackPage from 'analytics/TrackPage' @@ -19,11 +22,11 @@ import OnboardingFooter from '../OnboardingFooter' import type { StepProps } from '..' -const mapDispatchToProps = { isLedgerNano } +const mapDispatchToProps = { deviceModelId } class SelectDevice extends PureComponent { - handleIsLedgerNano = (isLedgerNano: boolean) => { - this.props.isLedgerNano(isLedgerNano) + handleDeviceModelId = (deviceModelId: DeviceModelId) => { + this.props.deviceModelId(deviceModelId) } handleContinue = () => { @@ -46,24 +49,34 @@ class SelectDevice extends PureComponent { this.handleIsLedgerNano(true)} + isActive={onboarding.deviceModelId === 'nanoX'} + onClick={() => this.handleDeviceModelId('nanoX')} + > + {onboarding.deviceModelId === 'nanoX' && } + + + + {getDeviceModel('nanoX').productName} + + this.handleDeviceModelId('nanoS')} > - {onboarding.isLedgerNano && } + {onboarding.deviceModelId === 'nanoS' && } - + - {t('onboarding.selectDevice.ledgerNanoCard.title')} + {getDeviceModel('nanoS').productName} this.handleIsLedgerNano(false)} + isActive={onboarding.deviceModelId === 'blue'} + onClick={() => this.handleDeviceModelId('blue')} > - {!onboarding.isLedgerNano && onboarding.isLedgerNano !== null && } + {onboarding.deviceModelId === 'blue' && } - {t('onboarding.selectDevice.ledgerBlueCard.title')} + {getDeviceModel('blue').productName} @@ -73,7 +86,7 @@ class SelectDevice extends PureComponent { t={t} nextStep={this.handleContinue} prevStep={() => jumpStep('init')} - isContinueDisabled={onboarding.isLedgerNano === null} + isContinueDisabled={onboarding.deviceModelId === ''} /> ) diff --git a/src/components/Onboarding/steps/SelectPIN/SelectPINRestoreNanoX.js b/src/components/Onboarding/steps/SelectPIN/SelectPINRestoreNanoX.js new file mode 100644 index 00000000..997bb4ae --- /dev/null +++ b/src/components/Onboarding/steps/SelectPIN/SelectPINRestoreNanoX.js @@ -0,0 +1,82 @@ +// @flow +import React, { PureComponent } from 'react' +import { translate } from 'react-i18next' +import { colors } from 'styles/theme' +import { i } from 'helpers/staticPath' + +import Box from 'components/base/Box' + +import type { T } from 'types/common' + +import IconChevronRight from 'icons/ChevronRight' + +import { IconOptionRow, DisclaimerBox, OptionRow, Inner } from '../../helperComponents' + +type Props = { + t: T, +} + +class SelectPINrestoreNanoX extends PureComponent { + render() { + const { t } = this.props + + const stepsLedgerNano = [ + { + key: 'step1', + icon: {'1.'}, + desc: t('onboarding.selectPIN.restore.instructions.nanoX.step1'), + }, + { + key: 'step2', + icon: {'2.'}, + desc: t('onboarding.selectPIN.restore.instructions.nanoX.step2'), + }, + { + key: 'step3', + icon: {'3.'}, + desc: t('onboarding.selectPIN.restore.instructions.nanoX.step3'), + }, + { + key: 'step4', + icon: {'4.'}, + desc: t('onboarding.selectPIN.restore.instructions.nanoX.step4'), + }, + { + key: 'step5', + icon: {'5.'}, + desc: t('onboarding.selectPIN.restore.instructions.nanoX.step5'), + }, + ] + const disclaimerNotes = [ + { + key: 'note1', + icon: , + desc: t('onboarding.selectPIN.disclaimer.note1'), + }, + { + key: 'note2', + icon: , + desc: t('onboarding.selectPIN.disclaimer.note2'), + }, + { + key: 'note3', + icon: , + desc: t('onboarding.selectPIN.disclaimer.note3'), + }, + ] + + return ( + + + + + {stepsLedgerNano.map(step => )} + + + + + ) + } +} + +export default translate()(SelectPINrestoreNanoX) diff --git a/src/components/Onboarding/steps/SelectPIN/SelectPINnanoX.js b/src/components/Onboarding/steps/SelectPIN/SelectPINnanoX.js new file mode 100644 index 00000000..600ef06d --- /dev/null +++ b/src/components/Onboarding/steps/SelectPIN/SelectPINnanoX.js @@ -0,0 +1,81 @@ +// @flow +import React, { PureComponent } from 'react' +import { translate, Trans } from 'react-i18next' +import { colors } from 'styles/theme' +import { i } from 'helpers/staticPath' + +import Box from 'components/base/Box' + +import type { T } from 'types/common' + +import IconChevronRight from 'icons/ChevronRight' + +import { IconOptionRow, DisclaimerBox, OptionRow, Inner } from '../../helperComponents' + +type Props = { + t: T, +} + +class SelectPINnanoX extends PureComponent { + render() { + const { t } = this.props + + const stepsLedgerNano = [ + { + key: 'step1', + icon: {'1.'}, + desc: t('onboarding.selectPIN.initialize.instructions.nanoX.step1'), + }, + { + key: 'step2', + icon: {'2.'}, + desc: ( + + + + ), + }, + { + key: 'step3', + icon: {'3.'}, + desc: t('onboarding.selectPIN.initialize.instructions.nanoX.step3'), + }, + { + key: 'step4', + icon: {'4.'}, + desc: t('onboarding.selectPIN.initialize.instructions.nanoX.step4'), + }, + ] + const disclaimerNotes = [ + { + key: 'note1', + icon: , + desc: t('onboarding.selectPIN.disclaimer.note1'), + }, + { + key: 'note2', + icon: , + desc: t('onboarding.selectPIN.disclaimer.note2'), + }, + { + key: 'note3', + icon: , + desc: t('onboarding.selectPIN.disclaimer.note3'), + }, + ] + + return ( + + + + + {stepsLedgerNano.map(step => )} + + + + + ) + } +} + +export default translate()(SelectPINnanoX) diff --git a/src/components/Onboarding/steps/SelectPIN/index.js b/src/components/Onboarding/steps/SelectPIN/index.js index 38370eb2..1ad65fad 100644 --- a/src/components/Onboarding/steps/SelectPIN/index.js +++ b/src/components/Onboarding/steps/SelectPIN/index.js @@ -1,23 +1,39 @@ // @flow import React from 'react' +import { getDeviceModel } from '@ledgerhq/devices' import Box from 'components/base/Box' import TrackPage from 'analytics/TrackPage' +import type { DeviceModelId } from 'reducers/onboarding' import GrowScroll from 'components/base/GrowScroll' import { Title, FixedTopContainer } from '../../helperComponents' import OnboardingFooter from '../../OnboardingFooter' import SelectPINnano from './SelectPINnano' import SelectPINblue from './SelectPINblue' +import SelectPINnanoX from './SelectPINnanoX' import SelectPINrestoreNano from './SelectPINrestoreNano' +import SelectPINRestoreNanoX from './SelectPINRestoreNanoX' import SelectPINrestoreBlue from './SelectPINrestoreBlue' - import type { StepProps } from '../..' +const SelectPin = ({ modelId, restore = false }: { modelId: DeviceModelId, restore?: boolean }) => { + switch (modelId) { + case 'nanoX': + return restore ? : + case 'blue': + return restore ? : + default: + return restore ? : + } +} + export default (props: StepProps) => { const { nextStep, prevStep, t, onboarding } = props + const model = getDeviceModel(onboarding.deviceModelId) + return ( @@ -25,20 +41,20 @@ export default (props: StepProps) => { category="Onboarding" name="Choose PIN" flowType={onboarding.flowType} - deviceType={onboarding.isLedgerNano ? 'Nano S' : 'Blue'} + deviceType={model.productName} /> {onboarding.flowType === 'restoreDevice' ? ( {t('onboarding.selectPIN.restore.title')} - {onboarding.isLedgerNano ? : } + ) : ( {t('onboarding.selectPIN.initialize.title')} - {onboarding.isLedgerNano ? : } + )} diff --git a/src/components/Onboarding/steps/SetPassword.js b/src/components/Onboarding/steps/SetPassword.js index 06efaca9..c356ad11 100644 --- a/src/components/Onboarding/steps/SetPassword.js +++ b/src/components/Onboarding/steps/SetPassword.js @@ -2,6 +2,8 @@ import React, { PureComponent, Fragment } from 'react' import { connect } from 'react-redux' +import { getDeviceModel } from '@ledgerhq/devices' + import { colors } from 'styles/theme' import db from 'helpers/db' @@ -105,7 +107,7 @@ class SetPassword extends PureComponent { category="Onboarding" name="Set Password" flowType={onboarding.flowType} - deviceType={onboarding.isLedgerNano ? 'Nano S' : 'Blue'} + deviceType={getDeviceModel(onboarding.deviceModelId).productName} /> diff --git a/src/components/Onboarding/steps/WriteSeed/WriteSeedRestore.js b/src/components/Onboarding/steps/WriteSeed/WriteSeedRestore.js index a193353e..bbe8063f 100644 --- a/src/components/Onboarding/steps/WriteSeed/WriteSeedRestore.js +++ b/src/components/Onboarding/steps/WriteSeed/WriteSeedRestore.js @@ -123,7 +123,7 @@ class WriteSeedRestore extends PureComponent { - {onboarding.isLedgerNano ? ( + {onboarding.deviceModelId === 'nanoS' ? ( {stepsNano.map(step => )} diff --git a/src/components/Onboarding/steps/WriteSeed/index.js b/src/components/Onboarding/steps/WriteSeed/index.js index bcae0463..24bb5c7a 100644 --- a/src/components/Onboarding/steps/WriteSeed/index.js +++ b/src/components/Onboarding/steps/WriteSeed/index.js @@ -1,7 +1,7 @@ // @flow import React from 'react' - +import { getDeviceModel } from '@ledgerhq/devices' import Box from 'components/base/Box' import TrackPage from 'analytics/TrackPage' @@ -17,6 +17,8 @@ import type { StepProps } from '../..' export default (props: StepProps) => { const { nextStep, prevStep, t, onboarding } = props + const model = getDeviceModel(onboarding.deviceModelId) + return ( @@ -24,12 +26,12 @@ export default (props: StepProps) => { category="Onboarding" name="Recovery Phase" flowType={onboarding.flowType} - deviceType={onboarding.isLedgerNano ? 'Nano S' : 'Blue'} + deviceType={model.productName} /> {onboarding.flowType === 'restoreDevice' ? ( - ) : onboarding.isLedgerNano ? ( + ) : onboarding.deviceModelId === 'nanoS' ? ( ) : ( diff --git a/src/reducers/onboarding.js b/src/reducers/onboarding.js index 14911cd5..3e4cea75 100644 --- a/src/reducers/onboarding.js +++ b/src/reducers/onboarding.js @@ -15,6 +15,8 @@ type Step = { }, } +export type DeviceModelId = 'nanoX' | 'nanoS' | 'blue' | '' + export type OnboardingState = { stepIndex: number, stepName: string, // TODO: specify that the string comes from Steps type @@ -27,7 +29,7 @@ export type OnboardingState = { genuineCheckUnavailable: ?Error, displayErrorScreen: boolean, }, - isLedgerNano: boolean | null, + deviceModelId: DeviceModelId, flowType: string, onboardingRelaunched?: boolean, } @@ -43,7 +45,7 @@ const initialState: OnboardingState = { genuineCheckUnavailable: null, displayErrorScreen: false, }, - isLedgerNano: null, + deviceModelId: '', flowType: '', onboardingRelaunched: false, steps: [ @@ -167,9 +169,9 @@ const handlers = { ...state, flowType, }), - ONBOARDING_SET_DEVICE_TYPE: (state: OnboardingState, { payload: isLedgerNano }) => ({ + ONBOARDING_SET_DEVICE_TYPE: (state: OnboardingState, { payload: deviceModelId }) => ({ ...state, - isLedgerNano, + deviceModelId, }), ONBOARDING_RELAUNCH: (state: OnboardingState, { payload: onboardingRelaunched }) => ({ ...initialState, @@ -187,5 +189,5 @@ export const nextStep = createAction('ONBOARDING_NEXT_STEP') export const prevStep = createAction('ONBOARDING_PREV_STEP') export const jumpStep = createAction('ONBOARDING_JUMP_STEP') export const updateGenuineCheck = createAction('UPDATE_GENUINE_CHECK') -export const isLedgerNano = createAction('ONBOARDING_SET_DEVICE_TYPE') +export const deviceModelId = createAction('ONBOARDING_SET_DEVICE_TYPE') export const flowType = createAction('ONBOARDING_SET_FLOW_TYPE') diff --git a/static/i18n/en/app.json b/static/i18n/en/app.json index e3dbe1b8..d4c82529 100644 --- a/static/i18n/en/app.json +++ b/static/i18n/en/app.json @@ -563,13 +563,7 @@ } }, "selectDevice": { - "title": "Select your device", - "ledgerNanoCard": { - "title": "Ledger Nano S" - }, - "ledgerBlueCard": { - "title": "Ledger Blue" - } + "title": "Select your device" }, "selectPIN": { "disclaimer": { @@ -586,6 +580,12 @@ "step3": "Press the left or right button to select a digit. Press both buttons to validate.", "step4": "Select ✓ to confirm your PIN code. Select ⬅ to erase a digit." }, + "nanoX": { + "step1": "Connect the Ledger Nano X to your computer.", + "step2": "Press both buttons to choose Set up as new device.", + "step3": "Press the left or right button to select a digit.", + "step4": "Select ✓ to confirm your PIN code. Select ⬅ to erase a digit." + }, "blue": { "step1": "Connect the Ledger Blue to your computer.", "step2": "Tap on <1><0>Configure as new device.", @@ -602,6 +602,13 @@ "step3": "Press the left or right button to select a digit. Press both buttons to validate.", "step4": "Select ✓ to confirm your PIN code. Select ⬅ to erase a digit." }, + "nanoX": { + "step1": "Connect your Ledger Nano X to your computer.", + "step2": "Press both buttons as instructed on your Ledger Nano X screen.", + "step3": "Press the left button to cancel Configure as new device.", + "step4": "Press the right button to select Restore configuration.", + "step5": "Choose a PIN code between 4 and 8 digits long." + }, "blue": { "step1": "Connect the Ledger Blue to your computer.", "step2": "Tap on <1><0>Restore configuration.", diff --git a/static/images/ledger-nano-onb.svg b/static/images/ledger-nano-s-onb.svg similarity index 100% rename from static/images/ledger-nano-onb.svg rename to static/images/ledger-nano-s-onb.svg diff --git a/static/images/ledger-nano-x-onb.svg b/static/images/ledger-nano-x-onb.svg new file mode 100644 index 00000000..1b9d4405 --- /dev/null +++ b/static/images/ledger-nano-x-onb.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/static/images/nano-x-error-onb.svg b/static/images/nano-x-error-onb.svg new file mode 100644 index 00000000..341a227b --- /dev/null +++ b/static/images/nano-x-error-onb.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/select-pin-nano-x-onb.svg b/static/images/select-pin-nano-x-onb.svg new file mode 100644 index 00000000..7693c90e --- /dev/null +++ b/static/images/select-pin-nano-x-onb.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + +