Browse Source

skeleton of onboarding flow and steps

master
NastiaS 7 years ago
parent
commit
a536f80c7e
  1. 15
      src/components/Onboarding/OnboardingFooter.js
  2. 10
      src/components/Onboarding/helperComponents.js
  3. 33
      src/components/Onboarding/index.js
  4. 11
      src/components/Onboarding/steps/Analytics.js
  5. 7
      src/components/Onboarding/steps/ChoosePIN.js
  6. 22
      src/components/Onboarding/steps/Finish.js
  7. 102
      src/components/Onboarding/steps/GenuineCheck.js
  8. 31
      src/components/Onboarding/steps/Init.js
  9. 114
      src/components/Onboarding/steps/SetPassword.js
  10. 27
      src/components/Onboarding/steps/Start.js
  11. 7
      src/components/Onboarding/steps/WriteSeed.js
  12. 2
      src/components/base/Modal/index.js
  13. 25
      src/reducers/onboarding.js

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

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

33
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<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, onboarding, prevStep, nextStep, jumpStep, t } = this.props
if (hasCompletedOnboarding) {
return null
}
@ -90,6 +111,8 @@ class Onboarding extends PureComponent<Props> {
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,

11
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
</Description>
<DeviceIcon>
<IconBlue size={60} />
<IconAnalytics size={136} />
</DeviceIcon>
<Box horizontal flow={2} align="center">
<CheckBox isChecked />
@ -43,14 +42,14 @@ export default (props: StepProps) => {
</AnalyticsText>
</Box>
</Box>
<ModalFooter horizontal align="center" justify="flex-end" flow={2}>
<OnboardingFooter horizontal align="center" justify="flex-end" flow={2}>
<Button small outline onClick={() => prevStep()}>
Go Back
</Button>
<Button small primary onClick={() => nextStep()}>
Continue
</Button>
</ModalFooter>
</OnboardingFooter>
</Box>
)
}

7
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
</Description>
</Box>
<ModalFooter horizontal align="center" justify="flex-end" flow={2}>
<OnboardingFooter horizontal align="center" justify="flex-end" flow={2}>
<Button small outline onClick={() => prevStep()}>
Go Back
</Button>
<Button small primary onClick={() => nextStep()}>
Continue
</Button>
</ModalFooter>
</OnboardingFooter>
</Box>
)
}

22
src/components/Onboarding/steps/SetUpWalletEnv.js → 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 (
<Box sticky alignItems="center" justifyContent="center">
<Box align="center">
<Title>This is WALLET SETUP screen. 1 line is the maximum</Title>
<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>
</Box>
<ModalFooter horizontal align="center" justify="flex-end" flow={2}>
<Button small outline onClick={() => prevStep()}>
Go Back
</Button>
<Button small primary onClick={() => nextStep()}>
Continue
<IconFinishOnboarding size={136} />
<Button small primary onClick={() => finish()}>
Open App
</Button>
</ModalFooter>
<Box onClick={() => jumpStep('start')} style={{ padding: 15 }}>
<Text color="smoke">I want to go back to Onboarding</Text>
</Box>
</Box>
</Box>
)
}

102
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 (
<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>
<ModalFooter 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
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>
</ModalFooter>
</Box>
)
{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

31
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 (
<Box sticky alignItems="center" justifyContent="center">
@ -22,34 +23,37 @@ export default (props: StepProps) => {
<br />
Lorem ipsum dolor amet ledger lorem dolor ipsum amet
</Description>
<Box>
<Box style={{ paddingBottom: 10 }}>
<Inner>
<DeviceContainer onClick={() => nextStep()}>
<DeviceIcon>
<IconNanoS size={24} />
{/* 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('genuineCheck')}>
<DeviceIcon>
<IconBlue size={24} />
<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>
<IconNanoS size={24} />
<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>
<DeviceIcon>
<IconBlue size={24} />
<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>
@ -75,6 +79,7 @@ const DeviceIcon = styled(Box).attrs({
color: 'graphite',
})`
width: 55px;
padding: 10px;
`
export const TrackChoiceTitle = styled(Box).attrs({
width: 152,

114
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<StepProps, State> {
state = {
isPasswordModalOpened: false,
isPasswordEnabled: false,
}
handleOpenPasswordModal = () => {
this.setState({ isPasswordModalOpened: true })
}
handleClosePasswordModal = () => {
this.setState({ isPasswordModalOpened: false })
}
const { nextStep, prevStep } = props
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>
<IconLock size={30} />
<Button small primary onClick={() => handleSetPassword()}>
Set Password
</Button>
<Box onClick={() => nextStep()} style={{ padding: 15 }}>
<Text color="smoke">I do not want to set it up</Text>
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>
<ModalFooter horizontal align="center" justify="flex-end" flow={2}>
<Button small outline onClick={() => prevStep()}>
Go Back
</Button>
<Button small primary onClick={() => nextStep()}>
Continue
</Button>
</ModalFooter>
</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>
)
}

7
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
</Description>
</Box>
<ModalFooter horizontal align="center" justify="flex-end" flow={2}>
<OnboardingFooter horizontal align="center" justify="flex-end" flow={2}>
<Button small outline onClick={() => prevStep()}>
Go Back
</Button>
<Button small primary onClick={() => nextStep()}>
Continue
</Button>
</ModalFooter>
</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({

25
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,
},
},
],

Loading…
Cancel
Save