diff --git a/.circleci/config.yml b/.circleci/config.yml index b0b6394a..97af1a3b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -13,10 +13,10 @@ jobs: - checkout - restore_cache: keys: - - v7-yarn-packages-{{ checksum "yarn.lock" }} + - v8-yarn-packages-{{ checksum "yarn.lock" }} - run: yarn install - save_cache: - key: v7-yarn-packages-{{ checksum "yarn.lock" }} + key: v8-yarn-packages-{{ checksum "yarn.lock" }} paths: - node_modules - run: yarn lint diff --git a/.eslintrc b/.eslintrc index 6be03c28..a5c7283d 100644 --- a/.eslintrc +++ b/.eslintrc @@ -21,6 +21,7 @@ "jest": false, "describe": false, "beforeEach": false, + "afterEach": false, "test": false, "it": false, "expect": false, diff --git a/.gitignore b/.gitignore index 65ca3013..40e58245 100644 --- a/.gitignore +++ b/.gitignore @@ -10,11 +10,3 @@ /build/linux/arch/src /build/linux/arch/*.tar.gz /build/linux/arch/*.tar.xz - -# TODO this should be in devs global gitignore -# it makes no sense to have it here -*.log -.DS_Store -.vscode -thumbs.db -jsconfig.json diff --git a/README.md b/README.md index a8eb732f..d2632b44 100644 --- a/README.md +++ b/README.md @@ -159,3 +159,15 @@ yarn reset-files ├── webpack : build configuration └── yarn.lock ``` + +## Troubleshooting + +#### The 'gyp==0.1' distribution was not found and is required by the application + +You will need to install the python gyp module + +``` +pip install git+https://chromium.googlesource.com/external/gyp +``` + +see [stackoverflow/40025591](https://stackoverflow.com/questions/40025591/the-gyp-0-1-distribution-was-not-found) diff --git a/build/linux/arch/PKGBUILD b/build/linux/arch/PKGBUILD index b00dc702..2947605f 100644 --- a/build/linux/arch/PKGBUILD +++ b/build/linux/arch/PKGBUILD @@ -2,7 +2,7 @@ # shellcheck disable=SC2154,SC2034,SC2164 pkgname=ledger-live -pkgver=1.0.7 +pkgver=1.1.0 pkgrel=1 pkgdesc="Open source companion app for your Ledger devices" arch=('x86_64') @@ -15,7 +15,7 @@ changelog= source=("https://github.com/LedgerHQ/ledger-live-desktop/archive/v${pkgver}.tar.gz" "ledger-live.desktop") -md5sums=('d60d772a03c0a1c59df07f93b0268a4c' +md5sums=('5dc6bd1e8d6dedcfe760d1c75565aad8' '52705147909a0a988907a23a71199092') # TODO sign with ledger pgp validpgpkeys=() diff --git a/package.json b/package.json index 59394e48..bde6a8a8 100644 --- a/package.json +++ b/package.json @@ -13,10 +13,11 @@ "dist": "bash ./scripts/dist.sh", "dist:dir": "bash ./scripts/dist-dir.sh", "compile": "bash ./scripts/compile.sh", - "lint": "eslint src webpack .storybook", + "lint": "eslint src webpack .storybook test-e2e", "flow": "flow", - "test": "jest", - "prettier": "prettier --write \"{src,webpack,.storybook}/**/*.{js,json}\"", + "test": "jest src", + "test-e2e": "jest test-e2e", + "prettier": "prettier --write \"{src,webpack,.storybook,test-e2e}/**/*.{js,json}\"", "ci": "yarn lint && yarn flow && yarn prettier && yarn test", "storybook": "NODE_ENV=development STORYBOOK_ENV=1 start-storybook -s ./static -p 4444", "publish-storybook": "bash ./scripts/legacy/publish-storybook.sh", @@ -38,7 +39,7 @@ "@ledgerhq/hw-transport": "^4.13.0", "@ledgerhq/hw-transport-node-hid": "4.22.0", "@ledgerhq/ledger-core": "2.0.0-rc.6", - "@ledgerhq/live-common": "3.0.2", + "@ledgerhq/live-common": "^3.3.0", "animated": "^0.2.2", "async": "^2.6.1", "axios": "^0.18.0", @@ -118,6 +119,7 @@ "@babel/preset-flow": "7.0.0-beta.42", "@babel/preset-react": "7.0.0-beta.42", "@babel/preset-stage-0": "7.0.0-beta.42", + "@octokit/rest": "^15.10.0", "@storybook/addon-actions": "^3.4.7", "@storybook/addon-knobs": "^3.4.7", "@storybook/addon-links": "^3.4.7", @@ -134,7 +136,7 @@ "chance": "^1.0.13", "concurrently": "3.5.1", "dotenv": "^5.0.1", - "electron": "1.8.7", + "electron": "1.8.8", "electron-builder": "20.14.7", "electron-devtools-installer": "^2.2.3", "electron-rebuild": "^1.7.3", @@ -158,6 +160,7 @@ "prettier": "^1.13.5", "react-hot-loader": "^4.3.2", "react-test-renderer": "^16.4.1", + "spectron": "^3.8.0", "webpack": "^4.6.0", "webpack-bundle-analyzer": "^2.11.1", "webpack-cli": "^2.0.14", diff --git a/scripts/create-draft-release.js b/scripts/create-draft-release.js new file mode 100644 index 00000000..7edb38d7 --- /dev/null +++ b/scripts/create-draft-release.js @@ -0,0 +1,85 @@ +#!/usr/bin/env node + +/* eslint-disable no-console */ + +const util = require('util') +const exec = util.promisify(require('child_process').exec) +const octokit = require('@octokit/rest')() + +const repo = { + owner: 'LedgerHQ', + repo: 'ledger-live-desktop', +} + +async function getTag() { + const { stdout } = await exec('git tag --points-at HEAD') + const tag = stdout.replace('\n', '') + + if (!tag) { + throw new Error(`Unable to get current tag. Is your HEAD on a tagged commit?`) + } + + return tag +} + +async function checkDraft(tag) { + const { status, data } = await octokit.repos.getReleases(repo) + + if (status !== 200) { + throw new Error(`Got HTTP status ${status} when trying to fetch releases list.`) + } + + for (const release of data) { + if (release.tag_name === tag) { + if (release.draft) { + return true + } + + throw new Error(`A release tagged ${tag} exists but is not a draft.`) + } + } + + return false +} + +async function createDraft(tag) { + const params = { + ...repo, + tag_name: tag, + name: tag, + draft: true, + prerelease: true, + } + + const { status } = await octokit.repos.createRelease(params) + + if (status !== 201) { + throw new Error(`Got HTTP status ${status} when trying to create the release draft.`) + } +} + +async function main() { + try { + const token = process.env.GH_TOKEN + const tag = await getTag() + + octokit.authenticate({ + type: 'token', + token, + }) + + const existingDraft = await checkDraft(tag) + + if (!existingDraft) { + console.log(`No draft exists for ${tag}, creating...`) + createDraft(tag) + } else { + console.log(`A draft already exists for ${tag}, nothing to do.`) + } + } catch (e) { + console.error(e) + process.exit(1) + } +} + +main() diff --git a/scripts/helpers/display-env.sh b/scripts/helpers/display-env.sh index 5f903413..2a94c8ad 100644 --- a/scripts/helpers/display-env.sh +++ b/scripts/helpers/display-env.sh @@ -7,9 +7,15 @@ if [ "$GIT_REVISION" == "" ]; then GIT_REVISION=$(git rev-parse HEAD) fi +if [[ $(uname) == 'Darwin' ]]; then + osVersion="$(sw_vers -productName) $(sw_vers -productVersion)" +else + osVersion="$(uname -srmo)" +fi + echo printf " │ \\e[4;1m%s\\e[0;0m\\n" "Ledger Live Desktop - ${GIT_REVISION}" -printf " │ \\e[1;30m%s\\e[1;0m\\n" "$(uname -srmo)" +printf " │ \\e[1;30m%s\\e[1;0m\\n" "${osVersion}" printf " │ \\e[2;1mcommit \\e[0;33m%s\\e[0;0m\\n" "$(git rev-parse HEAD)" echo diff --git a/scripts/release.sh b/scripts/release.sh index 3a68b4e7..a0912e45 100755 --- a/scripts/release.sh +++ b/scripts/release.sh @@ -25,6 +25,11 @@ fi if [ ! -d "static/fonts/museosans" ]; then if ! command -v aws ; then + if ! command -v apt ; then + echo "Museo Sans is missing, and I can't fetch it (no aws, no apt)" >&2 + exit 1 + fi + runJob "sudo apt install awscli" "installing aws cli..." "installed aws cli" "failed to install aws cli" fi @@ -52,6 +57,15 @@ fi # exit 1 # fi + +if [[ $(uname) == 'Linux' ]]; then # only run it on one target, to prevent race conditions + runJob \ + "node scripts/create-draft-release.js" \ + "creating a draft release on GitHub (if needed)..." \ + "draft release ready" \ + "failed to create a draft release" +fi + runJob "yarn compile" "compiling..." "compiled" "failed to compile" "verbose" runJob \ diff --git a/src/api/Ethereum.js b/src/api/Ethereum.js index 68f72f90..12a01c6b 100644 --- a/src/api/Ethereum.js +++ b/src/api/Ethereum.js @@ -1,12 +1,10 @@ // @flow import type { CryptoCurrency } from '@ledgerhq/live-common/lib/types' import { BigNumber } from 'bignumber.js' -import { createCustomErrorClass } from 'helpers/errors' +import { LedgerAPINotAvailable } from 'config/errors' import network from './network' import { blockchainBaseURL } from './Ledger' -export const LedgerAPINotAvailable = createCustomErrorClass('LedgerAPINotAvailable') - export type Block = { height: number } // TODO more fields actually export type Tx = { hash: string, diff --git a/src/api/Fees.js b/src/api/Fees.js index 19d32e77..fb380905 100644 --- a/src/api/Fees.js +++ b/src/api/Fees.js @@ -2,12 +2,10 @@ import invariant from 'invariant' import LRU from 'lru-cache' import type { Currency } from '@ledgerhq/live-common/lib/types' -import { createCustomErrorClass } from 'helpers/errors' +import { FeeEstimationFailed } from 'config/errors' import { blockchainBaseURL } from './Ledger' import network from './network' -const FeeEstimationFailed = createCustomErrorClass('FeeEstimationFailed') - export type Fees = { [_: string]: number, } diff --git a/src/api/network.js b/src/api/network.js index 8743b401..eac84f7f 100644 --- a/src/api/network.js +++ b/src/api/network.js @@ -3,13 +3,9 @@ import axios from 'axios' import { GET_CALLS_RETRY, GET_CALLS_TIMEOUT } from 'config/constants' import { retry } from 'helpers/promise' import logger from 'logger' -import { createCustomErrorClass } from 'helpers/errors' +import { LedgerAPIErrorWithMessage, LedgerAPIError, NetworkDown } from 'config/errors' import anonymizer from 'helpers/anonymizer' -export const LedgerAPIErrorWithMessage = createCustomErrorClass('LedgerAPIErrorWithMessage') -export const LedgerAPIError = createCustomErrorClass('LedgerAPIError') -export const NetworkDown = createCustomErrorClass('NetworkDown') - const userFriendlyError = (p: Promise, { url, method, startTime }): Promise => p.catch(error => { let errorToThrow diff --git a/src/bridge/EthereumJSBridge.js b/src/bridge/EthereumJSBridge.js index 65be4faa..e68f70d2 100644 --- a/src/bridge/EthereumJSBridge.js +++ b/src/bridge/EthereumJSBridge.js @@ -16,12 +16,9 @@ import { getDerivations } from 'helpers/derivations' import getAddressCommand from 'commands/getAddress' import signTransactionCommand from 'commands/signTransaction' import { getAccountPlaceholderName, getNewAccountPlaceholderName } from 'helpers/accountName' -import { createCustomErrorClass } from 'helpers/errors' -import { ETHAddressNonEIP } from 'config/errors' +import { NotEnoughBalance, ETHAddressNonEIP } from 'config/errors' import type { EditProps, WalletBridge } from './types' -const NotEnoughBalance = createCustomErrorClass('NotEnoughBalance') - type Transaction = { recipient: string, amount: BigNumber, diff --git a/src/bridge/LibcoreBridge.js b/src/bridge/LibcoreBridge.js index f2e3511e..642788d1 100644 --- a/src/bridge/LibcoreBridge.js +++ b/src/bridge/LibcoreBridge.js @@ -11,11 +11,10 @@ import libcoreSyncAccount from 'commands/libcoreSyncAccount' import libcoreSignAndBroadcast from 'commands/libcoreSignAndBroadcast' import libcoreGetFees from 'commands/libcoreGetFees' import libcoreValidAddress from 'commands/libcoreValidAddress' -import { createCustomErrorClass } from 'helpers/errors' +import { NotEnoughBalance } from 'config/errors' import type { WalletBridge, EditProps } from './types' const NOT_ENOUGH_FUNDS = 52 -const NotEnoughBalance = createCustomErrorClass('NotEnoughBalance') const notImplemented = new Error('LibcoreBridge: not implemented') diff --git a/src/bridge/RippleJSBridge.js b/src/bridge/RippleJSBridge.js index d720c5aa..f872eeb6 100644 --- a/src/bridge/RippleJSBridge.js +++ b/src/bridge/RippleJSBridge.js @@ -20,11 +20,9 @@ import { import FeesRippleKind from 'components/FeesField/RippleKind' import AdvancedOptionsRippleKind from 'components/AdvancedOptions/RippleKind' import { getAccountPlaceholderName, getNewAccountPlaceholderName } from 'helpers/accountName' -import { createCustomErrorClass } from 'helpers/errors' +import { NotEnoughBalance } from 'config/errors' import type { WalletBridge, EditProps } from './types' -const NotEnoughBalance = createCustomErrorClass('NotEnoughBalance') - type Transaction = { amount: BigNumber, recipient: string, diff --git a/src/bridge/index.js b/src/bridge/index.js index d2d470da..2fdabe04 100644 --- a/src/bridge/index.js +++ b/src/bridge/index.js @@ -12,6 +12,7 @@ const perFamily = { bitcoin: LibcoreBridge, ripple: RippleJSBridge, ethereum: EthereumJSBridge, + stellar: null, } if (USE_MOCK_DATA) { const mockBridge = makeMockBridge() diff --git a/src/commands/getCurrentFirmware.js b/src/commands/getCurrentFirmware.js index 773220ae..d4ae0c86 100644 --- a/src/commands/getCurrentFirmware.js +++ b/src/commands/getCurrentFirmware.js @@ -4,6 +4,7 @@ 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, @@ -11,7 +12,7 @@ type Input = { provider: number, } -type Result = * +type Result = FinalFirmware const cmd: Command = createCommand('getCurrentFirmware', data => fromPromise(getCurrentFirmware(data)), diff --git a/src/commands/getDeviceInfo.js b/src/commands/getDeviceInfo.js index 67c585fd..9cbbfae1 100644 --- a/src/commands/getDeviceInfo.js +++ b/src/commands/getDeviceInfo.js @@ -5,13 +5,15 @@ import { fromPromise } from 'rxjs/observable/fromPromise' import { withDevice } from 'helpers/deviceAccess' import getDeviceInfo from 'helpers/devices/getDeviceInfo' -import type { DeviceInfo } from 'helpers/devices/getDeviceInfo' +import type { DeviceInfo } from 'helpers/types' type Input = { devicePath: string, } -const cmd: Command = createCommand('getDeviceInfo', ({ devicePath }) => +type Result = DeviceInfo + +const cmd: Command = createCommand('getDeviceInfo', ({ devicePath }) => fromPromise(withDevice(devicePath)(transport => getDeviceInfo(transport))), ) diff --git a/src/commands/getIsGenuine.js b/src/commands/getIsGenuine.js index a1bb645d..e3e163b8 100644 --- a/src/commands/getIsGenuine.js +++ b/src/commands/getIsGenuine.js @@ -2,7 +2,7 @@ import { createCommand, Command } from 'helpers/ipc' import { fromPromise } from 'rxjs/observable/fromPromise' -import type { DeviceInfo } from 'helpers/devices/getDeviceInfo' +import type { DeviceInfo } from 'helpers/types' import getIsGenuine from 'helpers/devices/getIsGenuine' import { withDevice } from 'helpers/deviceAccess' diff --git a/src/commands/getLatestFirmwareForDevice.js b/src/commands/getLatestFirmwareForDevice.js index d216d2eb..e88f6a50 100644 --- a/src/commands/getLatestFirmwareForDevice.js +++ b/src/commands/getLatestFirmwareForDevice.js @@ -2,11 +2,11 @@ import { createCommand, Command } from 'helpers/ipc' import { fromPromise } from 'rxjs/observable/fromPromise' -import type { DeviceInfo } from 'helpers/devices/getDeviceInfo' +import type { DeviceInfo, OsuFirmware } from 'helpers/types' import getLatestFirmwareForDevice from '../helpers/devices/getLatestFirmwareForDevice' -type Result = * +type Result = ?(OsuFirmware & { shouldFlashMcu: boolean }) const cmd: Command = createCommand('getLatestFirmwareForDevice', data => fromPromise(getLatestFirmwareForDevice(data)), diff --git a/src/commands/index.js b/src/commands/index.js index 758e1416..8b1ff369 100644 --- a/src/commands/index.js +++ b/src/commands/index.js @@ -17,7 +17,6 @@ import installOsuFirmware from 'commands/installOsuFirmware' import isDashboardOpen from 'commands/isDashboardOpen' import libcoreGetFees from 'commands/libcoreGetFees' import libcoreGetVersion from 'commands/libcoreGetVersion' -import libcoreHardReset from 'commands/libcoreHardReset' import libcoreScanAccounts from 'commands/libcoreScanAccounts' import libcoreSignAndBroadcast from 'commands/libcoreSignAndBroadcast' import libcoreSyncAccount from 'commands/libcoreSyncAccount' @@ -49,7 +48,6 @@ const all: Array> = [ isDashboardOpen, libcoreGetFees, libcoreGetVersion, - libcoreHardReset, libcoreScanAccounts, libcoreSignAndBroadcast, libcoreSyncAccount, diff --git a/src/commands/installApp.js b/src/commands/installApp.js index 7fcc7db7..cd1a2e81 100644 --- a/src/commands/installApp.js +++ b/src/commands/installApp.js @@ -6,15 +6,15 @@ import { fromPromise } from 'rxjs/observable/fromPromise' import { withDevice } from 'helpers/deviceAccess' import installApp from 'helpers/apps/installApp' -import type { LedgerScriptParams } from 'helpers/types' +import type { ApplicationVersion } from 'helpers/types' type Input = { - app: LedgerScriptParams, + app: ApplicationVersion, devicePath: string, targetId: string | number, } -type Result = * +type Result = void const cmd: Command = createCommand( 'installApp', diff --git a/src/commands/installMcu.js b/src/commands/installMcu.js index 7d85e40b..660bd546 100644 --- a/src/commands/installMcu.js +++ b/src/commands/installMcu.js @@ -10,7 +10,7 @@ type Input = { devicePath: string, } -type Result = * +type Result = void const cmd: Command = createCommand('installMcu', ({ devicePath }) => fromPromise(withDevice(devicePath)(transport => installMcu(transport))), diff --git a/src/commands/installOsuFirmware.js b/src/commands/installOsuFirmware.js index f73a76d4..83b29a39 100644 --- a/src/commands/installOsuFirmware.js +++ b/src/commands/installOsuFirmware.js @@ -14,7 +14,7 @@ type Input = { firmware: Firmware, } -type Result = * +type Result = { success: boolean } const cmd: Command = createCommand( 'installOsuFirmware', diff --git a/src/commands/libcoreGetFees.js b/src/commands/libcoreGetFees.js index e6d5a005..ab96ff30 100644 --- a/src/commands/libcoreGetFees.js +++ b/src/commands/libcoreGetFees.js @@ -6,9 +6,7 @@ import withLibcore from 'helpers/withLibcore' import { createCommand, Command } from 'helpers/ipc' import * as accountIdHelper from 'helpers/accountId' import { isValidAddress, libcoreAmountToBigNumber, bigNumberToLibcoreAmount } from 'helpers/libcore' -import { createCustomErrorClass } from 'helpers/errors' - -const InvalidAddress = createCustomErrorClass('InvalidAddress') +import { InvalidAddress } from 'config/errors' type BitcoinLikeTransaction = { // TODO we rename this Transaction concept into transactionInput diff --git a/src/commands/libcoreHardReset.js b/src/commands/libcoreHardReset.js deleted file mode 100644 index 6b52a6a9..00000000 --- a/src/commands/libcoreHardReset.js +++ /dev/null @@ -1,21 +0,0 @@ -// @flow - -import { createCommand } from 'helpers/ipc' -import { fromPromise } from 'rxjs/observable/fromPromise' -import withLibcore from 'helpers/withLibcore' -import { createCustomErrorClass } from 'helpers/errors' - -const HardResetFail = createCustomErrorClass('HardResetFail') - -const cmd = createCommand('libcoreHardReset', () => - fromPromise( - withLibcore(async core => { - const result = await core.getPoolInstance().eraseDataSince(new Date(0)) - if (result !== core.ERROR_CODE.FUTURE_WAS_SUCCESSFULL) { - throw new HardResetFail(`Hard reset fail with ${result} (check core.ERROR_CODE)`) - } - }), - ), -) - -export default cmd diff --git a/src/commands/listAppVersions.js b/src/commands/listAppVersions.js index 494d7168..a802b2c6 100644 --- a/src/commands/listAppVersions.js +++ b/src/commands/listAppVersions.js @@ -2,11 +2,11 @@ import { createCommand, Command } from 'helpers/ipc' import { fromPromise } from 'rxjs/observable/fromPromise' -import type { DeviceInfo } from 'helpers/devices/getDeviceInfo' +import type { DeviceInfo, ApplicationVersion } from 'helpers/types' import listAppVersions from 'helpers/apps/listAppVersions' -type Result = * +type Result = Array const cmd: Command = createCommand('listAppVersions', deviceInfo => fromPromise(listAppVersions(deviceInfo)), diff --git a/src/commands/listApps.js b/src/commands/listApps.js index f049def3..13f78609 100644 --- a/src/commands/listApps.js +++ b/src/commands/listApps.js @@ -4,10 +4,11 @@ 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 = {} +type Input = void -type Result = * +type Result = Array const cmd: Command = createCommand('listApps', () => fromPromise(listApps())) diff --git a/src/commands/listCategories.js b/src/commands/listCategories.js index 3632a47f..641b4ad8 100644 --- a/src/commands/listCategories.js +++ b/src/commands/listCategories.js @@ -4,10 +4,11 @@ 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 = {} +type Input = void -type Result = * +type Result = Array const cmd: Command = createCommand('listCategories', () => fromPromise(listCategories()), diff --git a/src/commands/shouldFlashMcu.js b/src/commands/shouldFlashMcu.js index 3bae3f04..f58c629c 100644 --- a/src/commands/shouldFlashMcu.js +++ b/src/commands/shouldFlashMcu.js @@ -2,9 +2,10 @@ 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' +import type { DeviceInfo } from 'helpers/types' + type Result = boolean const cmd: Command = createCommand('shouldFlashMcu', data => diff --git a/src/commands/uninstallApp.js b/src/commands/uninstallApp.js index 59a61a3d..9b6df8a2 100644 --- a/src/commands/uninstallApp.js +++ b/src/commands/uninstallApp.js @@ -6,20 +6,20 @@ import { withDevice } from 'helpers/deviceAccess' import uninstallApp from 'helpers/apps/uninstallApp' -import type { LedgerScriptParams } from 'helpers/types' +import type { ApplicationVersion } from 'helpers/types' type Input = { - app: LedgerScriptParams, + app: ApplicationVersion, devicePath: string, targetId: string | number, } -type Result = * +type Result = void const cmd: Command = createCommand( 'uninstallApp', - ({ devicePath, targetId, ...rest }) => - fromPromise(withDevice(devicePath)(transport => uninstallApp(transport, targetId, rest))), + ({ devicePath, targetId, ...app }) => + fromPromise(withDevice(devicePath)(transport => uninstallApp(transport, targetId, app))), ) export default cmd diff --git a/src/components/DashboardPage/EmptyState.js b/src/components/DashboardPage/EmptyState.js index 0a0d63e3..f3ffe2b5 100644 --- a/src/components/DashboardPage/EmptyState.js +++ b/src/components/DashboardPage/EmptyState.js @@ -45,15 +45,25 @@ class EmptyState extends PureComponent { height="157" /> - {t('app:emptyState.dashboard.title')} + {t('app:emptyState.dashboard.title')} {t('app:emptyState.dashboard.desc')} - - diff --git a/src/components/EnsureDeviceApp.js b/src/components/EnsureDeviceApp.js index 4748a0a9..6bb0e89c 100644 --- a/src/components/EnsureDeviceApp.js +++ b/src/components/EnsureDeviceApp.js @@ -12,7 +12,6 @@ import getAddress from 'commands/getAddress' import { createCancelablePolling } from 'helpers/promise' import { standardDerivation } from 'helpers/derivations' import { isSegwitPath } from 'helpers/bip32' -import { BtcUnmatchedApp } from 'helpers/getAddressForCurrency/btc' import DeviceInteraction from 'components/DeviceInteraction' import Text from 'components/base/Text' @@ -21,7 +20,7 @@ import IconUsb from 'icons/Usb' import type { Device } from 'types/common' -import { WrongDeviceForAccount, CantOpenDevice } from 'config/errors' +import { WrongDeviceForAccount, CantOpenDevice, BtcUnmatchedApp } from 'config/errors' import { getCurrentDevice } from 'reducers/devices' const usbIcon = diff --git a/src/components/GenuineCheck.js b/src/components/GenuineCheck.js index b1012d4d..0b79cf69 100644 --- a/src/components/GenuineCheck.js +++ b/src/components/GenuineCheck.js @@ -9,7 +9,7 @@ import { delay, createCancelablePolling } from 'helpers/promise' import logger from 'logger' import type { T, Device } from 'types/common' -import type { DeviceInfo } from 'helpers/devices/getDeviceInfo' +import type { DeviceInfo } from 'helpers/types' import { GENUINE_TIMEOUT, DEVICE_INFOS_TIMEOUT, GENUINE_CACHE_DELAY } from 'config/constants' diff --git a/src/components/IsUnlocked.js b/src/components/IsUnlocked.js index ecd90d79..f9dc5fc8 100644 --- a/src/components/IsUnlocked.js +++ b/src/components/IsUnlocked.js @@ -12,12 +12,12 @@ import { i } from 'helpers/staticPath' import IconTriangleWarning from 'icons/TriangleWarning' import db from 'helpers/db' -import hardReset from 'helpers/hardReset' +import { hardReset } from 'helpers/reset' import { fetchAccounts } from 'actions/accounts' import { isLocked, unlock } from 'reducers/application' -import { createCustomErrorClass } from 'helpers/errors' +import { PasswordIncorrectError } from 'config/errors' import Box from 'components/base/Box' import InputPassword from 'components/base/InputPassword' @@ -26,8 +26,6 @@ import IconArrowRight from 'icons/ArrowRight' import Button from './base/Button/index' import ConfirmModal from './base/Modal/ConfirmModal' -const PasswordIncorrectError = createCustomErrorClass('PasswordIncorrect') - type InputValue = { password: string, } diff --git a/src/components/ManagerPage/AppSearchBar.js b/src/components/ManagerPage/AppSearchBar.js index cad33077..a9292f14 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 { LedgerScriptParams } from 'helpers/types' +import type { ApplicationVersion } from 'helpers/types' import Box from 'components/base/Box' import Space from 'components/base/Space' @@ -23,8 +23,8 @@ const CrossContainer = styled(Box).attrs({ ` type Props = { - list: Array, - children: (list: Array) => React$Node, + list: Array, + children: (list: Array) => React$Node, } type State = { diff --git a/src/components/ManagerPage/AppsList.js b/src/components/ManagerPage/AppsList.js index ae2644fe..e2f8bf1c 100644 --- a/src/components/ManagerPage/AppsList.js +++ b/src/components/ManagerPage/AppsList.js @@ -7,8 +7,7 @@ import { translate } from 'react-i18next' import { connect } from 'react-redux' import { compose } from 'redux' import type { Device, T } from 'types/common' -import type { LedgerScriptParams } from 'helpers/types' -import type { DeviceInfo } from 'helpers/devices/getDeviceInfo' +import type { Application, ApplicationVersion, DeviceInfo } from 'helpers/types' import { developerModeSelector } from 'reducers/settings' import listApps from 'commands/listApps' @@ -66,7 +65,7 @@ type Props = { type State = { status: Status, error: ?Error, - filteredAppVersionsList: LedgerScriptParams[], + filteredAppVersionsList: Array, appsLoaded: boolean, app: string, mode: Mode, @@ -102,9 +101,14 @@ class AppsList extends PureComponent { filterAppVersions = (applicationsList, compatibleAppVersionsList) => { if (!this.props.isDevMode) { - return compatibleAppVersionsList.filter( - version => applicationsList.find(e => e.id === version.app).category !== 2, - ) + return compatibleAppVersionsList.filter(version => { + const app = applicationsList.find(e => e.id === version.app) + if (app) { + return app.category !== 2 + } + + return false + }) } return compatibleAppVersionsList } @@ -112,7 +116,7 @@ class AppsList extends PureComponent { async fetchAppList() { try { const { deviceInfo } = this.props - const applicationsList = await listApps.send({}).toPromise() + const applicationsList: Array = await listApps.send().toPromise() const compatibleAppVersionsList = await listAppVersions.send(deviceInfo).toPromise() const filteredAppVersionsList = this.filterAppVersions( applicationsList, @@ -131,7 +135,7 @@ class AppsList extends PureComponent { } } - handleInstallApp = (app: LedgerScriptParams) => async () => { + handleInstallApp = (app: ApplicationVersion) => async () => { this.setState({ status: 'busy', app: app.name, mode: 'installing' }) try { const { @@ -146,7 +150,7 @@ class AppsList extends PureComponent { } } - handleUninstallApp = (app: LedgerScriptParams) => async () => { + handleUninstallApp = (app: ApplicationVersion) => async () => { this.setState({ status: 'busy', app: app.name, mode: 'uninstalling' }) try { const { diff --git a/src/components/ManagerPage/Dashboard.js b/src/components/ManagerPage/Dashboard.js index 2f04caff..3e726fab 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/devices/getDeviceInfo' +import type { DeviceInfo } from 'helpers/types' 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 c289054c..dcaba901 100644 --- a/src/components/ManagerPage/FirmwareUpdate.js +++ b/src/components/ManagerPage/FirmwareUpdate.js @@ -9,7 +9,7 @@ import invariant from 'invariant' import type { Device, T } from 'types/common' -import type { LedgerScriptParams } from 'helpers/types' +import type { DeviceInfo, OsuFirmware } from 'helpers/types' import type { StepId } from 'components/modals/UpdateFirmware' import getLatestFirmwareForDevice from 'commands/getLatestFirmwareForDevice' @@ -19,7 +19,6 @@ import installFinalFirmware from 'commands/installFinalFirmware' import installMcu from 'commands/installMcu' import DisclaimerModal from 'components/modals/UpdateFirmware/Disclaimer' import UpdateModal from 'components/modals/UpdateFirmware' -import type { DeviceInfo } from 'helpers/devices/getDeviceInfo' import Tooltip from 'components/base/Tooltip' import Box, { Card } from 'components/base/Box' @@ -43,7 +42,7 @@ type Props = { } type State = { - latestFirmware: ?LedgerScriptParams & ?{ shouldFlashMcu: boolean }, + latestFirmware: ?OsuFirmware & ?{ shouldFlashMcu: boolean }, modal: ModalStatus, stepId: ?StepId, shouldFlash: boolean, diff --git a/src/components/ManagerPage/index.js b/src/components/ManagerPage/index.js index 956d7c90..7c9631b2 100644 --- a/src/components/ManagerPage/index.js +++ b/src/components/ManagerPage/index.js @@ -6,7 +6,7 @@ import { openURL } from 'helpers/linking' import { urls } from 'config/urls' import type { Device } from 'types/common' -import type { DeviceInfo } from 'helpers/devices/getDeviceInfo' +import type { DeviceInfo } from 'helpers/types' import Dashboard from './Dashboard' diff --git a/src/components/Onboarding/OnboardingFooter.js b/src/components/Onboarding/OnboardingFooter.js index 3db431cc..e9ee8cac 100644 --- a/src/components/Onboarding/OnboardingFooter.js +++ b/src/components/Onboarding/OnboardingFooter.js @@ -26,7 +26,13 @@ const OnboardingFooter = ({ - diff --git a/src/components/Onboarding/steps/Analytics.js b/src/components/Onboarding/steps/Analytics.js index 81213aa2..90f09ea1 100644 --- a/src/components/Onboarding/steps/Analytics.js +++ b/src/components/Onboarding/steps/Analytics.js @@ -70,7 +70,7 @@ class Analytics extends PureComponent { deviceType={onboarding.isLedgerNano ? 'Nano S' : 'Blue'} /> - {t('onboarding:analytics.title')} + {t('onboarding:analytics.title')} {t('onboarding:analytics.desc')} diff --git a/src/components/Onboarding/steps/Finish.js b/src/components/Onboarding/steps/Finish.js index 969791e6..74d6053f 100644 --- a/src/components/Onboarding/steps/Finish.js +++ b/src/components/Onboarding/steps/Finish.js @@ -103,11 +103,11 @@ export default class Finish extends Component { - {t('onboarding:finish.title')} + {t('onboarding:finish.title')} {t('onboarding:finish.desc')} - diff --git a/src/components/QRCodeExporter.js b/src/components/QRCodeExporter.js index 2fc2d14e..57933e31 100644 --- a/src/components/QRCodeExporter.js +++ b/src/components/QRCodeExporter.js @@ -1,31 +1,21 @@ // @flow import React, { PureComponent } from 'react' - +import { createSelector } from 'reselect' import { connect } from 'react-redux' -import type { State } from 'reducers' + import { accountsSelector } from 'reducers/accounts' +import { makeChunks } from '@ledgerhq/live-common/lib/bridgestream/exporter' import QRCode from './base/QRCode' -// encode the app state to export into an array of chunks for the mobile app to understand. -// returned data frames are json stringified array with format: [ datalength, index, type, ...rest ] -// NB as soon as we have common types we'll move this in a ledgerhq/common project -function makeChunks(state: State): Array { - const chunksFormatVersion = 1 - const desktopVersion = __APP_VERSION__ - const data = [ - ['meta', chunksFormatVersion, 'desktop', desktopVersion], - ...accountsSelector(state).map(account => [ - 'account', - account.id, - account.name, - account.currency.id, - ]), - ] - return data.map((arr, i) => JSON.stringify([data.length, i, ...arr])) -} - -const mapStateToProps = (state: State) => ({ chunks: makeChunks(state) }) +const mapStateToProps = createSelector(accountsSelector, accounts => ({ + chunks: makeChunks({ + accounts, + exporterName: 'desktop', + exporterVersion: __APP_VERSION__, + pad: true, + }), +})) class QRCodeExporter extends PureComponent< { @@ -38,7 +28,7 @@ class QRCodeExporter extends PureComponent< }, > { static defaultProps = { - fps: 10, + fps: 4, size: 480, } diff --git a/src/components/RecipientAddress/index.js b/src/components/RecipientAddress/index.js index e825d2d4..b8f5b74a 100644 --- a/src/components/RecipientAddress/index.js +++ b/src/components/RecipientAddress/index.js @@ -101,9 +101,10 @@ class RecipientAddress extends PureComponent { ) : null + const preOnChange = text => onChange((text && text.replace(/\s/g, '')) || '') return ( - + ) } diff --git a/src/components/RenderError.js b/src/components/RenderError.js index ac591aa3..d22a2151 100644 --- a/src/components/RenderError.js +++ b/src/components/RenderError.js @@ -8,7 +8,7 @@ import { translate } from 'react-i18next' import { urls } from 'config/urls' import { i } from 'helpers/staticPath' -import hardReset from 'helpers/hardReset' +import { hardReset } from 'helpers/reset' import type { T } from 'types/common' diff --git a/src/components/SettingsPage/CleanButton.js b/src/components/SettingsPage/CleanButton.js index a5cd8c7f..7c3ff1d4 100644 --- a/src/components/SettingsPage/CleanButton.js +++ b/src/components/SettingsPage/CleanButton.js @@ -4,12 +4,10 @@ import React, { Fragment, PureComponent } from 'react' import { connect } from 'react-redux' import { translate } from 'react-i18next' import type { T } from 'types/common' -import { remote } from 'electron' import { cleanAccountsCache } from 'actions/accounts' -import db from 'helpers/db' -import { delay } from 'helpers/promise' import Button from 'components/base/Button' import { ConfirmModal } from 'components/base/Modal' +import { softReset } from 'helpers/reset' const mapDispatchToProps = { cleanAccountsCache, @@ -22,11 +20,13 @@ type Props = { type State = { opened: boolean, + isLoading: boolean, } class CleanButton extends PureComponent { state = { opened: false, + isLoading: false, } open = () => this.setState({ opened: true }) @@ -34,15 +34,18 @@ class CleanButton extends PureComponent { close = () => this.setState({ opened: false }) action = async () => { - this.props.cleanAccountsCache() - await delay(500) - db.cleanCache() - remote.getCurrentWindow().webContents.reload() + if (this.state.isLoading) return + try { + this.setState({ isLoading: true }) + await softReset({ cleanAccountsCache: this.props.cleanAccountsCache }) + } finally { + this.setState({ isLoading: false }) + } } render() { const { t } = this.props - const { opened } = this.state + const { opened, isLoading } = this.state return (