Browse Source

Merge pull request #319 from NastiaS/onboardingBranch

Onboarding branch
master
Meriadec Pillet 7 years ago
committed by GitHub
parent
commit
64c0fa70ed
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 32
      src/components/Onboarding/OnboardingBreadcrumb.js
  2. 15
      src/components/Onboarding/OnboardingFooter.js
  3. 38
      src/components/Onboarding/helperComponents.js
  4. 162
      src/components/Onboarding/index.js
  5. 72
      src/components/Onboarding/steps/Analytics.js
  6. 62
      src/components/Onboarding/steps/ChooseDevice.js
  7. 33
      src/components/Onboarding/steps/ChoosePIN.js
  8. 34
      src/components/Onboarding/steps/Finish.js
  9. 85
      src/components/Onboarding/steps/GenuineCheck.js
  10. 90
      src/components/Onboarding/steps/Init.js
  11. 93
      src/components/Onboarding/steps/SetPassword.js
  12. 27
      src/components/Onboarding/steps/Start.js
  13. 33
      src/components/Onboarding/steps/WriteSeed.js
  14. 2
      src/components/base/Modal/index.js
  15. 4
      src/reducers/index.js
  16. 147
      src/reducers/onboarding.js

32
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 <Breadcrumb currentStep={stepIndex} items={filteredSteps} />
}
export default connect(mapStateToProps)(OnboardingBreadcrumb)

15
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;
`

38
src/components/Onboarding/helperComponents.js

@ -0,0 +1,38 @@
// @flow
import styled from 'styled-components'
import { radii } from 'styles/theme'
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,
})``
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;
`

162
src/components/Onboarding/index.js

