Browse Source

Merge pull request #852 from valpinkman/feat/firmware-update-errors

minimal error handling for mvp
master
Gaëtan Renaudeau 7 years ago
committed by GitHub
parent
commit
dc0e6eb957
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 0
      src/components/EnsureDevice.js
  2. 19
      src/components/ManagerPage/FirmwareUpdate.js
  3. 2
      src/components/modals/UpdateFirmware/Disclaimer.js
  4. 30
      src/components/modals/UpdateFirmware/Installing.js
  5. 18
      src/components/modals/UpdateFirmware/index.js
  6. 56
      src/components/modals/UpdateFirmware/steps/01-step-install-full-firmware.js
  7. 36
      src/components/modals/UpdateFirmware/steps/02-step-flash-mcu.js
  8. 34
      src/components/modals/UpdateFirmware/steps/03-step-confirmation.js
  9. 6
      src/helpers/firmware/installFinalFirmware.js
  10. 16
      src/helpers/firmware/installMcu.js
  11. 9
      src/helpers/firmware/installOsuFirmware.js
  12. 3
      static/i18n/en/app.yml
  13. 5
      static/i18n/en/errors.yml

0
src/components/EnsureDevice.js

19
src/components/ManagerPage/FirmwareUpdate.js

@ -102,20 +102,17 @@ class FirmwareUpdate extends PureComponent<Props, State> {
invariant(latestFirmware, 'did not find a new firmware or firmware is not set')
this.setState({ modal: 'install' })
const { success } = await installOsuFirmware
const result = await installOsuFirmware
.send({ devicePath: device.path, firmware: latestFirmware, targetId: deviceInfo.targetId })
.toPromise()
return success
}
installFinalFirmware = async (device: Device) => {
const { success } = await installFinalFirmware.send({ devicePath: device.path }).toPromise()
return success
return result
}
flashMCU = async (device: Device) => {
await installMcu.send({ devicePath: device.path }).toPromise()
}
installFinalFirmware = (device: Device) =>
installFinalFirmware.send({ devicePath: device.path }).toPromise()
flashMCU = async (device: Device) => installMcu.send({ devicePath: device.path }).toPromise()
handleCloseModal = () => this.setState({ modal: 'closed' })
@ -123,6 +120,8 @@ class FirmwareUpdate extends PureComponent<Props, State> {
handleInstallModal = (stepId: StepId = 'idCheck', shouldFlash?: boolean) =>
this.setState({ modal: 'install', stepId, shouldFlash, ready: true })
handleDisclairmerNext = () => this.setState({ modal: 'install' })
render() {
const { deviceInfo, t } = this.props
const { latestFirmware, modal, stepId, shouldFlash, ready } = this.state
@ -157,7 +156,7 @@ class FirmwareUpdate extends PureComponent<Props, State> {
<DisclaimerModal
firmware={latestFirmware}
status={modal}
goToNextStep={this.handleInstallModal}
goToNextStep={this.handleDisclairmerNext}
onClose={this.handleCloseModal}
/>
<UpdateModal

2
src/components/modals/UpdateFirmware/Disclaimer.js

@ -69,7 +69,7 @@ class DisclaimerModal extends PureComponent<Props, State> {
<GradientBox />
</ModalContent>
<ModalFooter horizontal justifyContent="flex-end" style={{ width: '100%' }}>
<Button primary onClick={goToNextStep}>
<Button primary onClick={() => goToNextStep()}>
{t('app:manager.firmware.continue')}
</Button>
</ModalFooter>

30
src/components/modals/UpdateFirmware/Installing.js

@ -0,0 +1,30 @@
// @flow
import React, { Fragment } from 'react'
import { translate } from 'react-i18next'
import Box from 'components/base/Box'
import Text from 'components/base/Text'
import Spinner from 'components/base/Spinner'
import type { T } from 'types/common'
type Props = {
t: T,
}
function Installing({ t }: Props) {
return (
<Fragment>
<Box mx={7} align="center">
<Spinner color="fog" size={44} />
</Box>
<Box mx={7} mt={4} mb={7}>
<Text ff="Museo Sans|Regular" align="center" color="dark" fontSize={6}>
{t('app:manager.modal.installing')}
</Text>
</Box>
</Fragment>
)
}
export default translate()(Installing)

18
src/components/modals/UpdateFirmware/index.js

@ -64,6 +64,8 @@ export type StepProps = DefaultStepProps & {
installFinalFirmware: (device: Device) => void,
flashMCU: (device: Device) => void,
shouldFlashMcu: boolean,
error: ?Error,
setError: Error => void,
}
export type StepId = 'idCheck' | 'updateMCU' | 'finish'
@ -82,11 +84,15 @@ type Props = {
type State = {
stepId: StepId | string,
error: ?Error,
nonce: number,
}
class UpdateModal extends PureComponent<Props, State> {
state = {
stepId: this.props.stepId,
error: null,
nonce: 0,
}
STEPS = createSteps({
@ -96,26 +102,34 @@ class UpdateModal extends PureComponent<Props, State> {
: this.props.shouldFlashMcu,
})
setError = (e: Error) => this.setState({ error: e })
handleReset = () => this.setState({ stepId: 'idCheck', error: null, nonce: this.state.nonce++ })
handleStepChange = (step: Step) => this.setState({ stepId: step.id })
render(): React$Node {
const { status, t, firmware, onClose, ...props } = this.props
const { stepId } = this.state
const { stepId, error, nonce } = this.state
const additionalProps = {
firmware,
error,
onCloseModal: onClose,
setError: this.setError,
...props,
}
return (
<Modal
onClose={onClose}
onHide={this.handleReset}
isOpened={status === 'install'}
refocusWhenChange={stepId}
preventBackdropClick={stepId !== 'finish'}
preventBackdropClick={stepId !== 'finish' && !error}
render={() => (
<Stepper
key={nonce}
onStepChange={this.handleStepChange}
title={t('app:manager.firmware.update')}
initialStepId={stepId}

56
src/components/modals/UpdateFirmware/steps/01-step-install-full-firmware.js

@ -14,12 +14,13 @@ import { createCancelablePolling, delay } from 'helpers/promise'
import TrackPage from 'analytics/TrackPage'
import Box from 'components/base/Box'
import Text from 'components/base/Text'
import Progress from 'components/base/Progress'
import DeviceConfirm from 'components/DeviceConfirm'
import type { Device } from 'types/common'
import type { StepProps } from '../'
import Installing from '../Installing'
const Container = styled(Box).attrs({
alignItems: 'center',
fontSize: 4,
@ -95,26 +96,35 @@ class StepFullFirmwareInstall extends PureComponent<Props, State> {
firmware,
shouldFlashMcu,
transitionTo,
setError,
} = this.props
const { device, deviceInfo } = await this.ensureDevice()
// When device is connected, if in OSU, install Final Firmware
if (deviceInfo.isOSU) {
this.setState({ installing: true })
await installFinalFirmware(device)
transitionTo('finish')
} else {
await installOsuFirmware(device)
this.setState({ installing: true })
if (this._unsubConnect) this._unsubConnect()
if ((firmware && firmware.shouldFlashMcu) || shouldFlashMcu) {
delay(1000)
transitionTo('updateMCU')
} else {
const { device: freshDevice } = await this.ensureDevice()
await installFinalFirmware(freshDevice)
if (deviceInfo.isBootloader) {
transitionTo('updateMCU')
}
try {
if (deviceInfo.isOSU) {
this.setState({ installing: true })
await installFinalFirmware(device)
transitionTo('finish')
} else {
await installOsuFirmware(device)
this.setState({ installing: true })
if (this._unsubConnect) this._unsubConnect()
if ((firmware && firmware.shouldFlashMcu) || shouldFlashMcu) {
delay(1000)
transitionTo('updateMCU')
} else {
const { device: freshDevice } = await this.ensureDevice()
await installFinalFirmware(freshDevice)
transitionTo('finish')
}
}
} catch (error) {
setError(error)
transitionTo('finish')
}
}
@ -135,9 +145,7 @@ class StepFullFirmwareInstall extends PureComponent<Props, State> {
const { t, firmware } = this.props
return installing ? (
<Box mx={7} mt={5} style={{ width: '100%' }}>
<Progress infinite />
</Box>
<Installing />
) : (
<Fragment>
<Text ff="Open Sans|Regular" align="center" color="smoke">
@ -148,7 +156,9 @@ class StepFullFirmwareInstall extends PureComponent<Props, State> {
{t('app:manager.modal.identifier')}
</Text>
<Address>
{firmware && firmware.hash && this.formatHashName(firmware.hash).join('\n')}
{firmware &&
firmware.hash &&
this.formatHashName(firmware.hash.toUpperCase()).join('\n')}
</Address>
</Box>
<Box mt={5}>
@ -165,11 +175,7 @@ class StepFullFirmwareInstall extends PureComponent<Props, State> {
const { t } = this.props
return (
<Container>
<Title>
{installing
? t('app:manager.modal.installing')
: t('app:manager.modal.confirmIdentifier')}
</Title>
<Title>{installing ? '' : t('app:manager.modal.confirmIdentifier')}</Title>
<TrackPage category="Manager" name="InstallFirmware" />
{this.renderBody()}
</Container>

36
src/components/modals/UpdateFirmware/steps/02-step-flash-mcu.js

@ -14,12 +14,13 @@ import getDeviceInfo from 'commands/getDeviceInfo'
import TrackPage from 'analytics/TrackPage'
import Box from 'components/base/Box'
import Text from 'components/base/Text'
import Progress from 'components/base/Progress'
import type { Device } from 'types/common'
import type { StepProps } from '../'
import Installing from '../Installing'
const Container = styled(Box).attrs({
alignItems: 'center',
fontSize: 4,
@ -116,13 +117,19 @@ class StepFlashMcu extends PureComponent<Props, State> {
}
install = async () => {
const { transitionTo, installFinalFirmware } = this.props
const { transitionTo, installFinalFirmware, setError } = this.props
const { deviceInfo, device } = await this.getDeviceInfo()
if (deviceInfo.isBootloader) {
await this.flash()
this.install()
} else if (deviceInfo.isOSU) {
await installFinalFirmware(device)
try {
if (deviceInfo.isBootloader) {
await this.flash()
this.install()
} else if (deviceInfo.isOSU) {
await installFinalFirmware(device)
transitionTo('finish')
}
} catch (error) {
setError(error)
transitionTo('finish')
}
}
@ -137,16 +144,7 @@ class StepFlashMcu extends PureComponent<Props, State> {
const { t } = this.props
return installing ? (
<Fragment>
<Box mx={7} style={{ width: '100%' }}>
<Progress infinite />
</Box>
<Box mx={7} mt={4}>
<Text ff="Open Sans|Regular" align="center" color="smoke">
{t('app:manager.modal.mcuPin')}
</Text>
</Box>
</Fragment>
<Installing />
) : (
<Fragment>
<Box mx={7}>
@ -184,9 +182,7 @@ class StepFlashMcu extends PureComponent<Props, State> {
const { installing } = this.state
return (
<Container>
<Title>
{installing ? t('app:manager.modal.flashing') : t('app:manager.modal.mcuTitle')}
</Title>
<Title>{installing ? '' : t('app:manager.modal.mcuTitle')}</Title>
<TrackPage category="Manager" name="FlashMCU" />
{this.renderBody()}
</Container>

34
src/components/modals/UpdateFirmware/steps/03-step-confirmation.js

@ -7,7 +7,9 @@ import TrackPage from 'analytics/TrackPage'
import Box from 'components/base/Box'
import Text from 'components/base/Text'
import Button from 'components/base/Button'
import TranslatedError from 'components/TranslatedError'
import CheckCircle from 'icons/CheckCircle'
import ExclamationCircleThin from 'icons/ExclamationCircleThin'
import type { StepProps } from '../'
@ -25,7 +27,37 @@ const Title = styled(Box).attrs({
font-weight: 500;
`
function StepConfirmation({ t }: StepProps) {
function StepConfirmation({ t, error }: StepProps) {
if (error) {
return (
<Container>
<Box color="alertRed">
<ExclamationCircleThin size={44} />
</Box>
<Box
color="dark"
mt={4}
fontSize={6}
ff="Museo Sans|Regular"
textAlign="center"
style={{ maxWidth: 350 }}
>
<TranslatedError error={error} field="title" />
</Box>
<Box
color="graphite"
mt={4}
fontSize={6}
ff="Open Sans"
textAlign="center"
style={{ maxWidth: 350 }}
>
<TranslatedError error={error} field="description" />
</Box>
</Container>
)
}
return (
<Container>
<TrackPage category="Manager" name="FirmwareConfirmation" />

6
src/helpers/firmware/installFinalFirmware.js

@ -10,7 +10,6 @@ import getOsuFirmware from 'helpers/devices/getOsuFirmware'
import getDeviceInfo from 'helpers/devices/getDeviceInfo'
import getFinalFirmwareById from './getFinalFirmwareById'
const ManagerUnexpectedError = createCustomErrorClass('ManagerUnexpected')
const ManagerDeviceLockedError = createCustomErrorClass('ManagerDeviceLocked')
function remapSocketError(promise) {
@ -19,7 +18,7 @@ function remapSocketError(promise) {
case e.message.endsWith('6982'):
throw new ManagerDeviceLockedError()
default:
throw new ManagerUnexpectedError(e.message, { msg: e.message })
throw e
}
})
}
@ -48,7 +47,6 @@ export default async (transport: Transport<*>): Result => {
await remapSocketError(createDeviceSocket(transport, url).toPromise())
return { success: true }
} catch (error) {
const result = { success: false, error }
throw result
throw error
}
}

16
src/helpers/firmware/installMcu.js

@ -5,6 +5,20 @@ import { WS_MCU } from 'helpers/urls'
import { createDeviceSocket } from 'helpers/socket'
import getNextMCU from 'helpers/firmware/getNextMCU'
import getDeviceInfo from 'helpers/devices/getDeviceInfo'
import { createCustomErrorClass } from 'helpers/errors'
const ManagerDeviceLockedError = createCustomErrorClass('ManagerDeviceLocked')
function remapSocketError(promise) {
return promise.catch((e: Error) => {
switch (true) {
case e.message.endsWith('6982'):
throw new ManagerDeviceLockedError()
default:
throw e
}
})
}
type Result = Promise<*>
@ -17,5 +31,5 @@ export default async (transport: Transport<*>): Result => {
version: nextVersion.name,
}
const url = WS_MCU(params)
return createDeviceSocket(transport, url).toPromise()
return remapSocketError(createDeviceSocket(transport, url).toPromise())
}

9
src/helpers/firmware/installOsuFirmware.js

@ -8,19 +8,21 @@ import type { Firmware } from 'components/modals/UpdateFirmware'
import { createCustomErrorClass } from '../errors'
const ManagerUnexpectedError = createCustomErrorClass('ManagerUnexpected')
const ManagerNotEnoughSpaceError = createCustomErrorClass('ManagerNotEnoughSpace')
const ManagerDeviceLockedError = createCustomErrorClass('ManagerDeviceLocked')
const UserRefusedFirmwareUpdate = createCustomErrorClass('UserRefusedFirmwareUpdate')
function remapError(promise) {
return promise.catch((e: Error) => {
switch (true) {
case e.message.endsWith('6985'):
throw new UserRefusedFirmwareUpdate()
case e.message.endsWith('6982'):
throw new ManagerDeviceLockedError()
case e.message.endsWith('6a84') || e.message.endsWith('6a85'):
throw new ManagerNotEnoughSpaceError()
default:
throw new ManagerUnexpectedError(e.message, { msg: e.message })
throw e
}
})
}
@ -43,7 +45,6 @@ export default async (
await remapError(createDeviceSocket(transport, url).toPromise())
return { success: true }
} catch (error) {
const result = { success: false, error }
throw result
throw error
}
}

3
static/i18n/en/app.yml

@ -229,8 +229,7 @@ manager:
idCheck: Identifier check
updateMCU: Update MCU
confirm: Confirmation
installing: Installing firmware
flashing: Installing MCU
installing: Firmware updating...
confirmIdentifier: Confirm identifier
confirmIdentifierText: Please confirm identifier on your Device. Be sure the identifier is the same as below
identifier: Identifier

5
static/i18n/en/errors.yml

@ -62,7 +62,7 @@ ManagerDeviceLocked:
ManagerNotEnoughSpace:
title: Sorry, insufficient device storage
description: Uninstall some apps to increase available storage and try again.
ManagerUnexpectedError:
ManagerUnexpected:
title: That's unexpected ({{msg}}) #(Manager: {{msg}})
description: Please try again.
ManagerUninstallBTCDep:
@ -89,6 +89,9 @@ TransportError:
TransportStatusError:
title: 'Something went wrong. Please replug your device.'
description: '{{message}}'
UserRefusedFirmwareUpdate:
title: Firmware update refused on device
description: Please retry or contact Ledger Support
UserRefusedOnDevice:
title: Transaction refused on device
description: Please retry or contact Ledger Support in case of doubt.

Loading…
Cancel
Save