Browse Source

Merge pull request #808 from valpinkman/feat/resume-firmare-update

firmware update + resume update from any step
master
Gaëtan Renaudeau 7 years ago
committed by GitHub
parent
commit
85472f9b82
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      src/commands/index.js
  2. 14
      src/commands/shouldFlashMcu.js
  3. 7
      src/components/GenuineCheck.js
  4. 2
      src/components/ManagerPage/Dashboard.js
  5. 62
      src/components/ManagerPage/FirmwareUpdate.js
  6. 2
      src/components/base/Progress/index.js
  7. 46
      src/components/modals/UpdateFirmware/index.js
  8. 87
      src/components/modals/UpdateFirmware/steps/01-step-install-full-firmware.js
  9. 58
      src/components/modals/UpdateFirmware/steps/02-step-flash-mcu.js
  10. 2
      src/components/modals/UpdateFirmware/steps/03-step-confirmation.js
  11. 4
      src/helpers/devices/getLatestFirmwareForDevice.js
  12. 4
      src/helpers/devices/getOsuFirmware.js
  13. 55
      src/helpers/devices/shouldFlashMcu.js
  14. 4
      src/helpers/firmware/getNextMCU.js
  15. 23
      src/helpers/firmware/installFinalFirmware.js
  16. 2
      src/helpers/firmware/installOsuFirmware.js
  17. 3
      static/i18n/en/app.yml

2
src/commands/index.js

