diff --git a/.flowconfig b/.flowconfig index ce56a529..b7155c37 100644 --- a/.flowconfig +++ b/.flowconfig @@ -5,6 +5,7 @@ [untyped] .*/node_modules/react-select +/node_modules/qrloop/lib/Buffer.js* [include] diff --git a/package.json b/package.json index 2abb2303..a3e073dd 100644 --- a/package.json +++ b/package.json @@ -35,13 +35,13 @@ } }, "dependencies": { - "@ledgerhq/hw-app-btc": "^v4.30.0", - "@ledgerhq/hw-app-eth": "^4.24.0", - "@ledgerhq/hw-app-xrp": "^4.25.0", - "@ledgerhq/hw-transport": "^4.24.0", - "@ledgerhq/hw-transport-node-hid": "4.24.0", + "@ledgerhq/hw-app-btc": "^4.32.0", + "@ledgerhq/hw-app-eth": "^4.32.0", + "@ledgerhq/hw-app-xrp": "^4.32.0", + "@ledgerhq/hw-transport": "^4.32.0", + "@ledgerhq/hw-transport-node-hid": "^4.32.0", "@ledgerhq/ledger-core": "2.0.0-rc.14", - "@ledgerhq/live-common": "4.6.0", + "@ledgerhq/live-common": "4.8.0-beta.23", "animated": "^0.2.2", "async": "^2.6.1", "axios": "^0.18.0", @@ -189,4 +189,4 @@ "yarn": "^1.10.1" }, "private": true -} +} \ No newline at end of file diff --git a/src/api/Ethereum.js b/src/api/Ethereum.js index 12a01c6b..4542ddb6 100644 --- a/src/api/Ethereum.js +++ b/src/api/Ethereum.js @@ -1,7 +1,7 @@ // @flow import type { CryptoCurrency } from '@ledgerhq/live-common/lib/types' import { BigNumber } from 'bignumber.js' -import { LedgerAPINotAvailable } from 'config/errors' +import { LedgerAPINotAvailable } from '@ledgerhq/live-common/lib/errors' import network from './network' import { blockchainBaseURL } from './Ledger' diff --git a/src/api/Fees.js b/src/api/Fees.js index fb380905..476ea5d0 100644 --- a/src/api/Fees.js +++ b/src/api/Fees.js @@ -2,7 +2,7 @@ import invariant from 'invariant' import LRU from 'lru-cache' import type { Currency } from '@ledgerhq/live-common/lib/types' -import { FeeEstimationFailed } from 'config/errors' +import { FeeEstimationFailed } from '@ledgerhq/live-common/lib/errors' import { blockchainBaseURL } from './Ledger' import network from './network' diff --git a/src/api/network.js b/src/api/network.js index 0da18166..db0a3e5d 100644 --- a/src/api/network.js +++ b/src/api/network.js @@ -3,7 +3,11 @@ import axios from 'axios' import { GET_CALLS_RETRY, GET_CALLS_TIMEOUT } from 'config/constants' import { retry } from 'helpers/promise' import logger from 'logger' -import { LedgerAPIErrorWithMessage, LedgerAPIError, NetworkDown } from 'config/errors' +import { + LedgerAPIErrorWithMessage, + LedgerAPIError, + NetworkDown, +} from '@ledgerhq/live-common/lib/errors' import anonymizer from 'helpers/anonymizer' const userFriendlyError = (p: Promise, { url, method, startTime, ...rest }): Promise => diff --git a/src/bridge/EthereumJSBridge.js b/src/bridge/EthereumJSBridge.js index 0fd3f2a9..a337dd85 100644 --- a/src/bridge/EthereumJSBridge.js +++ b/src/bridge/EthereumJSBridge.js @@ -25,7 +25,7 @@ import { apiForCurrency } from 'api/Ethereum' import type { Tx } from 'api/Ethereum' import getAddressCommand from 'commands/getAddress' import signTransactionCommand from 'commands/signTransaction' -import { NotEnoughBalance, FeeNotLoaded, ETHAddressNonEIP } from 'config/errors' +import { NotEnoughBalance, FeeNotLoaded, ETHAddressNonEIP } from '@ledgerhq/live-common/lib/errors' import type { EditProps, WalletBridge } from './types' type Transaction = { diff --git a/src/bridge/LibcoreBridge.js b/src/bridge/LibcoreBridge.js index 8ed8de3d..16d52909 100644 --- a/src/bridge/LibcoreBridge.js +++ b/src/bridge/LibcoreBridge.js @@ -11,7 +11,7 @@ import libcoreSyncAccount from 'commands/libcoreSyncAccount' import libcoreSignAndBroadcast from 'commands/libcoreSignAndBroadcast' import libcoreGetFees, { extractGetFeesInputFromAccount } from 'commands/libcoreGetFees' import libcoreValidAddress from 'commands/libcoreValidAddress' -import { NotEnoughBalance, FeeNotLoaded } from 'config/errors' +import { NotEnoughBalance, FeeNotLoaded } from '@ledgerhq/live-common/lib/errors' import type { WalletBridge, EditProps } from './types' const NOT_ENOUGH_FUNDS = 52 diff --git a/src/bridge/RippleJSBridge.js b/src/bridge/RippleJSBridge.js index 31c9c356..c67a5a94 100644 --- a/src/bridge/RippleJSBridge.js +++ b/src/bridge/RippleJSBridge.js @@ -32,7 +32,7 @@ import { NotEnoughBalance, FeeNotLoaded, NotEnoughBalanceBecauseDestinationNotCreated, -} from 'config/errors' +} from '@ledgerhq/live-common/lib/errors' import type { WalletBridge, EditProps } from './types' type Transaction = { diff --git a/src/commands/.DS_Store b/src/commands/.DS_Store new file mode 100644 index 00000000..5008ddfc Binary files /dev/null and b/src/commands/.DS_Store differ diff --git a/src/commands/debugAppInfosForCurrency.js b/src/commands/debugAppInfosForCurrency.js index 1e13096a..dd576dda 100644 --- a/src/commands/debugAppInfosForCurrency.js +++ b/src/commands/debugAppInfosForCurrency.js @@ -1,10 +1,10 @@ // @flow import { getCryptoCurrencyById } from '@ledgerhq/live-common/lib/currencies' +import debugAppInfosForCurrency from '@ledgerhq/live-common/lib/hw/debugAppInfosForCurrency' import { createCommand, Command } from 'helpers/ipc' -import { fromPromise } from 'rxjs/observable/fromPromise' -import { withDevice } from 'helpers/deviceAccess' -import debugAppInfosForCurrency from 'helpers/debugAppInfosForCurrency' +import { from } from 'rxjs' +import { withDevice } from '@ledgerhq/live-common/lib/hw/deviceAccess' type Input = { currencyId: string, @@ -18,10 +18,8 @@ type Result = { const cmd: Command = createCommand( 'debugAppInfosForCurrency', ({ currencyId, devicePath }) => - fromPromise( - withDevice(devicePath)(transport => - debugAppInfosForCurrency(transport, getCryptoCurrencyById(currencyId)), - ), + withDevice(devicePath)(transport => + from(debugAppInfosForCurrency(transport, getCryptoCurrencyById(currencyId))), ), ) diff --git a/src/commands/firmwareMain.js b/src/commands/firmwareMain.js new file mode 100644 index 00000000..84101e02 --- /dev/null +++ b/src/commands/firmwareMain.js @@ -0,0 +1,17 @@ +// @flow + +import main from '@ledgerhq/live-common/lib/hw/firmwareUpdate-main' +import type { FirmwareUpdateContext } from '@ledgerhq/live-common/lib/types/manager' +import { createCommand, Command } from 'helpers/ipc' + +type Input = FirmwareUpdateContext + +type Result = { progress: number, installing: ?string } + +const cmd: Command = createCommand( + 'firmwareMain', + firmware => main('', firmware), + // devicePath='' HACK to not depend on a devicePath because it's dynamic +) + +export default cmd diff --git a/src/commands/firmwarePrepare.js b/src/commands/firmwarePrepare.js new file mode 100644 index 00000000..20b9a71f --- /dev/null +++ b/src/commands/firmwarePrepare.js @@ -0,0 +1,18 @@ +// @flow + +import prepare from '@ledgerhq/live-common/lib/hw/firmwareUpdate-prepare' +import type { FirmwareUpdateContext } from '@ledgerhq/live-common/lib/types/manager' +import { createCommand, Command } from 'helpers/ipc' + +type Input = { + devicePath: string, + firmware: FirmwareUpdateContext, +} + +type Result = { progress: number } + +const cmd: Command = createCommand('firmwarePrepare', ({ devicePath, firmware }) => + prepare(devicePath, firmware), +) + +export default cmd diff --git a/src/commands/firmwareRepair.js b/src/commands/firmwareRepair.js new file mode 100644 index 00000000..64009722 --- /dev/null +++ b/src/commands/firmwareRepair.js @@ -0,0 +1,14 @@ +// @flow + +import repair from '@ledgerhq/live-common/lib/hw/firmwareUpdate-repair' +import { createCommand, Command } from 'helpers/ipc' + +type Input = void +type Result = { progress: number } + +const cmd: Command = createCommand( + 'firmwareRepair', + () => repair(''), // devicePath='' HACK to not depend on a devicePath because it's dynamic +) + +export default cmd diff --git a/src/commands/getAddress.js b/src/commands/getAddress.js index 801f33e7..24fe89bc 100644 --- a/src/commands/getAddress.js +++ b/src/commands/getAddress.js @@ -2,18 +2,15 @@ import { getCryptoCurrencyById } from '@ledgerhq/live-common/lib/currencies' import { createCommand, Command } from 'helpers/ipc' -import { fromPromise } from 'rxjs/observable/fromPromise' -import { withDevice } from 'helpers/deviceAccess' -import getAddressForCurrency from 'helpers/getAddressForCurrency' - -import { DeviceAppVerifyNotSupported, UserRefusedAddress } from 'config/errors' +import { from } from 'rxjs' +import { withDevice } from '@ledgerhq/live-common/lib/hw/deviceAccess' +import getAddress from '@ledgerhq/live-common/lib/hw/getAddress' type Input = { currencyId: string, devicePath: string, path: string, verify?: boolean, - segwit?: boolean, } type Result = { @@ -25,20 +22,8 @@ type Result = { const cmd: Command = createCommand( 'getAddress', ({ currencyId, devicePath, path, ...options }) => - fromPromise( - withDevice(devicePath)(transport => - getAddressForCurrency(transport, getCryptoCurrencyById(currencyId), path, options), - ).catch(e => { - if (e && e.name === 'TransportStatusError') { - if (e.statusCode === 0x6b00 && options.verify) { - throw new DeviceAppVerifyNotSupported() - } - if (e.statusCode === 0x6985) { - throw new UserRefusedAddress() - } - } - throw e - }), + withDevice(devicePath)(transport => + from(getAddress(transport, getCryptoCurrencyById(currencyId), path, options.verify)), ), ) diff --git a/src/commands/getCurrentFirmware.js b/src/commands/getCurrentFirmware.js deleted file mode 100644 index d4ae0c86..00000000 --- a/src/commands/getCurrentFirmware.js +++ /dev/null @@ -1,21 +0,0 @@ -// @flow - -import { createCommand, Command } from 'helpers/ipc' -import { fromPromise } from 'rxjs/observable/fromPromise' - -import getCurrentFirmware from 'helpers/devices/getCurrentFirmware' -import type { FinalFirmware } from 'helpers/types' - -type Input = { - deviceId: string | number, - fullVersion: string, - provider: number, -} - -type Result = FinalFirmware - -const cmd: Command = createCommand('getCurrentFirmware', data => - fromPromise(getCurrentFirmware(data)), -) - -export default cmd diff --git a/src/commands/getDeviceInfo.js b/src/commands/getDeviceInfo.js index 9cbbfae1..a0529571 100644 --- a/src/commands/getDeviceInfo.js +++ b/src/commands/getDeviceInfo.js @@ -1,11 +1,10 @@ // @flow import { createCommand, Command } from 'helpers/ipc' -import { fromPromise } from 'rxjs/observable/fromPromise' -import { withDevice } from 'helpers/deviceAccess' - -import getDeviceInfo from 'helpers/devices/getDeviceInfo' -import type { DeviceInfo } from 'helpers/types' +import { from } from 'rxjs' +import { withDevice } from '@ledgerhq/live-common/lib/hw/deviceAccess' +import getDeviceInfo from '@ledgerhq/live-common/lib/hw/getDeviceInfo' +import type { DeviceInfo } from '@ledgerhq/live-common/lib/types/manager' type Input = { devicePath: string, @@ -14,7 +13,7 @@ type Input = { type Result = DeviceInfo const cmd: Command = createCommand('getDeviceInfo', ({ devicePath }) => - fromPromise(withDevice(devicePath)(transport => getDeviceInfo(transport))), + withDevice(devicePath)(transport => from(getDeviceInfo(transport))), ) export default cmd diff --git a/src/commands/getIsGenuine.js b/src/commands/getIsGenuine.js index e3e163b8..edc357e1 100644 --- a/src/commands/getIsGenuine.js +++ b/src/commands/getIsGenuine.js @@ -1,11 +1,12 @@ // @flow import { createCommand, Command } from 'helpers/ipc' -import { fromPromise } from 'rxjs/observable/fromPromise' -import type { DeviceInfo } from 'helpers/types' - -import getIsGenuine from 'helpers/devices/getIsGenuine' -import { withDevice } from 'helpers/deviceAccess' +import { of } from 'rxjs' +import { delay } from 'rxjs/operators' +import genuineCheck from '@ledgerhq/live-common/lib/hw/genuineCheck' +import { withDevice } from '@ledgerhq/live-common/lib/hw/deviceAccess' +import type { DeviceInfo } from '@ledgerhq/live-common/lib/types/manager' +import { SKIP_GENUINE } from 'config/constants' type Input = { devicePath: string, @@ -14,7 +15,10 @@ type Input = { type Result = string const cmd: Command = createCommand('getIsGenuine', ({ devicePath, deviceInfo }) => - fromPromise(withDevice(devicePath)(transport => getIsGenuine(transport, deviceInfo))), + withDevice(devicePath)( + transport => + SKIP_GENUINE ? of('0000').pipe(delay(1000)) : genuineCheck(transport, deviceInfo), + ), ) export default cmd diff --git a/src/commands/getLatestFirmwareForDevice.js b/src/commands/getLatestFirmwareForDevice.js index e88f6a50..22fe15b5 100644 --- a/src/commands/getLatestFirmwareForDevice.js +++ b/src/commands/getLatestFirmwareForDevice.js @@ -1,15 +1,14 @@ // @flow import { createCommand, Command } from 'helpers/ipc' -import { fromPromise } from 'rxjs/observable/fromPromise' -import type { DeviceInfo, OsuFirmware } from 'helpers/types' +import { from } from 'rxjs' +import type { DeviceInfo, FirmwareUpdateContext } from '@ledgerhq/live-common/lib/types/manager' +import manager from '@ledgerhq/live-common/lib/manager' -import getLatestFirmwareForDevice from '../helpers/devices/getLatestFirmwareForDevice' +type Result = ?FirmwareUpdateContext -type Result = ?(OsuFirmware & { shouldFlashMcu: boolean }) - -const cmd: Command = createCommand('getLatestFirmwareForDevice', data => - fromPromise(getLatestFirmwareForDevice(data)), +const cmd: Command = createCommand('getLatestFirmwareForDevice', deviceInfo => + from(manager.getLatestFirmwareForDevice(deviceInfo)), ) export default cmd diff --git a/src/commands/getMemInfo.js b/src/commands/getMemInfo.js deleted file mode 100644 index 848fa032..00000000 --- a/src/commands/getMemInfo.js +++ /dev/null @@ -1,19 +0,0 @@ -// @flow - -import { createCommand, Command } from 'helpers/ipc' -import { fromPromise } from 'rxjs/observable/fromPromise' - -import { withDevice } from 'helpers/deviceAccess' -import getMemInfo from 'helpers/devices/getMemInfo' - -type Input = { - devicePath: string, -} - -type Result = * - -const cmd: Command = createCommand('getMemInfo', ({ devicePath }) => - fromPromise(withDevice(devicePath)(transport => getMemInfo(transport))), -) - -export default cmd diff --git a/src/commands/index.js b/src/commands/index.js index 1022021c..c9f32998 100644 --- a/src/commands/index.js +++ b/src/commands/index.js @@ -4,17 +4,14 @@ import invariant from 'invariant' import type { Command } from 'helpers/ipc' import debugAppInfosForCurrency from 'commands/debugAppInfosForCurrency' +import firmwarePrepare from 'commands/firmwarePrepare' +import firmwareMain from 'commands/firmwareMain' +import firmwareRepair from 'commands/firmwareRepair' import getAddress from 'commands/getAddress' import getDeviceInfo from 'commands/getDeviceInfo' -import getCurrentFirmware from 'commands/getCurrentFirmware' import getIsGenuine from 'commands/getIsGenuine' import getLatestFirmwareForDevice from 'commands/getLatestFirmwareForDevice' -import getMemInfo from 'commands/getMemInfo' import installApp from 'commands/installApp' -import installFinalFirmware from 'commands/installFinalFirmware' -import installMcu from 'commands/installMcu' -import installOsuFirmware from 'commands/installOsuFirmware' -import isDashboardOpen from 'commands/isDashboardOpen' import killInternalProcess from 'commands/killInternalProcess' import libcoreGetFees from 'commands/libcoreGetFees' import libcoreGetVersion from 'commands/libcoreGetVersion' @@ -23,12 +20,8 @@ import libcoreScanFromXPUB from 'commands/libcoreScanFromXPUB' import libcoreSignAndBroadcast from 'commands/libcoreSignAndBroadcast' import libcoreSyncAccount from 'commands/libcoreSyncAccount' import libcoreValidAddress from 'commands/libcoreValidAddress' -import listApps from 'commands/listApps' -import listAppVersions from 'commands/listAppVersions' -import listCategories from 'commands/listCategories' import listenDevices from 'commands/listenDevices' import ping from 'commands/ping' -import shouldFlashMcu from 'commands/shouldFlashMcu' import signTransaction from 'commands/signTransaction' import testApdu from 'commands/testApdu' import testCrash from 'commands/testCrash' @@ -37,17 +30,14 @@ import uninstallApp from 'commands/uninstallApp' const all: Array> = [ debugAppInfosForCurrency, + firmwarePrepare, + firmwareMain, + firmwareRepair, getAddress, getDeviceInfo, - getCurrentFirmware, getIsGenuine, getLatestFirmwareForDevice, - getMemInfo, installApp, - installFinalFirmware, - installMcu, - installOsuFirmware, - isDashboardOpen, killInternalProcess, libcoreGetFees, libcoreGetVersion, @@ -56,12 +46,8 @@ const all: Array> = [ libcoreSignAndBroadcast, libcoreSyncAccount, libcoreValidAddress, - listApps, - listAppVersions, - listCategories, listenDevices, ping, - shouldFlashMcu, signTransaction, testApdu, testCrash, diff --git a/src/commands/installApp.js b/src/commands/installApp.js index cd1a2e81..3842ea21 100644 --- a/src/commands/installApp.js +++ b/src/commands/installApp.js @@ -1,25 +1,19 @@ // @flow - import { createCommand, Command } from 'helpers/ipc' -import { fromPromise } from 'rxjs/observable/fromPromise' - -import { withDevice } from 'helpers/deviceAccess' -import installApp from 'helpers/apps/installApp' - -import type { ApplicationVersion } from 'helpers/types' +import installApp from '@ledgerhq/live-common/lib/hw/installApp' +import { withDevice } from '@ledgerhq/live-common/lib/hw/deviceAccess' +import type { ApplicationVersion } from '@ledgerhq/live-common/lib/types/manager' type Input = { - app: ApplicationVersion, devicePath: string, targetId: string | number, + app: ApplicationVersion, } -type Result = void +type Result = { progress: number } -const cmd: Command = createCommand( - 'installApp', - ({ devicePath, targetId, ...app }) => - fromPromise(withDevice(devicePath)(transport => installApp(transport, targetId, app))), +const cmd: Command = createCommand('installApp', ({ devicePath, targetId, app }) => + withDevice(devicePath)(transport => installApp(transport, targetId, app)), ) export default cmd diff --git a/src/commands/installFinalFirmware.js b/src/commands/installFinalFirmware.js deleted file mode 100644 index 42671301..00000000 --- a/src/commands/installFinalFirmware.js +++ /dev/null @@ -1,21 +0,0 @@ -// @flow - -import { createCommand, Command } from 'helpers/ipc' -import { fromPromise } from 'rxjs/observable/fromPromise' -import { withDevice } from 'helpers/deviceAccess' - -import installFinalFirmware from 'helpers/firmware/installFinalFirmware' - -type Input = { - devicePath: string, -} - -type Result = { - success: boolean, -} - -const cmd: Command = createCommand('installFinalFirmware', ({ devicePath }) => - fromPromise(withDevice(devicePath)(transport => installFinalFirmware(transport))), -) - -export default cmd diff --git a/src/commands/installMcu.js b/src/commands/installMcu.js deleted file mode 100644 index 660bd546..00000000 --- a/src/commands/installMcu.js +++ /dev/null @@ -1,19 +0,0 @@ -// @flow - -import { createCommand, Command } from 'helpers/ipc' -import { fromPromise } from 'rxjs/observable/fromPromise' - -import { withDevice } from 'helpers/deviceAccess' -import installMcu from 'helpers/firmware/installMcu' - -type Input = { - devicePath: string, -} - -type Result = void - -const cmd: Command = createCommand('installMcu', ({ devicePath }) => - fromPromise(withDevice(devicePath)(transport => installMcu(transport))), -) - -export default cmd diff --git a/src/commands/installOsuFirmware.js b/src/commands/installOsuFirmware.js deleted file mode 100644 index 83b29a39..00000000 --- a/src/commands/installOsuFirmware.js +++ /dev/null @@ -1,27 +0,0 @@ -// @flow - -import { createCommand, Command } from 'helpers/ipc' -import { fromPromise } from 'rxjs/observable/fromPromise' - -import { withDevice } from 'helpers/deviceAccess' -import installOsuFirmware from 'helpers/firmware/installOsuFirmware' - -import type { Firmware } from 'components/modals/UpdateFirmware' - -type Input = { - devicePath: string, - targetId: string | number, - firmware: Firmware, -} - -type Result = { success: boolean } - -const cmd: Command = createCommand( - 'installOsuFirmware', - ({ devicePath, firmware, targetId }) => - fromPromise( - withDevice(devicePath)(transport => installOsuFirmware(transport, targetId, firmware)), - ), -) - -export default cmd diff --git a/src/commands/isDashboardOpen.js b/src/commands/isDashboardOpen.js deleted file mode 100644 index 2a33ff22..00000000 --- a/src/commands/isDashboardOpen.js +++ /dev/null @@ -1,19 +0,0 @@ -// @flow - -import { createCommand, Command } from 'helpers/ipc' -import { fromPromise } from 'rxjs/observable/fromPromise' -import { withDevice } from 'helpers/deviceAccess' - -import isDashboardOpen from '../helpers/devices/isDashboardOpen' - -type Input = { - devicePath: string, -} - -type Result = boolean - -const cmd: Command = createCommand('isDashboardOpen', ({ devicePath }) => - fromPromise(withDevice(devicePath)(transport => isDashboardOpen(transport))), -) - -export default cmd diff --git a/src/commands/libcoreGetFees.js b/src/commands/libcoreGetFees.js index 6a856df5..faebcdd0 100644 --- a/src/commands/libcoreGetFees.js +++ b/src/commands/libcoreGetFees.js @@ -13,7 +13,7 @@ import { bigNumberToLibcoreAmount, getOrCreateWallet, } from 'helpers/libcore' -import { InvalidAddress } from 'config/errors' +import { InvalidAddress } from '@ledgerhq/live-common/lib/errors' type BitcoinLikeTransaction = { // TODO we rename this Transaction concept into transactionInput diff --git a/src/commands/libcoreSignAndBroadcast.js b/src/commands/libcoreSignAndBroadcast.js index ca7389ea..30dd27ab 100644 --- a/src/commands/libcoreSignAndBroadcast.js +++ b/src/commands/libcoreSignAndBroadcast.js @@ -4,7 +4,7 @@ import logger from 'logger' import { BigNumber } from 'bignumber.js' import { StatusCodes } from '@ledgerhq/hw-transport' import Btc from '@ledgerhq/hw-app-btc' -import { Observable } from 'rxjs' +import { Observable, from } from 'rxjs' import { isSegwitDerivationMode } from '@ledgerhq/live-common/lib/derivation' import { getCryptoCurrencyById } from '@ledgerhq/live-common/lib/currencies' import type { OperationRaw, DerivationMode, CryptoCurrency } from '@ledgerhq/live-common/lib/types' @@ -14,11 +14,11 @@ import { bigNumberToLibcoreAmount, getOrCreateWallet, } from 'helpers/libcore' -import { UpdateYourApp } from 'config/errors' +import { UpdateYourApp } from '@ledgerhq/live-common/lib/errors' import withLibcore from 'helpers/withLibcore' import { createCommand, Command } from 'helpers/ipc' -import { withDevice } from 'helpers/deviceAccess' +import { withDevice } from '@ledgerhq/live-common/lib/hw/deviceAccess' type BitcoinLikeTransaction = { amount: string, @@ -246,22 +246,26 @@ export async function doSignAndBroadcast({ const hasTimestamp = !!njsWalletCurrency.bitcoinLikeNetworkParameters.UsesTimestampedTransaction // TODO: const timestampDelay = njsWalletCurrency.bitcoinLikeNetworkParameters.TimestampDelay - const signedTransaction = await withDevice(deviceId)(async transport => - signTransaction({ - hwApp: new Btc(transport), - currency, - blockHeight, - transaction: builded, - sigHashType: parseInt(sigHashType, 16), - hasTimestamp, - derivationMode, - }), - ).catch(e => { - if (e && e.statusCode === StatusCodes.INCORRECT_P1_P2) { - throw new UpdateYourApp(`UpdateYourApp ${currency.id}`, currency) - } - throw e - }) + const signedTransaction = await withDevice(deviceId)(transport => + from( + signTransaction({ + hwApp: new Btc(transport), + currency, + blockHeight, + transaction: builded, + sigHashType: parseInt(sigHashType, 16), + hasTimestamp, + derivationMode, + }), + ), + ) + .toPromise() + .catch(e => { + if (e && e.statusCode === StatusCodes.INCORRECT_P1_P2) { + throw new UpdateYourApp(`UpdateYourApp ${currency.id}`, currency) + } + throw e + }) if (!signedTransaction || isCancelled() || !njsAccount) return onSigned() diff --git a/src/commands/listAppVersions.js b/src/commands/listAppVersions.js deleted file mode 100644 index a802b2c6..00000000 --- a/src/commands/listAppVersions.js +++ /dev/null @@ -1,15 +0,0 @@ -// @flow - -import { createCommand, Command } from 'helpers/ipc' -import { fromPromise } from 'rxjs/observable/fromPromise' -import type { DeviceInfo, ApplicationVersion } from 'helpers/types' - -import listAppVersions from 'helpers/apps/listAppVersions' - -type Result = Array - -const cmd: Command = createCommand('listAppVersions', deviceInfo => - fromPromise(listAppVersions(deviceInfo)), -) - -export default cmd diff --git a/src/commands/listApps.js b/src/commands/listApps.js deleted file mode 100644 index 13f78609..00000000 --- a/src/commands/listApps.js +++ /dev/null @@ -1,15 +0,0 @@ -// @flow - -import { createCommand, Command } from 'helpers/ipc' -import { fromPromise } from 'rxjs/observable/fromPromise' - -import listApps from 'helpers/apps/listApps' -import type { Application } from 'helpers/types' - -type Input = void - -type Result = Array - -const cmd: Command = createCommand('listApps', () => fromPromise(listApps())) - -export default cmd diff --git a/src/commands/listCategories.js b/src/commands/listCategories.js deleted file mode 100644 index 641b4ad8..00000000 --- a/src/commands/listCategories.js +++ /dev/null @@ -1,17 +0,0 @@ -// @flow - -import { createCommand, Command } from 'helpers/ipc' -import { fromPromise } from 'rxjs/observable/fromPromise' - -import listCategories from 'helpers/apps/listCategories' -import type { Category } from 'helpers/types' - -type Input = void - -type Result = Array - -const cmd: Command = createCommand('listCategories', () => - fromPromise(listCategories()), -) - -export default cmd diff --git a/src/commands/shouldFlashMcu.js b/src/commands/shouldFlashMcu.js deleted file mode 100644 index f58c629c..00000000 --- a/src/commands/shouldFlashMcu.js +++ /dev/null @@ -1,15 +0,0 @@ -// @flow - -import { createCommand, Command } from 'helpers/ipc' -import { fromPromise } from 'rxjs/observable/fromPromise' -import shouldFlashMcu from 'helpers/devices/shouldFlashMcu' - -import type { DeviceInfo } from 'helpers/types' - -type Result = boolean - -const cmd: Command = createCommand('shouldFlashMcu', data => - fromPromise(shouldFlashMcu(data)), -) - -export default cmd diff --git a/src/commands/signTransaction.js b/src/commands/signTransaction.js index 7ffea26b..56ffebdc 100644 --- a/src/commands/signTransaction.js +++ b/src/commands/signTransaction.js @@ -1,8 +1,8 @@ // @flow import { createCommand, Command } from 'helpers/ipc' -import { fromPromise } from 'rxjs/observable/fromPromise' -import { withDevice } from 'helpers/deviceAccess' +import { from } from 'rxjs' +import { withDevice } from '@ledgerhq/live-common/lib/hw/deviceAccess' import signTransactionForCurrency from 'helpers/signTransactionForCurrency' type Input = { @@ -17,10 +17,8 @@ type Result = string const cmd: Command = createCommand( 'signTransaction', ({ currencyId, devicePath, path, transaction }) => - fromPromise( - withDevice(devicePath)(transport => - signTransactionForCurrency(currencyId)(transport, currencyId, path, transaction), - ), + withDevice(devicePath)(transport => + from(signTransactionForCurrency(currencyId)(transport, currencyId, path, transaction)), ), ) diff --git a/src/commands/testApdu.js b/src/commands/testApdu.js index 079cbcc1..652aaba1 100644 --- a/src/commands/testApdu.js +++ b/src/commands/testApdu.js @@ -3,8 +3,8 @@ // This is a test example for dev testing purpose. import { createCommand, Command } from 'helpers/ipc' -import { fromPromise } from 'rxjs/observable/fromPromise' -import { withDevice } from 'helpers/deviceAccess' +import { from } from 'rxjs' +import { withDevice } from '@ledgerhq/live-common/lib/hw/deviceAccess' type Input = { devicePath: string, @@ -15,11 +15,12 @@ type Result = { } const cmd: Command = createCommand('testApdu', ({ apduHex, devicePath }) => - fromPromise( - withDevice(devicePath)(async transport => { - const res = await transport.exchange(Buffer.from(apduHex, 'hex')) - return { responseHex: res.toString('hex') } - }), + withDevice(devicePath)(transport => + from( + transport + .exchange(Buffer.from(apduHex, 'hex')) + .then(res => ({ responseHex: res.toString('hex') })), + ), ), ) diff --git a/src/commands/uninstallApp.js b/src/commands/uninstallApp.js index 9b6df8a2..056ba069 100644 --- a/src/commands/uninstallApp.js +++ b/src/commands/uninstallApp.js @@ -1,12 +1,8 @@ // @flow - import { createCommand, Command } from 'helpers/ipc' -import { fromPromise } from 'rxjs/observable/fromPromise' -import { withDevice } from 'helpers/deviceAccess' - -import uninstallApp from 'helpers/apps/uninstallApp' - -import type { ApplicationVersion } from 'helpers/types' +import { withDevice } from '@ledgerhq/live-common/lib/hw/deviceAccess' +import uninstallApp from '@ledgerhq/live-common/lib/hw/uninstallApp' +import type { ApplicationVersion } from '@ledgerhq/live-common/lib/types/manager' type Input = { app: ApplicationVersion, @@ -14,12 +10,10 @@ type Input = { targetId: string | number, } -type Result = void +type Result = * -const cmd: Command = createCommand( - 'uninstallApp', - ({ devicePath, targetId, ...app }) => - fromPromise(withDevice(devicePath)(transport => uninstallApp(transport, targetId, app))), +const cmd: Command = createCommand('uninstallApp', ({ devicePath, targetId, app }) => + withDevice(devicePath)(transport => uninstallApp(transport, targetId, app)), ) export default cmd diff --git a/src/components/App.js b/src/components/App.js index a6b3d186..76a876e7 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -45,4 +45,5 @@ const App = ({ ) +// $FlowFixMe export default hot(module)(App) diff --git a/src/components/EnsureDeviceApp.js b/src/components/EnsureDeviceApp.js index e742a663..47f90e79 100644 --- a/src/components/EnsureDeviceApp.js +++ b/src/components/EnsureDeviceApp.js @@ -23,7 +23,11 @@ import IconUsb from 'icons/Usb' import type { Device } from 'types/common' -import { WrongDeviceForAccount, CantOpenDevice, UpdateYourApp } from 'config/errors' +import { + WrongDeviceForAccount, + CantOpenDevice, + UpdateYourApp, +} from '@ledgerhq/live-common/lib/errors' import { getCurrentDevice } from 'reducers/devices' const usbIcon = diff --git a/src/components/FeesField/BitcoinKind.js b/src/components/FeesField/BitcoinKind.js index 8eb47667..2b102792 100644 --- a/src/components/FeesField/BitcoinKind.js +++ b/src/components/FeesField/BitcoinKind.js @@ -8,7 +8,7 @@ import { translate } from 'react-i18next' import type { T } from 'types/common' -import { FeeNotLoaded } from 'config/errors' +import { FeeNotLoaded } from '@ledgerhq/live-common/lib/errors' import InputCurrency from 'components/base/InputCurrency' import Select from 'components/base/Select' import type { Fees } from 'api/Fees' diff --git a/src/components/FeesField/EthereumKind.js b/src/components/FeesField/EthereumKind.js index 0c51a07e..aa02c86a 100644 --- a/src/components/FeesField/EthereumKind.js +++ b/src/components/FeesField/EthereumKind.js @@ -4,7 +4,7 @@ import React, { Component } from 'react' import { BigNumber } from 'bignumber.js' import type { Account } from '@ledgerhq/live-common/lib/types' -import { FeeNotLoaded } from 'config/errors' +import { FeeNotLoaded } from '@ledgerhq/live-common/lib/errors' import InputCurrency from 'components/base/InputCurrency' import type { Fees } from 'api/Fees' import WithFeesAPI from '../WithFeesAPI' diff --git a/src/components/FeesField/RippleKind.js b/src/components/FeesField/RippleKind.js index 4a8ba3e9..8692cb12 100644 --- a/src/components/FeesField/RippleKind.js +++ b/src/components/FeesField/RippleKind.js @@ -4,7 +4,7 @@ import React, { Component } from 'react' import type { BigNumber } from 'bignumber.js' import type { Account } from '@ledgerhq/live-common/lib/types' import { apiForEndpointConfig, parseAPIValue } from 'api/Ripple' -import { FeeNotLoaded } from 'config/errors' +import { FeeNotLoaded } from '@ledgerhq/live-common/lib/errors' import InputCurrency from 'components/base/InputCurrency' import GenericContainer from './GenericContainer' diff --git a/src/components/GenuineCheck.js b/src/components/GenuineCheck.js index aef771e9..09107831 100644 --- a/src/components/GenuineCheck.js +++ b/src/components/GenuineCheck.js @@ -9,12 +9,18 @@ import { delay, createCancelablePolling } from 'helpers/promise' import logger from 'logger' import type { T, Device } from 'types/common' -import type { DeviceInfo } from 'helpers/types' +import manager from '@ledgerhq/live-common/lib/manager' +import type { DeviceInfo } from '@ledgerhq/live-common/lib/types/manager' import { GENUINE_TIMEOUT, DEVICE_INFOS_TIMEOUT, GENUINE_CACHE_DELAY } from 'config/constants' import { getCurrentDevice } from 'reducers/devices' -import { CantOpenDevice, DeviceNotGenuineError, DeviceGenuineSocketEarlyClose } from 'config/errors' +import { + CantOpenDevice, + DeviceNotGenuineError, + DeviceGenuineSocketEarlyClose, + UnexpectedBootloader, +} from '@ledgerhq/live-common/lib/errors' import getDeviceInfo from 'commands/getDeviceInfo' import getIsGenuine from 'commands/getIsGenuine' @@ -77,11 +83,26 @@ class GenuineCheck extends PureComponent { device: Device, deviceInfo: DeviceInfo, }) => { - if (deviceInfo.isOSU || deviceInfo.isBootloader) { + if (deviceInfo.isBootloader) { + logger.log('device is in bootloader mode') + throw new UnexpectedBootloader() + } + + if (deviceInfo.isOSU) { logger.log('device is in update mode. skipping genuine') return true } + // Preload things in parallel + Promise.all([ + // Step dashboard, we preload the applist before entering manager while we're still doing the genuine check + manager.getAppsList(deviceInfo), + // we also preload as much info as possible in case of a MCU + manager.getLatestFirmwareForDevice(deviceInfo), + ]).catch(e => { + logger.warn(e) + }) + if (genuineDevices.has(device)) { logger.log("genuine was already checked. don't check again") await delay(GENUINE_CACHE_DELAY) diff --git a/src/components/HSMStatusBanner.js b/src/components/HSMStatusBanner.js new file mode 100644 index 00000000..94606f72 --- /dev/null +++ b/src/components/HSMStatusBanner.js @@ -0,0 +1,171 @@ +// @flow + +import React, { PureComponent } from 'react' +import { warnings } from '@ledgerhq/live-common/lib/api/socket' +import { translate } from 'react-i18next' +import styled from 'styled-components' + +import { colors } from 'styles/theme' +import uniqueId from 'lodash/uniqueId' +import { openURL } from 'helpers/linking' +import IconCross from 'icons/Cross' +import IconExclamationCircle from 'icons/ExclamationCircle' +import IconChevronRight from 'icons/ChevronRight' + +import Box from 'components/base/Box' +import { SHOW_MOCK_HSMWARNINGS } from '../config/constants' +import { urls } from '../config/urls' + +const CloseIconContainer = styled.div` + position: absolute; + top: 0; + right: 0; + display: flex; + align-items: center; + justify-content: center; + padding: 10px; + border-bottom-left-radius: 4px; +` + +const CloseIcon = (props: *) => ( + + + +) + +type Props = { + t: *, +} + +type State = { + pendingMessages: HSMStatus[], +} + +type HSMStatus = { + id: string, + message: string, +} + +class HSMStatusBanner extends PureComponent { + state = { + pendingMessages: SHOW_MOCK_HSMWARNINGS + ? [ + { + id: 'mock1', + message: 'Lorem Ipsum dolor sit amet #1', + }, + ] + : [], + } + + componentDidMount() { + this.warningSub = warnings.subscribe({ + next: message => { + this.setState(prevState => ({ + pendingMessages: [...prevState.pendingMessages, { id: uniqueId(), message }], + })) + }, + }) + } + + componentWillUnmount() { + if (this.warningSub) { + this.warningSub.unsubscribe() + } + } + + warningSub = null + + dismiss = dismissedItem => + this.setState(prevState => ({ + pendingMessages: prevState.pendingMessages.filter(item => item.id !== dismissedItem.id), + })) + + render() { + const { t } = this.props + const { pendingMessages } = this.state + + if (!pendingMessages.length) return null + const item = pendingMessages[0] + + return ( + + + + ) + } +} + +class BannerItem extends PureComponent<{ + item: HSMStatus, + onItemDismiss: HSMStatus => void, + t: *, +}> { + onLinkClick = () => openURL(urls.contactSupport) + dismiss = () => this.props.onItemDismiss(this.props.item) + + render() { + const { item, t } = this.props + return ( + + + + + + {item.message} + + + + + ) + } +} + +const UnderlinedLink = styled.span` + border-bottom: 1px solid transparent; + &:hover { + border-bottom-color: white; + } +` + +const BannerItemLink = ({ t, onClick }: { t: *, onClick: void => * }) => ( + + + {t('common.learnMore')} + +) + +const styles = { + container: { + position: 'fixed', + left: 32, + bottom: 32, + zIndex: 100, + }, + banner: { + background: colors.orange, + overflow: 'hidden', + borderRadius: 4, + fontSize: 13, + paddingTop: 17, + padding: 15, + color: 'white', + fontWeight: 'bold', + paddingRight: 30, + width: 350, + }, + message: { + marginTop: -3, + }, +} + +export default translate()(HSMStatusBanner) diff --git a/src/components/IsUnlocked.js b/src/components/IsUnlocked.js index d2b22ada..391599db 100644 --- a/src/components/IsUnlocked.js +++ b/src/components/IsUnlocked.js @@ -17,7 +17,7 @@ import { hardReset } from 'helpers/reset' import { fetchAccounts } from 'actions/accounts' import { isLocked, unlock } from 'reducers/application' -import { PasswordIncorrectError } from 'config/errors' +import { PasswordIncorrectError } from '@ledgerhq/live-common/lib/errors' import Box from 'components/base/Box' import InputPassword from 'components/base/InputPassword' diff --git a/src/components/ManagerPage/AppSearchBar.js b/src/components/ManagerPage/AppSearchBar.js index 7e055e08..378bc423 100644 --- a/src/components/ManagerPage/AppSearchBar.js +++ b/src/components/ManagerPage/AppSearchBar.js @@ -3,7 +3,7 @@ import React, { PureComponent, Fragment } from 'react' import styled from 'styled-components' -import type { ApplicationVersion } from 'helpers/types' +import type { ApplicationVersion } from '@ledgerhq/live-common/lib/types/manager' import Box from 'components/base/Box' import Space from 'components/base/Space' diff --git a/src/components/ManagerPage/AppsList.js b/src/components/ManagerPage/AppsList.js index 91443678..115c8904 100644 --- a/src/components/ManagerPage/AppsList.js +++ b/src/components/ManagerPage/AppsList.js @@ -6,12 +6,12 @@ import styled from 'styled-components' import { translate } from 'react-i18next' import { connect } from 'react-redux' import { compose } from 'redux' + import type { Device, T } from 'types/common' -import type { ApplicationVersion, DeviceInfo } from 'helpers/types' +import type { ApplicationVersion, DeviceInfo } from '@ledgerhq/live-common/lib/types/manager' +import manager from '@ledgerhq/live-common/lib/manager' import { getFullListSortedCryptoCurrencies } from 'helpers/countervalues' import { developerModeSelector } from 'reducers/settings' -import listApps from 'commands/listApps' -import listAppVersions from 'commands/listAppVersions' import installApp from 'commands/installApp' import uninstallApp from 'commands/uninstallApp' import Box from 'components/base/Box' @@ -19,7 +19,7 @@ import Space from 'components/base/Space' import Modal, { ModalBody, ModalFooter, ModalTitle, ModalContent } from 'components/base/Modal' import Tooltip from 'components/base/Tooltip' import Text from 'components/base/Text' -import Progress from 'components/base/Progress' +import ProgressBar from 'components/ProgressBar' import Spinner from 'components/base/Spinner' import Button from 'components/base/Button' import TranslatedError from 'components/TranslatedError' @@ -65,6 +65,7 @@ type State = { appsLoaded: boolean, app: string, mode: Mode, + progress: number, } const oldAppsInstallDisabled = ['ZenCash', 'Ripple'] @@ -86,6 +87,7 @@ class AppsList extends PureComponent { appsLoaded: false, app: '', mode: 'home', + progress: 0, } componentDidMount() { @@ -98,53 +100,15 @@ class AppsList extends PureComponent { _unmounted = false - prepareAppList = ({ applicationsList, compatibleAppVersionsList, sortedCryptoCurrencies }) => { - const filtered = this.props.isDevMode - ? compatibleAppVersionsList.slice(0) - : compatibleAppVersionsList.filter(version => { - const app = applicationsList.find(e => e.id === version.app) - if (app) { - return app.category !== 2 - } - - return false - }) - - const sortedCryptoApps = [] - - // sort by crypto first - sortedCryptoCurrencies.forEach(crypto => { - const app = filtered.find( - item => item.name.toLowerCase() === crypto.managerAppName.toLowerCase(), - ) - if (app) { - filtered.splice(filtered.indexOf(app), 1) - sortedCryptoApps.push(app) - } - }) - - return sortedCryptoApps.concat(filtered) - } - async fetchAppList() { - try { - const { deviceInfo } = this.props - - const [ - applicationsList, - compatibleAppVersionsList, - sortedCryptoCurrencies, - ] = await Promise.all([ - listApps.send().toPromise(), - listAppVersions.send(deviceInfo).toPromise(), - getFullListSortedCryptoCurrencies(), - ]) + const { deviceInfo, isDevMode } = this.props - const filteredAppVersionsList = this.prepareAppList({ - applicationsList, - compatibleAppVersionsList, - sortedCryptoCurrencies, - }) + try { + const filteredAppVersionsList = await manager.getAppsList( + deviceInfo, + isDevMode, + getFullListSortedCryptoCurrencies, + ) if (!this._unmounted) { this.setState({ @@ -158,41 +122,37 @@ class AppsList extends PureComponent { } } - handleInstallApp = (app: ApplicationVersion) => async () => { - this.setState({ status: 'busy', app: app.name, mode: 'installing' }) - try { - const { - device: { path: devicePath }, - deviceInfo, - } = this.props - const data = { app, devicePath, targetId: deviceInfo.targetId } - await installApp.send(data).toPromise() - this.setState({ status: 'success' }) - } catch (err) { - this.setState({ status: 'error', error: err, mode: 'home' }) - } + sub: * + runAppScript = (app: ApplicationVersion, mode: *, cmd: *) => { + this.setState({ status: 'busy', app: app.name, mode, progress: 0 }) + const { + device: { path: devicePath }, + deviceInfo: { targetId }, + } = this.props + this.sub = cmd.send({ app, devicePath, targetId }).subscribe({ + next: patch => { + this.setState(patch) + }, + complete: () => { + this.setState({ status: 'success' }) + }, + error: error => { + this.setState({ status: 'error', error, app: '', mode: 'home' }) + }, + }) } - handleUninstallApp = (app: ApplicationVersion) => async () => { - this.setState({ status: 'busy', app: app.name, mode: 'uninstalling' }) - try { - const { - device: { path: devicePath }, - deviceInfo, - } = this.props - const data = { app, devicePath, targetId: deviceInfo.targetId } - await uninstallApp.send(data).toPromise() - this.setState({ status: 'success' }) - } catch (err) { - this.setState({ status: 'error', error: err, app: '', mode: 'home' }) - } - } + handleInstallApp = (app: ApplicationVersion) => () => + this.runAppScript(app, 'installing', installApp) + + handleUninstallApp = (app: ApplicationVersion) => () => + this.runAppScript(app, 'uninstalling', uninstallApp) handleCloseModal = () => this.setState({ status: 'idle', mode: 'home' }) renderModal = () => { const { t } = this.props - const { app, status, error, mode } = this.state + const { app, status, error, mode, progress } = this.state return ( { {t(`manager.apps.${mode}`, { app })} - + diff --git a/src/components/ManagerPage/Dashboard.js b/src/components/ManagerPage/Dashboard.js index 3e6d3687..b26e3b09 100644 --- a/src/components/ManagerPage/Dashboard.js +++ b/src/components/ManagerPage/Dashboard.js @@ -4,7 +4,7 @@ import { translate } from 'react-i18next' import styled from 'styled-components' import type { T, Device } from 'types/common' -import type { DeviceInfo } from 'helpers/types' +import type { DeviceInfo } from '@ledgerhq/live-common/lib/types/manager' import Box from 'components/base/Box' import Text from 'components/base/Text' diff --git a/src/components/ManagerPage/FirmwareUpdate.js b/src/components/ManagerPage/FirmwareUpdate.js index 90e997b1..9eb9d547 100644 --- a/src/components/ManagerPage/FirmwareUpdate.js +++ b/src/components/ManagerPage/FirmwareUpdate.js @@ -3,20 +3,13 @@ import React, { PureComponent, Fragment } from 'react' import { translate } from 'react-i18next' -import isEqual from 'lodash/isEqual' -import isEmpty from 'lodash/isEmpty' -import invariant from 'invariant' import type { Device, T } from 'types/common' -import type { DeviceInfo, OsuFirmware } from 'helpers/types' +import type { DeviceInfo, FirmwareUpdateContext } from '@ledgerhq/live-common/lib/types/manager' import type { StepId } from 'components/modals/UpdateFirmware' import getLatestFirmwareForDevice from 'commands/getLatestFirmwareForDevice' -import shouldFlashMcu from 'commands/shouldFlashMcu' -import installOsuFirmware from 'commands/installOsuFirmware' -import installFinalFirmware from 'commands/installFinalFirmware' -import installMcu from 'commands/installMcu' import DisclaimerModal from 'components/modals/UpdateFirmware/Disclaimer' import UpdateModal from 'components/modals/UpdateFirmware' @@ -42,32 +35,34 @@ type Props = { } type State = { - latestFirmware: ?OsuFirmware & ?{ shouldFlashMcu: boolean }, + firmware: ?FirmwareUpdateContext, modal: ModalStatus, stepId: ?StepId, - shouldFlash: boolean, ready: boolean, } const intializeState = ({ deviceInfo }): State => ({ - latestFirmware: null, + firmware: null, modal: 'closed', stepId: deviceInfo.isBootloader ? 'updateMCU' : 'idCheck', - shouldFlash: false, ready: false, }) class FirmwareUpdate extends PureComponent { state = intializeState(this.props) - componentDidMount() { + async componentDidMount() { const { deviceInfo } = this.props - if (!deviceInfo.isOSU && !deviceInfo.isBootloader) { - this.fetchLatestFirmware() - } else if (deviceInfo.isOSU) { - this.shouldFlashMcu() - } else if (deviceInfo.isBootloader) { - this.handleInstallModal('updateMCU', true) + const firmware = await getLatestFirmwareForDevice.send(deviceInfo).toPromise() + if (firmware && !this._unmounting) { + /* eslint-disable */ + this.setState({ + firmware, + ready: true, + modal: deviceInfo.isOSU ? 'install' : 'closed', + stepId: deviceInfo.isOSU ? 'updateMCU' : 'idCheck', + }) + /* eslint-enable */ } } @@ -77,55 +72,15 @@ class FirmwareUpdate extends PureComponent { _unmounting = false - fetchLatestFirmware = async () => { - const { deviceInfo } = this.props - const latestFirmware = await getLatestFirmwareForDevice.send(deviceInfo).toPromise() - if ( - !isEmpty(latestFirmware) && - !isEqual(this.state.latestFirmware, latestFirmware) && - !this._unmounting - ) { - 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) => { - const { latestFirmware } = this.state - const { deviceInfo } = this.props - invariant(latestFirmware, 'did not find a new firmware or firmware is not set') - - this.setState({ modal: 'install' }) - const result = await installOsuFirmware - .send({ devicePath: device.path, firmware: latestFirmware, targetId: deviceInfo.targetId }) - .toPromise() - - return result - } - - installFinalFirmware = (device: Device) => - installFinalFirmware.send({ devicePath: device.path }).toPromise() - - flashMCU = async (device: Device) => installMcu.send({ devicePath: device.path }).toPromise() - handleCloseModal = () => this.setState({ modal: 'closed' }) handleDisclaimerModal = () => this.setState({ modal: 'disclaimer' }) - handleInstallModal = (stepId: StepId = 'idCheck', shouldFlash?: boolean) => - this.setState({ modal: 'install', stepId, shouldFlash, ready: true }) handleDisclaimerNext = () => this.setState({ modal: 'install' }) render() { const { deviceInfo, t, device } = this.props - const { latestFirmware, modal, stepId, shouldFlash, ready } = this.state + const { firmware, modal, stepId, ready } = this.state return ( @@ -151,12 +106,12 @@ class FirmwareUpdate extends PureComponent { })} - + {ready ? ( { status={modal} stepId={stepId} onClose={this.handleCloseModal} - firmware={latestFirmware} - shouldFlashMcu={shouldFlash} - installOsuFirmware={this.installOsuFirmware} - installFinalFirmware={this.installFinalFirmware} - flashMCU={this.flashMCU} + firmware={firmware} /> ) : null} diff --git a/src/components/ManagerPage/UpdateFirmwareButton.js b/src/components/ManagerPage/UpdateFirmwareButton.js index fcfc529e..66d6e314 100644 --- a/src/components/ManagerPage/UpdateFirmwareButton.js +++ b/src/components/ManagerPage/UpdateFirmwareButton.js @@ -3,19 +3,14 @@ import React, { Fragment } from 'react' import { translate } from 'react-i18next' import type { T } from 'types/common' - +import type { OsuFirmware, FinalFirmware } from '@ledgerhq/live-common/lib/types/manager' import Button from 'components/base/Button' import Text from 'components/base/Text' import { getCleanVersion } from 'components/ManagerPage/FirmwareUpdate' -type FirmwareInfos = { - name: string, - notes: string, -} - type Props = { t: T, - firmware: ?FirmwareInfos, + firmware: ?{ osu: OsuFirmware, finalFirmware: FinalFirmware }, onClick: () => void, } @@ -23,14 +18,14 @@ const UpdateFirmwareButton = ({ t, firmware, onClick }: Props) => firmware ? ( - {t('manager.firmware.latest', { version: getCleanVersion(firmware.name) })} + {t('manager.firmware.latest', { version: getCleanVersion(firmware.osu.name) })} + + + + ) + } +} + +const mapDispatchToProps = { + push, +} + +export default compose( + translate(), + withRouter, + connect( + null, + mapDispatchToProps, + ), +)(RepairDeviceButton) diff --git a/src/components/SettingsPage/sections/Help.js b/src/components/SettingsPage/sections/Help.js index c4577020..10949ceb 100644 --- a/src/components/SettingsPage/sections/Help.js +++ b/src/components/SettingsPage/sections/Help.js @@ -11,6 +11,7 @@ import ExportLogsBtn from 'components/ExportLogsBtn' import OpenUserDataDirectoryBtn from 'components/OpenUserDataDirectoryBtn' import CleanButton from '../CleanButton' import ResetButton from '../ResetButton' +import RepairDeviceButton from '../RepairDeviceButton' import AboutRowItem from '../AboutRowItem' import LaunchOnboardingBtn from '../LaunchOnboardingBtn' @@ -72,6 +73,9 @@ class SectionHelp extends PureComponent { > + + + ) diff --git a/src/components/base/Modal/ConfirmModal.js b/src/components/base/Modal/ConfirmModal.js index 9ed7cbe6..0512a981 100644 --- a/src/components/base/Modal/ConfirmModal.js +++ b/src/components/base/Modal/ConfirmModal.js @@ -25,11 +25,13 @@ type Props = { t: T, isLoading?: boolean, analyticsName: string, + cancellable?: boolean, } class ConfirmModal extends PureComponent { render() { const { + cancellable, isOpened, title, subTitle, @@ -54,7 +56,7 @@ class ConfirmModal extends PureComponent { preventBackdropClick={isLoading} {...props} render={({ onClose }) => ( - + {title} diff --git a/src/components/base/Modal/RepairModal.js b/src/components/base/Modal/RepairModal.js new file mode 100644 index 00000000..37d00df9 --- /dev/null +++ b/src/components/base/Modal/RepairModal.js @@ -0,0 +1,203 @@ +// @flow + +import React, { PureComponent } from 'react' +import { translate } from 'react-i18next' +import styled from 'styled-components' + +import type { T } from 'types/common' + +import { i } from 'helpers/staticPath' +import TrackPage from 'analytics/TrackPage' +import Button from 'components/base/Button' +import Box from 'components/base/Box' +import Text from 'components/base/Text' +import ProgressCircle from 'components/ProgressCircle' +import TranslatedError from 'components/TranslatedError' +import ExclamationCircleThin from 'icons/ExclamationCircleThin' + +import { Modal, ModalContent, ModalBody, ModalTitle, ModalFooter } from './index' + +const Container = styled(Box).attrs({ + alignItems: 'center', + fontSize: 4, + color: 'dark', +})`` + +const Bullet = styled.span` + font-weight: 600; + color: #142533; +` + +const Separator = styled(Box).attrs({ + color: 'fog', +})` + height: 1px; + width: 100%; + background-color: currentColor; +` + +const DisclaimerStep = ({ desc }: { desc?: string }) => ( + + {desc ? ( + + {desc} + + ) : null} + +) + +const FlashStep = ({ progress, t }: { progress: number, t: * }) => + progress === 0 ? ( + + + + {'1.'} + {t('manager.modal.mcuFirst')} + + {t('manager.modal.mcuFirst')} + + + + + {'2.'} + {t('manager.modal.mcuSecond')} + + {t('manager.modal.mcuFirst')} + + + ) : ( + + + + + + {t(`manager.modal.steps.flash`)} + + + + {t('manager.modal.mcuPin')} + + + + ) + +const ErrorStep = ({ error }: { error: Error }) => ( + + + + + + + + + + + + + +) + +type Props = { + isOpened: boolean, + isDanger: boolean, + title: string, + subTitle?: string, + desc: string, + renderIcon?: Function, + confirmText?: string, + cancelText?: string, + onReject: Function, + onConfirm: Function, + t: T, + isLoading?: boolean, + analyticsName: string, + cancellable?: boolean, + progress: number, + error?: Error, +} + +class RepairModal extends PureComponent { + render() { + const { + cancellable, + isOpened, + title, + desc, + confirmText, + isDanger, + onReject, + onConfirm, + isLoading, + renderIcon, + t, + analyticsName, + progress, + error, + ...props + } = this.props + + const realConfirmText = confirmText || t('common.confirm') + + return ( + ( + + + {title} + {error ? ( + + ) : isLoading ? ( + + ) : ( + + )} + + {!isLoading ? ( + + + {error ? null : ( + + )} + + ) : null} + + )} + /> + ) + } +} + +export default translate()(RepairModal) diff --git a/src/components/base/Modal/index.js b/src/components/base/Modal/index.js index ca84f4fb..cad43fda 100644 --- a/src/components/base/Modal/index.js +++ b/src/components/base/Modal/index.js @@ -22,6 +22,7 @@ import GrowScroll from 'components/base/GrowScroll' export { default as ModalBody } from './ModalBody' export { default as ConfirmModal } from './ConfirmModal' +export { default as RepairModal } from './RepairModal' export { default as ModalTitle } from './ModalTitle' const springConfig = { diff --git a/src/components/layout/Default.js b/src/components/layout/Default.js index f79744ac..02830da4 100644 --- a/src/components/layout/Default.js +++ b/src/components/layout/Default.js @@ -35,6 +35,7 @@ import SideBar from 'components/MainSideBar' import TopBar from 'components/TopBar' import SyncBackground from 'components/SyncBackground' import SyncContinuouslyPendingOperations from '../SyncContinouslyPendingOperations' +import HSMStatusBanner from '../HSMStatusBanner' const Main = styled(GrowScroll).attrs({ px: 6, @@ -106,6 +107,7 @@ class Default extends Component { +
(this._scrollContainer = n)} tabIndex={-1}> diff --git a/src/components/modals/AccountSettingRenderBody.js b/src/components/modals/AccountSettingRenderBody.js index ae029eb2..0575d4c1 100644 --- a/src/components/modals/AccountSettingRenderBody.js +++ b/src/components/modals/AccountSettingRenderBody.js @@ -17,7 +17,7 @@ import { setDataModal } from 'reducers/modals' import { getBridgeForCurrency } from 'bridge' -import { AccountNameRequiredError, EnpointConfigError } from 'config/errors' +import { AccountNameRequiredError, EnpointConfigError } from '@ledgerhq/live-common/lib/errors' import TrackPage from 'analytics/TrackPage' import Spoiler from 'components/base/Spoiler' diff --git a/src/components/modals/Receive/steps/04-step-receive-funds.js b/src/components/modals/Receive/steps/04-step-receive-funds.js index 59e1cf37..45cd33e1 100644 --- a/src/components/modals/Receive/steps/04-step-receive-funds.js +++ b/src/components/modals/Receive/steps/04-step-receive-funds.js @@ -5,10 +5,9 @@ import React, { PureComponent } from 'react' import TrackPage from 'analytics/TrackPage' import getAddress from 'commands/getAddress' -import { isSegwitDerivationMode } from '@ledgerhq/live-common/lib/derivation' import Box from 'components/base/Box' import CurrentAddressForAccount from 'components/CurrentAddressForAccount' -import { DisconnectedDevice, WrongDeviceForAccount } from 'config/errors' +import { DisconnectedDevice, WrongDeviceForAccount } from '@ledgerhq/live-common/lib/errors' import type { StepProps } from '..' @@ -25,14 +24,14 @@ export default class StepReceiveFunds extends PureComponent { if (!device || !account) { throw new DisconnectedDevice() } - const params = { - currencyId: account.currency.id, - devicePath: device.path, - path: account.freshAddressPath, - segwit: isSegwitDerivationMode(account.derivationMode), - verify: true, - } - const { address } = await getAddress.send(params).toPromise() + const { address } = await getAddress + .send({ + currencyId: account.currency.id, + devicePath: device.path, + path: account.freshAddressPath, + verify: true, + }) + .toPromise() if (address !== account.freshAddress) { throw new WrongDeviceForAccount(`WrongDeviceForAccount ${account.name}`, { diff --git a/src/components/modals/Send/fields/RecipientField.js b/src/components/modals/Send/fields/RecipientField.js index ab23cb3c..c4e9f92e 100644 --- a/src/components/modals/Send/fields/RecipientField.js +++ b/src/components/modals/Send/fields/RecipientField.js @@ -9,8 +9,8 @@ import Box from 'components/base/Box' import LabelWithExternalIcon from 'components/base/LabelWithExternalIcon' import RecipientAddress from 'components/RecipientAddress' import { track } from 'analytics/segment' -import { createCustomErrorClass } from 'helpers/errors' -import { CantScanQRCode } from 'config/errors' +import { createCustomErrorClass } from '@ledgerhq/live-common/lib/errors/helpers' +import { CantScanQRCode } from '@ledgerhq/live-common/lib/errors' type Props = { t: T, diff --git a/src/components/modals/Send/index.js b/src/components/modals/Send/index.js index 6ed05d4c..17801759 100644 --- a/src/components/modals/Send/index.js +++ b/src/components/modals/Send/index.js @@ -20,7 +20,7 @@ import type { StepProps as DefaultStepProps } from 'components/base/Stepper' import { getCurrentDevice } from 'reducers/devices' import { accountsSelector } from 'reducers/accounts' import { closeModal, openModal } from 'reducers/modals' -import { DisconnectedDevice, UserRefusedOnDevice } from 'config/errors' +import { DisconnectedDevice, UserRefusedOnDevice } from '@ledgerhq/live-common/lib/errors' import Modal from 'components/base/Modal' import Stepper from 'components/base/Stepper' diff --git a/src/components/modals/UpdateFirmware/Disclaimer.js b/src/components/modals/UpdateFirmware/Disclaimer.js index f4fce175..76d16fae 100644 --- a/src/components/modals/UpdateFirmware/Disclaimer.js +++ b/src/components/modals/UpdateFirmware/Disclaimer.js @@ -3,7 +3,7 @@ import React, { PureComponent, Fragment } from 'react' import { translate, Trans } from 'react-i18next' - +import type { OsuFirmware, FinalFirmware } from '@ledgerhq/live-common/lib/types/manager' import type { T } from 'types/common' import Modal, { ModalBody, ModalFooter, ModalTitle, ModalContent } from 'components/base/Modal' @@ -18,15 +18,13 @@ import type { ModalStatus } from 'components/ManagerPage/FirmwareUpdate' import { getCleanVersion } from 'components/ManagerPage/FirmwareUpdate' -type FirmwareInfos = { - name: string, - notes: string, -} - type Props = { t: T, status: ModalStatus, - firmware: FirmwareInfos, + firmware: { + osu: ?OsuFirmware, + final: ?FinalFirmware, + }, goToNextStep: () => void, onClose: () => void, } @@ -50,7 +48,9 @@ class DisclaimerModal extends PureComponent { You are about to install - {`firmware version ${firmware ? getCleanVersion(firmware.name) : ''}`} + {`firmware version ${ + firmware && firmware.osu ? getCleanVersion(firmware.osu.name) : '' + }`} @@ -59,14 +59,16 @@ class DisclaimerModal extends PureComponent { {t('manager.firmware.disclaimerAppReinstall')} - - - - {firmware.notes} - - - - + {firmware && firmware.osu ? ( + + + + {firmware.osu.notes} + + + + + ) : null}