From 9b1e24c9ad67cc2d8fa82bf84d702ee88880161d Mon Sep 17 00:00:00 2001 From: meriadec Date: Mon, 7 May 2018 16:56:00 +0200 Subject: [PATCH 1/3] Skeleton for the new onboarding --- .../Onboarding/OnboardingBreadcrumb.js | 32 +++++ src/components/Onboarding/index.js | 118 ++++++------------ src/components/Onboarding/steps/Init.js | 18 +++ src/components/Onboarding/steps/UserChoice.js | 17 +++ src/reducers/index.js | 4 + src/reducers/onboarding.js | 84 +++++++++++++ 6 files changed, 193 insertions(+), 80 deletions(-) create mode 100644 src/components/Onboarding/OnboardingBreadcrumb.js create mode 100644 src/components/Onboarding/steps/Init.js create mode 100644 src/components/Onboarding/steps/UserChoice.js create mode 100644 src/reducers/onboarding.js diff --git a/src/components/Onboarding/OnboardingBreadcrumb.js b/src/components/Onboarding/OnboardingBreadcrumb.js new file mode 100644 index 00000000..8e789699 --- /dev/null +++ b/src/components/Onboarding/OnboardingBreadcrumb.js @@ -0,0 +1,32 @@ +// @flow + +import React from 'react' +import { connect } from 'react-redux' +import findIndex from 'lodash/findIndex' + +import type { OnboardingState } from 'reducers/onboarding' + +import Breadcrumb from 'components/Breadcrumb' + +const mapStateToProps = state => ({ + onboarding: state.onboarding, +}) + +type Props = { + onboarding: OnboardingState, +} + +function OnboardingBreadcrumb(props: Props) { + const { onboarding } = props + const { stepName } = 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 +} + +export default connect(mapStateToProps)(OnboardingBreadcrumb) diff --git a/src/components/Onboarding/index.js b/src/components/Onboarding/index.js index 0a429687..8ed00c46 100644 --- a/src/components/Onboarding/index.js +++ b/src/components/Onboarding/index.js @@ -7,108 +7,83 @@ import { connect } from 'react-redux' import styled from 'styled-components' 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 Box from 'components/base/Box' -import Button from 'components/base/Button' - -const STEPS = [ - { - title: ({ t }) => t('onboarding:step1.title'), - render: ({ t }: StepProps) => {t('onboarding:step1.greetings')}, - }, - { - title: ({ t }) => t('onboarding:step2.title'), - render: ({ t }: StepProps) => {t('onboarding:step2.greetings')}, - }, - { - title: ({ t }) => t('onboarding:step3.title'), - render: ({ t }: StepProps) => ( - - {t('onboarding:step3.greetings')} - - {'step 3 image'} - - {t('onboarding:step3.description')} - - ), - }, - { - title: ({ t }) => t('onboarding:step4.title'), - render: ({ t }: StepProps) => {t('onboarding:step4.greetings')}, - }, -] + +import OnboardingBreadcrumb from './OnboardingBreadcrumb' +import InitStep from './steps/Init' +import UserChoice from './steps/UserChoice' + +const STEPS = { + init: InitStep, + userChoice: UserChoice, +} const mapStateToProps = state => ({ hasCompletedOnboarding: state.settings.hasCompletedOnboarding, + onboarding: state.onboarding, }) const mapDispatchToProps = { saveSettings, + nextStep, + prevStep, + jumpStep, } type Props = { t: T, hasCompletedOnboarding: boolean, saveSettings: Function, + onboarding: OnboardingState, + prevStep: Function, + nextStep: Function, + jumpStep: Function, } -type StepProps = { +export type StepProps = { t: T, + prevStep: Function, + nextStep: Function, + jumpStep: Function, + finish: Function, } -type State = { - stepIndex: number, -} - -class Onboarding extends PureComponent { - state = { - stepIndex: 0, - } - - prev = () => this.setState({ stepIndex: Math.max(0, this.state.stepIndex - 1) }) - next = () => this.setState({ stepIndex: Math.min(STEPS.length - 1, this.state.stepIndex + 1) }) +class Onboarding extends PureComponent { finish = () => this.props.saveSettings({ hasCompletedOnboarding: true }) render() { - const { hasCompletedOnboarding, t } = this.props - const { stepIndex } = this.state + const { hasCompletedOnboarding, onboarding, prevStep, nextStep, jumpStep, t } = this.props if (hasCompletedOnboarding) { return null } - const step = STEPS[stepIndex] + const StepComponent = STEPS[onboarding.stepName] + const step = onboarding.steps[onboarding.stepIndex] - if (!step) { + if (!StepComponent || !step) { + console.warn(`You reached an impossible onboarding step.`) // eslint-disable-line return null } - const stepProps = { + const stepProps: StepProps = { t, + onboarding, + prevStep, + nextStep, + jumpStep, + finish: this.finish, } return ( - - - - {stepIndex === STEPS.length - 1 ? ( - - ) : ( - - )} - - {step.title(stepProps)} - {step.render(stepProps)} - + {step.options.showBreadcrumb && } + ) } @@ -116,9 +91,6 @@ class Onboarding extends PureComponent { const Container = styled(Box).attrs({ bg: 'white', - p: 6, - align: 'center', - justify: 'center', })` position: fixed; top: 0; @@ -128,18 +100,4 @@ const Container = styled(Box).attrs({ z-index: 100; ` -const Inner = styled(Box).attrs({ - bg: 'lightGraphite', - p: 4, -})` - border: 1px solid rgba(0, 0, 0, 0.1); - height: 400px; - width: 400px; - border-radius: 3px; -` - -const StepTitle = styled(Box).attrs({ - fontSize: 8, -})`` - export default compose(connect(mapStateToProps, mapDispatchToProps), translate())(Onboarding) diff --git a/src/components/Onboarding/steps/Init.js b/src/components/Onboarding/steps/Init.js new file mode 100644 index 00000000..76ae3cb3 --- /dev/null +++ b/src/components/Onboarding/steps/Init.js @@ -0,0 +1,18 @@ +// @flow + +import React from 'react' + +import Button from 'components/base/Button' +import Box from 'components/base/Box' + +import type { StepProps } from '..' + +export default (props: StepProps) => { + const { nextStep } = props + return ( + + hey im step init + + + ) +} diff --git a/src/components/Onboarding/steps/UserChoice.js b/src/components/Onboarding/steps/UserChoice.js new file mode 100644 index 00000000..a4d407a8 --- /dev/null +++ b/src/components/Onboarding/steps/UserChoice.js @@ -0,0 +1,17 @@ +// @flow + +import React from 'react' + +import Button from 'components/base/Button' + +import type { StepProps } from '..' + +export default (props: StepProps) => { + const { jumpStep } = props + return ( +
+ hey im step user choice + +
+ ) +} diff --git a/src/reducers/index.js b/src/reducers/index.js index 5963f822..a73be396 100644 --- a/src/reducers/index.js +++ b/src/reducers/index.js @@ -13,6 +13,7 @@ import devices from './devices' import modals from './modals' import settings from './settings' import update from './update' +import onboarding from './onboarding' import type { AccountsState } from './accounts' import type { ApplicationState } from './application' @@ -20,6 +21,7 @@ import type { DevicesState } from './devices' import type { ModalsState } from './modals' import type { SettingsState } from './settings' import type { UpdateState } from './update' +import type { OnboardingState } from './onboarding' export type State = { accounts: AccountsState, @@ -30,6 +32,7 @@ export type State = { router: LocationShape, settings: SettingsState, update: UpdateState, + onboarding: OnboardingState, } export default combineReducers({ @@ -41,4 +44,5 @@ export default combineReducers({ router, settings, update, + onboarding, }) diff --git a/src/reducers/onboarding.js b/src/reducers/onboarding.js new file mode 100644 index 00000000..22a24fa3 --- /dev/null +++ b/src/reducers/onboarding.js @@ -0,0 +1,84 @@ +// @flow + +import { handleActions, createAction } from 'redux-actions' + +type Step = { + name: string, + external?: boolean, + label?: string, + options: { + showFooter: boolean, + showBackground: boolean, + showBreadcrumb: boolean, + }, +} + +export type OnboardingState = { + stepIndex: number, + stepName: string, // TODO: specify that the string comes from Steps type + steps: Step[], +} + +const state: OnboardingState = { + stepIndex: 0, + stepName: 'init', + steps: [ + { + name: 'init', + external: true, + options: { + showFooter: false, + showBackground: true, + showBreadcrumb: false, + }, + }, + { + name: 'userChoice', + label: 'something:translated', + options: { + showFooter: false, + showBackground: true, + showBreadcrumb: true, + }, + }, + ], +} + +const handlers = { + ONBOARDING_NEXT_STEP: state => { + const step = state.steps.find(step => step.name === state.stepName) + if (!step) { + return state + } + const index = state.steps.indexOf(step) + if (index > state.steps.length - 2) { + return state + } + return { ...state, stepName: state.steps[index + 1].name, stepIndex: index + 1 } + }, + ONBOARDING_PREV_STEP: state => { + const step = state.steps.find(step => step.name === state.stepName) + if (!step) { + return state + } + const index = state.steps.indexOf(step) + if (index < 1) { + return state + } + return { ...state, stepName: state.steps[index - 1].name, stepIndex: index - 1 } + }, + ONBOARDING_JUMP_STEP: (state, { payload: stepName }) => { + const step = state.steps.find(step => step.name === stepName) + if (!step) { + return state + } + const index = state.steps.indexOf(step) + return { ...state, stepName: step.name, stepIndex: index } + }, +} + +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') From 0551de43be58959bc649affa1d44ff8e1659b8e6 Mon Sep 17 00:00:00 2001 From: NastiaS Date: Fri, 11 May 2018 08:58:08 +0200 Subject: [PATCH 2/3] onboarding wip --- src/components/Onboarding/helperComponents.js | 28 +++++++ src/components/Onboarding/index.js | 25 +++++- src/components/Onboarding/steps/Analytics.js | 73 ++++++++++++++++++ .../Onboarding/steps/ChooseDevice.js | 62 +++++++++++++++ src/components/Onboarding/steps/ChoosePIN.js | 34 ++++++++ .../Onboarding/steps/GenuineCheck.js | 37 +++++++++ src/components/Onboarding/steps/Init.js | 77 +++++++++++++++++-- .../Onboarding/steps/SetPassword.js | 47 +++++++++++ .../Onboarding/steps/SetUpWalletEnv.js | 34 ++++++++ src/components/Onboarding/steps/UserChoice.js | 17 ---- src/components/Onboarding/steps/WriteSeed.js | 34 ++++++++ src/reducers/onboarding.js | 58 +++++++++++++- 12 files changed, 498 insertions(+), 28 deletions(-) create mode 100644 src/components/Onboarding/helperComponents.js create mode 100644 src/components/Onboarding/steps/Analytics.js create mode 100644 src/components/Onboarding/steps/ChooseDevice.js create mode 100644 src/components/Onboarding/steps/ChoosePIN.js create mode 100644 src/components/Onboarding/steps/GenuineCheck.js create mode 100644 src/components/Onboarding/steps/SetPassword.js create mode 100644 src/components/Onboarding/steps/SetUpWalletEnv.js delete mode 100644 src/components/Onboarding/steps/UserChoice.js create mode 100644 src/components/Onboarding/steps/WriteSeed.js diff --git a/src/components/Onboarding/helperComponents.js b/src/components/Onboarding/helperComponents.js new file mode 100644 index 00000000..05a39154 --- /dev/null +++ b/src/components/Onboarding/helperComponents.js @@ -0,0 +1,28 @@ +// @flow +import styled from 'styled-components' + +import Box from 'components/base/Box' + +export const Title = styled(Box).attrs({ + width: 152, + height: 27, + ff: 'Museo Sans|Regular', + fontSize: 7, + color: 'dark', +})`` + +export const Description = styled(Box).attrs({ + width: 340, + height: 36, + ff: 'Open Sans|Regular', + fontSize: 4, + textAlign: 'center', + color: 'smoke', +})` + margin: 10px auto 25px; +` +export const Inner = styled(Box).attrs({ + horizontal: true, + grow: true, + flow: 4, +})`` diff --git a/src/components/Onboarding/index.js b/src/components/Onboarding/index.js index 8ed00c46..01e32d38 100644 --- a/src/components/Onboarding/index.js +++ b/src/components/Onboarding/index.js @@ -16,11 +16,23 @@ import Box from 'components/base/Box' import OnboardingBreadcrumb from './OnboardingBreadcrumb' import InitStep from './steps/Init' -import UserChoice from './steps/UserChoice' +import ChooseDevice from './steps/ChooseDevice' +import ChoosePIN from './steps/ChoosePIN' +import WriteSeed from './steps/WriteSeed' +import GenuineCheck from './steps/GenuineCheck' +import SetUpWalletEnv from './steps/SetUpWalletEnv' +import SetPassword from './steps/SetPassword' +import Analytics from './steps/Analytics' const STEPS = { init: InitStep, - userChoice: UserChoice, + chooseDevice: ChooseDevice, + choosePIN: ChoosePIN, + writeSeed: WriteSeed, + genuineCheck: GenuineCheck, + setupWalletEnv: SetUpWalletEnv, + setPassword: SetPassword, + analytics: Analytics, } const mapStateToProps = state => ({ @@ -83,7 +95,9 @@ class Onboarding extends PureComponent { return ( {step.options.showBreadcrumb && } - + + + ) } @@ -91,6 +105,7 @@ class Onboarding extends PureComponent { const Container = styled(Box).attrs({ bg: 'white', + p: 5, })` position: fixed; top: 0; @@ -99,5 +114,7 @@ const Container = styled(Box).attrs({ bottom: 0; z-index: 100; ` - +const StepContainer = styled(Box).attrs({ + p: 20, +})`` export default compose(connect(mapStateToProps, mapDispatchToProps), translate())(Onboarding) diff --git a/src/components/Onboarding/steps/Analytics.js b/src/components/Onboarding/steps/Analytics.js new file mode 100644 index 00000000..5839dd3b --- /dev/null +++ b/src/components/Onboarding/steps/Analytics.js @@ -0,0 +1,73 @@ +// @flow + +import React from 'react' +import styled from 'styled-components' + +import Box from 'components/base/Box' +import Button from 'components/base/Button' +import { ModalFooter } from 'components/base/Modal/index' +import IconBlue from 'icons/device/Blue' +import CheckBox from 'components/base/CheckBox' +import { Title, Description } from '../helperComponents' + +import type { StepProps } from '..' + +export default (props: StepProps) => { + const { nextStep, prevStep } = props + return ( + + + This is ANALYTICS screen. 1 line is the maximum + + 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 +
+ + + + + + + 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 +
+
+ + + + 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 +
+
+
+ + + + +
+ ) +} + +export const AnalyticsText = styled(Box).attrs({ + ff: 'Open Sans|Regular', + fontSize: 4, + textAlign: 'left', + color: 'smoke', +})` + margin: 10px auto 25px; + padding-left: 10px; +` +const DeviceIcon = styled(Box).attrs({ + alignItems: 'center', + justifyContent: 'center', + color: 'graphite', +})` + width: 55px; +` diff --git a/src/components/Onboarding/steps/ChooseDevice.js b/src/components/Onboarding/steps/ChooseDevice.js new file mode 100644 index 00000000..7da5e8a8 --- /dev/null +++ b/src/components/Onboarding/steps/ChooseDevice.js @@ -0,0 +1,62 @@ +// @flow + +import React from 'react' +import styled from 'styled-components' + +import Box from 'components/base/Box' +import IconNanoS from 'icons/device/NanoS' +import IconBlue from 'icons/device/Blue' +import { Title, Description, Inner } from '../helperComponents' + +import type { StepProps } from '..' + +export default (props: StepProps) => { + const { nextStep } = props + + return ( + + + This is the title of the screen. 1 line is the maximum + + 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 +
+ + + nextStep()}> + + + + Ledger Nano S + Please replace it with the final wording once it’s done. + + + + + + Ledger Blue + Please replace it with the final wording once it’s done. + + + +
+
+ ) +} + +const DeviceContainer = styled(Box).attrs({ + alignItems: 'center', + justifyContent: 'center', +})` + width: 218px; + height: 204px; + border: 1px solid #d8d8d8; +` +const DeviceIcon = styled(Box).attrs({ + alignItems: 'center', + justifyContent: 'center', + color: 'graphite', +})` + width: 55px; +` diff --git a/src/components/Onboarding/steps/ChoosePIN.js b/src/components/Onboarding/steps/ChoosePIN.js new file mode 100644 index 00000000..0c5cbde3 --- /dev/null +++ b/src/components/Onboarding/steps/ChoosePIN.js @@ -0,0 +1,34 @@ +// @flow + +import React from 'react' + +import Box from 'components/base/Box' +import Button from 'components/base/Button' +import { ModalFooter } from 'components/base/Modal/index' +import { Title, Description } from '../helperComponents' + +import type { StepProps } from '..' + +export default (props: StepProps) => { + const { nextStep, prevStep } = props + return ( + + + This is CHOOSE PIN screen. 1 line is the maximum + + 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 +
+
+ + + + +
+ ) +} diff --git a/src/components/Onboarding/steps/GenuineCheck.js b/src/components/Onboarding/steps/GenuineCheck.js new file mode 100644 index 00000000..9f12f451 --- /dev/null +++ b/src/components/Onboarding/steps/GenuineCheck.js @@ -0,0 +1,37 @@ +// @flow + +import React from 'react' + +import Box from 'components/base/Box' +import Button from 'components/base/Button' +import { ModalFooter } from 'components/base/Modal/index' +import { Title, Description } from '../helperComponents' + +import type { StepProps } from '..' + +export default (props: StepProps) => { + const { nextStep, prevStep, jumpStep } = props + return ( + + + This is CHOOSE PIN screen. 1 line is the maximum + + 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 +
+
+ + + + + +
+ ) +} diff --git a/src/components/Onboarding/steps/Init.js b/src/components/Onboarding/steps/Init.js index 76ae3cb3..0dba255f 100644 --- a/src/components/Onboarding/steps/Init.js +++ b/src/components/Onboarding/steps/Init.js @@ -1,18 +1,85 @@ // @flow import React from 'react' +import styled from 'styled-components' -import Button from 'components/base/Button' import Box from 'components/base/Box' +import IconNanoS from 'icons/device/NanoS' +import IconBlue from 'icons/device/Blue' +import { Title, Description, Inner } from '../helperComponents' import type { StepProps } from '..' export default (props: StepProps) => { - const { nextStep } = props + const { nextStep, jumpStep } = props + /* TODO: all titles, descriptions to be wrapped in a translation tag once defined */ return ( - - hey im step init - + + + This is the title of the screen. 1 line is the maximum + + 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 +
+ + + nextStep()}> + + + + Clean Nano S setup + Please replace it with the final wording once it’s done. + + jumpStep('genuineCheck')}> + + + + Existing seed + Clean setup + Please replace it with the final wording once it’s done. + + + + nextStep()}> + + + + Migrate accounts + Please replace it with the final wording once it’s done. + + + + + + Not a user, but would love to + Please replace it with the final wording once it’s done. + + + +
) } + +const DeviceContainer = styled(Box).attrs({ + alignItems: 'center', + justifyContent: 'center', +})` + width: 218px; + height: 204px; + border: 1px solid #d8d8d8; +` +const DeviceIcon = styled(Box).attrs({ + alignItems: 'center', + justifyContent: 'center', + color: 'graphite', +})` + width: 55px; +` +export const TrackChoiceTitle = styled(Box).attrs({ + width: 152, + height: 27, + ff: 'Museo Sans|Regular', + fontSize: 5, + color: 'dark', +})`` diff --git a/src/components/Onboarding/steps/SetPassword.js b/src/components/Onboarding/steps/SetPassword.js new file mode 100644 index 00000000..449c45c2 --- /dev/null +++ b/src/components/Onboarding/steps/SetPassword.js @@ -0,0 +1,47 @@ +// @flow + +import React from 'react' + +import Box from 'components/base/Box' +import Button from 'components/base/Button' +import { ModalFooter } from 'components/base/Modal/index' +import Text from 'components/base/Text' + +import IconLock from 'icons/Lock' +import type { StepProps } from '..' +import { Title, Description } from '../helperComponents' + +export default (props: StepProps) => { + const handleSetPassword = () => { + console.warn('SET PASSWORD TRIGGER') // eslint-disable-line + } + const { nextStep, prevStep } = props + return ( + + + This is SET PASSWORD screen. 1 line is the maximum + + 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 +
+ + + nextStep()} style={{ padding: 15 }}> + I do not want to set it up + +
+ + + + + +
+ ) +} diff --git a/src/components/Onboarding/steps/SetUpWalletEnv.js b/src/components/Onboarding/steps/SetUpWalletEnv.js new file mode 100644 index 00000000..644fe00a --- /dev/null +++ b/src/components/Onboarding/steps/SetUpWalletEnv.js @@ -0,0 +1,34 @@ +// @flow + +import React from 'react' + +import Box from 'components/base/Box' +import Button from 'components/base/Button' +import { ModalFooter } from 'components/base/Modal/index' + +import type { StepProps } from '..' +import { Title, Description } from '../helperComponents' + +export default (props: StepProps) => { + const { nextStep, prevStep } = props + return ( + + + This is WALLET SETUP screen. 1 line is the maximum + + 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 +
+
+ + + + +
+ ) +} diff --git a/src/components/Onboarding/steps/UserChoice.js b/src/components/Onboarding/steps/UserChoice.js deleted file mode 100644 index a4d407a8..00000000 --- a/src/components/Onboarding/steps/UserChoice.js +++ /dev/null @@ -1,17 +0,0 @@ -// @flow - -import React from 'react' - -import Button from 'components/base/Button' - -import type { StepProps } from '..' - -export default (props: StepProps) => { - const { jumpStep } = props - return ( -
- hey im step user choice - -
- ) -} diff --git a/src/components/Onboarding/steps/WriteSeed.js b/src/components/Onboarding/steps/WriteSeed.js new file mode 100644 index 00000000..f3616dca --- /dev/null +++ b/src/components/Onboarding/steps/WriteSeed.js @@ -0,0 +1,34 @@ +// @flow + +import React from 'react' + +import Box from 'components/base/Box' +import Button from 'components/base/Button' +import { ModalFooter } from 'components/base/Modal/index' +import { Title, Description } from '../helperComponents' + +import type { StepProps } from '..' + +export default (props: StepProps) => { + const { nextStep, prevStep } = props + return ( + + + This is WRITE SEED screen. 1 line is the maximum + + 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 +
+
+ + + + +
+ ) +} diff --git a/src/reducers/onboarding.js b/src/reducers/onboarding.js index 22a24fa3..61ed1f88 100644 --- a/src/reducers/onboarding.js +++ b/src/reducers/onboarding.js @@ -33,8 +33,62 @@ const state: OnboardingState = { }, }, { - name: 'userChoice', - label: 'something:translated', + name: 'chooseDevice', + label: 'chooseDevice:translated', + options: { + showFooter: false, + showBackground: true, + showBreadcrumb: true, + }, + }, + { + name: 'choosePIN', + label: 'choosePIN:translated', + options: { + showFooter: false, + showBackground: true, + showBreadcrumb: true, + }, + }, + { + name: 'writeSeed', + label: 'writeSeed:translated', + options: { + showFooter: false, + showBackground: true, + showBreadcrumb: true, + }, + }, + { + name: 'genuineCheck', + label: 'genuineCheck:translated', + options: { + showFooter: false, + showBackground: true, + showBreadcrumb: true, + }, + }, + { + name: 'setupWalletEnv', + label: 'Install Apps & Create Account:translated', + options: { + showFooter: false, + showBackground: true, + showBreadcrumb: true, + }, + }, + { + name: 'setPassword', + label: 'Password:translated', + options: { + showFooter: false, + showBackground: true, + showBreadcrumb: true, + }, + }, + { + name: 'analytics', + label: 'Analytics & Bug report:translated', options: { showFooter: false, showBackground: true, From a536f80c7ef84219ea89dcf08c10437c0104951c Mon Sep 17 00:00:00 2001 From: NastiaS Date: Fri, 11 May 2018 17:16:32 +0200 Subject: [PATCH 3/3] skeleton of onboarding flow and steps --- src/components/Onboarding/OnboardingFooter.js | 15 +++ src/components/Onboarding/helperComponents.js | 10 ++ src/components/Onboarding/index.js | 33 ++++- src/components/Onboarding/steps/Analytics.js | 11 +- src/components/Onboarding/steps/ChoosePIN.js | 7 +- .../steps/{SetUpWalletEnv.js => Finish.js} | 22 ++-- .../Onboarding/steps/GenuineCheck.js | 102 +++++++++++----- src/components/Onboarding/steps/Init.js | 31 +++-- .../Onboarding/steps/SetPassword.js | 114 ++++++++++++------ src/components/Onboarding/steps/Start.js | 27 +++++ src/components/Onboarding/steps/WriteSeed.js | 7 +- src/components/base/Modal/index.js | 2 +- src/reducers/onboarding.js | 25 ++-- 13 files changed, 293 insertions(+), 113 deletions(-) create mode 100644 src/components/Onboarding/OnboardingFooter.js rename src/components/Onboarding/steps/{SetUpWalletEnv.js => Finish.js} (56%) create mode 100644 src/components/Onboarding/steps/Start.js diff --git a/src/components/Onboarding/OnboardingFooter.js b/src/components/Onboarding/OnboardingFooter.js new file mode 100644 index 00000000..a7ef3966 --- /dev/null +++ b/src/components/Onboarding/OnboardingFooter.js @@ -0,0 +1,15 @@ +// @flow + +import styled from 'styled-components' +import { radii } from 'styles/theme' + +import Box from 'components/base/Box' + +export const OnboardingFooter = 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; +` diff --git a/src/components/Onboarding/helperComponents.js b/src/components/Onboarding/helperComponents.js index 05a39154..2c0da32a 100644 --- a/src/components/Onboarding/helperComponents.js +++ b/src/components/Onboarding/helperComponents.js @@ -1,5 +1,6 @@ // @flow import styled from 'styled-components' +import { radii } from 'styles/theme' import Box from 'components/base/Box' @@ -26,3 +27,12 @@ export const Inner = styled(Box).attrs({ grow: true, flow: 4, })`` + +export const OnboardingFooter = 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; +` diff --git a/src/components/Onboarding/index.js b/src/components/Onboarding/index.js index 01e32d38..ca9dbdf7 100644 --- a/src/components/Onboarding/index.js +++ b/src/components/Onboarding/index.js @@ -11,18 +11,23 @@ import type { OnboardingState } from 'reducers/onboarding' import { saveSettings } from 'actions/settings' import { nextStep, prevStep, jumpStep } from 'reducers/onboarding' +import { getCurrentDevice } from 'reducers/devices' + +// TODO: re-write it without auto lock, fixed width of the password modal, not dynamic titles +import { unlock } from 'reducers/application' import Box from 'components/base/Box' -import OnboardingBreadcrumb from './OnboardingBreadcrumb' +import Start from './steps/Start' import InitStep from './steps/Init' +import OnboardingBreadcrumb from './OnboardingBreadcrumb' import ChooseDevice from './steps/ChooseDevice' import ChoosePIN from './steps/ChoosePIN' import WriteSeed from './steps/WriteSeed' import GenuineCheck from './steps/GenuineCheck' -import SetUpWalletEnv from './steps/SetUpWalletEnv' import SetPassword from './steps/SetPassword' import Analytics from './steps/Analytics' +import Finish from './steps/Finish' const STEPS = { init: InitStep, @@ -30,14 +35,16 @@ const STEPS = { choosePIN: ChoosePIN, writeSeed: WriteSeed, genuineCheck: GenuineCheck, - setupWalletEnv: SetUpWalletEnv, setPassword: SetPassword, analytics: Analytics, + finish: Finish, + start: Start, } const mapStateToProps = state => ({ hasCompletedOnboarding: state.settings.hasCompletedOnboarding, onboarding: state.onboarding, + getCurrentDevice: getCurrentDevice(state), }) const mapDispatchToProps = { @@ -45,6 +52,7 @@ const mapDispatchToProps = { nextStep, prevStep, jumpStep, + unlock, } type Props = { @@ -55,6 +63,8 @@ type Props = { prevStep: Function, nextStep: Function, jumpStep: Function, + getCurrentDevice: Function, + unlock: Function, } export type StepProps = { @@ -63,14 +73,25 @@ export type StepProps = { nextStep: Function, jumpStep: Function, finish: Function, + savePassword: Function, + getDeviceInfo: Function, } class Onboarding extends PureComponent { + getDeviceInfo = () => this.props.getCurrentDevice finish = () => this.props.saveSettings({ hasCompletedOnboarding: true }) + savePassword = hash => { + this.props.saveSettings({ + password: { + isEnabled: hash !== undefined, + value: hash, + }, + }) + this.props.unlock() + } render() { const { hasCompletedOnboarding, onboarding, prevStep, nextStep, jumpStep, t } = this.props - if (hasCompletedOnboarding) { return null } @@ -90,6 +111,8 @@ class Onboarding extends PureComponent { nextStep, jumpStep, finish: this.finish, + savePassword: this.savePassword, + getDeviceInfo: this.getDeviceInfo, } return ( @@ -112,7 +135,7 @@ const Container = styled(Box).attrs({ left: 0; right: 0; bottom: 0; - z-index: 100; + z-index: 25; ` const StepContainer = styled(Box).attrs({ p: 20, diff --git a/src/components/Onboarding/steps/Analytics.js b/src/components/Onboarding/steps/Analytics.js index 5839dd3b..799832fa 100644 --- a/src/components/Onboarding/steps/Analytics.js +++ b/src/components/Onboarding/steps/Analytics.js @@ -5,10 +5,9 @@ import styled from 'styled-components' import Box from 'components/base/Box' import Button from 'components/base/Button' -import { ModalFooter } from 'components/base/Modal/index' -import IconBlue from 'icons/device/Blue' +import IconAnalytics from 'icons/LockScreen' import CheckBox from 'components/base/CheckBox' -import { Title, Description } from '../helperComponents' +import { Title, Description, OnboardingFooter } from '../helperComponents' import type { StepProps } from '..' @@ -24,7 +23,7 @@ export default (props: StepProps) => { Lorem ipsum dolor amet ledger lorem dolor ipsum amet - + @@ -43,14 +42,14 @@ export default (props: StepProps) => {
- + - + ) } diff --git a/src/components/Onboarding/steps/ChoosePIN.js b/src/components/Onboarding/steps/ChoosePIN.js index 0c5cbde3..794f4b71 100644 --- a/src/components/Onboarding/steps/ChoosePIN.js +++ b/src/components/Onboarding/steps/ChoosePIN.js @@ -4,8 +4,7 @@ import React from 'react' import Box from 'components/base/Box' import Button from 'components/base/Button' -import { ModalFooter } from 'components/base/Modal/index' -import { Title, Description } from '../helperComponents' +import { Title, Description, OnboardingFooter } from '../helperComponents' import type { StepProps } from '..' @@ -21,14 +20,14 @@ export default (props: StepProps) => { Lorem ipsum dolor amet ledger lorem dolor ipsum amet - + - + ) } diff --git a/src/components/Onboarding/steps/SetUpWalletEnv.js b/src/components/Onboarding/steps/Finish.js similarity index 56% rename from src/components/Onboarding/steps/SetUpWalletEnv.js rename to src/components/Onboarding/steps/Finish.js index 644fe00a..513afa7d 100644 --- a/src/components/Onboarding/steps/SetUpWalletEnv.js +++ b/src/components/Onboarding/steps/Finish.js @@ -4,31 +4,31 @@ import React from 'react' import Box from 'components/base/Box' import Button from 'components/base/Button' -import { ModalFooter } from 'components/base/Modal/index' +import Text from 'components/base/Text' +import IconFinishOnboarding from 'icons/LockScreen' import type { StepProps } from '..' import { Title, Description } from '../helperComponents' export default (props: StepProps) => { - const { nextStep, prevStep } = props + const { finish, jumpStep } = props return ( - This is WALLET SETUP screen. 1 line is the maximum + This is ENJOY THE APP screen. 1 line is the maximum 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
-
- - - - + jumpStep('start')} style={{ padding: 15 }}> + I want to go back to Onboarding + +
) } diff --git a/src/components/Onboarding/steps/GenuineCheck.js b/src/components/Onboarding/steps/GenuineCheck.js index 9f12f451..d4f69ced 100644 --- a/src/components/Onboarding/steps/GenuineCheck.js +++ b/src/components/Onboarding/steps/GenuineCheck.js @@ -1,37 +1,85 @@ // @flow -import React from 'react' +import React, { PureComponent } from 'react' import Box from 'components/base/Box' import Button from 'components/base/Button' -import { ModalFooter } from 'components/base/Modal/index' -import { Title, Description } from '../helperComponents' + +import { Title, Description, OnboardingFooter } from '../helperComponents' import type { StepProps } from '..' -export default (props: StepProps) => { - const { nextStep, prevStep, jumpStep } = props - return ( - - - This is CHOOSE PIN screen. 1 line is the maximum - - 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 -
-
- - - - - -
- ) + {showDeviceInfo && ( + + + The manufacturer is {currentDevice.manufacturer} + The release number is {currentDevice.release} + + + )} + {showError && ( + + Connect your device please + + )} + + + + + + + ) + } } + +export default GenuineCheck diff --git a/src/components/Onboarding/steps/Init.js b/src/components/Onboarding/steps/Init.js index 0dba255f..0ed00ea6 100644 --- a/src/components/Onboarding/steps/Init.js +++ b/src/components/Onboarding/steps/Init.js @@ -2,16 +2,17 @@ import React from 'react' import styled from 'styled-components' +import { shell } from 'electron' import Box from 'components/base/Box' -import IconNanoS from 'icons/device/NanoS' -import IconBlue from 'icons/device/Blue' +import IconUser from 'icons/User' import { Title, Description, Inner } from '../helperComponents' import type { StepProps } from '..' export default (props: StepProps) => { const { nextStep, jumpStep } = props + const handleOpenLink = (url: string) => () => shell.openExternal(url) /* TODO: all titles, descriptions to be wrapped in a translation tag once defined */ return ( @@ -22,34 +23,37 @@ export default (props: StepProps) => {
Lorem ipsum dolor amet ledger lorem dolor ipsum amet - + nextStep()}> - - + {/* colors are temp, we don't have icons now */} + + Clean Nano S setup Please replace it with the final wording once it’s done. - jumpStep('genuineCheck')}> - - + jumpStep('choosePIN')}> + + Existing seed + Clean setup Please replace it with the final wording once it’s done. + + nextStep()}> - - + + Migrate accounts Please replace it with the final wording once it’s done. - - - + + + Not a user, but would love to Please replace it with the final wording once it’s done. @@ -75,6 +79,7 @@ const DeviceIcon = styled(Box).attrs({ color: 'graphite', })` width: 55px; + padding: 10px; ` export const TrackChoiceTitle = styled(Box).attrs({ width: 152, diff --git a/src/components/Onboarding/steps/SetPassword.js b/src/components/Onboarding/steps/SetPassword.js index 449c45c2..215438dd 100644 --- a/src/components/Onboarding/steps/SetPassword.js +++ b/src/components/Onboarding/steps/SetPassword.js @@ -1,47 +1,93 @@ // @flow -import React from 'react' +import React, { PureComponent } from 'react' +import bcrypt from 'bcryptjs' + +import { setEncryptionKey } from 'helpers/db' import Box from 'components/base/Box' import Button from 'components/base/Button' -import { ModalFooter } from 'components/base/Modal/index' import Text from 'components/base/Text' -import IconLock from 'icons/Lock' +import IconSetPassword from 'icons/LockScreen' +import PasswordModal from 'components/SettingsPage/PasswordModal' + import type { StepProps } from '..' -import { Title, Description } from '../helperComponents' -export default (props: StepProps) => { - const handleSetPassword = () => { - console.warn('SET PASSWORD TRIGGER') // eslint-disable-line +import { Title, Description, OnboardingFooter } from '../helperComponents' + +type State = { + isPasswordModalOpened: boolean, + isPasswordEnabled: boolean, +} + +class SetPassword extends PureComponent { + state = { + isPasswordModalOpened: false, + isPasswordEnabled: false, + } + + handleOpenPasswordModal = () => { + this.setState({ isPasswordModalOpened: true }) + } + handleClosePasswordModal = () => { + this.setState({ isPasswordModalOpened: false }) } - const { nextStep, prevStep } = props - return ( - - - This is SET PASSWORD screen. 1 line is the maximum - - 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 -
- - - nextStep()} style={{ padding: 15 }}> - I do not want to set it up + handleChangePassword = (password: string) => { + window.requestIdleCallback(() => { + setEncryptionKey('accounts', password) + const hash = password ? bcrypt.hashSync(password, 8) : undefined + this.props.savePassword(hash) + }) + } + + handleInputChange = (key: string) => (value: string) => { + this.setState({ [key]: value }) + } + + render() { + const { nextStep, prevStep, t } = this.props + const { isPasswordModalOpened, isPasswordEnabled } = this.state + return ( + + + This is SET PASSWORD screen. 1 line is the maximum + + 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 +
+ + + {/* we might not be able to re-use what we have currently without modifications + the title and descriptions are not dynamic, we might need deffirent size as well */} + {isPasswordModalOpened && ( + + )} + nextStep()} style={{ padding: 15 }}> + I do not want to set it up +
+ + + +
- - - - - -
- ) + ) + } } + +export default SetPassword diff --git a/src/components/Onboarding/steps/Start.js b/src/components/Onboarding/steps/Start.js new file mode 100644 index 00000000..e3586584 --- /dev/null +++ b/src/components/Onboarding/steps/Start.js @@ -0,0 +1,27 @@ +// @flow + +import React from 'react' + +import Box from 'components/base/Box' +import Button from 'components/base/Button' + +import LedgerLogo from 'icons/LockScreen' +import type { StepProps } from '..' +import { Title, Description } from '../helperComponents' + +export default (props: StepProps) => { + const { jumpStep } = props + return ( + + + + Ledger Live + Welcome to the new Ledger Live Desktop app. + Let’s get started! + + + + ) +} diff --git a/src/components/Onboarding/steps/WriteSeed.js b/src/components/Onboarding/steps/WriteSeed.js index f3616dca..31fe37ea 100644 --- a/src/components/Onboarding/steps/WriteSeed.js +++ b/src/components/Onboarding/steps/WriteSeed.js @@ -4,8 +4,7 @@ import React from 'react' import Box from 'components/base/Box' import Button from 'components/base/Button' -import { ModalFooter } from 'components/base/Modal/index' -import { Title, Description } from '../helperComponents' +import { Title, Description, OnboardingFooter } from '../helperComponents' import type { StepProps } from '..' @@ -21,14 +20,14 @@ export default (props: StepProps) => { Lorem ipsum dolor amet ledger lorem dolor ipsum amet
- + - +
) } diff --git a/src/components/base/Modal/index.js b/src/components/base/Modal/index.js index 9e36938f..c15d186d 100644 --- a/src/components/base/Modal/index.js +++ b/src/components/base/Modal/index.js @@ -61,7 +61,7 @@ const Container = styled(Box).attrs({ }), })` position: fixed; - z-index: 20; + z-index: 30; ` const Backdrop = styled(Box).attrs({ diff --git a/src/reducers/onboarding.js b/src/reducers/onboarding.js index 61ed1f88..f392298a 100644 --- a/src/reducers/onboarding.js +++ b/src/reducers/onboarding.js @@ -21,8 +21,17 @@ export type OnboardingState = { const state: OnboardingState = { stepIndex: 0, - stepName: 'init', + stepName: 'start', steps: [ + { + name: 'start', + external: true, + options: { + showFooter: false, + showBackground: true, + showBreadcrumb: false, + }, + }, { name: 'init', external: true, @@ -69,8 +78,8 @@ const state: OnboardingState = { }, }, { - name: 'setupWalletEnv', - label: 'Install Apps & Create Account:translated', + name: 'setPassword', + label: 'Password:translated', options: { showFooter: false, showBackground: true, @@ -78,8 +87,8 @@ const state: OnboardingState = { }, }, { - name: 'setPassword', - label: 'Password:translated', + name: 'analytics', + label: 'Analytics & Bug report:translated', options: { showFooter: false, showBackground: true, @@ -87,12 +96,12 @@ const state: OnboardingState = { }, }, { - name: 'analytics', - label: 'Analytics & Bug report:translated', + name: 'finish', + external: true, options: { showFooter: false, showBackground: true, - showBreadcrumb: true, + showBreadcrumb: false, }, }, ],