@ -26,6 +26,7 @@ import listAppVersions from 'commands/listAppVersions'
import listCategories from 'commands/listCategories' import listCategories from 'commands/listCategories'
import listenDevices from 'commands/listenDevices' import listenDevices from 'commands/listenDevices'
import ping from 'commands/ping' import ping from 'commands/ping'
import shouldFlashMcu from 'commands/shouldFlashMcu'
import signTransaction from 'commands/signTransaction' import signTransaction from 'commands/signTransaction'
import testApdu from 'commands/testApdu' import testApdu from 'commands/testApdu'
import testCrash from 'commands/testCrash' import testCrash from 'commands/testCrash'
@ -56,6 +57,7 @@ const all: Array<Command<any, any>> = [
listCategories, listCategories,
listenDevices, listenDevices,
ping, ping,
shouldFlashMcu,
signTransaction, signTransaction,
testApdu, testApdu,
testCrash, testCrash,

14
src/commands/shouldFlashMcu.js

@ -0,0 +1,14 @@
// @flow
import { createCommand, Command } from 'helpers/ipc'
import { fromPromise } from 'rxjs/observable/fromPromise'
import type { DeviceInfo } from 'helpers/devices/getDeviceInfo'
import shouldFlashMcu from 'helpers/devices/shouldFlashMcu'
type Result = boolean
const cmd: Command<DeviceInfo, Result> = createCommand('shouldFlashMcu', data =>
fromPromise(shouldFlashMcu(data)),
)
export default cmd

7
src/components/GenuineCheck.js

@ -30,9 +30,9 @@ const DeviceNotGenuineError = createCustomErrorClass('DeviceNotGenuine')
type Props = { type Props = {
t: T, t: T,
onSuccess: void => void,
onFail?: Error => void, onFail?: Error => void,
onUnavailable?: Error => void, onUnavailable?: Error => void,
onSuccess: (*) => void,
device: ?Device, device: ?Device,
} }
@ -72,6 +72,11 @@ class GenuineCheck extends PureComponent<Props> {
device: Device, device: Device,
deviceInfo: DeviceInfo, deviceInfo: DeviceInfo,
}) => { }) => {
if (deviceInfo.isOSU || deviceInfo.isBootloader) {
logger.log('device is in update mode. skipping genuine')
return true
}
if (genuineDevices.has(device)) { if (genuineDevices.has(device)) {
logger.log("genuine was already checked. don't check again") logger.log("genuine was already checked. don't check again")
await delay(GENUINE_CACHE_DELAY) await delay(GENUINE_CACHE_DELAY)

2
src/components/ManagerPage/Dashboard.js

@ -33,7 +33,9 @@ const Dashboard = ({ device, deviceInfo, t }: Props) => (
<FirmwareUpdate deviceInfo={deviceInfo} device={device} /> <FirmwareUpdate deviceInfo={deviceInfo} device={device} />
</Box> </Box>
<Box mt={5}> <Box mt={5}>
{deviceInfo.isOSU || deviceInfo.isBootloader ? null : (
<AppsList device={device} deviceInfo={deviceInfo} /> <AppsList device={device} deviceInfo={deviceInfo} />
)}
</Box> </Box>
</Box> </Box>
) )

62
src/components/ManagerPage/FirmwareUpdate.js

@ -6,13 +6,14 @@ import { translate } from 'react-i18next'
import isEqual from 'lodash/isEqual' import isEqual from 'lodash/isEqual'
import isEmpty from 'lodash/isEmpty' import isEmpty from 'lodash/isEmpty'
import invariant from 'invariant' import invariant from 'invariant'
import logger from 'logger'
import type { Device, T } from 'types/common' import type { Device, T } from 'types/common'
import type { LedgerScriptParams } from 'helpers/common' import type { LedgerScriptParams } from 'helpers/common'
import type { StepId } from 'components/modals/UpdateFirmware'
import getLatestFirmwareForDevice from 'commands/getLatestFirmwareForDevice' import getLatestFirmwareForDevice from 'commands/getLatestFirmwareForDevice'
import shouldFlashMcu from 'commands/shouldFlashMcu'
import installOsuFirmware from 'commands/installOsuFirmware' import installOsuFirmware from 'commands/installOsuFirmware'
import installFinalFirmware from 'commands/installFinalFirmware' import installFinalFirmware from 'commands/installFinalFirmware'
import installMcu from 'commands/installMcu' import installMcu from 'commands/installMcu'
@ -40,23 +41,32 @@ type Props = {
} }
type State = { type State = {
latestFirmware: ?LedgerScriptParams & ?{ shouldUpdateMcu: boolean }, latestFirmware: ?LedgerScriptParams & ?{ shouldFlashMcu: boolean },
modal: ModalStatus, modal: ModalStatus,
stepId: ?StepId,
shouldFlash: boolean,
ready: boolean,
} }
class FirmwareUpdate extends PureComponent<Props, State> { const intializeState = ({ deviceInfo }): State => ({
state = {
latestFirmware: null, latestFirmware: null,
modal: 'closed', modal: 'closed',
} stepId: deviceInfo.isBootloader ? 'updateMCU' : 'idCheck',
shouldFlash: false,
ready: false,
})
componentDidMount() { class FirmwareUpdate extends PureComponent<Props, State> {
this.fetchLatestFirmware() state = intializeState(this.props)
}
componentDidUpdate() { componentDidMount() {
if (isEmpty(this.state.latestFirmware)) { const { deviceInfo } = this.props
if (!deviceInfo.isOSU && !deviceInfo.isBootloader) {
this.fetchLatestFirmware() this.fetchLatestFirmware()
} else if (deviceInfo.isOSU) {
this.shouldFlashMcu()
} else if (deviceInfo.isBootloader) {
this.handleInstallModal('updateMCU', true)
} }
} }
@ -74,12 +84,19 @@ class FirmwareUpdate extends PureComponent<Props, State> {
!isEqual(this.state.latestFirmware, latestFirmware) && !isEqual(this.state.latestFirmware, latestFirmware) &&
!this._unmounting !this._unmounting
) { ) {
this.setState({ latestFirmware }) this.setState({ latestFirmware, ready: true })
}
}
shouldFlashMcu = async () => {
const { deviceInfo } = this.props
const shouldFlash = await shouldFlashMcu.send(deviceInfo).toPromise()
if (!this._unmounting) {
this.setState({ shouldFlash, modal: 'install', stepId: 'idCheck', ready: true })
} }
} }
installOsuFirmware = async (device: Device) => { installOsuFirmware = async (device: Device) => {
try {
const { latestFirmware } = this.state const { latestFirmware } = this.state
const { deviceInfo } = this.props const { deviceInfo } = this.props
invariant(latestFirmware, 'did not find a new firmware or firmware is not set') invariant(latestFirmware, 'did not find a new firmware or firmware is not set')
@ -89,20 +106,11 @@ class FirmwareUpdate extends PureComponent<Props, State> {
.send({ devicePath: device.path, firmware: latestFirmware, targetId: deviceInfo.targetId }) .send({ devicePath: device.path, firmware: latestFirmware, targetId: deviceInfo.targetId })
.toPromise() .toPromise()
return success return success
} catch (err) {
logger.log(err)
throw err
}
} }
installFinalFirmware = async (device: Device) => { installFinalFirmware = async (device: Device) => {
try {
const { success } = await installFinalFirmware.send({ devicePath: device.path }).toPromise() const { success } = await installFinalFirmware.send({ devicePath: device.path }).toPromise()
return success return success
} catch (err) {
logger.log(err)
throw err
}
} }
flashMCU = async (device: Device) => { flashMCU = async (device: Device) => {
@ -112,11 +120,13 @@ class FirmwareUpdate extends PureComponent<Props, State> {
handleCloseModal = () => this.setState({ modal: 'closed' }) handleCloseModal = () => this.setState({ modal: 'closed' })
handleDisclaimerModal = () => this.setState({ modal: 'disclaimer' }) handleDisclaimerModal = () => this.setState({ modal: 'disclaimer' })
handleInstallModal = () => this.setState({ modal: 'install' }) handleInstallModal = (stepId: StepId = 'idCheck', shouldFlash?: boolean) =>
this.setState({ modal: 'install', stepId, shouldFlash, ready: true })
render() { render() {
const { deviceInfo, t } = this.props const { deviceInfo, t } = this.props
const { latestFirmware, modal } = this.state const { latestFirmware, modal, stepId, shouldFlash, ready } = this.state
return ( return (
<Card p={4}> <Card p={4}>
<Box horizontal align="center" flow={2}> <Box horizontal align="center" flow={2}>
@ -142,7 +152,7 @@ class FirmwareUpdate extends PureComponent<Props, State> {
</Box> </Box>
<UpdateFirmwareButton firmware={latestFirmware} onClick={this.handleDisclaimerModal} /> <UpdateFirmwareButton firmware={latestFirmware} onClick={this.handleDisclaimerModal} />
</Box> </Box>
{latestFirmware && ( {ready ? (
<Fragment> <Fragment>
<DisclaimerModal <DisclaimerModal
firmware={latestFirmware} firmware={latestFirmware}
@ -152,14 +162,16 @@ class FirmwareUpdate extends PureComponent<Props, State> {
/> />
<UpdateModal <UpdateModal
status={modal} status={modal}
stepId={stepId}
onClose={this.handleCloseModal} onClose={this.handleCloseModal}
firmware={latestFirmware} firmware={latestFirmware}
shouldFlashMcu={shouldFlash}
installOsuFirmware={this.installOsuFirmware} installOsuFirmware={this.installOsuFirmware}
installFinalFirmware={this.installFinalFirmware} installFinalFirmware={this.installFinalFirmware}
flashMCU={this.flashMCU} flashMCU={this.flashMCU}
/> />
</Fragment> </Fragment>
)} ) : null}
</Card> </Card>
) )
} }

2
src/components/base/Progress/index.js

@ -71,7 +71,7 @@ type State = {}
class Progress extends Component<Props, State> { class Progress extends Component<Props, State> {
static defaultProps = { static defaultProps = {
infinite: false, infinite: false,
timing: 3000, timing: 2500,
color: 'wallet', color: 'wallet',
} }

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

@ -16,19 +16,7 @@ import StepFullFirmwareInstall from './steps/01-step-install-full-firmware'
import StepFlashMcu from './steps/02-step-flash-mcu' import StepFlashMcu from './steps/02-step-flash-mcu'
import StepConfirmation, { StepConfirmFooter } from './steps/03-step-confirmation' import StepConfirmation, { StepConfirmFooter } from './steps/03-step-confirmation'
export type Firmware = LedgerScriptParams & { shouldUpdateMcu: boolean } const createSteps = ({ t, shouldFlashMcu }: { t: T, shouldFlashMcu: boolean }): Array<*> => {
export type StepProps = DefaultStepProps & {
firmware: Firmware,
onCloseModal: () => void,
installOsuFirmware: (device: Device) => void,
installFinalFirmware: (device: Device) => void,
flashMCU: (device: Device) => void,
}
type StepId = 'idCheck' | 'updateMCU' | 'finish'
const createSteps = ({ t, firmware }: { t: T, firmware: Firmware }): Array<*> => {
const updateStep = { const updateStep = {
id: 'idCheck', id: 'idCheck',
label: t('app:manager.modal.steps.idCheck'), label: t('app:manager.modal.steps.idCheck'),
@ -58,7 +46,7 @@ const createSteps = ({ t, firmware }: { t: T, firmware: Firmware }): Array<*> =>
const steps = [updateStep] const steps = [updateStep]
if (firmware.shouldUpdateMcu) { if (shouldFlashMcu) {
steps.push(mcuStep) steps.push(mcuStep)
} }
@ -67,11 +55,25 @@ const createSteps = ({ t, firmware }: { t: T, firmware: Firmware }): Array<*> =>
return steps return steps
} }
export type Firmware = LedgerScriptParams & { shouldFlashMcu: boolean }
export type StepProps = DefaultStepProps & {
firmware: Firmware,
onCloseModal: () => void,
installOsuFirmware: (device: Device) => void,
installFinalFirmware: (device: Device) => void,
flashMCU: (device: Device) => void,
shouldFlashMcu: boolean,
}
export type StepId = 'idCheck' | 'updateMCU' | 'finish'
type Props = { type Props = {
t: T, t: T,
status: ModalStatus, status: ModalStatus,
onClose: () => void, onClose: () => void,
firmware: Firmware, firmware: Firmware,
shouldFlashMcu: boolean,
installOsuFirmware: (device: Device) => void, installOsuFirmware: (device: Device) => void,
installFinalFirmware: (device: Device) => void, installFinalFirmware: (device: Device) => void,
flashMCU: (device: Device) => void, flashMCU: (device: Device) => void,
@ -83,15 +85,16 @@ type State = {
} }
class UpdateModal extends PureComponent<Props, State> { class UpdateModal extends PureComponent<Props, State> {
static defaultProps = {
stepId: 'idCheck',
}
state = { state = {
stepId: this.props.stepId, stepId: this.props.stepId,
} }
STEPS = createSteps({ t: this.props.t, firmware: this.props.firmware }) STEPS = createSteps({
t: this.props.t,
shouldFlashMcu: this.props.firmware
? this.props.firmware.shouldFlashMcu
: this.props.shouldFlashMcu,
})
handleStepChange = (step: Step) => this.setState({ stepId: step.id }) handleStepChange = (step: Step) => this.setState({ stepId: step.id })
@ -110,11 +113,12 @@ class UpdateModal extends PureComponent<Props, State> {
onClose={onClose} onClose={onClose}
isOpened={status === 'install'} isOpened={status === 'install'}
refocusWhenChange={stepId} refocusWhenChange={stepId}
preventBackdropClick={false} preventBackdropClick={stepId !== 'finish'}
render={() => ( render={() => (
<Stepper <Stepper
onStepChange={this.handleStepChange}
title={t('app:manager.firmware.update')} title={t('app:manager.firmware.update')}
initialStepId="idCheck" initialStepId={stepId}
steps={this.STEPS} steps={this.STEPS}
{...additionalProps} {...additionalProps}
> >

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

@ -9,7 +9,7 @@ import { DEVICE_INFOS_TIMEOUT } from 'config/constants'
import getDeviceInfo from 'commands/getDeviceInfo' import getDeviceInfo from 'commands/getDeviceInfo'
import { getCurrentDevice } from 'reducers/devices' import { getCurrentDevice } from 'reducers/devices'
import { createCancelablePolling } from 'helpers/promise' import { createCancelablePolling, delay } from 'helpers/promise'
import Box from 'components/base/Box' import Box from 'components/base/Box'
import Text from 'components/base/Text' import Text from 'components/base/Text'
@ -46,13 +46,7 @@ const Address = styled(Box).attrs({
cursor: text; cursor: text;
user-select: text; user-select: text;
width: 325px; width: 325px;
` text-align: center;
const Ellipsis = styled.span`
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
width: 100%;
` `
type Props = StepProps & { type Props = StepProps & {
@ -69,7 +63,7 @@ class StepFullFirmwareInstall extends PureComponent<Props, State> {
} }
componentDidMount() { componentDidMount() {
this.install() // this.install()
} }
componentWillUnmount() { componentWillUnmount() {
@ -94,55 +88,66 @@ class StepFullFirmwareInstall extends PureComponent<Props, State> {
} }
install = async () => { install = async () => {
const { installOsuFirmware, installFinalFirmware } = this.props const {
installOsuFirmware,
installFinalFirmware,
firmware,
shouldFlashMcu,
transitionTo,
} = this.props
const { device, deviceInfo } = await this.ensureDevice() const { device, deviceInfo } = await this.ensureDevice()
// When device is connected, if in OSU, install Final Firmware
if (deviceInfo.isOSU) { if (deviceInfo.isOSU) {
this.setState({ installing: true }) this.setState({ installing: true })
const finalSuccess = await installFinalFirmware(device) await installFinalFirmware(device)
if (finalSuccess) this.transitionTo() transitionTo('finish')
} } else {
await installOsuFirmware(device)
const success = await installOsuFirmware(device)
if (success) {
this.setState({ installing: true }) this.setState({ installing: true })
if (this._unsubConnect) this._unsubConnect() if (this._unsubConnect) this._unsubConnect()
const { device: cleanDevice } = await this.ensureDevice() if ((firmware && firmware.shouldFlashMcu) || shouldFlashMcu) {
const finalSuccess = await installFinalFirmware(cleanDevice) delay(1000)
if (finalSuccess) { transitionTo('updateMCU')
this.transitionTo() } else {
const { device: freshDevice } = await this.ensureDevice()
await installFinalFirmware(freshDevice)
transitionTo('finish')
} }
} }
} }
transitionTo = () => { formatHashName = (hash: string): string[] => {
const { firmware, transitionTo } = this.props if (!hash) {
if (firmware.shouldUpdateMcu) { return []
transitionTo('updateMCU')
} else {
transitionTo('finish')
} }
const length = hash.length
const half = Math.ceil(length / 2)
const start = hash.slice(0, half)
const end = hash.slice(half)
return [start, end]
} }
renderBody = () => { renderBody = () => {
const { installing } = this.state const { installing } = this.state
const { firmware, t } = this.props const { t, firmware } = this.props
if (installing) { return installing ? (
return ( <Box mx={7} mt={5} style={{ width: '100%' }}>
<Box mx={7}> <Progress infinite />
<Progress infinite style={{ width: '100%' }} />
</Box> </Box>
) ) : (
}
return (
<Fragment> <Fragment>
<Text ff="Open Sans|Regular" align="center" color="smoke">
{t('app:manager.modal.confirmIdentifierText')}
</Text>
<Box mx={7} mt={5}> <Box mx={7} mt={5}>
<Text ff="Open Sans|SemiBold" align="center" color="smoke"> <Text ff="Open Sans|SemiBold" align="center" color="smoke">
{t('app:manager.modal.identifier')} {t('app:manager.modal.identifier')}
</Text> </Text>
<Address> <Address>
<Ellipsis>{firmware && firmware.hash}</Ellipsis> {firmware && firmware.hash && this.formatHashName(firmware.hash).join('\n')}
</Address> </Address>
</Box> </Box>
<Box mt={5}> <Box mt={5}>
@ -155,13 +160,15 @@ class StepFullFirmwareInstall extends PureComponent<Props, State> {
_unsubConnect: * _unsubConnect: *
render() { render() {
const { installing } = this.state
const { t } = this.props const { t } = this.props
return ( return (
<Container> <Container>
<Title>{t('app:manager.modal.confirmIdentifier')}</Title> <Title>
<Text ff="Open Sans|Regular" align="center" color="smoke"> {installing
{t('app:manager.modal.confirmIdentifierText')} ? t('app:manager.modal.installing')
</Text> : t('app:manager.modal.confirmIdentifier')}
</Title>
{this.renderBody()} {this.renderBody()}
</Container> </Container>
) )

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

@ -60,7 +60,7 @@ class StepFlashMcu extends PureComponent<Props, State> {
} }
componentDidMount() { componentDidMount() {
this.install() // this.install()
} }
componentWillUnmount() { componentWillUnmount() {
@ -68,7 +68,7 @@ class StepFlashMcu extends PureComponent<Props, State> {
if (this._unsubDeviceInfo) this._unsubDeviceInfo() if (this._unsubDeviceInfo) this._unsubDeviceInfo()
} }
waitForDeviceInBootloader = () => { getDeviceInfo = () => {
const { unsubscribe, promise } = createCancelablePolling(async () => { const { unsubscribe, promise } = createCancelablePolling(async () => {
const { device } = this.props const { device } = this.props
if (!device) { if (!device) {
@ -78,16 +78,14 @@ class StepFlashMcu extends PureComponent<Props, State> {
.send({ devicePath: device.path }) .send({ devicePath: device.path })
.pipe(timeout(DEVICE_INFOS_TIMEOUT)) .pipe(timeout(DEVICE_INFOS_TIMEOUT))
.toPromise() .toPromise()
if (!deviceInfo.isBootloader) {
throw new Error('Device is not in bootloader')
}
return { device, deviceInfo } return { device, deviceInfo }
}) })
this._unsubConnect = unsubscribe this._unsubDeviceInfo = unsubscribe
return promise return promise
} }
getDeviceInfo = () => { waitForDeviceInBootloader = () => {
const { unsubscribe, promise } = createCancelablePolling(async () => { const { unsubscribe, promise } = createCancelablePolling(async () => {
const { device } = this.props const { device } = this.props
if (!device) { if (!device) {
@ -97,9 +95,13 @@ class StepFlashMcu extends PureComponent<Props, State> {
.send({ devicePath: device.path }) .send({ devicePath: device.path })
.pipe(timeout(DEVICE_INFOS_TIMEOUT)) .pipe(timeout(DEVICE_INFOS_TIMEOUT))
.toPromise() .toPromise()
if (!deviceInfo.isBootloader) {
throw new Error('Device is not in bootloader')
}
return { device, deviceInfo } return { device, deviceInfo }
}) })
this._unsubDeviceInfo = unsubscribe this._unsubConnect = unsubscribe
return promise return promise
} }
@ -113,29 +115,38 @@ class StepFlashMcu extends PureComponent<Props, State> {
} }
install = async () => { install = async () => {
const { transitionTo } = this.props const { transitionTo, installFinalFirmware } = this.props
this.flash() const { deviceInfo, device } = await this.getDeviceInfo()
const deviceInfo = await this.getDeviceInfo()
if (deviceInfo.isBootloader) { if (deviceInfo.isBootloader) {
this.flash() await this.flash()
} else { this.install()
} else if (deviceInfo.isOSU) {
await installFinalFirmware(device)
transitionTo('finish') transitionTo('finish')
} }
} }
firstFlash = async () => {
await this.flash()
this.install()
}
renderBody = () => { renderBody = () => {
const { installing } = this.state const { installing } = this.state
const { t } = this.props const { t } = this.props
if (installing) { return installing ? (
return ( <Fragment>
<Box mx={7}> <Box mx={7} style={{ width: '100%' }}>
<Progress infinite style={{ width: '100%' }} /> <Progress infinite />
</Box> </Box>
) <Box mx={7} mt={4}>
} <Text ff="Open Sans|Regular" align="center" color="smoke">
{t('app:manager.modal.mcuPin')}
return ( </Text>
</Box>
</Fragment>
) : (
<Fragment> <Fragment>
<Box mx={7}> <Box mx={7}>
<Text ff="Open Sans|Regular" align="center" color="smoke"> <Text ff="Open Sans|Regular" align="center" color="smoke">
@ -169,9 +180,12 @@ class StepFlashMcu extends PureComponent<Props, State> {
render() { render() {
const { t } = this.props const { t } = this.props
const { installing } = this.state
return ( return (
<Container> <Container>
<Title>{t('app:manager.modal.mcuTitle')}</Title> <Title>
{installing ? t('app:manager.modal.flashing') : t('app:manager.modal.mcuTitle')}
</Title>
{this.renderBody()} {this.renderBody()}
</Container> </Container>
) )

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

@ -31,7 +31,7 @@ function StepConfirmation({ t }: StepProps) {
<CheckCircle size={44} /> <CheckCircle size={44} />
</Box> </Box>
<Title>{t('app:manager.modal.successTitle')}</Title> <Title>{t('app:manager.modal.successTitle')}</Title>
<Box mt={2} mb={8}> <Box mt={2} mb={5}>
<Text ff="Open Sans|Regular" fontSize={4} color="graphite"> <Text ff="Open Sans|Regular" fontSize={4} color="graphite">
{t('app:manager.modal.successText')} {t('app:manager.modal.successText')}
</Text> </Text>

4
src/helpers/devices/getLatestFirmwareForDevice.js

@ -49,11 +49,11 @@ export default async (deviceInfo: DeviceInfo) => {
if (!seFirmwareFinalVersion.mcu_versions.includes(...currentMcuVersionId)) { if (!seFirmwareFinalVersion.mcu_versions.includes(...currentMcuVersionId)) {
return { return {
...se_firmware_osu_version, ...se_firmware_osu_version,
shouldUpdateMcu: true, shouldFlashMcu: true,
} }
} }
return { ...se_firmware_osu_version, shouldUpdateMcu: false } return { ...se_firmware_osu_version, shouldFlashMcu: false }
} catch (err) { } catch (err) {
const error = Error(err.message) const error = Error(err.message)
error.stack = err.stack error.stack = err.stack

4
src/helpers/devices/getOsuFirmware.js

@ -6,17 +6,17 @@ import { GET_CURRENT_OSU } from 'helpers/urls'
type Input = { type Input = {
version: string, version: string,
deviceId: string | number, deviceId: string | number,
provider: number,
} }
export default async (input: Input): Promise<*> => { export default async (input: Input): Promise<*> => {
const provider = 1
const { data } = await network({ const { data } = await network({
method: 'POST', method: 'POST',
url: GET_CURRENT_OSU, url: GET_CURRENT_OSU,
data: { data: {
device_version: input.deviceId, device_version: input.deviceId,
version_name: `${input.version}-osu`, version_name: `${input.version}-osu`,
provider, provider: input.provider,
}, },
}) })
return data return data

55
src/helpers/devices/shouldFlashMcu.js

@ -0,0 +1,55 @@
// @flow
import network from 'api/network'
import { GET_LATEST_FIRMWARE } from 'helpers/urls'
import type { DeviceInfo } from 'helpers/devices/getDeviceInfo'
import getFinalFirmwareById from 'helpers/firmware/getFinalFirmwareById'
import getMcus from 'helpers/firmware/getMcus'
import getOsuFirmware from './getOsuFirmware'
import getDeviceVersion from './getDeviceVersion'
export default async (deviceInfo: DeviceInfo): Promise<boolean> => {
try {
// Get device infos from targetId
const deviceVersion = await getDeviceVersion(deviceInfo.targetId, deviceInfo.providerId)
// Get firmware infos with firmware name and device version
const seFirmwareVersion = await getOsuFirmware({
version: deviceInfo.fullVersion,
deviceId: deviceVersion.id,
provider: deviceInfo.providerId,
})
// Fetch next possible firmware
const { data } = await network({
method: 'POST',
url: GET_LATEST_FIRMWARE,
data: {
current_se_firmware_final_version: seFirmwareVersion.id,
device_version: deviceVersion.id,
provider: deviceInfo.providerId,
},
})
if (data.result === 'null') {
return false
}
const { se_firmware_osu_version } = data
const { next_se_firmware_final_version } = se_firmware_osu_version
const seFirmwareFinalVersion = await getFinalFirmwareById(next_se_firmware_final_version)
const mcus = await getMcus()
const currentMcuVersionId = mcus
.filter(mcu => mcu.name === deviceInfo.mcuVersion)
.map(mcu => mcu.id)
return !seFirmwareFinalVersion.mcu_versions.includes(...currentMcuVersionId)
} catch (err) {
const error = Error(err.message)
error.stack = err.stack
throw error
}
}

4
src/helpers/firmware/getNextMCU.js

@ -16,8 +16,8 @@ export default async (bootloaderVersion: string): Promise<*> => {
}, },
}) })
// FIXME: nextVersion will not be able to "default" when Error // FIXME: nextVersion will not be able to "default" when
// handling is standardize on the API side // Error handling is standardize on the API side
if (data === 'default' || !data.name) { if (data === 'default' || !data.name) {
throw new LatestMCUInstalledError('there is no next mcu version to install') throw new LatestMCUInstalledError('there is no next mcu version to install')
} }

23
src/helpers/firmware/installFinalFirmware.js

@ -4,18 +4,37 @@ import type { DeviceInfo } from 'helpers/devices/getDeviceInfo'
import { WS_INSTALL } from 'helpers/urls' import { WS_INSTALL } from 'helpers/urls'
import { createDeviceSocket } from 'helpers/socket' import { createDeviceSocket } from 'helpers/socket'
import { createCustomErrorClass } from 'helpers/errors'
import getDeviceVersion from 'helpers/devices/getDeviceVersion' import getDeviceVersion from 'helpers/devices/getDeviceVersion'
import getOsuFirmware from 'helpers/devices/getOsuFirmware' import getOsuFirmware from 'helpers/devices/getOsuFirmware'
import getDeviceInfo from 'helpers/devices/getDeviceInfo' import getDeviceInfo from 'helpers/devices/getDeviceInfo'
import getFinalFirmwareById from './getFinalFirmwareById' import getFinalFirmwareById from './getFinalFirmwareById'
const ManagerUnexpectedError = createCustomErrorClass('ManagerUnexpected')
const ManagerDeviceLockedError = createCustomErrorClass('ManagerDeviceLocked')
function remapSocketError(promise) {
return promise.catch((e: Error) => {
switch (true) {
case e.message.endsWith('6982'):
throw new ManagerDeviceLockedError()
default:
throw new ManagerUnexpectedError(e.message, { msg: e.message })
}
})
}
type Result = Promise<{ success: boolean, error?: string }> type Result = Promise<{ success: boolean, error?: string }>
export default async (transport: Transport<*>): Result => { export default async (transport: Transport<*>): Result => {
try { try {
const deviceInfo: DeviceInfo = await getDeviceInfo(transport) const deviceInfo: DeviceInfo = await getDeviceInfo(transport)
const device = await getDeviceVersion(deviceInfo.targetId, deviceInfo.providerId) const device = await getDeviceVersion(deviceInfo.targetId, deviceInfo.providerId)
const firmware = await getOsuFirmware({ deviceId: device.id, version: deviceInfo.fullVersion }) const firmware = await getOsuFirmware({
deviceId: device.id,
version: deviceInfo.fullVersion,
provider: deviceInfo.providerId,
})
const { next_se_firmware_final_version } = firmware const { next_se_firmware_final_version } = firmware
const nextFirmware = await getFinalFirmwareById(next_se_firmware_final_version) const nextFirmware = await getFinalFirmwareById(next_se_firmware_final_version)
@ -26,7 +45,7 @@ export default async (transport: Transport<*>): Result => {
} }
const url = WS_INSTALL(params) const url = WS_INSTALL(params)
await createDeviceSocket(transport, url).toPromise() await remapSocketError(createDeviceSocket(transport, url).toPromise())
return { success: true } return { success: true }
} catch (error) { } catch (error) {
const result = { success: false, error } const result = { success: false, error }

2
src/helpers/firmware/installOsuFirmware.js

@ -38,7 +38,7 @@ export default async (
...firmware, ...firmware,
firmwareKey: firmware.firmware_key, firmwareKey: firmware.firmware_key,
} }
delete params.shouldUpdateMcu delete params.shouldFlashMcu
const url = WS_INSTALL(params) const url = WS_INSTALL(params)
await remapError(createDeviceSocket(transport, url).toPromise()) await remapError(createDeviceSocket(transport, url).toPromise())
return { success: true } return { success: true }

3
static/i18n/en/app.yml

@ -226,12 +226,15 @@ manager:
idCheck: Identifier check idCheck: Identifier check
updateMCU: Update MCU updateMCU: Update MCU
confirm: Confirmation confirm: Confirmation
installing: Installing firmware
flashing: Installing MCU
confirmIdentifier: Confirm identifier confirmIdentifier: Confirm identifier
confirmIdentifierText: Please confirm identifier on your Device. Be sure the identifier is the same as below confirmIdentifierText: Please confirm identifier on your Device. Be sure the identifier is the same as below
identifier: Identifier identifier: Identifier
mcuTitle: Updating MCU mcuTitle: Updating MCU
mcuFirst: Unplug your device from your computer mcuFirst: Unplug your device from your computer
mcuSecond: Press and hold left button and plug your device until the processing screen appears mcuSecond: Press and hold left button and plug your device until the processing screen appears
mcuPin: If asked on device, please enter your pin code to finish the process
successTitle: Firmware has been updated with success successTitle: Firmware has been updated with success
successText: You can now re-install your applications on your device successText: You can now re-install your applications on your device
title: Manager title: Manager

Loading…
Cancel
Save