@ -7,108 +7,120 @@ 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 { 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 Button from 'components/base/Button'
const STEPS = [
{
title: ({ t }) => t('onboarding:step1.title'),
render: ({ t }: StepProps) => <Box>{t('onboarding:step1.greetings')}</Box>,
},
{
title: ({ t }) => t('onboarding:step2.title'),
render: ({ t }: StepProps) => <Box>{t('onboarding:step2.greetings')}</Box>,
},
{
title: ({ t }) => t('onboarding:step3.title'),
render: ({ t }: StepProps) => (
<Box>
{t('onboarding:step3.greetings')}
<Box bg="grey" align="center" justify="center" style={{ width: 200, height: 200 }}>
{'step 3 image'}
</Box>
{t('onboarding:step3.description')}
</Box>
),
},
{
title: ({ t }) => t('onboarding:step4.title'),
render: ({ t }: StepProps) => <Box>{t('onboarding:step4.greetings')}</Box>,
},
]
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 SetPassword from './steps/SetPassword'
import Analytics from './steps/Analytics'
import Finish from './steps/Finish'
const STEPS = {
init: InitStep,
chooseDevice: ChooseDevice,
choosePIN: ChoosePIN,
writeSeed: WriteSeed,
genuineCheck: GenuineCheck,
setPassword: SetPassword,
analytics: Analytics,
finish: Finish,
start: Start,
}
const mapStateToProps = state => ({
hasCompletedOnboarding: state.settings.hasCompletedOnboarding,
onboarding: state.onboarding,
getCurrentDevice: getCurrentDevice(state),
})
const mapDispatchToProps = {
saveSettings,
nextStep,
prevStep,
jumpStep,
unlock,
}
type Props = {
t: T,
hasCompletedOnboarding: boolean,
saveSettings: Function,
onboarding: OnboardingState,
prevStep: Function,
nextStep: Function,
jumpStep: Function,
getCurrentDevice: Function,
unlock: Function,
}
type StepProps = {
export type StepProps = {
t: T,
prevStep: Function,
nextStep: Function,
jumpStep: Function,
finish: Function,
savePassword: Function,
getDeviceInfo: Function,
}
type State = {
stepIndex: number,
}
class Onboarding extends PureComponent<Props, State> {
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<Props> {
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, 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,
savePassword: this.savePassword,
getDeviceInfo: this.getDeviceInfo,
}
return (
<Container>
<Inner>
<Box horizontal flow={2}>
<Button primary onClick={this.prev}>
{'prev step'}
</Button>
{stepIndex === STEPS.length - 1 ? (
<Button danger onClick={this.finish}>
{'finish'}
</Button>
) : (
<Button primary onClick={this.next}>
{'next step'}
</Button>
)}
</Box>
<StepTitle>{step.title(stepProps)}</StepTitle>
{step.render(stepProps)}
</Inner>
{step.options.showBreadcrumb && <OnboardingBreadcrumb />}
<StepContainer>
<StepComponent {...stepProps} />
</StepContainer>
</Container>
)
}
@ -116,30 +128,16 @@ class Onboarding extends PureComponent<Props, State> {
const Container = styled(Box).attrs({
bg: 'white',
p: 6,
align: 'center',
justify: 'center',
p: 5,
})`
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 100;
z-index: 25;
`
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,
const StepContainer = styled(Box).attrs({
p: 20,
})``
export default compose(connect(mapStateToProps, mapDispatchToProps), translate())(Onboarding)

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

@ -0,0 +1,72 @@
// @flow
import React from 'react'
import styled from 'styled-components'
import Box from 'components/base/Box'
import Button from 'components/base/Button'
import IconAnalytics from 'icons/LockScreen'
import CheckBox from 'components/base/CheckBox'
import { Title, Description, OnboardingFooter } from '../helperComponents'
import type { StepProps } from '..'
export default (props: StepProps) => {
const { nextStep, prevStep } = props
return (
<Box sticky alignItems="center" justifyContent="center">
<Box align="center">
<Title>This is ANALYTICS screen. 1 line is the maximum</Title>
<Description>
This is a long text, please replace it with the final wording once its done.
<br />
Lorem ipsum dolor amet ledger lorem dolor ipsum amet
</Description>
<DeviceIcon>
<IconAnalytics size={136} />
</DeviceIcon>
<Box horizontal flow={2} align="center">
<CheckBox isChecked />
<AnalyticsText>
This is a long text, please replace it with the final wording once its done.
<br />
Lorem ipsum dolor amet ledger lorem dolor ipsum amet
</AnalyticsText>
</Box>
<Box horizontal flow={2} align="center">
<CheckBox isChecked={false} />
<AnalyticsText>
This is a long text, please replace it with the final wording once its done.
<br />
Lorem ipsum dolor amet ledger lorem dolor ipsum amet
</AnalyticsText>
</Box>
</Box>
<OnboardingFooter horizontal align="center" justify="flex-end" flow={2}>
<Button small outline onClick={() => prevStep()}>
Go Back
</Button>
<Button small primary onClick={() => nextStep()}>
Continue
</Button>
</OnboardingFooter>
</Box>
)
}
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;
`

62
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 (
<Box sticky alignItems="center" justifyContent="center">
<Box align="center">
<Title>This is the title of the screen. 1 line is the maximum</Title>
<Description>
This is a long text, please replace it with the final wording once its done.
<br />
Lorem ipsum dolor amet ledger lorem dolor ipsum amet
</Description>
<Box>
<Inner>
<DeviceContainer onClick={() => nextStep()}>
<DeviceIcon>
<IconNanoS size={46} />
</DeviceIcon>
<Title>Ledger Nano S</Title>
<Description>Please replace it with the final wording once its done.</Description>
</DeviceContainer>
<DeviceContainer>
<DeviceIcon>
<IconBlue size={46} />
</DeviceIcon>
<Title>Ledger Blue</Title>
<Description>Please replace it with the final wording once its done.</Description>
</DeviceContainer>
</Inner>
</Box>
</Box>
</Box>
)
}
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;
`

33
src/components/Onboarding/steps/ChoosePIN.js

@ -0,0 +1,33 @@
// @flow
import React from 'react'
import Box from 'components/base/Box'
import Button from 'components/base/Button'
import { Title, Description, OnboardingFooter } from '../helperComponents'
import type { StepProps } from '..'
export default (props: StepProps) => {
const { nextStep, prevStep } = props
return (
<Box sticky alignItems="center" justifyContent="center">
<Box align="center">
<Title>This is CHOOSE PIN screen. 1 line is the maximum</Title>
<Description>
This is a long text, please replace it with the final wording once its done.
<br />
Lorem ipsum dolor amet ledger lorem dolor ipsum amet
</Description>
</Box>
<OnboardingFooter horizontal align="center" justify="flex-end" flow={2}>
<Button small outline onClick={() => prevStep()}>
Go Back
</Button>
<Button small primary onClick={() => nextStep()}>
Continue
</Button>
</OnboardingFooter>
</Box>
)
}

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

@ -0,0 +1,34 @@
// @flow
import React from 'react'
import Box from 'components/base/Box'
import Button from 'components/base/Button'
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 { finish, jumpStep } = props
return (
<Box sticky alignItems="center" justifyContent="center">
<Box align="center">
<Title>This is ENJOY THE APP screen. 1 line is the maximum</Title>
<Description>
This is a long text, please replace it with the final wording once its done.
<br />
Lorem ipsum dolor amet ledger lorem dolor ipsum amet
</Description>
<IconFinishOnboarding size={136} />
<Button small primary onClick={() => finish()}>
Open App
</Button>
<Box onClick={() => jumpStep('start')} style={{ padding: 15 }}>
<Text color="smoke">I want to go back to Onboarding</Text>
</Box>
</Box>
</Box>
)
}

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

@ -0,0 +1,85 @@
// @flow
import React, { PureComponent } from 'react'
import Box from 'components/base/Box'
import Button from 'components/base/Button'
import { Title, Description, OnboardingFooter } from '../helperComponents'
import type { StepProps } from '..'
type State = {
currentDevice: {
manufacturer: string,
release: number,
},
showDeviceInfo: boolean,
showError: boolean,
}
// 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,
}
handleCheckDevice = () => {
const currentDeviceInfo = this.props.getDeviceInfo()
if (currentDeviceInfo) {
this.setState({ showError: false, currentDevice: currentDeviceInfo, showDeviceInfo: true })
} else {
this.setState({ showError: true })
}
}
render() {
const { nextStep, prevStep, jumpStep } = this.props
const { showDeviceInfo, currentDevice, showError } = this.state
return (
<Box sticky alignItems="center" justifyContent="center">
<Box align="center">
<Title>This is GENUINE CHECK screen. 1 line is the maximum</Title>
<Description>
This is a long text, please replace it with the final wording once its done.
<br />
Lorem ipsum dolor amet ledger lorem dolor ipsum amet
</Description>
</Box>
<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>
)}
{showError && (
<Box>
<Description color="red">Connect your device please</Description>
</Box>
)}
<OnboardingFooter horizontal align="center" justify="flex-end" flow={2}>
<Button small outline onClick={() => prevStep()}>
Go Back
</Button>
<Button big danger onClick={() => jumpStep('init')}>
Test JUMP!
</Button>
<Button small primary onClick={() => nextStep()}>
Continue
</Button>
</OnboardingFooter>
</Box>
)
}
}
export default GenuineCheck

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

