Browse Source

Merge pull request #898 from LedgerHQ/develop

Prepare for rc.2
master
Gaëtan Renaudeau 7 years ago
committed by GitHub
parent
commit
bbfe82acec
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      src/bridge/EthereumJSBridge.js
  2. 29
      src/bridge/LibcoreBridge.js
  3. 10
      src/bridge/RippleJSBridge.js
  4. 2
      src/bridge/UnsupportedBridge.js
  5. 4
      src/bridge/makeMockBridge.js
  6. 2
      src/bridge/types.js
  7. 9
      src/components/RequestAmount/index.js
  8. 16
      src/components/modals/Send/fields/AmountField.js
  9. 5
      src/components/modals/Send/steps/01-step-amount.js
  10. 5
      src/components/modals/Send/steps/04-step-confirmation.js
  11. 7
      src/components/modals/UpdateFirmware/Installing.js
  12. 2
      src/components/modals/UpdateFirmware/steps/02-step-flash-mcu.js
  13. 7
      src/config/constants.js
  14. 2
      src/helpers/anonymizer.js
  15. 4
      src/helpers/apps/installApp.js
  16. 4
      src/helpers/apps/uninstallApp.js
  17. 7
      src/helpers/socket.js
  18. 4
      src/helpers/urls.js
  19. 2
      static/i18n/en/app.yml
  20. 28
      static/i18n/fr/app.yml
  21. 6
      static/i18n/fr/errors.yml

6
src/bridge/EthereumJSBridge.js

