UNPKG

@ledgerhq/live-common

Version:
120 lines (114 loc) 4.31 kB
import { DeviceId, DeviceInfo } from "@ledgerhq/types-live"; import { concat, Observable, of } from "rxjs"; import { scan, switchMap } from "rxjs/operators"; import { updateFirmwareTask, UpdateFirmwareTaskEvent } from "../tasks/updateFirmware"; import { getDeviceInfoTask, GetDeviceInfoTaskErrorEvent } from "../tasks/getDeviceInfo"; import { FullActionState, initialSharedActionState, sharedReducer } from "./core"; import { getLatestFirmwareTask, GetLatestFirmwareTaskErrorEvent } from "../tasks/getLatestFirmware"; export type updateFirmwareActionArgs = { deviceId: DeviceId; }; export type UpdateFirmwareActionState = FullActionState<{ step: | "preparingUpdate" // initial step where we retrieve all the necessary information for the update | "allowSecureChannelRequested" // the user has refused the allow secure channel that could have been triggered at the initial step | "allowSecureChannelDenied" // we need a secure connection to the HSM to install the operating system updater (osu) | "installingOsu" // step that installs the operating system updater, always happen | "installOsuDevicePermissionRequested" // permission requested for istalling the osu | "installOsuDevicePermissionGranted" // the user has confirmed the firmware installation on the device | "installOsuDevicePermissionDenied" // the user has refused the firmware installation on the device | "flashingMcu" // step that flashes the mcu of the device, only happens when necessary | "flashingBootloader" // step that flashes the bootloader of the device, only happen when necessary | "firmwareUpdateCompleted"; // final step when the device has reconnected after the firwmare update has been completed progress: number; updatedDeviceInfo?: DeviceInfo; error: { type: "UpdateFirmwareError"; name: string }; }>; export const initialState: UpdateFirmwareActionState = { step: "preparingUpdate", progress: 0, ...initialSharedActionState, }; /** * Device Action used to update the firmware installed on a Ledger device * @param deviceId the transport id of the device to be updated * @returns an Observables that emits the evolution of the state machine of the firmware installation */ export function updateFirmwareAction({ deviceId, }: updateFirmwareActionArgs): Observable<UpdateFirmwareActionState> { return concat( of(initialState), getDeviceInfoTask({ deviceId }).pipe( switchMap(event => { if (event.type !== "data") { return of(event); } const { deviceInfo } = event; return getLatestFirmwareTask({ deviceId, deviceInfo }); }), switchMap(event => { if (event.type !== "data") { return of(event); } else { return updateFirmwareTask({ deviceId, updateContext: event.firmwareUpdateContext, }); } }), scan< UpdateFirmwareTaskEvent | GetLatestFirmwareTaskErrorEvent | GetDeviceInfoTaskErrorEvent, UpdateFirmwareActionState >((currentState, event) => { switch (event.type) { case "taskError": return { ...currentState, error: { type: "UpdateFirmwareError", name: event.error, }, }; case "installingOsu": case "flashingMcu": case "flashingBootloader": return { ...initialState, step: event.type, progress: event.progress, }; case "allowSecureChannelRequested": case "allowSecureChannelDenied": case "installOsuDevicePermissionRequested": case "installOsuDevicePermissionGranted": case "installOsuDevicePermissionDenied": return { ...initialState, step: event.type }; case "firmwareUpdateCompleted": return { ...initialState, step: event.type, updatedDeviceInfo: event.updatedDeviceInfo, }; default: return { ...currentState, ...sharedReducer({ event, }), }; } }, initialState), ), ); }