Browse Source

Merge pull request #587 from valpinkman/refactor/connection-workflow

Refactor/connection workflow
master
Valentin D. Pinkman 7 years ago
committed by GitHub
parent
commit
3a2898de4d
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 13
      src/components/DeviceConnect/index.js
  2. 49
      src/components/GenuineCheckModal/index.js
  3. 6
      src/components/ManagerPage/index.js
  4. 13
      src/components/Onboarding/steps/GenuineCheck.js
  5. 15
      src/components/TriggerOnMount/index.js
  6. 0
      src/components/Workflow/EnsureDashboard.js
  7. 0
      src/components/Workflow/EnsureDevice.js
  8. 3
      src/components/Workflow/EnsureGenuine.js
  9. 172
      src/components/Workflow/WorkflowDefault.js
  10. 4
      src/components/Workflow/WorkflowWithIcon.js
  11. 16
      src/components/Workflow/index.js
  12. 2
      src/components/modals/Debug.js

13
src/components/DeviceConnect/index.js

@ -22,6 +22,8 @@ import IconHome from 'icons/Home'
import * as IconDevice from 'icons/device' import * as IconDevice from 'icons/device'
// TODO: CHECK IF COMPONENT CAN BE REMOVED
const Step = styled(Box).attrs({ const Step = styled(Box).attrs({
borderRadius: 1, borderRadius: 1,
justifyContent: 'center', justifyContent: 'center',
@ -147,7 +149,10 @@ type Props = {
deviceSelected: ?Device, deviceSelected: ?Device,
onChangeDevice: Device => void, onChangeDevice: Device => void,
t: T, t: T,
error: ?Error, errors: ?{
genuineError: ?Error,
dashboardError: ?Error,
},
} }
const emitChangeDevice = props => { const emitChangeDevice = props => {
@ -187,7 +192,7 @@ class DeviceConnect extends PureComponent<Props> {
genuineCheckStatus, genuineCheckStatus,
withGenuineCheck, withGenuineCheck,
appOpened, appOpened,
error, errors,
currency, currency,
t, t,
onChangeDevice, onChangeDevice,
@ -304,11 +309,11 @@ class DeviceConnect extends PureComponent<Props> {
</Step> </Step>
)} )}
{appState.fail ? ( {errors && (errors.genuineError || errors.dashboardError) ? (
<Info hasErrors> <Info hasErrors>
<IconInfoCircle size={12} /> <IconInfoCircle size={12} />
<Box shrink selectable> <Box shrink selectable>
<TranslatedError error={error} /> <TranslatedError error={errors.genuineError || errors.dashboardError} />
</Box> </Box>
</Info> </Info>
) : null} ) : null}

49
src/components/GenuineCheckModal/index.js

@ -1,26 +1,16 @@
// @flow // @flow
import logger from 'logger'
import React, { PureComponent } from 'react' import React, { PureComponent } from 'react'
import { compose } from 'redux'
import { connect } from 'react-redux'
import { translate } from 'react-i18next' import { translate } from 'react-i18next'
import type { T, Device } from 'types/common' import type { T } from 'types/common'
import { getCurrentDevice } from 'reducers/devices'
import DeviceConnect from 'components/DeviceConnect'
import EnsureDeviceApp from 'components/EnsureDeviceApp'
import Modal, { ModalBody, ModalTitle, ModalContent } from 'components/base/Modal' import Modal, { ModalBody, ModalTitle, ModalContent } from 'components/base/Modal'
import Workflow from 'components/Workflow'
const mapStateToProps = state => ({ import WorkflowDefault from 'components/Workflow/WorkflowDefault'
currentDevice: getCurrentDevice(state),
})
type Props = { type Props = {
t: T, t: T,
currentDevice: ?Device,
onGenuineCheck: (isGenuine: boolean) => void, onGenuineCheck: (isGenuine: boolean) => void,
} }
@ -28,33 +18,23 @@ type State = {}
class GenuineCheck extends PureComponent<Props, State> { class GenuineCheck extends PureComponent<Props, State> {
renderBody = ({ onClose }) => { renderBody = ({ onClose }) => {
const { t, currentDevice, onGenuineCheck } = this.props const { t, onGenuineCheck } = this.props
// TODO: use the real devices list. for now we force choosing only // TODO: use the real devices list. for now we force choosing only
// the current device because we don't handle multi device in MVP // the current device because we don't handle multi device in MVP
const reducedDevicesList = currentDevice ? [currentDevice] : []
return ( return (
<ModalBody onClose={onClose}> <ModalBody onClose={onClose}>
<ModalTitle>{t('app:genuinecheck.modal.title')}</ModalTitle> <ModalTitle>{t('app:genuinecheck.modal.title')}</ModalTitle>
<ModalContent> <ModalContent>
<EnsureDeviceApp <Workflow
deviceSelected={currentDevice} onGenuineCheck={isGenuine => onGenuineCheck(isGenuine)}
withGenuineCheck renderDefault={(device, deviceInfo, isGenuine, errors) => (
onGenuineCheck={onGenuineCheck} <WorkflowDefault
onStatusChange={status => { device={device}
logger.log(`status changed to ${status}`) deviceInfo={deviceInfo}
}} isGenuine={isGenuine}
render={({ appStatus, genuineCheckStatus, deviceSelected, error }) => ( errors={errors} // TODO: FIX ERRORS
<DeviceConnect
appOpened={
appStatus === 'success' ? 'success' : appStatus === 'fail' ? 'fail' : null
}
withGenuineCheck
genuineCheckStatus={genuineCheckStatus}
devices={reducedDevicesList}
deviceSelected={deviceSelected}
error={error}
/> />
)} )}
/> />
@ -69,7 +49,4 @@ class GenuineCheck extends PureComponent<Props, State> {
} }
} }
export default compose( export default translate()(GenuineCheck)
connect(mapStateToProps),
translate(),
)(GenuineCheck)

6
src/components/ManagerPage/index.js

@ -6,8 +6,8 @@ import React from 'react'
import type { Node } from 'react' import type { Node } from 'react'
import type { Device } from 'types/common' import type { Device } from 'types/common'
import Workflow from './Workflow' import Workflow from 'components/Workflow'
import WorkflowDefault from './WorkflowDefault' import WorkflowWithIcon from 'components/Workflow/WorkflowWithIcon'
import Dashboard from './Dashboard' import Dashboard from './Dashboard'
type DeviceInfo = { type DeviceInfo = {
@ -43,7 +43,7 @@ function ManagerPage(): Node {
genuineError: ?Error, genuineError: ?Error,
}, },
) => ( ) => (
<WorkflowDefault <WorkflowWithIcon
device={device} device={device}
deviceInfo={deviceInfo} deviceInfo={deviceInfo}
errors={errors} errors={errors}

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

@ -87,13 +87,14 @@ class GenuineCheck extends PureComponent<StepProps, State> {
} }
handleOpenGenuineCheckModal = () => this.setState({ isGenuineCheckModalOpened: true }) handleOpenGenuineCheckModal = () => this.setState({ isGenuineCheckModalOpened: true })
handleCloseGenuineCheckModal = () => this.setState({ isGenuineCheckModalOpened: false }) handleCloseGenuineCheckModal = (cb?: Function) =>
this.setState(state => ({ ...state, isGenuineCheckModalOpened: false }), () => cb && cb())
handleGenuineCheck = async isGenuine => { handleGenuineCheck = isGenuine => {
await new Promise(r => setTimeout(r, 1e3)) // let's wait a bit before closing modal this.handleCloseGenuineCheckModal(() => {
this.handleCloseGenuineCheckModal() this.props.updateGenuineCheck({
this.props.updateGenuineCheck({ isDeviceGenuine: isGenuine,
isDeviceGenuine: isGenuine, })
}) })
} }

15
src/components/TriggerOnMount/index.js

@ -0,0 +1,15 @@
// @flow
import { PureComponent } from 'react'
type Props = {
callback: () => void,
}
class TriggerOnMount extends PureComponent<Props> {
componentDidMount() {
const { callback } = this.props
callback()
}
}
export default TriggerOnMount

0
src/components/ManagerPage/EnsureDashboard.js → src/components/Workflow/EnsureDashboard.js

0
src/components/ManagerPage/EnsureDevice.js → src/components/Workflow/EnsureDevice.js

3
src/components/ManagerPage/EnsureGenuine.js → src/components/Workflow/EnsureGenuine.js

@ -2,7 +2,6 @@
import { PureComponent } from 'react' import { PureComponent } from 'react'
import isEqual from 'lodash/isEqual' import isEqual from 'lodash/isEqual'
import type { Node } from 'react'
import type { Device } from 'types/common' import type { Device } from 'types/common'
import getIsGenuine from 'commands/getIsGenuine' import getIsGenuine from 'commands/getIsGenuine'
@ -20,7 +19,7 @@ type DeviceInfos = {
type Props = { type Props = {
device: ?Device, device: ?Device,
infos: ?DeviceInfos, infos: ?DeviceInfos,
children: (isGenuine: ?boolean, error: ?Error) => Node, children: (isGenuine: ?boolean, error: ?Error) => *,
} }
type State = { type State = {

172
src/components/Workflow/WorkflowDefault.js

@ -0,0 +1,172 @@
// @flow
/* eslint-disable react/jsx-no-literals */
import React from 'react'
import { Trans, translate } from 'react-i18next'
import styled from 'styled-components'
import isNull from 'lodash/isNull'
import type { Device } from 'types/common'
import Box from 'components/base/Box'
import Spinner from 'components/base/Spinner'
import IconCheck from 'icons/Check'
import IconExclamationCircle from 'icons/ExclamationCircle'
import IconUsb from 'icons/Usb'
import IconHome from 'icons/Home'
const Step = styled(Box).attrs({
borderRadius: 1,
justifyContent: 'center',
fontSize: 4,
})`
border: 1px solid
${p =>
p.validated
? p.theme.colors.wallet
: p.hasErrors
? p.theme.colors.alertRed
: p.theme.colors.fog};
`
const StepIcon = styled(Box).attrs({
alignItems: 'center',
justifyContent: 'center',
})`
width: 64px;
`
const StepContent = styled(Box).attrs({
color: 'dark',
horizontal: true,
alignItems: 'center',
})`
height: 60px;
line-height: 1.2;
strong {
font-weight: 600;
}
`
const WrapperIconCurrency = styled(Box).attrs({
alignItems: 'center',
justifyContent: 'center',
})`
border: 1px solid ${p => p.theme.colors[p.color]};
border-radius: 8px;
height: 24px;
width: 24px;
`
const StepCheck = ({ checked, hasErrors }: { checked: boolean, hasErrors?: boolean }) => (
<Box pr={5}>
{checked ? (
<Box color="wallet">
<IconCheck size={16} />
</Box>
) : hasErrors ? (
<Box color="alertRed">
<IconExclamationCircle size={16} />
</Box>
) : (
<Spinner size={16} />
)}
</Box>
)
StepCheck.defaultProps = {
hasErrors: false,
}
type DeviceInfo = {
targetId: number | string,
version: string,
final: boolean,
mcu: boolean,
}
type Error = {
message: string,
stack: string,
}
type Props = {
// t: T,
device: ?Device,
deviceInfo: ?DeviceInfo,
errors: {
dashboardError: ?Error,
genuineError: ?Error,
},
isGenuine: boolean,
}
const WorkflowDefault = ({ device, deviceInfo, errors, isGenuine }: Props) => (
<Box flow={4} ff="Open Sans">
<Step validated={!!device}>
<StepContent>
<StepIcon>
<IconUsb size={36} />
</StepIcon>
<Box grow shrink>
<Trans i18nKey="app:deviceConnect.step1.connect" parent="div">
Connect your <strong>Ledger device</strong> to your computer and enter your{' '}
<strong>PIN code</strong> on your device
</Trans>
</Box>
<StepCheck checked={!!device} />
</StepContent>
</Step>
<Step validated={!!device && !!deviceInfo} hasErrors={!!device && !!errors.dashboardError}>
<StepContent>
<StepIcon>
<WrapperIconCurrency>
<IconHome size={12} />
</WrapperIconCurrency>
</StepIcon>
<Box grow shrink>
<Trans i18nKey="deviceConnect:dashboard.open" parent="div">
{'Go to the '}
<strong>{'dashboard'}</strong>
{' on your device'}
</Trans>
</Box>
<StepCheck
checked={!!device && !!deviceInfo}
hasErrors={!!device && !!errors.dashboardError}
/>
</StepContent>
</Step>
{/* GENUINE CHECK */}
{/* ------------- */}
<Step
validated={(!!device && !isNull(isGenuine) && isGenuine && !errors.genuineError) || undefined}
hasErrors={(!!device && !isNull(isGenuine) && !isGenuine) || errors.genuineError || undefined}
>
<StepContent>
<StepIcon>
<WrapperIconCurrency>
<IconCheck size={12} />
</WrapperIconCurrency>
</StepIcon>
<Box grow shrink>
<Trans i18nKey="deviceConnect:stepGenuine.open" parent="div">
{'Confirm '}
<strong>{'authentication'}</strong>
{' on your device'}
</Trans>
</Box>
<StepCheck
checked={!!device && !isNull(isGenuine) && isGenuine}
hasErrors={(!!device && !isNull(isGenuine) && !isGenuine) || undefined}
/>
</StepContent>
</Step>
</Box>
)
export default translate()(WorkflowDefault)

4
src/components/ManagerPage/WorkflowDefault.js → src/components/Workflow/WorkflowWithIcon.js

@ -105,7 +105,7 @@ type Props = {
isGenuine: boolean, isGenuine: boolean,
} }
const WorkflowDefault = ({ device, deviceInfo, errors, isGenuine, t }: Props) => ( const WorkflowWithIcon = ({ device, deviceInfo, errors, isGenuine, t }: Props) => (
<Box align="center" justify="center" sticky> <Box align="center" justify="center" sticky>
<Box align="center" style={{ maxWidth: 460, padding: '0 10px' }}> <Box align="center" style={{ maxWidth: 460, padding: '0 10px' }}>
<img <img
@ -194,4 +194,4 @@ const WorkflowDefault = ({ device, deviceInfo, errors, isGenuine, t }: Props) =>
</Box> </Box>
) )
export default translate()(WorkflowDefault) export default translate()(WorkflowWithIcon)

16
src/components/ManagerPage/Workflow.js → src/components/Workflow/index.js

@ -31,9 +31,10 @@ type Props = {
genuineError: ?Error, genuineError: ?Error,
}, },
) => Node, ) => Node,
renderMcuUpdate: (deviceInfo: DeviceInfo) => Node, renderMcuUpdate?: (deviceInfo: DeviceInfo) => Node,
renderFinalUpdate: (deviceInfo: DeviceInfo) => Node, renderFinalUpdate?: (deviceInfo: DeviceInfo) => Node,
renderDashboard: (device: Device, deviceInfo: DeviceInfo) => Node, renderDashboard?: (device: Device, deviceInfo: DeviceInfo, isGenuine: boolean) => Node,
onGenuineCheck?: (isGenuine: boolean) => void,
renderError?: (dashboardError: ?Error, genuineError: ?Error) => Node, renderError?: (dashboardError: ?Error, genuineError: ?Error) => Node,
} }
type State = {} type State = {}
@ -46,6 +47,7 @@ class Workflow extends PureComponent<Props, State> {
renderMcuUpdate, renderMcuUpdate,
renderError, renderError,
renderDefault, renderDefault,
onGenuineCheck,
} = this.props } = this.props
return ( return (
<EnsureDevice> <EnsureDevice>
@ -63,16 +65,18 @@ class Workflow extends PureComponent<Props, State> {
}) })
} }
if (deviceInfo && deviceInfo.mcu) { if (deviceInfo && deviceInfo.mcu && renderMcuUpdate) {
return renderMcuUpdate(deviceInfo) return renderMcuUpdate(deviceInfo)
} }
if (deviceInfo && deviceInfo.final) { if (deviceInfo && deviceInfo.final && renderFinalUpdate) {
return renderFinalUpdate(deviceInfo) return renderFinalUpdate(deviceInfo)
} }
if (isGenuine && deviceInfo && device && !dashboardError && !genuineError) { if (isGenuine && deviceInfo && device && !dashboardError && !genuineError) {
return renderDashboard(device, deviceInfo) if (onGenuineCheck) onGenuineCheck(isGenuine)
if (renderDashboard) return renderDashboard(device, deviceInfo, isGenuine)
} }
return renderDefault(device, deviceInfo, isGenuine, { return renderDefault(device, deviceInfo, isGenuine, {

2
src/components/modals/Debug.js

@ -7,7 +7,7 @@ import Modal, { ModalBody, ModalTitle, ModalContent } from 'components/base/Moda
import Button from 'components/base/Button' import Button from 'components/base/Button'
import Box from 'components/base/Box' import Box from 'components/base/Box'
import Input from 'components/base/Input' import Input from 'components/base/Input'
import EnsureDevice from 'components/ManagerPage/EnsureDevice' import EnsureDevice from 'components/Workflow/EnsureDevice'
import { getDerivations } from 'helpers/derivations' import { getDerivations } from 'helpers/derivations'
import getAddress from 'commands/getAddress' import getAddress from 'commands/getAddress'
import testInterval from 'commands/testInterval' import testInterval from 'commands/testInterval'

Loading…
Cancel
Save