@ -13,8 +13,11 @@ import { getDerivations } from 'helpers/derivations'
import getAddressCommand from 'commands/getAddress' import getAddressCommand from 'commands/getAddress'
import signTransactionCommand from 'commands/signTransaction' import signTransactionCommand from 'commands/signTransaction'
import { getAccountPlaceholderName, getNewAccountPlaceholderName } from 'helpers/accountName' import { getAccountPlaceholderName, getNewAccountPlaceholderName } from 'helpers/accountName'
import { createCustomErrorClass } from 'helpers/errors'
import type { EditProps, WalletBridge } from './types' import type { EditProps, WalletBridge } from './types'
const NotEnoughBalance = createCustomErrorClass('NotEnoughBalance')
// TODO in future it would be neat to support eip55 // TODO in future it would be neat to support eip55
type Transaction = { type Transaction = {
@ -363,7 +366,8 @@ const EthereumBridge: WalletBridge<Transaction> = {
EditAdvancedOptions, EditAdvancedOptions,
canBeSpent: (a, t) => Promise.resolve(t.amount <= a.balance), checkCanBeSpent: (a, t) =>
t.amount <= a.balance ? Promise.resolve() : Promise.reject(new NotEnoughBalance()),
getTotalSpent: (a, t) => Promise.resolve(t.amount + t.gasPrice * t.gasLimit), getTotalSpent: (a, t) => Promise.resolve(t.amount + t.gasPrice * t.gasLimit),
getMaxAmount: (a, t) => Promise.resolve(a.balance - t.gasPrice * t.gasLimit), getMaxAmount: (a, t) => Promise.resolve(a.balance - t.gasPrice * t.gasLimit),

29
src/bridge/LibcoreBridge.js

@ -11,8 +11,12 @@ import libcoreSyncAccount from 'commands/libcoreSyncAccount'
import libcoreSignAndBroadcast from 'commands/libcoreSignAndBroadcast' import libcoreSignAndBroadcast from 'commands/libcoreSignAndBroadcast'
import libcoreGetFees from 'commands/libcoreGetFees' import libcoreGetFees from 'commands/libcoreGetFees'
import libcoreValidAddress from 'commands/libcoreValidAddress' import libcoreValidAddress from 'commands/libcoreValidAddress'
import { createCustomErrorClass } from 'helpers/errors'
import type { WalletBridge, EditProps } from './types' import type { WalletBridge, EditProps } from './types'
const NOT_ENOUGH_FUNDS = 52
const NotEnoughBalance = createCustomErrorClass('NotEnoughBalance')
const notImplemented = new Error('LibcoreBridge: not implemented') const notImplemented = new Error('LibcoreBridge: not implemented')
type Transaction = { type Transaction = {
@ -65,10 +69,13 @@ const isRecipientValid = (currency, recipient) => {
const feesLRU = LRU({ max: 100 }) const feesLRU = LRU({ max: 100 })
const getFeesKey = (a, t) =>
`${a.id}_${a.blockHeight || 0}_${t.amount}_${t.recipient}_${t.feePerByte}`
const getFees = async (a, transaction) => { const getFees = async (a, transaction) => {
const isValid = await isRecipientValid(a.currency, transaction.recipient) const isValid = await isRecipientValid(a.currency, transaction.recipient)
if (!isValid) return null if (!isValid) return null
const key = `${a.id}_${transaction.amount}_${transaction.recipient}_${transaction.feePerByte}` const key = getFeesKey(a, transaction)
let promise = feesLRU.get(key) let promise = feesLRU.get(key)
if (promise) return promise if (promise) return promise
promise = libcoreGetFees promise = libcoreGetFees
@ -79,6 +86,19 @@ const getFees = async (a, transaction) => {
return promise return promise
} }
const checkCanBeSpent = (a, t) =>
!t.amount
? Promise.resolve()
: getFees(a, t)
.then(() => {})
.catch(e => {
if (e.code === NOT_ENOUGH_FUNDS) {
throw new NotEnoughBalance()
}
feesLRU.del(getFeesKey(a, t))
throw e
})
const LibcoreBridge: WalletBridge<Transaction> = { const LibcoreBridge: WalletBridge<Transaction> = {
scanAccountsOnDevice(currency, devicePath) { scanAccountsOnDevice(currency, devicePath) {
return libcoreScanAccounts return libcoreScanAccounts
@ -173,12 +193,7 @@ const LibcoreBridge: WalletBridge<Transaction> = {
isValidTransaction: (a, t) => (t.amount > 0 && t.recipient && true) || false, isValidTransaction: (a, t) => (t.amount > 0 && t.recipient && true) || false,
canBeSpent: (a, t) => checkCanBeSpent,
!t.amount
? Promise.resolve(true)
: getFees(a, t)
.then(() => true)
.catch(() => false),
getTotalSpent: (a, t) => getTotalSpent: (a, t) =>
!t.amount !t.amount

10
src/bridge/RippleJSBridge.js

@ -19,8 +19,11 @@ import {
import FeesRippleKind from 'components/FeesField/RippleKind' import FeesRippleKind from 'components/FeesField/RippleKind'
import AdvancedOptionsRippleKind from 'components/AdvancedOptions/RippleKind' import AdvancedOptionsRippleKind from 'components/AdvancedOptions/RippleKind'
import { getAccountPlaceholderName, getNewAccountPlaceholderName } from 'helpers/accountName' import { getAccountPlaceholderName, getNewAccountPlaceholderName } from 'helpers/accountName'
import { createCustomErrorClass } from 'helpers/errors'
import type { WalletBridge, EditProps } from './types' import type { WalletBridge, EditProps } from './types'
const NotEnoughBalance = createCustomErrorClass('NotEnoughBalance')
type Transaction = { type Transaction = {
amount: number, amount: number,
recipient: string, recipient: string,
@ -461,9 +464,12 @@ const RippleJSBridge: WalletBridge<Transaction> = {
isValidTransaction: (a, t) => (t.amount > 0 && t.recipient && true) || false, isValidTransaction: (a, t) => (t.amount > 0 && t.recipient && true) || false,
canBeSpent: async (a, t) => { checkCanBeSpent: async (a, t) => {
const r = await getServerInfo(a.endpointConfig) const r = await getServerInfo(a.endpointConfig)
return t.amount + t.fee + parseAPIValue(r.validatedLedger.reserveBaseXRP) <= a.balance if (t.amount + t.fee + parseAPIValue(r.validatedLedger.reserveBaseXRP) <= a.balance) {
return
}
throw new NotEnoughBalance()
}, },
getTotalSpent: (a, t) => Promise.resolve(t.amount + t.fee), getTotalSpent: (a, t) => Promise.resolve(t.amount + t.fee),

2
src/bridge/UnsupportedBridge.js

@ -31,7 +31,7 @@ const UnsupportedBridge: WalletBridge<*> = {
getTransactionRecipient: () => '', getTransactionRecipient: () => '',
canBeSpent: () => Promise.resolve(false), checkCanBeSpent: () => Promise.resolve(),
getTotalSpent: () => Promise.resolve(0), getTotalSpent: () => Promise.resolve(0),

4
src/bridge/makeMockBridge.js

@ -32,7 +32,7 @@ function makeMockBridge(opts?: Opts): WalletBridge<*> {
extraInitialTransactionProps, extraInitialTransactionProps,
getTotalSpent, getTotalSpent,
getMaxAmount, getMaxAmount,
canBeSpent, checkCanBeSpent,
} = { } = {
...defaultOpts, ...defaultOpts,
...opts, ...opts,
@ -145,7 +145,7 @@ function makeMockBridge(opts?: Opts): WalletBridge<*> {
isValidTransaction: (a, t) => (t.amount > 0 && t.recipient && true) || false, isValidTransaction: (a, t) => (t.amount > 0 && t.recipient && true) || false,
canBeSpent, checkCanBeSpent,
getTotalSpent, getTotalSpent,

2
src/bridge/types.js

@ -82,7 +82,7 @@ export interface WalletBridge<Transaction> {
// render the whole advanced part of the form // render the whole advanced part of the form
EditAdvancedOptions?: *; // React$ComponentType<EditProps<Transaction>>; EditAdvancedOptions?: *; // React$ComponentType<EditProps<Transaction>>;
canBeSpent(account: Account, transaction: Transaction): Promise<boolean>; checkCanBeSpent(account: Account, transaction: Transaction): Promise<void>;
getTotalSpent(account: Account, transaction: Transaction): Promise<number>; getTotalSpent(account: Account, transaction: Transaction): Promise<number>;

9
src/components/RequestAmount/index.js

@ -21,9 +21,6 @@ import InputCurrency from 'components/base/InputCurrency'
import Button from 'components/base/Button' import Button from 'components/base/Button'
import Box from 'components/base/Box' import Box from 'components/base/Box'
import type { State } from 'reducers' import type { State } from 'reducers'
import { createCustomErrorClass } from 'helpers/errors'
const NotEnoughBalance = createCustomErrorClass('NotEnoughBalance')
const InputRight = styled(Box).attrs({ const InputRight = styled(Box).attrs({
ff: 'Rubik', ff: 'Rubik',
@ -50,7 +47,7 @@ type OwnProps = {
// left value (always the one which is returned) // left value (always the one which is returned)
value: number, value: number,
canBeSpent: boolean, canBeSpentError: ?Error,
// max left value // max left value
max: number, max: number,
@ -141,14 +138,14 @@ export class RequestAmount extends PureComponent<Props> {
renderInputs(containerProps: Object) { renderInputs(containerProps: Object) {
// TODO move this inlined into render() for less spaghetti // TODO move this inlined into render() for less spaghetti
const { value, account, rightCurrency, getCounterValue, canBeSpent } = this.props const { value, account, rightCurrency, getCounterValue, canBeSpentError } = this.props
const right = getCounterValue(value) || 0 const right = getCounterValue(value) || 0
const rightUnit = rightCurrency.units[0] const rightUnit = rightCurrency.units[0]
// FIXME: no way InputCurrency pure can work here. inlined InputRight (should be static func?), inline containerProps object.. // FIXME: no way InputCurrency pure can work here. inlined InputRight (should be static func?), inline containerProps object..
return ( return (
<Box horizontal grow shrink> <Box horizontal grow shrink>
<InputCurrency <InputCurrency
error={canBeSpent ? null : new NotEnoughBalance()} error={canBeSpentError}
containerProps={containerProps} containerProps={containerProps}
defaultUnit={account.unit} defaultUnit={account.unit}
value={value} value={value}

16
src/components/modals/Send/fields/AmountField.js

@ -4,9 +4,9 @@ import Box from 'components/base/Box'
import Label from 'components/base/Label' import Label from 'components/base/Label'
import RequestAmount from 'components/RequestAmount' import RequestAmount from 'components/RequestAmount'
class AmountField extends Component<*, { canBeSpent: boolean }> { class AmountField extends Component<*, { canBeSpentError: ?Error }> {
state = { state = {
canBeSpent: true, canBeSpentError: null,
} }
componentDidMount() { componentDidMount() {
this.resync() this.resync()
@ -26,9 +26,13 @@ class AmountField extends Component<*, { canBeSpent: boolean }> {
async resync() { async resync() {
const { account, bridge, transaction } = this.props const { account, bridge, transaction } = this.props
const syncId = ++this.syncId const syncId = ++this.syncId
const canBeSpent = await bridge.canBeSpent(account, transaction) try {
await bridge.checkCanBeSpent(account, transaction)
if (this.syncId !== syncId) return if (this.syncId !== syncId) return
this.setState({ canBeSpent }) this.setState({ canBeSpentError: null })
} catch (canBeSpentError) {
this.setState({ canBeSpentError })
}
} }
onChange = (amount: number) => { onChange = (amount: number) => {
@ -38,14 +42,14 @@ class AmountField extends Component<*, { canBeSpent: boolean }> {
render() { render() {
const { bridge, account, transaction, t } = this.props const { bridge, account, transaction, t } = this.props
const { canBeSpent } = this.state const { canBeSpentError } = this.state
return ( return (
<Box flow={1}> <Box flow={1}>
<Label>{t('app:send.steps.amount.amount')}</Label> <Label>{t('app:send.steps.amount.amount')}</Label>
<RequestAmount <RequestAmount
withMax={false} withMax={false}
account={account} account={account}
canBeSpent={canBeSpent} canBeSpentError={canBeSpentError}
onChange={this.onChange} onChange={this.onChange}
value={bridge.getTransactionAmount(account, transaction)} value={bridge.getTransactionAmount(account, transaction)}
/> />

5
src/components/modals/Send/steps/01-step-amount.js

@ -58,6 +58,7 @@ export default ({
bridge && bridge &&
transaction && ( transaction && (
<AmountField <AmountField
key={account.id}
account={account} account={account}
bridge={bridge} bridge={bridge}
transaction={transaction} transaction={transaction}
@ -134,7 +135,9 @@ export class StepAmountFooter extends PureComponent<
try { try {
const totalSpent = await bridge.getTotalSpent(account, transaction) const totalSpent = await bridge.getTotalSpent(account, transaction)
if (syncId !== this.syncId) return if (syncId !== this.syncId) return
const canBeSpent = await bridge.canBeSpent(account, transaction) const canBeSpent = await bridge
.checkCanBeSpent(account, transaction)
.then(() => true, () => false)
if (syncId !== this.syncId) return if (syncId !== this.syncId) return
this.setState({ totalSpent, canBeSpent, isSyncing: false }) this.setState({ totalSpent, canBeSpent, isSyncing: false })
} catch (err) { } catch (err) {

5
src/components/modals/Send/steps/04-step-confirmation.js

@ -2,7 +2,6 @@
import React, { Fragment } from 'react' import React, { Fragment } from 'react'
import styled from 'styled-components' import styled from 'styled-components'
import { getAccountOperationExplorer } from '@ledgerhq/live-common/lib/explorers'
import { MODAL_OPERATION_DETAILS } from 'config/constants' import { MODAL_OPERATION_DETAILS } from 'config/constants'
import { colors } from 'styles/theme' import { colors } from 'styles/theme'
@ -82,12 +81,9 @@ export function StepConfirmationFooter({
openModal, openModal,
closeModal, closeModal,
}: StepProps<*>) { }: StepProps<*>) {
const url =
optimisticOperation && account && getAccountOperationExplorer(account, optimisticOperation)
return ( return (
<Fragment> <Fragment>
{optimisticOperation ? ( {optimisticOperation ? (
url ? (
<Button <Button
ml={2} ml={2}
event="Send Flow Step 4 View OpD Clicked" event="Send Flow Step 4 View OpD Clicked"
@ -104,7 +100,6 @@ export function StepConfirmationFooter({
> >
{t('app:send.steps.confirmation.success.cta')} {t('app:send.steps.confirmation.success.cta')}
</Button> </Button>
) : null
) : error ? ( ) : error ? (
<Button <Button
ml={2} ml={2}

7
src/components/modals/UpdateFirmware/Installing.js

@ -18,11 +18,16 @@ function Installing({ t }: Props) {
<Box mx={7} align="center"> <Box mx={7} align="center">
<Spinner color="fog" size={44} /> <Spinner color="fog" size={44} />
</Box> </Box>
<Box mx={7} mt={4} mb={7}> <Box mx={7} mt={4} mb={2}>
<Text ff="Museo Sans|Regular" align="center" color="dark" fontSize={6}> <Text ff="Museo Sans|Regular" align="center" color="dark" fontSize={6}>
{t('app:manager.modal.installing')} {t('app:manager.modal.installing')}
</Text> </Text>
</Box> </Box>
<Box mx={7} mt={4} mb={7}>
<Text ff="Open Sans|Regular" align="center" color="graphite" fontSize={4}>
{t('app:manager.modal.mcuPin')}
</Text>
</Box>
</Fragment> </Fragment>
) )
} }

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

@ -127,6 +127,8 @@ class StepFlashMcu extends PureComponent<Props, State> {
} else if (deviceInfo.isOSU) { } else if (deviceInfo.isOSU) {
await installFinalFirmware(device) await installFinalFirmware(device)
transitionTo('finish') transitionTo('finish')
} else {
transitionTo('finish')
} }
} catch (error) { } catch (error) {
setError(error) setError(error)

7
src/config/constants.js

@ -53,11 +53,8 @@ export const MANAGER_API_BASE = stringFromEnv(
'MANAGER_API_BASE', 'MANAGER_API_BASE',
'https://beta.manager.live.ledger.fr/api', 'https://beta.manager.live.ledger.fr/api',
) )
export const BASE_SOCKET_URL = stringFromEnv('BASE_SOCKET_URL', 'ws://api.ledgerwallet.com/update')
export const BASE_SOCKET_URL_SECURE = stringFromEnv( export const BASE_SOCKET_URL = stringFromEnv('BASE_SOCKET_URL', 'wss://api.ledgerwallet.com/update')
'BASE_SOCKET_URL',
'wss://api.ledgerwallet.com/update',
)
// Flags // Flags

2
src/helpers/anonymizer.js

@ -51,7 +51,7 @@ export default {
.replace(/\/addresses\/[^/]+/g, '/addresses/<HIDDEN>') .replace(/\/addresses\/[^/]+/g, '/addresses/<HIDDEN>')
.replace(/blockHash=[^&]+/g, 'blockHash=<HIDDEN>'), .replace(/blockHash=[^&]+/g, 'blockHash=<HIDDEN>'),
appURI: (uri: string): string => uri.replace(/account\/[^/]/g, 'account/<HIDDEN>'), appURI: (uri: string): string => uri.replace(/account\/[^/]+/g, 'account/<HIDDEN>'),
filepath: filepathReplace, filepath: filepathReplace,

4
src/helpers/apps/installApp.js

@ -2,7 +2,7 @@
import qs from 'qs' import qs from 'qs'
import type Transport from '@ledgerhq/hw-transport' import type Transport from '@ledgerhq/hw-transport'
import { BASE_SOCKET_URL_SECURE } from 'config/constants' import { BASE_SOCKET_URL } from 'config/constants'
import { createDeviceSocket } from 'helpers/socket' import { createDeviceSocket } from 'helpers/socket'
import type { LedgerScriptParams } from 'helpers/common' import type { LedgerScriptParams } from 'helpers/common'
@ -44,6 +44,6 @@ export default async function installApp(
...app, ...app,
firmwareKey: app.firmware_key, firmwareKey: app.firmware_key,
} }
const url = `${BASE_SOCKET_URL_SECURE}/install?${qs.stringify(params)}` const url = `${BASE_SOCKET_URL}/install?${qs.stringify(params)}`
return remapError(createDeviceSocket(transport, url).toPromise()) return remapError(createDeviceSocket(transport, url).toPromise())
} }

4
src/helpers/apps/uninstallApp.js

@ -2,7 +2,7 @@
import qs from 'qs' import qs from 'qs'
import type Transport from '@ledgerhq/hw-transport' import type Transport from '@ledgerhq/hw-transport'
import { BASE_SOCKET_URL_SECURE } from 'config/constants' import { BASE_SOCKET_URL } from 'config/constants'
import { createDeviceSocket } from 'helpers/socket' import { createDeviceSocket } from 'helpers/socket'
import type { LedgerScriptParams } from 'helpers/common' import type { LedgerScriptParams } from 'helpers/common'
@ -38,6 +38,6 @@ export default async function uninstallApp(
firmware: app.delete, firmware: app.delete,
firmwareKey: app.delete_key, firmwareKey: app.delete_key,
} }
const url = `${BASE_SOCKET_URL_SECURE}/install?${qs.stringify(params)}` const url = `${BASE_SOCKET_URL}/install?${qs.stringify(params)}`
return remapError(createDeviceSocket(transport, url).toPromise()) return remapError(createDeviceSocket(transport, url).toPromise())
} }

7
src/helpers/socket.js

@ -25,7 +25,7 @@ export const createDeviceSocket = (transport: Transport<*>, url: string) =>
try { try {
ws = new Websocket(url) ws = new Websocket(url)
} catch (err) { } catch (err) {
o.error(new WebsocketConnectionFailed(err.message)) o.error(new WebsocketConnectionFailed(err.message, { url }))
return () => {} return () => {}
} }
invariant(ws, 'websocket is available') invariant(ws, 'websocket is available')
@ -36,7 +36,7 @@ export const createDeviceSocket = (transport: Transport<*>, url: string) =>
ws.on('error', e => { ws.on('error', e => {
logger.websocket('ERROR', e) logger.websocket('ERROR', e)
o.error(new WebsocketConnectionError(e.message)) o.error(new WebsocketConnectionError(e.message, { url }))
}) })
ws.on('close', () => { ws.on('close', () => {
@ -98,7 +98,7 @@ export const createDeviceSocket = (transport: Transport<*>, url: string) =>
error: msg => { error: msg => {
logger.websocket('ERROR', msg.data) logger.websocket('ERROR', msg.data)
throw new DeviceSocketFail(msg.data) throw new DeviceSocketFail(msg.data, { url })
}, },
} }
@ -108,6 +108,7 @@ export const createDeviceSocket = (transport: Transport<*>, url: string) =>
if (!(msg.query in handlers)) { if (!(msg.query in handlers)) {
throw new DeviceSocketNoHandler(`Cannot handle msg of type ${msg.query}`, { throw new DeviceSocketNoHandler(`Cannot handle msg of type ${msg.query}`, {
query: msg.query, query: msg.query,
url,
}) })
} }
logger.websocket('RECEIVE', msg) logger.websocket('RECEIVE', msg)

4
src/helpers/urls.js

@ -1,7 +1,7 @@
// @flow // @flow
import qs from 'qs' import qs from 'qs'
import { MANAGER_API_BASE, BASE_SOCKET_URL_SECURE } from 'config/constants' import { MANAGER_API_BASE, BASE_SOCKET_URL } from 'config/constants'
import type { LedgerScriptParams } from 'helpers/common' import type { LedgerScriptParams } from 'helpers/common'
const urlBuilder = (base: string) => (endpoint: string): string => `${base}/${endpoint}` const urlBuilder = (base: string) => (endpoint: string): string => `${base}/${endpoint}`
@ -9,7 +9,7 @@ const urlBuilder = (base: string) => (endpoint: string): string => `${base}/${en
const managerUrlbuilder = urlBuilder(MANAGER_API_BASE) const managerUrlbuilder = urlBuilder(MANAGER_API_BASE)
const wsURLBuilder = (endpoint: string) => (params?: Object) => const wsURLBuilder = (endpoint: string) => (params?: Object) =>
`${BASE_SOCKET_URL_SECURE}/${endpoint}${params ? `?${qs.stringify(params)}` : ''}` `${BASE_SOCKET_URL}/${endpoint}${params ? `?${qs.stringify(params)}` : ''}`
// const wsURLBuilderProxy = (endpoint: string) => (params?: Object) => // const wsURLBuilderProxy = (endpoint: string) => (params?: Object) =>
// `ws://manager.ledger.fr:3501/${endpoint}${params ? `?${qs.stringify(params)}` : ''}` // `ws://manager.ledger.fr:3501/${endpoint}${params ? `?${qs.stringify(params)}` : ''}`

2
static/i18n/en/app.yml

@ -245,7 +245,7 @@ manager:
subtitle: Install or uninstall apps on your device subtitle: Install or uninstall apps on your device
device: device:
title: Connect your device title: Connect your device
desc: 'Follow the steps below to use the device Manager' desc: 'Follow the steps below to open the Manager'
cta: Connect my device cta: Connect my device
errors: errors:
noDevice: No device is connected (TEMPLATE NEEDED) noDevice: No device is connected (TEMPLATE NEEDED)

28
static/i18n/fr/app.yml

@ -217,32 +217,32 @@ manager:
installed: 'Firmware version {{version}}' installed: 'Firmware version {{version}}'
titleNano: Ledger Nano S titleNano: Ledger Nano S
titleBlue: Ledger Blue titleBlue: Ledger Blue
update: Update firmware update: Update
continue: Continue update continue: Continue
latest: 'Firmware version {{version}} is available' latest: 'Firmware version {{version}} is available'
disclaimerTitle: 'You are about to install the latest <1><0>firmware {{version}}</0></1>' disclaimerTitle: 'You are about to install <1><0>firmware version {{version}}</0></1>'
disclaimerAppDelete: Please note that all the apps installed on your device will be deleted. disclaimerAppDelete: Apps installed on your device have to re-installed after the update.
disclaimerAppReinstall: You will be able to re-install your apps after the firmware update disclaimerAppReinstall: "This does not affect your crypto assets in the blockchain."
modal: modal:
steps: steps:
idCheck: Identifier check idCheck: Identifier
updateMCU: Update MCU updateMCU: MCU update
confirm: Confirmation confirm: Confirmation
installing: Firmware updating... installing: Firmware updating...
confirmIdentifier: Confirm identifier confirmIdentifier: Verify the identifier
confirmIdentifierText: Please confirm identifier on your Device. Be sure the identifier is the same as below confirmIdentifierText: Verify that the identifier on your device is the same as the identifier below. Press the right button to confirm.
identifier: Identifier identifier: Identifier
mcuTitle: Updating MCU mcuTitle: Updating MCU
mcuFirst: Unplug your device from your computer mcuFirst: Disconnect the USB cable from your device
mcuSecond: Press and hold left button and plug your device until the processing screen appears mcuSecond: Press the left button and hold it while you reconnect the USB cable until the processing screen appears
mcuPin: If asked on device, please enter your pin code to finish the process mcuPin: If asked on device, please enter your pin code to finish the process
successTitle: Firmware has been updated with success successTitle: Firmware updated
successText: You can now re-install your applications on your device successText: You may re-install the apps on your device
title: Manager title: Manager
subtitle: Install or uninstall apps on your device subtitle: Install or uninstall apps on your device
device: device:
title: Connect your device title: Connect your device
desc: 'Follow the steps below to use the device Manager' desc: 'Follow the steps below to open the Manager'
cta: Connect my device cta: Connect my device
errors: errors:
noDevice: No device is connected (TEMPLATE NEEDED) noDevice: No device is connected (TEMPLATE NEEDED)

6
static/i18n/fr/errors.yml

@ -23,6 +23,12 @@ DisconnectedDevice:
Error: Error:
title: '{{message}}' title: '{{message}}'
description: Something went wrong. Please retry or contact us. description: Something went wrong. Please retry or contact us.
"Invariant Violation":
title: '{{message}}'
description: Something went wrong. Please retry or contact us.
InternalError:
title: '{{message}}'
description: Something went wrong. Please retry or contact us.
TypeError: TypeError:
title: '{{message}}' title: '{{message}}'
description: Something went wrong. Please retry or contact us. description: Something went wrong. Please retry or contact us.

Loading…
Cancel
Save