Browse Source

Merge branch 'develop' into refactor-modal

develop
Juan Cortés Ross 6 years ago
committed by GitHub
parent
commit
9fda79949e
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 16
      package.json
  2. 15
      scripts/check-no-dups.sh
  3. 1
      src/bridge/BridgeSyncContext.js
  4. 8
      src/bridge/EthereumJSBridge.js
  5. 8
      src/bridge/LibcoreBridge.js
  6. 20
      src/bridge/RippleJSBridge.js
  7. 2
      src/bridge/makeMockBridge.js
  8. 4
      src/bridge/types.js
  9. BIN
      src/commands/.DS_Store
  10. 84
      src/components/ManagerPage/AppsList.js
  11. 3
      src/components/ManagerPage/ManagerApp.js
  12. 5
      src/components/Onboarding/steps/GenuineCheck/GenuineCheckUnavailable.js
  13. 2
      src/components/SelectExchange.js
  14. 14
      src/components/SettingsPage/RepairDeviceButton.js
  15. 13
      src/components/SettingsPage/sections/Export.js
  16. 4
      src/components/modals/AddAccounts/index.js
  17. 4
      src/components/modals/Receive/index.js
  18. 10
      src/components/modals/Send/fields/RecipientField.js
  19. 4
      src/components/modals/Send/index.js
  20. 2
      src/components/modals/Send/steps/01-step-amount.js
  21. 6
      src/components/modals/UpdateFirmware/index.js
  22. 13
      static/i18n/en/app.json
  23. 90
      yarn.lock

16
package.json

