From 055432e1d73f3e9c4e74f8565dc990ff2400c234 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Renaudeau?= Date: Thu, 14 Jun 2018 12:02:18 +0200 Subject: [PATCH] Errors for i18n should protect the i18n with a '' --- src/api/Ethereum.js | 7 ++++- src/api/Fees.js | 5 +++- src/api/Ledger.js | 14 ++++++--- src/bridge/BridgeSyncContext.js | 3 +- src/bridge/RippleJSBridge.js | 15 ++++++---- src/commands/index.js | 5 ++-- src/components/DeviceConnect/index.js | 7 +++-- src/components/EnsureDeviceApp/index.js | 29 ++++++++++++------- src/components/GenuineCheckModal/index.js | 4 +-- src/components/RequestAmount/index.js | 5 +++- src/components/ThrowBlock.js | 5 +++- src/components/TranslatedError.js | 25 ++++++++++++++++ src/components/base/FormattedVal/index.js | 9 ++---- src/components/base/Input/index.js | 7 ++++- src/components/modals/AddAccounts/index.js | 5 ++-- .../steps/02-step-connect-device.js | 6 ++-- .../AddAccounts/steps/03-step-import.js | 10 ++----- src/components/modals/Receive/index.js | 5 +++- .../modals/Send/04-step-confirmation.js | 11 ++++--- src/components/modals/StepConnectDevice.js | 4 +-- src/helpers/createCustomErrorClass.js | 13 +++++++++ src/helpers/getAddressForCurrency/btc.js | 7 ++++- src/helpers/libcore.js | 5 +++- .../signTransactionForCurrency/ethereum.js | 12 ++++---- static/i18n/en/errors.yml | 13 +++++++++ 25 files changed, 162 insertions(+), 69 deletions(-) create mode 100644 src/components/TranslatedError.js create mode 100644 src/helpers/createCustomErrorClass.js create mode 100644 static/i18n/en/errors.yml diff --git a/src/api/Ethereum.js b/src/api/Ethereum.js index 6d0260b0..216c65a5 100644 --- a/src/api/Ethereum.js +++ b/src/api/Ethereum.js @@ -1,8 +1,11 @@ // @flow import type { CryptoCurrency } from '@ledgerhq/live-common/lib/types' +import createCustomErrorClass from 'helpers/createCustomErrorClass' 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, @@ -42,7 +45,9 @@ export type API = { export const apiForCurrency = (currency: CryptoCurrency): API => { const baseURL = blockchainBaseURL(currency) if (!baseURL) { - throw new Error(`ledger API not available for currency ${currency.id}`) + throw new LedgerAPINotAvailable(`LedgerAPINotAvailable ${currency.id}`, { + currencyName: currency.name, + }) } return { async getTransactions(address, blockHash) { diff --git a/src/api/Fees.js b/src/api/Fees.js index 6796f66f..21063c9d 100644 --- a/src/api/Fees.js +++ b/src/api/Fees.js @@ -1,9 +1,12 @@ // @flow import invariant from 'invariant' import type { Currency } from '@ledgerhq/live-common/lib/types' +import createCustomErrorClass from 'helpers/createCustomErrorClass' import { blockchainBaseURL } from './Ledger' import network from './network' +const FeeEstimationFailed = createCustomErrorClass('FeeEstimationFailed') + export type Fees = { [_: string]: number, } @@ -15,5 +18,5 @@ export const getEstimatedFees = async (currency: Currency): Promise => { if (data) { return data } - throw new Error(`fee estimation failed. status=${status}`) + throw new FeeEstimationFailed(`FeeEstimationFailed ${status}`, { httpStatus: status }) } diff --git a/src/api/Ledger.js b/src/api/Ledger.js index ea5572bc..722a90a8 100644 --- a/src/api/Ledger.js +++ b/src/api/Ledger.js @@ -1,9 +1,14 @@ // @flow import type { Currency } from '@ledgerhq/live-common/lib/types' import logger from 'logger' +import createCustomErrorClass from 'helpers/createCustomErrorClass' const BASE_URL = process.env.LEDGER_REST_API_BASE || 'https://api.ledgerwallet.com/' +export const LedgerAPIErrorWithMessage = createCustomErrorClass('LedgerAPIErrorWithMessage') +export const LedgerAPIError = createCustomErrorClass('LedgerAPIError') +export const NetworkDown = createCustomErrorClass('NetworkDown') + export const userFriendlyError = (p: Promise): Promise => p.catch(error => { if (error.response) { @@ -24,16 +29,17 @@ export const userFriendlyError = (p: Promise): Promise => logger.warn("can't parse server result", e) } } - throw new Error(msg) + throw new LedgerAPIErrorWithMessage(msg) } } - logger.log('Ledger API: HTTP status', error.response.status, 'data: ', error.response.data) - throw new Error('A problem occurred with Ledger API. Please try again later.') + const { status } = error.response + logger.log('Ledger API: HTTP status', status, 'data: ', error.response.data) + throw new LedgerAPIError(`LedgerAPIError ${status}`, { status }) } else if (error.request) { // The request was made but no response was received // `error.request` is an instance of XMLHttpRequest in the browser and an instance of // http.ClientRequest in node.js - throw new Error('Your network is down. Please try again later.') + throw new NetworkDown() } throw error }) diff --git a/src/bridge/BridgeSyncContext.js b/src/bridge/BridgeSyncContext.js index 08b94f50..83f71842 100644 --- a/src/bridge/BridgeSyncContext.js +++ b/src/bridge/BridgeSyncContext.js @@ -3,6 +3,7 @@ // it handles automatically re-calling synchronize // this is an even high abstraction than the bridge +import invariant from 'invariant' import logger from 'logger' import shuffle from 'lodash/shuffle' import React, { Component } from 'react' @@ -65,7 +66,7 @@ class Provider extends Component { return } const account = this.props.accounts.find(a => a.id === accountId) - if (!account) throw new Error('account not found') + invariant(account, 'account not found') const bridge = getBridgeForCurrency(account.currency) diff --git a/src/bridge/RippleJSBridge.js b/src/bridge/RippleJSBridge.js index fbe57a0d..27353db0 100644 --- a/src/bridge/RippleJSBridge.js +++ b/src/bridge/RippleJSBridge.js @@ -1,4 +1,5 @@ // @flow +import invariant from 'invariant' import { Observable } from 'rxjs' import React from 'react' import bs58check from 'ripple-bs58check' @@ -298,9 +299,10 @@ const RippleJSBridge: WalletBridge = { if (finished) return const balance = parseAPIValue(info.xrpBalance) - if (isNaN(balance) || !isFinite(balance)) { - throw new Error(`Ripple: invalid balance=${balance} for address ${address}`) - } + invariant( + !isNaN(balance) && isFinite(balance), + `Ripple: invalid balance=${balance} for address ${address}`, + ) const transactions = await api.getTransactions(address, { minLedgerVersion, @@ -375,9 +377,10 @@ const RippleJSBridge: WalletBridge = { } const balance = parseAPIValue(info.xrpBalance) - if (isNaN(balance) || !isFinite(balance)) { - throw new Error(`Ripple: invalid balance=${balance} for address ${freshAddress}`) - } + invariant( + !isNaN(balance) && isFinite(balance), + `Ripple: invalid balance=${balance} for address ${freshAddress}`, + ) o.next(a => ({ ...a, balance })) diff --git a/src/commands/index.js b/src/commands/index.js index 463c7248..e540e588 100644 --- a/src/commands/index.js +++ b/src/commands/index.js @@ -1,5 +1,6 @@ // @flow +import invariant from 'invariant' import type { Command } from 'helpers/ipc' import getAddress from 'commands/getAddress' @@ -53,9 +54,7 @@ const all: Array> = [ ] all.forEach(cmd => { - if (all.some(c => c !== cmd && c.id === cmd.id)) { - throw new Error(`duplicate command '${cmd.id}'`) - } + invariant(!all.some(c => c !== cmd && c.id === cmd.id), `duplicate command '${cmd.id}'`) }) export default all diff --git a/src/components/DeviceConnect/index.js b/src/components/DeviceConnect/index.js index 3370a8e1..679e1162 100644 --- a/src/components/DeviceConnect/index.js +++ b/src/components/DeviceConnect/index.js @@ -12,6 +12,7 @@ import noop from 'lodash/noop' import Box from 'components/base/Box' import Spinner from 'components/base/Spinner' import CryptoCurrencyIcon from 'components/CryptoCurrencyIcon' +import TranslatedError from 'components/TranslatedError' import IconCheck from 'icons/Check' import IconExclamationCircle from 'icons/ExclamationCircle' @@ -146,7 +147,7 @@ type Props = { deviceSelected: ?Device, onChangeDevice: Device => void, t: T, - errorMessage: ?string, + error: ?Error, } const emitChangeDevice = props => { @@ -186,7 +187,7 @@ class DeviceConnect extends PureComponent { genuineCheckStatus, withGenuineCheck, appOpened, - errorMessage, + error, currency, t, onChangeDevice, @@ -306,7 +307,7 @@ class DeviceConnect extends PureComponent { - {String(errorMessage || '')} + ) : null} diff --git a/src/components/EnsureDeviceApp/index.js b/src/components/EnsureDeviceApp/index.js index a400d494..964f3ad5 100644 --- a/src/components/EnsureDeviceApp/index.js +++ b/src/components/EnsureDeviceApp/index.js @@ -13,9 +13,13 @@ import type { State as StoreState } from 'reducers/index' import getAddress from 'commands/getAddress' import { standardDerivation } from 'helpers/derivations' import isDashboardOpen from 'commands/isDashboardOpen' +import createCustomErrorClass from 'helpers/createCustomErrorClass' import { CHECK_APP_INTERVAL_WHEN_VALID, CHECK_APP_INTERVAL_WHEN_INVALID } from 'config/constants' +export const WrongAppOpened = createCustomErrorClass('WrongAppOpened') +export const WrongDeviceForAccount = createCustomErrorClass('WrongDeviceForAccount') + type OwnProps = { currency?: ?CryptoCurrency, deviceSelected: ?Device, @@ -31,7 +35,7 @@ type OwnProps = { devices: Device[], deviceSelected: ?Device, deviceStatus: DeviceStatus, - errorMessage: ?string, + error: ?Error, }) => React$Element<*>, } @@ -48,7 +52,7 @@ type GenuineCheckStatus = 'success' | 'fail' | 'progress' type State = { deviceStatus: DeviceStatus, appStatus: AppStatus, - errorMessage: ?string, + error: ?Error, genuineCheckStatus: GenuineCheckStatus, } @@ -62,7 +66,7 @@ class EnsureDeviceApp extends PureComponent { state = { appStatus: 'progress', deviceStatus: this.props.deviceSelected ? 'connected' : 'unconnected', - errorMessage: null, + error: null, genuineCheckStatus: 'progress', } @@ -139,7 +143,7 @@ class EnsureDeviceApp extends PureComponent { e.message === 'Cannot write to HID device') ) { logger.log(e) - throw new Error(`You must open application ‘${cur.name}’ on the device`) + throw new WrongAppOpened(`WrongAppOpened ${cur.id}`, { currencyName: cur.name }) } throw e }) @@ -148,10 +152,13 @@ class EnsureDeviceApp extends PureComponent { const { freshAddress } = account if (account && freshAddress !== address) { logger.warn({ freshAddress, address }) - throw new Error(`You must use the device associated to the account ‘${account.name}’`) + throw new WrongDeviceForAccount(`WrongDeviceForAccount ${account.name}`, { + accountName: account.name, + }) } } } else { + logger.warn('EnsureDeviceApp for using dashboard is DEPRECATED !!!') // FIXME REMOVE THIS ! should use EnsureDashboard dedicated component. const isDashboard = isDashboardOpen.send({ devicePath: deviceSelected.path }).toPromise() @@ -166,7 +173,7 @@ class EnsureDeviceApp extends PureComponent { this.handleGenuineCheck() } } catch (e) { - this.handleStatusChange(this.state.deviceStatus, 'fail', e.message) + this.handleStatusChange(this.state.deviceStatus, 'fail', e) isSuccess = false } @@ -182,12 +189,12 @@ class EnsureDeviceApp extends PureComponent { _timeout: * _unmounted = false - handleStatusChange = (deviceStatus, appStatus, errorMessage = null) => { + handleStatusChange = (deviceStatus, appStatus, error = null) => { const { onStatusChange } = this.props clearTimeout(this._timeout) if (!this._unmounted) { - this.setState({ deviceStatus, appStatus, errorMessage }) - onStatusChange && onStatusChange(deviceStatus, appStatus, errorMessage) + this.setState({ deviceStatus, appStatus, error }) + onStatusChange && onStatusChange(deviceStatus, appStatus, error) } } @@ -202,7 +209,7 @@ class EnsureDeviceApp extends PureComponent { render() { const { currency, account, devices, deviceSelected, render } = this.props - const { appStatus, deviceStatus, genuineCheckStatus, errorMessage } = this.state + const { appStatus, deviceStatus, genuineCheckStatus, error } = this.state if (render) { // if cur is not provided, we assume we want to check if user is on @@ -216,7 +223,7 @@ class EnsureDeviceApp extends PureComponent { deviceSelected: deviceStatus === 'connected' ? deviceSelected : null, deviceStatus, genuineCheckStatus, - errorMessage, + error, }) } diff --git a/src/components/GenuineCheckModal/index.js b/src/components/GenuineCheckModal/index.js index b055606e..a9c938a4 100644 --- a/src/components/GenuineCheckModal/index.js +++ b/src/components/GenuineCheckModal/index.js @@ -45,7 +45,7 @@ class GenuineCheck extends PureComponent { onStatusChange={status => { logger.log(`status changed to ${status}`) }} - render={({ appStatus, genuineCheckStatus, deviceSelected, errorMessage }) => ( + render={({ appStatus, genuineCheckStatus, deviceSelected, error }) => ( { genuineCheckStatus={genuineCheckStatus} devices={reducedDevicesList} deviceSelected={deviceSelected} - errorMessage={errorMessage} + error={error} /> )} /> diff --git a/src/components/RequestAmount/index.js b/src/components/RequestAmount/index.js index f298a38b..9732bba1 100644 --- a/src/components/RequestAmount/index.js +++ b/src/components/RequestAmount/index.js @@ -21,6 +21,9 @@ import InputCurrency from 'components/base/InputCurrency' import Button from 'components/base/Button' import Box from 'components/base/Box' import type { State } from 'reducers' +import createCustomErrorClass from 'helpers/createCustomErrorClass' + +const NotEnoughBalance = createCustomErrorClass('NotEnoughBalance') const InputRight = styled(Box).attrs({ ff: 'Rubik', @@ -145,7 +148,7 @@ export class RequestAmount extends PureComponent { return ( - {`Error: ${error.message}`} + + +