@ -0,0 +1,90 @@
// @flow
import React from 'react'
import styled from 'styled-components'
import { shell } from 'electron'
import Box from 'components/base/Box'
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 (
<Box sticky alignItems="center" justifyContent="center">
<Box align="center">
<Title>This is the title of the screen. 1 line is the maximum</Title>
<Description>
This is a long text, please replace it with the final wording once its done.
<br />
Lorem ipsum dolor amet ledger lorem dolor ipsum amet
</Description>
<Box style={{ paddingBottom: 10 }}>
<Inner>
<DeviceContainer onClick={() => nextStep()}>
{/* colors are temp, we don't have icons now */}
<DeviceIcon style={{ color: '#66be54' }}>
<IconUser size={24} />
</DeviceIcon>
<TrackChoiceTitle>Clean Nano S setup</TrackChoiceTitle>
<Description>Please replace it with the final wording once its done.</Description>
</DeviceContainer>
<DeviceContainer onClick={() => jumpStep('choosePIN')}>
<DeviceIcon style={{ color: '#66be54' }}>
<IconUser size={24} />
</DeviceIcon>
<TrackChoiceTitle>Existing seed + Clean setup</TrackChoiceTitle>
<Description>Please replace it with the final wording once its done.</Description>
</DeviceContainer>
</Inner>
</Box>
<Box>
<Inner>
<DeviceContainer onClick={() => nextStep()}>
<DeviceIcon style={{ color: '#6490f1' }}>
<IconUser size={24} />
</DeviceIcon>
<TrackChoiceTitle>Migrate accounts</TrackChoiceTitle>
<Description>Please replace it with the final wording once its done.</Description>
</DeviceContainer>
<DeviceContainer onClick={handleOpenLink('https://www.ledger.fr/')}>
<DeviceIcon style={{ color: '#ea2e41' }}>
<IconUser size={24} />
</DeviceIcon>
<TrackChoiceTitle>Not a user, but would love to</TrackChoiceTitle>
<Description>Please replace it with the final wording once its done.</Description>
</DeviceContainer>
</Inner>
</Box>
</Box>
</Box>
)
}
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;
padding: 10px;
`
export const TrackChoiceTitle = styled(Box).attrs({
width: 152,
height: 27,
ff: 'Museo Sans|Regular',
fontSize: 5,
color: 'dark',
})``

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

@ -0,0 +1,93 @@
// @flow
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 Text from 'components/base/Text'
import IconSetPassword from 'icons/LockScreen'
import PasswordModal from 'components/SettingsPage/PasswordModal'
import type { StepProps } from '..'
import { Title, Description, OnboardingFooter } from '../helperComponents'
type State = {
isPasswordModalOpened: boolean,
isPasswordEnabled: boolean,
}
class SetPassword extends PureComponent<StepProps, State> {
state = {
isPasswordModalOpened: false,
isPasswordEnabled: false,
}
handleOpenPasswordModal = () => {
this.setState({ isPasswordModalOpened: true })
}
handleClosePasswordModal = () => {
this.setState({ isPasswordModalOpened: false })
}
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 (
<Box sticky alignItems="center" justifyContent="center">
<Box align="center">
<Title>This is SET PASSWORD screen. 1 line is the maximum</Title>
<Description>
This is a long text, please replace it with the final wording once its done.
<br />
Lorem ipsum dolor amet ledger lorem dolor ipsum amet
</Description>
<IconSetPassword size={136} />
<Button small primary onClick={() => this.handleOpenPasswordModal()}>
Set Password
</Button>
{/* 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 && (
<PasswordModal
t={t}
isOpened={isPasswordModalOpened}
onClose={this.handleClosePasswordModal}
onChangePassword={this.handleChangePassword}
isPasswordEnabled={isPasswordEnabled}
currentPasswordHash=""
/>
)}
<Box onClick={() => nextStep()} style={{ padding: 15 }}>
<Text color="smoke">I do not want to set it up</Text>
</Box>
</Box>
<OnboardingFooter horizontal flow={2}>
<Button small outline onClick={() => prevStep()}>
Go Back
</Button>
<Button small primary onClick={() => nextStep()}>
Continue
</Button>
</OnboardingFooter>
</Box>
)
}
}
export default SetPassword

27
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 (
<Box sticky alignItems="center" justifyContent="center">
<Box align="center" alignItems="center">
<LedgerLogo size={136} />
<Title>Ledger Live</Title>
<Title>Welcome to the new Ledger Live Desktop app.</Title>
<Description>Lets get started!</Description>
<Button small primary onClick={() => jumpStep('init')}>
Get Started
</Button>
</Box>
</Box>
)
}

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

@ -0,0 +1,33 @@
// @flow
import React from 'react'
import Box from 'components/base/Box'
import Button from 'components/base/Button'
import { Title, Description, OnboardingFooter } from '../helperComponents'
import type { StepProps } from '..'
export default (props: StepProps) => {
const { nextStep, prevStep } = props
return (
<Box sticky alignItems="center" justifyContent="center">
<Box align="center">
<Title>This is WRITE SEED screen. 1 line is the maximum</Title>
<Description>
This is a long text, please replace it with the final wording once its done.
<br />
Lorem ipsum dolor amet ledger lorem dolor ipsum amet
</Description>
</Box>
<OnboardingFooter horizontal align="center" justify="flex-end" flow={2}>
<Button small outline onClick={() => prevStep()}>
Go Back
</Button>
<Button small primary onClick={() => nextStep()}>
Continue
</Button>
</OnboardingFooter>
</Box>
)
}

2
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({

4
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,
})

147
src/reducers/onboarding.js

@ -0,0 +1,147 @@
// @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: 'start',
steps: [
{
name: 'start',
external: true,
options: {
showFooter: false,
showBackground: true,
showBreadcrumb: false,
},
},
{
name: 'init',
external: true,
options: {
showFooter: false,
showBackground: true,
showBreadcrumb: false,
},
},
{
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: 'setPassword',
label: 'Password:translated',
options: {
showFooter: false,
showBackground: true,
showBreadcrumb: true,
},
},
{
name: 'analytics',
label: 'Analytics & Bug report:translated',
options: {
showFooter: false,
showBackground: true,
showBreadcrumb: true,
},
},
{
name: 'finish',
external: true,
options: {
showFooter: false,
showBackground: true,
showBreadcrumb: false,
},
},
],
}
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')
Loading…
Cancel
Save