@ -20,7 +20,7 @@
"test-e2e": "jest test-e2e", "test-e2e": "jest test-e2e",
"test-sync": "bash test-e2e/sync/launch.sh", "test-sync": "bash test-e2e/sync/launch.sh",
"prettier": "prettier --write \"{src,webpack,.storybook,test-e2e}/**/*.{js,json}\"", "prettier": "prettier --write \"{src,webpack,.storybook,test-e2e}/**/*.{js,json}\"",
"ci": "yarn lint && yarn flow && yarn prettier && yarn test", "ci": "yarn check --integrity && ./scripts/check-no-dups.sh && yarn lint && yarn flow && yarn prettier && yarn test",
"storybook": "NODE_ENV=development STORYBOOK_ENV=1 start-storybook -s ./static -p 4444", "storybook": "NODE_ENV=development STORYBOOK_ENV=1 start-storybook -s ./static -p 4444",
"publish-storybook": "bash ./scripts/legacy/publish-storybook.sh", "publish-storybook": "bash ./scripts/legacy/publish-storybook.sh",
"reset-files": "bash ./scripts/legacy/reset-files.sh" "reset-files": "bash ./scripts/legacy/reset-files.sh"
@ -35,11 +35,12 @@
} }
}, },
"dependencies": { "dependencies": {
"@ledgerhq/hw-app-btc": "^4.34.0", "@ledgerhq/errors": "^4.35.1",
"@ledgerhq/hw-app-eth": "^4.32.0", "@ledgerhq/hw-app-btc": "^4.35.0",
"@ledgerhq/hw-app-xrp": "^4.32.0", "@ledgerhq/hw-app-eth": "^4.35.0",
"@ledgerhq/hw-transport": "^4.32.0", "@ledgerhq/hw-app-xrp": "^4.35.0",
"@ledgerhq/hw-transport-node-hid": "^4.32.0", "@ledgerhq/hw-transport": "^4.35.0",
"@ledgerhq/hw-transport-node-hid": "^4.35.0",
"@ledgerhq/ledger-core": "2.0.0-rc.16", "@ledgerhq/ledger-core": "2.0.0-rc.16",
"@ledgerhq/live-common": "4.15.0-beta.0", "@ledgerhq/live-common": "4.15.0-beta.0",
"animated": "^0.2.2", "animated": "^0.2.2",
@ -183,7 +184,8 @@
"webpack": "^4.6.0", "webpack": "^4.6.0",
"webpack-bundle-analyzer": "^2.11.1", "webpack-bundle-analyzer": "^2.11.1",
"webpack-cli": "^2.0.14", "webpack-cli": "^2.0.14",
"yaml-loader": "^0.5.0" "yaml-loader": "^0.5.0",
"yarn-deduplicate": "^1.1.1"
}, },
"engines": { "engines": {
"node": ">=8.9.0 <=8.15.0", "node": ">=8.9.0 <=8.15.0",

15
scripts/check-no-dups.sh

@ -0,0 +1,15 @@
#!/bin/bash
yarn-deduplicate -l | grep \@ledgerhq
if [ $? -eq 0 ]; then
echo "Found duplicates in @ledgerhq/* – fix it with yarn-deduplicate"
exit 1
fi
yarn-deduplicate -l | grep \"react
if [ $? -eq 0 ]; then
echo "Found duplicates in some react packages – fix it with yarn-deduplicate"
exit 1
fi

1
src/bridge/BridgeSyncContext.js

@ -100,6 +100,7 @@ class Provider extends Component<BridgeSyncProviderOwnProps, Sync> {
next() next()
}, },
error: error => { error: error => {
logger.critical(error)
this.props.setAccountSyncState(accountId, { pending: false, error }) this.props.setAccountSyncState(accountId, { pending: false, error })
next() next()
}, },

8
src/bridge/EthereumJSBridge.js

@ -129,7 +129,7 @@ function isRecipientValid(currency, recipient) {
} }
// Returns a warning if we detect a non-eip address // Returns a warning if we detect a non-eip address
function getRecipientWarning(currency, recipient) { function getRecipientWarning(account, recipient) {
if (!recipient.match(/^0x[0-9a-fA-F]{40}$/)) return null if (!recipient.match(/^0x[0-9a-fA-F]{40}$/)) return null
const slice = recipient.substr(2) const slice = recipient.substr(2)
const isFullUpper = slice === slice.toUpperCase() const isFullUpper = slice === slice.toUpperCase()
@ -420,9 +420,9 @@ const EthereumBridge: WalletBridge<Transaction> = {
pullMoreOperations: () => Promise.resolve(a => a), // NOT IMPLEMENTED pullMoreOperations: () => Promise.resolve(a => a), // NOT IMPLEMENTED
isRecipientValid: (currency, recipient) => Promise.resolve(isRecipientValid(currency, recipient)), isRecipientValid: (account, recipient) => Promise.resolve(isRecipientValid(account, recipient)),
getRecipientWarning: (currency, recipient) => getRecipientWarning: (account, recipient) =>
Promise.resolve(getRecipientWarning(currency, recipient)), Promise.resolve(getRecipientWarning(account, recipient)),
createTransaction: () => ({ createTransaction: () => ({
amount: BigNumber(0), amount: BigNumber(0),

8
src/bridge/LibcoreBridge.js

@ -60,15 +60,15 @@ const EditAdvancedOptions = ({ onChange, value }: EditProps<Transaction>) => (
const recipientValidLRU = LRU({ max: 100 }) const recipientValidLRU = LRU({ max: 100 })
const isRecipientValid = (currency, recipient) => { const isRecipientValid = (account, recipient) => {
const key = `${currency.id}_${recipient}` const key = `${account.currency.id}_${recipient}`
let promise = recipientValidLRU.get(key) let promise = recipientValidLRU.get(key)
if (promise) return promise if (promise) return promise
if (!recipient) return Promise.resolve(false) if (!recipient) return Promise.resolve(false)
promise = libcoreValidAddress promise = libcoreValidAddress
.send({ .send({
address: recipient, address: recipient,
currencyId: currency.id, currencyId: account.currency.id,
}) })
.toPromise() .toPromise()
recipientValidLRU.set(key, promise) recipientValidLRU.set(key, promise)
@ -83,7 +83,7 @@ const getFeesKey = (a, t) =>
}` }`
const getFees = async (a, transaction) => { const getFees = async (a, transaction) => {
const isValid = await isRecipientValid(a.currency, transaction.recipient) const isValid = await isRecipientValid(a, transaction.recipient)
if (!isValid) return null if (!isValid) return null
const key = getFeesKey(a, transaction) const key = getFeesKey(a, transaction)
let promise = feesLRU.get(key) let promise = feesLRU.get(key)

20
src/bridge/RippleJSBridge.js

@ -34,6 +34,7 @@ import {
NotEnoughBalance, NotEnoughBalance,
FeeNotLoaded, FeeNotLoaded,
NotEnoughBalanceBecauseDestinationNotCreated, NotEnoughBalanceBecauseDestinationNotCreated,
InvalidAddressBecauseDestinationIsAlsoSource,
} from '@ledgerhq/errors' } from '@ledgerhq/errors'
import type { WalletBridge, EditProps } from './types' import type { WalletBridge, EditProps } from './types'
@ -136,15 +137,23 @@ async function signAndBroadcast({ a, t, deviceId, isCancelled, onSigned, onOpera
} }
} }
function isRecipientValid(recipient) { function isRecipientValid(account, recipient) {
try { try {
bs58check.decode(recipient) bs58check.decode(recipient)
return true
return !(account && account.freshAddress === recipient)
} catch (e) { } catch (e) {
return false return false
} }
} }
function getRecipientWarning(account, recipient) {
if (account.freshAddress === recipient) {
return new InvalidAddressBecauseDestinationIsAlsoSource()
}
return null
}
function mergeOps(existing: Operation[], newFetched: Operation[]) { function mergeOps(existing: Operation[], newFetched: Operation[]) {
const ids = existing.map(o => o.id) const ids = existing.map(o => o.id)
const all = existing.concat(newFetched.filter(o => !ids.includes(o.id))) const all = existing.concat(newFetched.filter(o => !ids.includes(o.id)))
@ -270,7 +279,7 @@ const getServerInfo = (map => endpointConfig => {
})({}) })({})
const recipientIsNew = async (endpointConfig, recipient) => { const recipientIsNew = async (endpointConfig, recipient) => {
if (!isRecipientValid(recipient)) return false if (!isRecipientValid(null, recipient)) return false
const api = apiForEndpointConfig(RippleAPI, endpointConfig) const api = apiForEndpointConfig(RippleAPI, endpointConfig)
try { try {
await api.connect() await api.connect()
@ -505,8 +514,9 @@ const RippleJSBridge: WalletBridge<Transaction> = {
pullMoreOperations: () => Promise.resolve(a => a), // FIXME not implemented pullMoreOperations: () => Promise.resolve(a => a), // FIXME not implemented
isRecipientValid: (currency, recipient) => Promise.resolve(isRecipientValid(recipient)), isRecipientValid: (account, recipient) => Promise.resolve(isRecipientValid(account, recipient)),
getRecipientWarning: () => Promise.resolve(null), getRecipientWarning: (account, recipient) =>
Promise.resolve(getRecipientWarning(account, recipient)),
createTransaction: () => ({ createTransaction: () => ({
amount: BigNumber(0), amount: BigNumber(0),

2
src/bridge/makeMockBridge.js

@ -128,7 +128,7 @@ function makeMockBridge(opts?: Opts): WalletBridge<*> {
} }
}, },
isRecipientValid: (currency, recipient) => Promise.resolve(recipient.length > 0), isRecipientValid: (account, recipient) => Promise.resolve(recipient.length > 0),
getRecipientWarning: () => Promise.resolve(null), getRecipientWarning: () => Promise.resolve(null),
createTransaction: () => ({ createTransaction: () => ({

4
src/bridge/types.js

@ -52,8 +52,8 @@ export interface WalletBridge<Transaction> {
// count is user's desired number of ops to pull (but implementation can decide to ignore it or not) // count is user's desired number of ops to pull (but implementation can decide to ignore it or not)
pullMoreOperations(initialAccount: Account, count: number): Promise<(Account) => Account>; pullMoreOperations(initialAccount: Account, count: number): Promise<(Account) => Account>;
isRecipientValid(currency: Currency, recipient: string): Promise<boolean>; isRecipientValid(account: Account, recipient: string): Promise<boolean>;
getRecipientWarning(currency: Currency, recipient: string): Promise<?Error>; getRecipientWarning(account: Account, recipient: string): Promise<?Error>;
// Related to send funds: // Related to send funds:

BIN
src/commands/.DS_Store

Binary file not shown.

84
src/components/ManagerPage/AppsList.js

@ -40,15 +40,35 @@ const mapStateToProps = state => ({
const List = styled(Box).attrs({ const List = styled(Box).attrs({
horizontal: true, horizontal: true,
m: -3,
})` })`
flex-wrap: wrap; flex-wrap: wrap;
> * {
width: calc(50% - 10px);
margin-bottom: 20px;
&:nth-child(even) {
margin-left: 20px;
}
@media (max-width: 1000px) {
width: 100%;
&:nth-child(even) {
margin-left: 0;
}
}
}
` `
const ICONS_FALLBACK = { const ICONS_FALLBACK = {
bitcoin_testnet: 'bitcoin', bitcoin_testnet: 'bitcoin',
} }
const CATALOG_INFO_ICON = (
<Box color="grey">
<IconInfoCircle size={12} />
</Box>
)
type Status = 'loading' | 'idle' | 'busy' | 'success' | 'error' type Status = 'loading' | 'idle' | 'busy' | 'success' | 'error'
type Mode = 'home' | 'installing' | 'uninstalling' type Mode = 'home' | 'installing' | 'uninstalling'
@ -80,6 +100,20 @@ const LoadingApp = () => (
const loadingApp = <LoadingApp /> const loadingApp = <LoadingApp />
const FAKE_LIST = (
<List>
{loadingApp}
{loadingApp}
{loadingApp}
{loadingApp}
{loadingApp}
{loadingApp}
{loadingApp}
{loadingApp}
{loadingApp}
</List>
)
class AppsList extends PureComponent<Props, State> { class AppsList extends PureComponent<Props, State> {
state = { state = {
status: 'loading', status: 'loading',
@ -272,22 +306,16 @@ class AppsList extends PureComponent<Props, State> {
)} )}
</AppSearchBar> </AppSearchBar>
{this.renderModal()} {this.renderModal()}
{!appsLoaded && ( {!appsLoaded && FAKE_LIST}
<Fragment> </Box>
<Space of={30} /> )
<List> }
{loadingApp}
{loadingApp} renderTooltip = () => {
{loadingApp} const { t } = this.props
{loadingApp} return (
{loadingApp} <Box ff="Open Sans|SemiBold" fontSize={2}>
{loadingApp} {t('manager.apps.help')}
{loadingApp}
{loadingApp}
{loadingApp}
</List>
</Fragment>
)}
</Box> </Box>
) )
} }
@ -296,24 +324,12 @@ class AppsList extends PureComponent<Props, State> {
const { t } = this.props const { t } = this.props
return ( return (
<Box flow={6}> <Box>
<Box> <Box mb={4} color="dark" ff="Museo Sans" fontSize={5} flow={2} horizontal align="center">
<Box mb={4} color="dark" ff="Museo Sans" fontSize={5} flow={2} horizontal align="center"> <span>{t('manager.apps.all')}</span>
<span style={{ lineHeight: 1 }}>{t('manager.apps.all')}</span> <Tooltip render={this.renderTooltip}>{CATALOG_INFO_ICON}</Tooltip>
<Tooltip
render={() => (
<Box ff="Open Sans|SemiBold" fontSize={2}>
{t('manager.apps.help')}
</Box>
)}
>
<Box color="grey">
<IconInfoCircle size={12} />
</Box>
</Tooltip>
</Box>
{this.renderList()}
</Box> </Box>
{this.renderList()}
</Box> </Box>
) )
} }

3
src/components/ManagerPage/ManagerApp.js

@ -14,15 +14,12 @@ import Button from 'components/base/Button'
export const Container = styled(Box).attrs({ export const Container = styled(Box).attrs({
horizontal: true, horizontal: true,
my: 2,
mx: 3,
p: 4, p: 4,
bg: 'white', bg: 'white',
boxShadow: p => (p.noShadow ? -1 : 0), boxShadow: p => (p.noShadow ? -1 : 0),
borderRadius: 4, borderRadius: 4,
flow: 2, flow: 2,
})` })`
width: calc(50% - 30px);
line-height: normal; line-height: normal;
` `

5
src/components/Onboarding/steps/GenuineCheck/GenuineCheckUnavailable.js

@ -31,6 +31,8 @@ export function GenuineCheckUnavailableFooter({
</Button> </Button>
<Box horizontal ml="auto"> <Box horizontal ml="auto">
<Button <Button
outline
outlineColor="alertRed"
disabled={false} disabled={false}
event="Onboarding Skip Genuine Check" event="Onboarding Skip Genuine Check"
onClick={() => nextStep()} onClick={() => nextStep()}
@ -38,9 +40,6 @@ export function GenuineCheckUnavailableFooter({
> >
{t('common.skipThisStep')} {t('common.skipThisStep')}
</Button> </Button>
<Button onClick={nextStep} disabled primary>
{t('common.continue')}
</Button>
</Box> </Box>
</OnboardingFooterWrapper> </OnboardingFooterWrapper>
) )

2
src/components/SelectExchange.js

@ -87,7 +87,7 @@ class SelectExchange extends Component<
this.setState({ exchanges, isLoading: false }) this.setState({ exchanges, isLoading: false })
} }
} catch (error) { } catch (error) {
logger.error(error) logger.critical(error)
if (!this._unmounted && this._loadId === _loadId) { if (!this._unmounted && this._loadId === _loadId) {
this.setState({ error, isLoading: false }) this.setState({ error, isLoading: false })
} }

14
src/components/SettingsPage/RepairDeviceButton.js

@ -6,6 +6,7 @@ import { connect } from 'react-redux'
import { withRouter } from 'react-router' import { withRouter } from 'react-router'
import { translate } from 'react-i18next' import { translate } from 'react-i18next'
import { push } from 'react-router-redux' import { push } from 'react-router-redux'
import logger from 'logger'
import type { T } from 'types/common' import type { T } from 'types/common'
import firmwareRepair from 'commands/firmwareRepair' import firmwareRepair from 'commands/firmwareRepair'
@ -32,27 +33,38 @@ class RepairDeviceButton extends PureComponent<Props, State> {
progress: 0, progress: 0,
} }
componentWillUnmount() {
if (this.timeout) {
clearTimeout(this.timeout)
}
}
open = () => this.setState({ opened: true, error: null }) open = () => this.setState({ opened: true, error: null })
sub: * sub: *
timeout: *
close = () => { close = () => {
if (this.sub) this.sub.unsubscribe() if (this.sub) this.sub.unsubscribe()
if (this.timeout) clearTimeout(this.timeout)
this.setState({ opened: false, isLoading: false, error: null, progress: 0 }) this.setState({ opened: false, isLoading: false, error: null, progress: 0 })
} }
repair = (version = null) => { repair = (version = null) => {
if (this.state.isLoading) return if (this.state.isLoading) return
const { push } = this.props const { push } = this.props
this.setState({ isLoading: true }) this.timeout = setTimeout(() => this.setState({ isLoading: true }), 500)
this.sub = firmwareRepair.send({ version }).subscribe({ this.sub = firmwareRepair.send({ version }).subscribe({
next: patch => { next: patch => {
this.setState(patch) this.setState(patch)
}, },
error: error => { error: error => {
logger.critical(error)
if (this.timeout) clearTimeout(this.timeout)
this.setState({ error, isLoading: false, progress: 0 }) this.setState({ error, isLoading: false, progress: 0 })
}, },
complete: () => { complete: () => {
if (this.timeout) clearTimeout(this.timeout)
this.setState({ opened: false, isLoading: false, progress: 0 }, () => { this.setState({ opened: false, isLoading: false, progress: 0 }, () => {
push('/manager') push('/manager')
}) })

13
src/components/SettingsPage/sections/Export.js

@ -69,7 +69,10 @@ class SectionExport extends PureComponent<Props, State> {
<Text ff="Open Sans|SemiBold" color="dark"> <Text ff="Open Sans|SemiBold" color="dark">
{'+'} {'+'}
</Text> </Text>
{'button in Accounts'} {'button in'}
<Text ff="Open Sans|SemiBold" color="dark">
{'Accounts'}
</Text>
</Trans> </Trans>
</Box> </Box>
), ),
@ -93,7 +96,13 @@ class SectionExport extends PureComponent<Props, State> {
icon: <BulletRowIcon>{'3'}</BulletRowIcon>, icon: <BulletRowIcon>{'3'}</BulletRowIcon>,
desc: ( desc: (
<Box style={{ display: 'block' }}> <Box style={{ display: 'block' }}>
<Trans i18nKey="settings.export.modal.step3" /> <Trans i18nKey="settings.export.modal.step3">
{'Scan the'}
<Text ff="Open Sans|SemiBold" color="dark">
{'LiveQR Code'}
</Text>
{'until the loader hits 100%'}
</Trans>
</Box> </Box>
), ),
}, },

4
src/components/modals/AddAccounts/index.js

@ -24,6 +24,7 @@ import { closeModal } from 'reducers/modals'
import Modal from 'components/base/Modal' import Modal from 'components/base/Modal'
import Stepper from 'components/base/Stepper' import Stepper from 'components/base/Stepper'
import { validateNameEdition } from '@ledgerhq/live-common/lib/account' import { validateNameEdition } from '@ledgerhq/live-common/lib/account'
import logger from 'logger'
import StepChooseCurrency, { StepChooseCurrencyFooter } from './steps/01-step-choose-currency' import StepChooseCurrency, { StepChooseCurrencyFooter } from './steps/01-step-choose-currency'
import StepConnectDevice, { StepConnectDeviceFooter } from './steps/02-step-connect-device' import StepConnectDevice, { StepConnectDeviceFooter } from './steps/02-step-connect-device'
@ -166,6 +167,9 @@ class AddAccounts extends PureComponent<Props, State> {
handleSetCurrency = (currency: ?Currency) => this.setState({ currency }) handleSetCurrency = (currency: ?Currency) => this.setState({ currency })
handleSetScanStatus = (scanStatus: string, err: ?Error = null) => { handleSetScanStatus = (scanStatus: string, err: ?Error = null) => {
if (err) {
logger.critical(err)
}
this.setState({ scanStatus, err }) this.setState({ scanStatus, err })
} }

4
src/components/modals/Receive/index.js

@ -8,6 +8,7 @@ import { createStructuredSelector } from 'reselect'
import SyncSkipUnderPriority from 'components/SyncSkipUnderPriority' import SyncSkipUnderPriority from 'components/SyncSkipUnderPriority'
import logger from 'logger'
import Track from 'analytics/Track' import Track from 'analytics/Track'
import type { Account } from '@ledgerhq/live-common/lib/types' import type { Account } from '@ledgerhq/live-common/lib/types'
@ -141,6 +142,9 @@ class ReceiveModal extends PureComponent<Props, State> {
handleChangeAppOpened = (isAppOpened: boolean) => this.setState({ isAppOpened }) handleChangeAppOpened = (isAppOpened: boolean) => this.setState({ isAppOpened })
handleChangeAddressVerified = (isAddressVerified: boolean, err: ?Error) => { handleChangeAddressVerified = (isAddressVerified: boolean, err: ?Error) => {
if (err && err.name !== 'UserRefusedAddress') {
logger.critical(err)
}
this.setState({ isAddressVerified, verifyAddressError: err }) this.setState({ isAddressVerified, verifyAddressError: err })
} }

10
src/components/modals/Send/fields/RecipientField.js

@ -53,8 +53,8 @@ class RecipientField<Transaction> extends Component<
const { account, bridge, transaction } = this.props const { account, bridge, transaction } = this.props
const syncId = ++this.syncId const syncId = ++this.syncId
const recipient = bridge.getTransactionRecipient(account, transaction) const recipient = bridge.getTransactionRecipient(account, transaction)
const isValid = await bridge.isRecipientValid(account.currency, recipient) const isValid = await bridge.isRecipientValid(account, recipient)
const warning = await bridge.getRecipientWarning(account.currency, recipient) const warning = await bridge.getRecipientWarning(account, recipient)
if (syncId !== this.syncId) return if (syncId !== this.syncId) return
if (this.isUnmounted) return if (this.isUnmounted) return
this.setState({ isValid, warning }) this.setState({ isValid, warning })
@ -69,9 +69,7 @@ class RecipientField<Transaction> extends Component<
if (amount) { if (amount) {
t = bridge.editTransactionAmount(account, t, amount) t = bridge.editTransactionAmount(account, t, amount)
} }
const warning = fromQRCode const warning = fromQRCode ? await bridge.getRecipientWarning(account, recipient) : null
? await bridge.getRecipientWarning(account.currency, recipient)
: null
if (this.isUnmounted) return false if (this.isUnmounted) return false
if (warning) { if (warning) {
// clear the input if field has warning AND has a warning // clear the input if field has warning AND has a warning
@ -97,7 +95,7 @@ class RecipientField<Transaction> extends Component<
const error = const error =
!value || isValid !value || isValid
? QRCodeRefusedReason ? QRCodeRefusedReason
: new InvalidAddress(null, { currencyName: account.currency.name }) : warning || new InvalidAddress(null, { currencyName: account.currency.name })
return ( return (
<Box flow={1}> <Box flow={1}>

4
src/components/modals/Send/index.js

@ -12,6 +12,7 @@ import Track from 'analytics/Track'
import { updateAccountWithUpdater } from 'actions/accounts' import { updateAccountWithUpdater } from 'actions/accounts'
import { MODAL_SEND } from 'config/constants' import { MODAL_SEND } from 'config/constants'
import { getBridgeForCurrency } from 'bridge' import { getBridgeForCurrency } from 'bridge'
import logger from 'logger'
import type { WalletBridge } from 'bridge/types' import type { WalletBridge } from 'bridge/types'
import type { T, Device } from 'types/common' import type { T, Device } from 'types/common'
@ -180,6 +181,9 @@ class SendModal extends PureComponent<Props, State<*>> {
} }
handleTransactionError = (error: Error) => { handleTransactionError = (error: Error) => {
if (!(error instanceof UserRefusedOnDevice)) {
logger.critical(error)
}
const stepVerificationIndex = this.STEPS.findIndex(step => step.id === 'verification') const stepVerificationIndex = this.STEPS.findIndex(step => step.id === 'verification')
if (stepVerificationIndex === -1) return if (stepVerificationIndex === -1) return
this.setState({ error }) this.setState({ error })

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

@ -133,7 +133,7 @@ export class StepAmountFooter extends PureComponent<
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 isRecipientValid = await bridge.isRecipientValid( const isRecipientValid = await bridge.isRecipientValid(
account.currency, account,
bridge.getTransactionRecipient(account, transaction), bridge.getTransactionRecipient(account, transaction),
) )
if (syncId !== this.syncId) return if (syncId !== this.syncId) return

6
src/components/modals/UpdateFirmware/index.js

@ -11,6 +11,7 @@ import type { FirmwareUpdateContext } from '@ledgerhq/live-common/lib/types/mana
import type { StepProps as DefaultStepProps, Step } from 'components/base/Stepper' import type { StepProps as DefaultStepProps, Step } from 'components/base/Stepper'
import type { ModalStatus } from 'components/ManagerPage/FirmwareUpdate' import type { ModalStatus } from 'components/ManagerPage/FirmwareUpdate'
import logger from 'logger'
import { FreezeDeviceChangeEvents } from '../../ManagerPage/HookDeviceChange' import { FreezeDeviceChangeEvents } from '../../ManagerPage/HookDeviceChange'
import StepFullFirmwareInstall from './steps/01-step-install-full-firmware' import StepFullFirmwareInstall from './steps/01-step-install-full-firmware'
@ -82,7 +83,10 @@ class UpdateModal extends PureComponent<Props, State> {
t: this.props.t, t: this.props.t,
}) })
setError = (e: Error) => this.setState({ error: e }) setError = (e: Error) => {
logger.critical(e)
this.setState({ error: e })
}
handleReset = () => this.setState({ stepId: 'idCheck', error: null, nonce: this.state.nonce++ }) handleReset = () => this.setState({ stepId: 'idCheck', error: null, nonce: this.state.nonce++ })

13
static/i18n/en/app.json

@ -366,10 +366,10 @@
"modal": { "modal": {
"button": "Done", "button": "Done",
"title": "Scan to export to mobile", "title": "Scan to export to mobile",
"listTitle": "To import accounts on your Ledger Live Mobile app:", "listTitle": "On the Ledger Live mobile app:",
"step1": "Tap the <1><0>+</0></1> button in Accounts", "step1": "Tap the <1><0>+</0></1> button in <3><0>Accounts</0></3>",
"step2": "Tap <1><0>Import desktop accounts</0></1>", "step2": "Tap <1><0>Import desktop accounts</0></1>",
"step3": "Scan until the loader hits 100%" "step3": "Scan the <1><0>LiveQR code</0></1> until the loader hits 100%"
} }
}, },
"display": { "display": {
@ -783,7 +783,7 @@
"description": "Please retry. Interacting with Ledger's API server went wrong." "description": "Please retry. Interacting with Ledger's API server went wrong."
}, },
"LedgerAPIErrorWithMessage": { "LedgerAPIErrorWithMessage": {
"title": "Oops, {{message}}", "title": "{{message}}",
"description": "Please retry or contact Ledger Support" "description": "Please retry or contact Ledger Support"
}, },
"LedgerAPINotAvailable": { "LedgerAPINotAvailable": {
@ -900,6 +900,9 @@
"InvalidAddress": { "InvalidAddress": {
"title": "This is not a valid {{currencyName}} address" "title": "This is not a valid {{currencyName}} address"
}, },
"InvalidAddressBecauseDestinationIsAlsoSource": {
"title": "Recipient address is the same as the sender address"
},
"CantOpenDevice": { "CantOpenDevice": {
"title": "Oops, couldn’t connect to device", "title": "Oops, couldn’t connect to device",
"description": "Device detected but connection failed. Please try again or contact us if the problem persists." "description": "Device detected but connection failed. Please try again or contact us if the problem persists."
@ -919,4 +922,4 @@
"description": "Please contact Ledger Support" "description": "Please contact Ledger Support"
} }
} }
} }

90
yarn.lock

@ -1677,56 +1677,48 @@
camelcase "^5.0.0" camelcase "^5.0.0"
prettier "^1.13.7" prettier "^1.13.7"
"@ledgerhq/errors@^4.32.0": "@ledgerhq/errors@^4.32.0", "@ledgerhq/errors@^4.35.1":
version "4.33.7" version "4.35.1"
resolved "https://registry.yarnpkg.com/@ledgerhq/errors/-/errors-4.33.7.tgz#b78becd20e8a68f7115ad0986fa357a8adddf6b7" resolved "https://registry.yarnpkg.com/@ledgerhq/errors/-/errors-4.35.1.tgz#3f162dc05480e444083b6381bd098df187751633"
integrity sha512-1vKWcttI5NHpT6rMKKuxWPAjfwDgfgUTf/AyNAT5KXHlhiLvqnA3NDCdNUVadajVNKSa/s1u1ZWKismtbfePzg== integrity sha512-2Bo3/NRKyz3ddR07TvZ87VpDJc8fz4+ONLJnhzC0mwIwu+Pxal6SgCBiGtv503oGxkgDuG5PtODZBaehWkGRnQ==
"@ledgerhq/hw-app-btc@^4.32.0": "@ledgerhq/hw-app-btc@^4.32.0", "@ledgerhq/hw-app-btc@^4.35.0":
version "4.32.0" version "4.35.0"
resolved "https://registry.yarnpkg.com/@ledgerhq/hw-app-btc/-/hw-app-btc-4.32.0.tgz#e883dcaa3ebb4aca1e2cb27acfc47b8db4e85f3f" resolved "https://registry.yarnpkg.com/@ledgerhq/hw-app-btc/-/hw-app-btc-4.35.0.tgz#aafd655c988da39f774b0a4706e7f8897222f414"
integrity sha512-N/RxtkPVjTDwU+lDPQQE7+4YQMXaXStDrpufQbDn0NXoaJ8KgY+QGkOH6bkuwV+LQvc7rEaM7E3p7/t58KJpMg== integrity sha512-oX9YcQAuU+rOJm/lE7YF5+JXNppHcUv23ZltGz5CbWHnhm7Tqo4MOR8N5oSnHKlHW+IawfWCPN5PqdF7RGyQ5w==
dependencies: dependencies:
"@ledgerhq/hw-transport" "^4.32.0" "@ledgerhq/hw-transport" "^4.35.0"
create-hash "^1.1.3" create-hash "^1.1.3"
"@ledgerhq/hw-app-btc@^4.34.0": "@ledgerhq/hw-app-eth@^4.32.0", "@ledgerhq/hw-app-eth@^4.35.0":
version "4.34.0" version "4.35.0"
resolved "https://registry.yarnpkg.com/@ledgerhq/hw-app-btc/-/hw-app-btc-4.34.0.tgz#0bbc46afd29de04ac6a73582fbf9a09fcf5ed117" resolved "https://registry.yarnpkg.com/@ledgerhq/hw-app-eth/-/hw-app-eth-4.35.0.tgz#3a8c1b0db87224a24ff5441a0e819122e5585f2d"
integrity sha512-xR4rH8o8YRvyhnTvb8g89NAJQQqXJkApiFtCvduBamu5V+rDvhHYlFu2B+CU6g8lzLFACMDIqJqXbmwT80AGjw== integrity sha512-MSDr8+CaoXhtm64ELuI/8wpcfmrMUjzGJgASY6bnjc82vAW+6sHNZlTU0zWRTZxqQUuZ8WpuJP159cf92MWq3g==
dependencies: dependencies:
"@ledgerhq/hw-transport" "^4.32.0" "@ledgerhq/hw-transport" "^4.35.0"
create-hash "^1.1.3"
"@ledgerhq/hw-app-eth@^4.32.0": "@ledgerhq/hw-app-xrp@^4.32.0", "@ledgerhq/hw-app-xrp@^4.35.0":
version "4.32.0" version "4.35.0"
resolved "https://registry.yarnpkg.com/@ledgerhq/hw-app-eth/-/hw-app-eth-4.32.0.tgz#7d43ca2c7952f1fb726e02c3b4485be10af481a2" resolved "https://registry.yarnpkg.com/@ledgerhq/hw-app-xrp/-/hw-app-xrp-4.35.0.tgz#f6aec06ae53f8732d90f745963a2de96c2ffa432"
integrity sha512-d22WinjcsqJNoZSI+6UpTWZ7hl+UhL2dFeVeliCwtBWSj40z6F25MpoviGxPsv0WC7IUjayw+a9jIRcOJ5kkIw== integrity sha512-kQLdr9xrYvkFR9+QVyTNtmSGFDfrQ63ac0QhWKEoILiiQ0dxfZ7qCCp/qPJk/sx9H8dMX37X6y+xAnSU1frbfg==
dependencies: dependencies:
"@ledgerhq/hw-transport" "^4.32.0" "@ledgerhq/hw-transport" "^4.35.0"
"@ledgerhq/hw-app-xrp@^4.32.0":
version "4.32.0"
resolved "https://registry.yarnpkg.com/@ledgerhq/hw-app-xrp/-/hw-app-xrp-4.32.0.tgz#260daafa9de1073598ea91ddfeb168dc437edd50"
integrity sha512-MNmLAGUp7Bnj/mjg1Lo5bK1v+q/QPYw7RJAbI4Vl1A4Fsqj6oiZspnSK+BTHGp+CRJavCwumjKuf5y3X5Dp8cA==
dependencies:
"@ledgerhq/hw-transport" "^4.32.0"
bip32-path "0.4.2" bip32-path "0.4.2"
"@ledgerhq/hw-transport-node-hid@^4.32.0": "@ledgerhq/hw-transport-node-hid@^4.35.0":
version "4.33.3" version "4.35.0"
resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport-node-hid/-/hw-transport-node-hid-4.33.3.tgz#5e96dca2be0a23d80814303f262398087b208a6a" resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport-node-hid/-/hw-transport-node-hid-4.35.0.tgz#0eba08e5edd14a8c779ebaf73ec21976ee5f112e"
integrity sha512-hmNAm7k385RJXY38hVUpzYgGgyk9QjScD3erNlFCTO8FnnxmEJCFUmVhWkv4sTwufuUJSpXL3ZXXNZ44qLMJpg== integrity sha512-Otnymk9B7qCEfjych/SvTvJsMM+DqyoB0saEwL80ukjuGFqMunecrG5w8nC4aCc169IVz70Spkg2uU90TBUCuw==
dependencies: dependencies:
"@ledgerhq/hw-transport" "^4.32.0" "@ledgerhq/hw-transport" "^4.35.0"
lodash "^4.17.11" lodash "^4.17.11"
node-hid "^0.7.2" node-hid "^0.7.2"
usb "^1.3.3" usb "^1.3.3"
"@ledgerhq/hw-transport@^4.21.0", "@ledgerhq/hw-transport@^4.32.0": "@ledgerhq/hw-transport@^4.21.0", "@ledgerhq/hw-transport@^4.32.0", "@ledgerhq/hw-transport@^4.35.0":
version "4.32.0" version "4.35.0"
resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport/-/hw-transport-4.32.0.tgz#592b9dc51459cb1cd31ce9444cf943f627bc4beb" resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport/-/hw-transport-4.35.0.tgz#aa7b851111ed759cd7489fa07a7b34c1773e8314"
integrity sha512-Wgsk9UHC4RShqYoDeIEeKgHZOvNCtB0WWIG0xqlVPzS+IcKDkIxtXQw7hTA7GQSuDuGeauVtlbTQ5yat6+2/BA== integrity sha512-o8ekdoCkHMvOByIKDmAMNDjm8Q5cu+sbqmebPtGrHAPbgIZBUbNA5UupY/Om+xypdxXYnuBw+MF8FyIVOjnIsg==
dependencies: dependencies:
events "^3.0.0" events "^3.0.0"
@ -2258,6 +2250,11 @@
text-table "^0.2.0" text-table "^0.2.0"
webpack-log "^1.1.2" webpack-log "^1.1.2"
"@yarnpkg/lockfile@^1.1.0":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz#e77a97fbd345b76d83245edcd17d393b1b41fb31"
integrity sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==
abab@^1.0.4: abab@^1.0.4:
version "1.0.4" version "1.0.4"
resolved "https://registry.yarnpkg.com/abab/-/abab-1.0.4.tgz#5faad9c2c07f60dd76770f71cf025b62a63cfd4e" resolved "https://registry.yarnpkg.com/abab/-/abab-1.0.4.tgz#5faad9c2c07f60dd76770f71cf025b62a63cfd4e"
@ -5225,6 +5222,11 @@ commander@2.6.0:
resolved "https://registry.yarnpkg.com/commander/-/commander-2.6.0.tgz#9df7e52fb2a0cb0fb89058ee80c3104225f37e1d" resolved "https://registry.yarnpkg.com/commander/-/commander-2.6.0.tgz#9df7e52fb2a0cb0fb89058ee80c3104225f37e1d"
integrity sha1-nfflL7Kgyw+4kFjugMMQQiXzfh0= integrity sha1-nfflL7Kgyw+4kFjugMMQQiXzfh0=
commander@^2.10.0:
version "2.19.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a"
integrity sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==
commander@~2.13.0: commander@~2.13.0:
version "2.13.0" version "2.13.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.13.0.tgz#6964bca67685df7c1f1430c584f07d7597885b9c" resolved "https://registry.yarnpkg.com/commander/-/commander-2.13.0.tgz#6964bca67685df7c1f1430c584f07d7597885b9c"
@ -13211,12 +13213,7 @@ react-inspector@^2.2.2:
babel-runtime "^6.26.0" babel-runtime "^6.26.0"
is-dom "^1.0.9" is-dom "^1.0.9"
react-is@^16.3.1, react-is@^16.4.1: react-is@^16.3.1, react-is@^16.3.2, react-is@^16.4.1:
version "16.4.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.4.1.tgz#d624c4650d2c65dbd52c72622bbf389435d9776e"
integrity sha512-xpb0PpALlFWNw/q13A+1aHeyJyLYCg0/cCHPUA43zYluZuIPHaHL3k8OBsTgQtxqW0FhyDEMvi8fZ/+7+r4OSQ==
react-is@^16.3.2:
version "16.5.2" version "16.5.2"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.5.2.tgz#e2a7b7c3f5d48062eb769fcb123505eb928722e3" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.5.2.tgz#e2a7b7c3f5d48062eb769fcb123505eb928722e3"
integrity sha512-hSl7E6l25GTjNEZATqZIuWOgSnpXb3kD0DVCujmg46K5zLxsbiKaaT6VO9slkSBDPZfYs30lwfJwbOFOnoEnKQ== integrity sha512-hSl7E6l25GTjNEZATqZIuWOgSnpXb3kD0DVCujmg46K5zLxsbiKaaT6VO9slkSBDPZfYs30lwfJwbOFOnoEnKQ==
@ -17062,6 +17059,15 @@ yargs@~3.10.0:
decamelize "^1.0.0" decamelize "^1.0.0"
window-size "0.1.0" window-size "0.1.0"
yarn-deduplicate@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/yarn-deduplicate/-/yarn-deduplicate-1.1.1.tgz#19b4a87654b66f55bf3a4bd6b153b4e4ab1b6e6d"
integrity sha512-2FDJ1dFmtvqhRmfja89ohYzpaheCYg7BFBSyaUq+kxK0y61C9oHv1XaQovCWGJtP2WU8PksQOgzMVV7oQOobzw==
dependencies:
"@yarnpkg/lockfile" "^1.1.0"
commander "^2.10.0"
semver "^5.3.0"
yauzl@2.4.1: yauzl@2.4.1:
version "2.4.1" version "2.4.1"
resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.4.1.tgz#9528f442dab1b2284e58b4379bb194e22e0c4005" resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.4.1.tgz#9528f442dab1b2284e58b4379bb194e22e0c4005"

Loading…
Cancel
Save