Browse Source

Create GenuineCheck component

master
meriadec 7 years ago
parent
commit
d4283dd8c7
No known key found for this signature in database GPG Key ID: 1D2FC2305E2CB399
  1. 148
      src/components/GenuineCheck.js
  2. 1
      src/config/constants.js
  3. 24
      src/helpers/promise.js
  4. 2
      static/i18n/en/errors.yml

148
src/components/GenuineCheck.js

@ -0,0 +1,148 @@
// @flow
import React, { PureComponent } from 'react'
import { timeout } from 'rxjs/operators/timeout'
import { connect } from 'react-redux'
import { compose } from 'redux'
import { translate, Trans } from 'react-i18next'
import type { T, Device } from 'types/common'
import type { DeviceInfo } from 'helpers/devices/getDeviceInfo'
import { GENUINE_TIMEOUT, DEVICE_INFOS_TIMEOUT } from 'config/constants'
import { createCancelablePolling } from 'helpers/promise'
import { getCurrentDevice } from 'reducers/devices'
import { createCustomErrorClass } from 'helpers/errors'
import getDeviceInfo from 'commands/getDeviceInfo'
import getIsGenuine from 'commands/getIsGenuine'
import DeviceInteraction from 'components/DeviceInteraction'
import Text from 'components/base/Text'
import IconUsb from 'icons/Usb'
import IconHome from 'icons/Home'
import IconEye from 'icons/Eye'
const DeviceNotGenuineError = createCustomErrorClass('DeviceNotGenuine')
type Props = {
t: T,
onSuccess: void => void,
onFail: Error => void,
onUnavailable: Error => void,
device: ?Device,
}
const usbIcon = <IconUsb size={36} />
const homeIcon = <IconHome size={24} />
const eyeIcon = <IconEye size={24} />
const mapStateToProps = state => ({
device: getCurrentDevice(state),
})
const Bold = props => <Text ff="Open Sans|Bold" {...props} />
class GenuineCheck extends PureComponent<Props> {
connectInteractionHandler = () =>
createCancelablePolling(() => {
const { device } = this.props
if (!device) return Promise.reject()
return Promise.resolve(device)
})
checkDashboardInteractionHandler = ({ device }: { device: Device }) =>
createCancelablePolling(() =>
getDeviceInfo
.send({ devicePath: device.path })
.pipe(timeout(DEVICE_INFOS_TIMEOUT))
.toPromise(),
)
checkGenuineInteractionHandler = async ({
device,
infos,
}: {
device: Device,
infos: DeviceInfo,
}) => {
const res = await getIsGenuine
.send({
devicePath: device.path,
deviceInfo: infos,
})
.pipe(timeout(GENUINE_TIMEOUT))
.toPromise()
const isGenuine = res === '0000'
if (!isGenuine) {
return Promise.reject(new Error('Device not genuine')) // TODO: use custom error class
}
return Promise.resolve(true)
}
handleFail = (err: Error) => {
const { onFail, onUnavailable } = this.props
if (err instanceof DeviceNotGenuineError) {
onFail(err)
} else {
onUnavailable(err)
}
}
render() {
const { onSuccess } = this.props
const steps = [
{
id: 'device',
title: (
<Trans i18nKey="app:deviceConnect.step1.connect" parent="div">
{'Connect and unlock your '}
<Bold>{'Ledger device'}</Bold>
</Trans>
),
icon: usbIcon,
run: this.connectInteractionHandler,
},
{
id: 'infos',
title: (
<Trans i18nKey="deviceConnect:dashboard.open" parent="div">
{'Navigate to the '}
<Bold>{'dashboard'}</Bold>
{' on your device'}
</Trans>
),
icon: homeIcon,
run: this.checkDashboardInteractionHandler,
},
{
id: 'isGenuine',
title: (
<Trans i18nKey="deviceConnect:stepGenuine.open" parent="div">
{'Allow the '}
<Bold>{'Ledger Manager'}</Bold>
{' on your device'}
</Trans>
),
icon: eyeIcon,
run: this.checkGenuineInteractionHandler,
},
]
return (
<DeviceInteraction
waitBeforeSuccess={500}
steps={steps}
onSuccess={onSuccess}
onFail={this.handleFail}
/>
)
}
}
export default compose(
translate(),
connect(mapStateToProps),
)(GenuineCheck)

1
src/config/constants.js

@ -26,6 +26,7 @@ export const LISTEN_DEVICES_POLLING_INTERVAL = intFromEnv('LISTEN_DEVICES_POLLIN
export const SYNC_MAX_CONCURRENT = intFromEnv('LEDGER_SYNC_MAX_CONCURRENT', 1)
export const SYNC_BOOT_DELAY = 2 * 1000
export const SYNC_ALL_INTERVAL = 120 * 1000
export const DEVICE_INFOS_TIMEOUT = intFromEnv('DEVICE_INFOS_TIMEOUT', 5 * 1000)
export const GENUINE_TIMEOUT = intFromEnv('GENUINE_TIMEOUT', 120 * 1000)
export const SYNC_TIMEOUT = intFromEnv('SYNC_TIMEOUT', 30 * 1000)
export const OUTDATED_CONSIDERED_DELAY = intFromEnv('OUTDATED_CONSIDERED_DELAY', 5 * 60 * 1000)

24
src/helpers/promise.js

@ -31,3 +31,27 @@ export function retry<A>(f: () => Promise<A>, options?: $Shape<typeof defaults>)
export function idleCallback() {
return new Promise(resolve => window.requestIdleCallback(resolve))
}
export function createCancelablePolling(
job: any => Promise<any>,
{ pollingInterval = 500 }: { pollingInterval: number } = {},
) {
let isUnsub = false
const unsubscribe = () => (isUnsub = true)
const getUnsub = () => isUnsub
const promise = new Promise(resolve => {
async function poll() {
try {
const res = await job()
if (getUnsub()) return
resolve(res)
} catch (err) {
await delay(pollingInterval)
if (getUnsub()) return
poll()
}
}
poll()
})
return { unsubscribe, promise }
}

2
static/i18n/en/errors.yml

@ -29,3 +29,5 @@ ManagerDeviceLocked: Device is locked
ManagerAppAlreadyInstalled: App is already installed
ManagerAppRelyOnBTC: You must install Bitcoin application first
ManagerUninstallBTCDep: You must uninstall other altcoins first
DeviceNotGenuine: Device is not genuine

Loading…
Cancel
Save