diff --git a/.circleci/config.yml b/.circleci/config.yml index e590dbc9..79faeabd 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -10,7 +10,7 @@ jobs: <<: *defaults steps: - run: sudo apt-get update - - run: sudo apt-get install -y libudev-dev + - run: sudo apt-get install -y libudev-dev libusb-1.0-0-dev - run: name: Install latest yarn command: | diff --git a/.flowconfig b/.flowconfig index b7155c37..9a82dfc0 100644 --- a/.flowconfig +++ b/.flowconfig @@ -6,6 +6,8 @@ [untyped] .*/node_modules/react-select /node_modules/qrloop/lib/Buffer.js* +/node_modules/@ledgerhq/live-common/lib/partners/icons/react/* +/node_modules/@ledgerhq/live-common/lib/partners/react.jsarn [include] @@ -18,5 +20,6 @@ flow-defs [options] module.system.node.resolve_dirname=node_modules module.system.node.resolve_dirname=./src +munge_underscores=true [strict] diff --git a/package.json b/package.json index 1dd1fdb9..ad43dc8d 100644 --- a/package.json +++ b/package.json @@ -35,13 +35,14 @@ } }, "dependencies": { + "@ledgerhq/devices": "^4.39.0", "@ledgerhq/errors": "^4.39.0", - "@ledgerhq/hw-app-btc": "^4.35.0", - "@ledgerhq/hw-app-eth": "^4.35.0", - "@ledgerhq/hw-app-xrp": "^4.35.0", - "@ledgerhq/hw-transport": "^4.35.0", - "@ledgerhq/hw-transport-node-hid": "^4.35.0", - "@ledgerhq/ledger-core": "2.0.0-rc.16", + "@ledgerhq/hw-app-btc": "^4.39.0", + "@ledgerhq/hw-app-eth": "^4.39.0", + "@ledgerhq/hw-app-xrp": "^4.39.0", + "@ledgerhq/hw-transport": "^4.39.0", + "@ledgerhq/hw-transport-node-hid": "^4.40.0", + "@ledgerhq/ledger-core": "2.0.0-rc.21", "@ledgerhq/live-common": "4.20.0", "animated": "^0.2.2", "async": "^2.6.1", diff --git a/src/analytics/segment.js b/src/analytics/segment.js index 0d5f01e7..dacff414 100644 --- a/src/analytics/segment.js +++ b/src/analytics/segment.js @@ -3,6 +3,7 @@ import uuid from 'uuid/v4' import logger from 'logger' import invariant from 'invariant' +import { getDeviceModel } from '@ledgerhq/devices' import { getSystemLocale } from 'helpers/systemLocale' import { langAndRegionSelector, shareAnalyticsSelector } from 'reducers/settings' import { getCurrentDevice } from 'reducers/devices' @@ -34,7 +35,7 @@ const extraProperties = store => { const systemLocale = getSystemLocale() const device = getCurrentDevice(state) const deviceInfo = device && { - productId: device.productId, + productId: getDeviceModel(device.modelId).usbProductId, } return { appVersion: __APP_VERSION__, diff --git a/src/commands/index.js b/src/commands/index.js index 893c6d53..f158dba1 100644 --- a/src/commands/index.js +++ b/src/commands/index.js @@ -16,6 +16,7 @@ import installApp from 'commands/installApp' import killInternalProcess from 'commands/killInternalProcess' import libcoreGetFees from 'commands/libcoreGetFees' import libcoreGetVersion from 'commands/libcoreGetVersion' +import libcoreReset from 'commands/libcoreReset' import libcoreScanAccounts from 'commands/libcoreScanAccounts' import libcoreScanFromXPUB from 'commands/libcoreScanFromXPUB' import libcoreSignAndBroadcast from 'commands/libcoreSignAndBroadcast' @@ -44,6 +45,7 @@ const all: Array> = [ killInternalProcess, libcoreGetFees, libcoreGetVersion, + libcoreReset, libcoreScanAccounts, libcoreScanFromXPUB, libcoreSignAndBroadcast, diff --git a/src/commands/killInternalProcess.js b/src/commands/killInternalProcess.js index 428fecd4..33ad0102 100644 --- a/src/commands/killInternalProcess.js +++ b/src/commands/killInternalProcess.js @@ -1,10 +1,10 @@ // @flow import { createCommand, Command } from 'helpers/ipc' -import { of } from 'rxjs' +import { never } from 'rxjs' type Input = void -type Result = boolean +type Result = void const cmd: Command = createCommand('killInternalProcess', () => { setTimeout(() => { @@ -12,7 +12,8 @@ const cmd: Command = createCommand('killInternalProcess', () => { // special exit code for better identification process.exit(42) }) - return of(true) + // The command shouldn't finish now because process.exit will make it end! + return never() }) export default cmd diff --git a/src/commands/libcoreReset.js b/src/commands/libcoreReset.js new file mode 100644 index 00000000..9e3d9f3e --- /dev/null +++ b/src/commands/libcoreReset.js @@ -0,0 +1,14 @@ +// @flow + +import { createCommand, Command } from 'helpers/ipc' +import { from } from 'rxjs' +import withLibcore from 'helpers/withLibcore' + +type Input = void +type Result = boolean + +const cmd: Command = createCommand('libcoreReset', () => + from(withLibcore(core => core.getPoolInstance().freshResetAll())), +) + +export default cmd diff --git a/src/components/DashboardPage/index.js b/src/components/DashboardPage/index.js index f3317bb5..02ee3ea3 100644 --- a/src/components/DashboardPage/index.js +++ b/src/components/DashboardPage/index.js @@ -3,7 +3,7 @@ import React, { PureComponent, Fragment } from 'react' import uniq from 'lodash/uniq' import { compose } from 'redux' -import IconNanoX from 'icons/device/NanoX' +import IconNanoX from 'icons/device/NanoXBanner' import { translate } from 'react-i18next' import { connect } from 'react-redux' import { push } from 'react-router-redux' diff --git a/src/components/EnsureDeviceApp.js b/src/components/EnsureDeviceApp.js index 623166a9..abe7c37c 100644 --- a/src/components/EnsureDeviceApp.js +++ b/src/components/EnsureDeviceApp.js @@ -98,8 +98,11 @@ class EnsureDeviceApp extends Component<{ id: 'device', title: ( - {'Connect and unlock your '} + {'Connect your'} {'Ledger device'} + {'to your computer and enter your'} + {'PIN code'} + {' on your device'} ), icon: usbIcon, diff --git a/src/components/ExchangePage/index.js b/src/components/ExchangePage/index.js deleted file mode 100644 index 0a1e46a9..00000000 --- a/src/components/ExchangePage/index.js +++ /dev/null @@ -1,126 +0,0 @@ -// @flow - -import React, { PureComponent } from 'react' -import { translate } from 'react-i18next' -import shuffle from 'lodash/shuffle' - -import type { T } from 'types/common' -import { urls } from 'config/urls' -import { i } from 'helpers/staticPath' - -import TrackPage from 'analytics/TrackPage' -import Box from 'components/base/Box' -import ExchangeCard from './ExchangeCard' - -import CoinhouseLogo from './logos/coinhouse' -import ChangellyLogo from './logos/changelly' -import CoinmamaLogo from './logos/bigmama' -import SimplexLogo from './logos/simplex' -import PaybisLogo from './logos/paybis' -import Coinberry from './logos/coinberry' -import BtcDirect from './logos/btcdirect' - -type Props = { - t: T, -} - -const cards = shuffle([ - { - key: 'coinhouse', - id: 'coinhouse', - url: urls.coinhouse, - logo: , - }, - { - key: 'changelly', - id: 'changelly', - url: urls.changelly, - logo: , - }, - { - key: 'coinmama', - id: 'coinmama', - url: urls.coinmama, - logo: , - }, - { - key: 'simplex', - id: 'simplex', - url: urls.simplex, - logo: , - }, - { - key: 'paybis', - id: 'paybis', - url: urls.paybis, - logo: , - }, - { - key: 'luno', - id: 'luno', - url: urls.luno, - logo: Luno, - }, - { - key: 'shapeshift', - id: 'shapeshift', - url: urls.shapeshift, - logo: Shapeshift, - }, - { - key: 'genesis', - id: 'genesis', - url: urls.genesis, - logo: Genesis, - }, - { - key: 'kyberSwap', - id: 'kyberSwap', - url: urls.kyberSwap, - logo: KyberSwap, - }, - { - key: 'changeNow', - id: 'changeNow', - url: urls.changeNow, - logo: ChangeNow, - }, - { - key: 'thorSwap', - id: 'thorSwap', - url: urls.thorSwap, - logo: ThorSwap, - }, - { - key: 'coinberry', - id: 'coinberry', - url: urls.coinberry, - logo: , - }, - { - key: 'btcDirect', - id: 'btcDirect', - url: urls.btcDirect, - logo: , - }, -]) - -class ExchangePage extends PureComponent { - render() { - const { t } = this.props - return ( - - - - {t('exchange.title')} - - - {t('exchange.desc')} - - {cards.map(card => )} - - ) - } -} - -export default translate()(ExchangePage) diff --git a/src/components/ExchangePage/logos/bigmama.js b/src/components/ExchangePage/logos/bigmama.js deleted file mode 100644 index 58dd5df2..00000000 --- a/src/components/ExchangePage/logos/bigmama.js +++ /dev/null @@ -1,16 +0,0 @@ -// @flow - -import React from 'react' - -const inner = ( - - - - -) - -export default ({ width }: { width: number }) => ( - - {inner} - -) diff --git a/src/components/ExchangePage/logos/btcdirect.js b/src/components/ExchangePage/logos/btcdirect.js deleted file mode 100644 index 70840be4..00000000 --- a/src/components/ExchangePage/logos/btcdirect.js +++ /dev/null @@ -1,67 +0,0 @@ -// @flow - -import React from 'react' - -const styles = { - fill: '#0086fb', -} - -const inner = ( - <> - - - - - - - - - - - - - - -) - -export default ({ width }: { width: number }) => ( - - {inner} - -) diff --git a/src/components/ExchangePage/logos/changelly.js b/src/components/ExchangePage/logos/changelly.js deleted file mode 100644 index 0a57bed0..00000000 --- a/src/components/ExchangePage/logos/changelly.js +++ /dev/null @@ -1,26 +0,0 @@ -// @flow - -import React from 'react' - -const inner = ( - - - - - -) - -export default ({ width }: { width: number }) => ( - - {inner} - -) diff --git a/src/components/ExchangePage/logos/coinberry.js b/src/components/ExchangePage/logos/coinberry.js deleted file mode 100644 index 46396d39..00000000 --- a/src/components/ExchangePage/logos/coinberry.js +++ /dev/null @@ -1,116 +0,0 @@ -// @flow - -import React from 'react' - -const styles = { - blue: { - fill: '#334F93', - }, - pink: { - fill: '#EC2D6E', - }, -} - -const inner = ( - <> - - - - - - - - - - - - - - - - - -) - -export default ({ width }: { width: number }) => ( - - {inner} - -) diff --git a/src/components/ExchangePage/logos/coinhouse.js b/src/components/ExchangePage/logos/coinhouse.js deleted file mode 100644 index b8cdf175..00000000 --- a/src/components/ExchangePage/logos/coinhouse.js +++ /dev/null @@ -1,58 +0,0 @@ -// @flow - -import React, { Fragment } from 'react' - -const inner = ( - - - - - - - - - - - - - -) - -export default ({ width }: { width: number }) => ( - - {inner} - -) diff --git a/src/components/ExchangePage/logos/paybis.js b/src/components/ExchangePage/logos/paybis.js deleted file mode 100644 index 2c6336ac..00000000 --- a/src/components/ExchangePage/logos/paybis.js +++ /dev/null @@ -1,46 +0,0 @@ -// @flow - -import React, { Fragment } from 'react' - -const inner = ( - - - - - - - - - - - - - - - - -) - -export default ({ width, height }: { width: number, height: number }) => ( - - {inner} - -) diff --git a/src/components/ExchangePage/logos/simplex.js b/src/components/ExchangePage/logos/simplex.js deleted file mode 100644 index 94d68a37..00000000 --- a/src/components/ExchangePage/logos/simplex.js +++ /dev/null @@ -1,27 +0,0 @@ -// @flow - -import React, { Fragment } from 'react' - -const inner = ( - - - - - -) - -export default ({ width, height }: { width: number, height: number }) => ( - - {inner} - -) diff --git a/src/components/GenuineCheck.js b/src/components/GenuineCheck.js index d3487591..8f25ec41 100644 --- a/src/components/GenuineCheck.js +++ b/src/components/GenuineCheck.js @@ -146,7 +146,7 @@ class GenuineCheck extends PureComponent { id: 'device', title: ( - {'Connect and unlock your '} + {'Connect and unlock your'} {'Ledger device'} ), @@ -157,9 +157,9 @@ class GenuineCheck extends PureComponent { id: 'deviceInfo', title: ( - {'Navigate to the '} - {'dashboard'} - {' on your device'} + {'Navigate to the'} + {'Dashboard'} + {'on your device'} ), icon: homeIcon, @@ -169,9 +169,9 @@ class GenuineCheck extends PureComponent { id: 'isGenuine', title: ( - {'Allow '} + {'Allow'} {'Ledger Manager'} - {' on your device'} + {'on your device'} ), icon: genuineCheckIcon, diff --git a/src/components/MainSideBar/index.js b/src/components/MainSideBar/index.js index 4ada30c9..4ec9af02 100644 --- a/src/components/MainSideBar/index.js +++ b/src/components/MainSideBar/index.js @@ -88,7 +88,7 @@ class MainSideBar extends PureComponent { handleOpenSendModal = () => this.props.openModal(MODAL_SEND) handleOpenReceiveModal = () => this.props.openModal(MODAL_RECEIVE) handleClickManager = () => this.push('/manager') - handleClickExchange = () => this.push('/exchange') + handleClickExchange = () => this.push('/partners') handleClickDev = () => this.push('/dev') handleOpenImportModal = () => this.props.openModal(MODAL_ADD_ACCOUNTS) diff --git a/src/components/ManagerPage/DeviceInfos.js b/src/components/ManagerPage/DeviceInfos.js index df2c87c2..b60e5db0 100644 --- a/src/components/ManagerPage/DeviceInfos.js +++ b/src/components/ManagerPage/DeviceInfos.js @@ -5,7 +5,7 @@ import React, { PureComponent } from 'react' import Text from 'components/base/Text' import Box, { Card } from 'components/base/Box' import Button from 'components/base/Button' - +import { getDeviceModel } from '@ledgerhq/devices' import type { Device, MemoryInfos } from 'types/common' import MemInfos from './MemInfos' @@ -43,10 +43,11 @@ class DeviceInfos extends PureComponent { return {'You dont have any device connected'} } + const deviceInfos = getDeviceModel(device.modelId) + const title = ( - {device.manufacturer} - {` ${device.product}`} + {`${deviceInfos.productName}`} ) return ( diff --git a/src/components/ManagerPage/FirmwareUpdate.js b/src/components/ManagerPage/FirmwareUpdate.js index 9eb9d547..f9f9d4ee 100644 --- a/src/components/ManagerPage/FirmwareUpdate.js +++ b/src/components/ManagerPage/FirmwareUpdate.js @@ -3,10 +3,10 @@ import React, { PureComponent, Fragment } from 'react' import { translate } from 'react-i18next' +import { getDeviceModel } from '@ledgerhq/devices' +import type { DeviceInfo, FirmwareUpdateContext } from '@ledgerhq/live-common/lib/types/manager' import type { Device, T } from 'types/common' - -import type { DeviceInfo, FirmwareUpdateContext } from '@ledgerhq/live-common/lib/types/manager' import type { StepId } from 'components/modals/UpdateFirmware' import getLatestFirmwareForDevice from 'commands/getLatestFirmwareForDevice' @@ -18,6 +18,7 @@ import Box, { Card } from 'components/base/Box' import Text from 'components/base/Text' import NanoS from 'icons/device/NanoS' +import NanoX from 'icons/device/NanoX' import Blue from 'icons/device/Blue' import CheckFull from 'icons/CheckFull' @@ -26,6 +27,17 @@ import UpdateFirmwareButton from './UpdateFirmwareButton' export const getCleanVersion = (input: string): string => input.endsWith('-osu') ? input.replace('-osu', '') : input +const Icon = ({ type }: { type: string }) => { + switch (type) { + case 'blue': + return + case 'nanoX': + return + default: + return + } +} + export type ModalStatus = 'closed' | 'disclaimer' | 'install' | 'error' | 'success' type Props = { @@ -81,18 +93,19 @@ class FirmwareUpdate extends PureComponent { render() { const { deviceInfo, t, device } = this.props const { firmware, modal, stepId, ready } = this.state + + const deviceSpecs = getDeviceModel(device.modelId) + return ( - {device.product === 'Blue' ? : } + - {device.product === 'Blue' - ? t('manager.firmware.titleBlue') - : t('manager.firmware.titleNano')} + {deviceSpecs.productName} t('manager.yourDeviceIsGenuine')}> diff --git a/src/components/ManagerPage/ManagerGenuineCheck.js b/src/components/ManagerPage/ManagerGenuineCheck.js index fed46656..b8782b4c 100644 --- a/src/components/ManagerPage/ManagerGenuineCheck.js +++ b/src/components/ManagerPage/ManagerGenuineCheck.js @@ -28,7 +28,7 @@ class ManagerGenuineCheck extends PureComponent { connect your device {t('manager.device.title')} diff --git a/src/components/Onboarding/index.js b/src/components/Onboarding/index.js index 557567bc..e511db45 100644 --- a/src/components/Onboarding/index.js +++ b/src/components/Onboarding/index.js @@ -19,7 +19,7 @@ import { prevStep, jumpStep, updateGenuineCheck, - isLedgerNano, + deviceModelId, flowType, relaunchOnboarding, onboardingRelaunchedSelector, @@ -102,7 +102,7 @@ export type StepProps = { getDeviceInfo: Function, updateGenuineCheck: Function, openModal: Function, - isLedgerNano: Function, + deviceModelId: Function, flowType: Function, } @@ -171,7 +171,7 @@ class Onboarding extends PureComponent { settings, updateGenuineCheck, openModal, - isLedgerNano, + deviceModelId, flowType, prevStep, nextStep, diff --git a/src/components/Onboarding/steps/Analytics.js b/src/components/Onboarding/steps/Analytics.js index 6e1a01d0..ca032261 100644 --- a/src/components/Onboarding/steps/Analytics.js +++ b/src/components/Onboarding/steps/Analytics.js @@ -3,6 +3,8 @@ import React, { PureComponent } from 'react' import styled from 'styled-components' import { connect } from 'react-redux' +import { getDeviceModel } from '@ledgerhq/devices' + import { saveSettings } from 'actions/settings' import Box from 'components/base/Box' import Switch from 'components/base/Switch' @@ -67,13 +69,15 @@ class Analytics extends PureComponent { const { nextStep, t, onboarding } = this.props const { analyticsToggle, sentryLogsToggle } = this.state + const model = getDeviceModel(onboarding.deviceModelId) + return ( {t('onboarding.analytics.title')} diff --git a/src/components/Onboarding/steps/Finish.js b/src/components/Onboarding/steps/Finish.js index cde25867..925317cf 100644 --- a/src/components/Onboarding/steps/Finish.js +++ b/src/components/Onboarding/steps/Finish.js @@ -3,6 +3,8 @@ import React, { Component } from 'react' import { openURL } from 'helpers/linking' import styled from 'styled-components' +import { getDeviceModel } from '@ledgerhq/devices' + import { i } from 'helpers/staticPath' import { urls } from 'config/urls' @@ -66,13 +68,16 @@ export default class Finish extends Component { render() { const { finish, t, onboarding } = this.props const { emit } = this.state + + const model = getDeviceModel(onboarding.deviceModelId) + return ( diff --git a/src/components/Onboarding/steps/GenuineCheck/GenuineCheckErrorPage.js b/src/components/Onboarding/steps/GenuineCheck/GenuineCheckErrorPage.js index 65518f46..9afa0f64 100644 --- a/src/components/Onboarding/steps/GenuineCheck/GenuineCheckErrorPage.js +++ b/src/components/Onboarding/steps/GenuineCheck/GenuineCheckErrorPage.js @@ -11,9 +11,21 @@ import Box from 'components/base/Box' import Button from 'components/base/Button' import ExternalLinkButton from 'components/base/ExternalLinkButton' import TrackPage from 'analytics/TrackPage' +import { getDeviceModel } from '@ledgerhq/devices' import { Title, Description, OnboardingFooterWrapper } from '../../helperComponents' +const Img = ({ type }: { type: string }) => { + switch (type) { + case 'blue': + return + case 'nanoX': + return + default: + return + } +} + type Props = { t: T, redoGenuineCheck: () => void, @@ -23,12 +35,15 @@ type Props = { class GenuineCheckErrorPage extends PureComponent { trackErrorPage = (page: string) => { const { onboarding } = this.props + + const model = getDeviceModel(onboarding.deviceModelId) + return ( ) } @@ -59,11 +74,7 @@ class GenuineCheckErrorPage extends PureComponent { )} - {onboarding.isLedgerNano ? ( - - ) : ( - - )} + ) diff --git a/src/components/Onboarding/steps/GenuineCheck/GenuineCheckUnavailable.js b/src/components/Onboarding/steps/GenuineCheck/GenuineCheckUnavailable.js index 727d59f3..0af9fe65 100644 --- a/src/components/Onboarding/steps/GenuineCheck/GenuineCheckUnavailable.js +++ b/src/components/Onboarding/steps/GenuineCheck/GenuineCheckUnavailable.js @@ -1,8 +1,9 @@ // @flow import React from 'react' -import { colors } from 'styles/theme' +import { getDeviceModel } from '@ledgerhq/devices' +import { colors } from 'styles/theme' import type { T } from 'types/common' import type { OnboardingState } from 'reducers/onboarding' @@ -54,6 +55,8 @@ export function GenuineCheckUnavailableMessage({ t: T, onboarding: OnboardingState, }) { + const model = getDeviceModel(onboarding.deviceModelId) + return ( diff --git a/src/components/Onboarding/steps/GenuineCheck/index.js b/src/components/Onboarding/steps/GenuineCheck/index.js index b8ab0de8..c1754965 100644 --- a/src/components/Onboarding/steps/GenuineCheck/index.js +++ b/src/components/Onboarding/steps/GenuineCheck/index.js @@ -3,8 +3,9 @@ import React, { PureComponent } from 'react' import { connect } from 'react-redux' import styled from 'styled-components' -import { colors } from 'styles/theme' +import { getDeviceModel } from '@ledgerhq/devices' +import { colors } from 'styles/theme' import { updateGenuineCheck } from 'reducers/onboarding' import Box from 'components/base/Box' @@ -165,13 +166,15 @@ class GenuineCheck extends PureComponent { return this.renderGenuineFail() } + const model = getDeviceModel(onboarding.deviceModelId) + return ( {t('onboarding.genuineCheck.title')} diff --git a/src/components/Onboarding/steps/SelectDevice.js b/src/components/Onboarding/steps/SelectDevice.js index c65e253b..c34b84ea 100644 --- a/src/components/Onboarding/steps/SelectDevice.js +++ b/src/components/Onboarding/steps/SelectDevice.js @@ -3,11 +3,14 @@ import React, { PureComponent } from 'react' import styled from 'styled-components' import { connect } from 'react-redux' +import { getDeviceModel } from '@ledgerhq/devices' + import { i } from 'helpers/staticPath' import { rgba } from 'styles/helpers' -import { isLedgerNano } from 'reducers/onboarding' +import { deviceModelId } from 'reducers/onboarding' +import type { DeviceModelId } from 'reducers/onboarding' import Box from 'components/base/Box' import TrackPage from 'analytics/TrackPage' @@ -19,11 +22,11 @@ import OnboardingFooter from '../OnboardingFooter' import type { StepProps } from '..' -const mapDispatchToProps = { isLedgerNano } +const mapDispatchToProps = { deviceModelId } class SelectDevice extends PureComponent { - handleIsLedgerNano = (isLedgerNano: boolean) => { - this.props.isLedgerNano(isLedgerNano) + handleDeviceModelId = (deviceModelId: DeviceModelId) => { + this.props.deviceModelId(deviceModelId) } handleContinue = () => { @@ -46,24 +49,34 @@ class SelectDevice extends PureComponent { this.handleIsLedgerNano(true)} + isActive={onboarding.deviceModelId === 'nanoX'} + onClick={() => this.handleDeviceModelId('nanoX')} + > + {onboarding.deviceModelId === 'nanoX' && } + + + + {getDeviceModel('nanoX').productName} + + this.handleDeviceModelId('nanoS')} > - {onboarding.isLedgerNano && } + {onboarding.deviceModelId === 'nanoS' && } - + - {t('onboarding.selectDevice.ledgerNanoCard.title')} + {getDeviceModel('nanoS').productName} this.handleIsLedgerNano(false)} + isActive={onboarding.deviceModelId === 'blue'} + onClick={() => this.handleDeviceModelId('blue')} > - {!onboarding.isLedgerNano && onboarding.isLedgerNano !== null && } + {onboarding.deviceModelId === 'blue' && } - {t('onboarding.selectDevice.ledgerBlueCard.title')} + {getDeviceModel('blue').productName} @@ -73,7 +86,7 @@ class SelectDevice extends PureComponent { t={t} nextStep={this.handleContinue} prevStep={() => jumpStep('init')} - isContinueDisabled={onboarding.isLedgerNano === null} + isContinueDisabled={onboarding.deviceModelId === ''} /> ) diff --git a/src/components/Onboarding/steps/SelectPIN/SelectPINRestoreNanoX.js b/src/components/Onboarding/steps/SelectPIN/SelectPINRestoreNanoX.js new file mode 100644 index 00000000..997bb4ae --- /dev/null +++ b/src/components/Onboarding/steps/SelectPIN/SelectPINRestoreNanoX.js @@ -0,0 +1,82 @@ +// @flow +import React, { PureComponent } from 'react' +import { translate } from 'react-i18next' +import { colors } from 'styles/theme' +import { i } from 'helpers/staticPath' + +import Box from 'components/base/Box' + +import type { T } from 'types/common' + +import IconChevronRight from 'icons/ChevronRight' + +import { IconOptionRow, DisclaimerBox, OptionRow, Inner } from '../../helperComponents' + +type Props = { + t: T, +} + +class SelectPINrestoreNanoX extends PureComponent { + render() { + const { t } = this.props + + const stepsLedgerNano = [ + { + key: 'step1', + icon: {'1.'}, + desc: t('onboarding.selectPIN.restore.instructions.nanoX.step1'), + }, + { + key: 'step2', + icon: {'2.'}, + desc: t('onboarding.selectPIN.restore.instructions.nanoX.step2'), + }, + { + key: 'step3', + icon: {'3.'}, + desc: t('onboarding.selectPIN.restore.instructions.nanoX.step3'), + }, + { + key: 'step4', + icon: {'4.'}, + desc: t('onboarding.selectPIN.restore.instructions.nanoX.step4'), + }, + { + key: 'step5', + icon: {'5.'}, + desc: t('onboarding.selectPIN.restore.instructions.nanoX.step5'), + }, + ] + const disclaimerNotes = [ + { + key: 'note1', + icon: , + desc: t('onboarding.selectPIN.disclaimer.note1'), + }, + { + key: 'note2', + icon: , + desc: t('onboarding.selectPIN.disclaimer.note2'), + }, + { + key: 'note3', + icon: , + desc: t('onboarding.selectPIN.disclaimer.note3'), + }, + ] + + return ( + + + + + {stepsLedgerNano.map(step => )} + + + + + ) + } +} + +export default translate()(SelectPINrestoreNanoX) diff --git a/src/components/Onboarding/steps/SelectPIN/SelectPINnanoX.js b/src/components/Onboarding/steps/SelectPIN/SelectPINnanoX.js new file mode 100644 index 00000000..600ef06d --- /dev/null +++ b/src/components/Onboarding/steps/SelectPIN/SelectPINnanoX.js @@ -0,0 +1,81 @@ +// @flow +import React, { PureComponent } from 'react' +import { translate, Trans } from 'react-i18next' +import { colors } from 'styles/theme' +import { i } from 'helpers/staticPath' + +import Box from 'components/base/Box' + +import type { T } from 'types/common' + +import IconChevronRight from 'icons/ChevronRight' + +import { IconOptionRow, DisclaimerBox, OptionRow, Inner } from '../../helperComponents' + +type Props = { + t: T, +} + +class SelectPINnanoX extends PureComponent { + render() { + const { t } = this.props + + const stepsLedgerNano = [ + { + key: 'step1', + icon: {'1.'}, + desc: t('onboarding.selectPIN.initialize.instructions.nanoX.step1'), + }, + { + key: 'step2', + icon: {'2.'}, + desc: ( + + + + ), + }, + { + key: 'step3', + icon: {'3.'}, + desc: t('onboarding.selectPIN.initialize.instructions.nanoX.step3'), + }, + { + key: 'step4', + icon: {'4.'}, + desc: t('onboarding.selectPIN.initialize.instructions.nanoX.step4'), + }, + ] + const disclaimerNotes = [ + { + key: 'note1', + icon: , + desc: t('onboarding.selectPIN.disclaimer.note1'), + }, + { + key: 'note2', + icon: , + desc: t('onboarding.selectPIN.disclaimer.note2'), + }, + { + key: 'note3', + icon: , + desc: t('onboarding.selectPIN.disclaimer.note3'), + }, + ] + + return ( + + + + + {stepsLedgerNano.map(step => )} + + + + + ) + } +} + +export default translate()(SelectPINnanoX) diff --git a/src/components/Onboarding/steps/SelectPIN/index.js b/src/components/Onboarding/steps/SelectPIN/index.js index 38370eb2..1ad65fad 100644 --- a/src/components/Onboarding/steps/SelectPIN/index.js +++ b/src/components/Onboarding/steps/SelectPIN/index.js @@ -1,23 +1,39 @@ // @flow import React from 'react' +import { getDeviceModel } from '@ledgerhq/devices' import Box from 'components/base/Box' import TrackPage from 'analytics/TrackPage' +import type { DeviceModelId } from 'reducers/onboarding' import GrowScroll from 'components/base/GrowScroll' import { Title, FixedTopContainer } from '../../helperComponents' import OnboardingFooter from '../../OnboardingFooter' import SelectPINnano from './SelectPINnano' import SelectPINblue from './SelectPINblue' +import SelectPINnanoX from './SelectPINnanoX' import SelectPINrestoreNano from './SelectPINrestoreNano' +import SelectPINRestoreNanoX from './SelectPINRestoreNanoX' import SelectPINrestoreBlue from './SelectPINrestoreBlue' - import type { StepProps } from '../..' +const SelectPin = ({ modelId, restore = false }: { modelId: DeviceModelId, restore?: boolean }) => { + switch (modelId) { + case 'nanoX': + return restore ? : + case 'blue': + return restore ? : + default: + return restore ? : + } +} + export default (props: StepProps) => { const { nextStep, prevStep, t, onboarding } = props + const model = getDeviceModel(onboarding.deviceModelId) + return ( @@ -25,20 +41,20 @@ export default (props: StepProps) => { category="Onboarding" name="Choose PIN" flowType={onboarding.flowType} - deviceType={onboarding.isLedgerNano ? 'Nano S' : 'Blue'} + deviceType={model.productName} /> {onboarding.flowType === 'restoreDevice' ? ( {t('onboarding.selectPIN.restore.title')} - {onboarding.isLedgerNano ? : } + ) : ( {t('onboarding.selectPIN.initialize.title')} - {onboarding.isLedgerNano ? : } + )} diff --git a/src/components/Onboarding/steps/SetPassword.js b/src/components/Onboarding/steps/SetPassword.js index 06efaca9..c356ad11 100644 --- a/src/components/Onboarding/steps/SetPassword.js +++ b/src/components/Onboarding/steps/SetPassword.js @@ -2,6 +2,8 @@ import React, { PureComponent, Fragment } from 'react' import { connect } from 'react-redux' +import { getDeviceModel } from '@ledgerhq/devices' + import { colors } from 'styles/theme' import db from 'helpers/db' @@ -105,7 +107,7 @@ class SetPassword extends PureComponent { category="Onboarding" name="Set Password" flowType={onboarding.flowType} - deviceType={onboarding.isLedgerNano ? 'Nano S' : 'Blue'} + deviceType={getDeviceModel(onboarding.deviceModelId).productName} /> diff --git a/src/components/Onboarding/steps/WriteSeed/WriteSeedRestore.js b/src/components/Onboarding/steps/WriteSeed/WriteSeedRestore.js index a193353e..bbe8063f 100644 --- a/src/components/Onboarding/steps/WriteSeed/WriteSeedRestore.js +++ b/src/components/Onboarding/steps/WriteSeed/WriteSeedRestore.js @@ -123,7 +123,7 @@ class WriteSeedRestore extends PureComponent { - {onboarding.isLedgerNano ? ( + {onboarding.deviceModelId === 'nanoS' ? ( {stepsNano.map(step => )} diff --git a/src/components/Onboarding/steps/WriteSeed/index.js b/src/components/Onboarding/steps/WriteSeed/index.js index bcae0463..24bb5c7a 100644 --- a/src/components/Onboarding/steps/WriteSeed/index.js +++ b/src/components/Onboarding/steps/WriteSeed/index.js @@ -1,7 +1,7 @@ // @flow import React from 'react' - +import { getDeviceModel } from '@ledgerhq/devices' import Box from 'components/base/Box' import TrackPage from 'analytics/TrackPage' @@ -17,6 +17,8 @@ import type { StepProps } from '../..' export default (props: StepProps) => { const { nextStep, prevStep, t, onboarding } = props + const model = getDeviceModel(onboarding.deviceModelId) + return ( @@ -24,12 +26,12 @@ export default (props: StepProps) => { category="Onboarding" name="Recovery Phase" flowType={onboarding.flowType} - deviceType={onboarding.isLedgerNano ? 'Nano S' : 'Blue'} + deviceType={model.productName} /> {onboarding.flowType === 'restoreDevice' ? ( - ) : onboarding.isLedgerNano ? ( + ) : onboarding.deviceModelId === 'nanoS' ? ( ) : ( diff --git a/src/components/ExchangePage/ExchangeCard.js b/src/components/PartnersPage/PartnerCard.js similarity index 67% rename from src/components/ExchangePage/ExchangeCard.js rename to src/components/PartnersPage/PartnerCard.js index 5b8366d4..57c12c9d 100644 --- a/src/components/ExchangePage/ExchangeCard.js +++ b/src/components/PartnersPage/PartnerCard.js @@ -11,29 +11,30 @@ import { FakeLink } from 'components/base/Link' type CardType = { id: string, - logo: any, + Logo: any, url: string, } -export default class ExchangeCard extends PureComponent<{ t: T, card: CardType }> { +export default class PartnerCard extends PureComponent<{ t: T, card: CardType }> { onClick = () => { const { card } = this.props - openURL(card.url, 'VisitExchange', { id: card.id }) + openURL(card.url, 'VisitPartner', { id: card.id }) } + render() { const { - card: { logo, id }, + card: { Logo, id }, t, } = this.props return ( - - {logo} + + - {t(`exchange.${id}`)} + {t(`partners.${id}`)} - {t('exchange.visitWebsite')} + {t('partners.visitWebsite')} diff --git a/src/components/PartnersPage/index.js b/src/components/PartnersPage/index.js new file mode 100644 index 00000000..7b5a0460 --- /dev/null +++ b/src/components/PartnersPage/index.js @@ -0,0 +1,33 @@ +// @flow + +import React, { PureComponent } from 'react' +import { T, translate } from 'react-i18next' + +import partners from '@ledgerhq/live-common/lib/partners/react' +import TrackPage from 'analytics/TrackPage' +import Box from 'components/base/Box' +import PartnerCard from './PartnerCard' + +type Props = { + t: T, +} + +class PartnersPage extends PureComponent { + render() { + const { t } = this.props + return ( + + + + {t('partners.title')} + + + {t('partners.desc')} + + {partners.map(card => )} + + ) + } +} + +export default translate()(PartnersPage) diff --git a/src/components/SettingsPage/CleanButton.js b/src/components/SettingsPage/CleanButton.js index 8a3081ea..f1954aa4 100644 --- a/src/components/SettingsPage/CleanButton.js +++ b/src/components/SettingsPage/CleanButton.js @@ -6,6 +6,7 @@ import { translate } from 'react-i18next' import logger from 'logger' import type { T } from 'types/common' import { cleanAccountsCache } from 'actions/accounts' +import SyncSkipUnderPriority from 'components/SyncSkipUnderPriority' import Button from 'components/base/Button' import ConfirmModal from 'components/base/Modal/ConfirmModal' import { softReset } from 'helpers/reset' @@ -69,7 +70,9 @@ class CleanButton extends PureComponent { title={t('settings.softResetModal.title')} subTitle={t('common.areYouSure')} desc={t('settings.softResetModal.desc')} - /> + > + + diff --git a/src/components/SettingsPage/ResetButton.js b/src/components/SettingsPage/ResetButton.js index 2708c75e..daa282b2 100644 --- a/src/components/SettingsPage/ResetButton.js +++ b/src/components/SettingsPage/ResetButton.js @@ -7,6 +7,7 @@ import { translate } from 'react-i18next' import logger from 'logger' import type { T } from 'types/common' import { hardReset } from 'helpers/reset' +import SyncSkipUnderPriority from 'components/SyncSkipUnderPriority' import Box from 'components/base/Box' import Button from 'components/base/Button' import ConfirmModal from 'components/base/Modal/ConfirmModal' @@ -73,7 +74,9 @@ class ResetButton extends PureComponent { )} - /> + > + + diff --git a/src/components/base/Modal/ConfirmModal.js b/src/components/base/Modal/ConfirmModal.js index 416eb5a7..a2ff1e83 100644 --- a/src/components/base/Modal/ConfirmModal.js +++ b/src/components/base/Modal/ConfirmModal.js @@ -29,6 +29,7 @@ type Props = { analyticsName: string, cancellable?: boolean, centered?: boolean, + children?: *, } class ConfirmModal extends PureComponent { @@ -50,6 +51,7 @@ class ConfirmModal extends PureComponent { t, analyticsName, centered, + children, ...props } = this.props @@ -91,6 +93,7 @@ class ConfirmModal extends PureComponent { {desc} + {children} )} /> diff --git a/src/components/layout/Default.js b/src/components/layout/Default.js index 14156b39..8926fa12 100644 --- a/src/components/layout/Default.js +++ b/src/components/layout/Default.js @@ -19,7 +19,7 @@ import Idler from 'components/Idler' import AccountPage from 'components/AccountPage' import DashboardPage from 'components/DashboardPage' import ManagerPage from 'components/ManagerPage' -import ExchangePage from 'components/ExchangePage' +import PartnersPage from 'components/PartnersPage' import DevToolsPage from 'components/DevToolsPage' import SettingsPage from 'components/SettingsPage' import KeyboardContent from 'components/KeyboardContent' @@ -117,7 +117,7 @@ class Default extends Component { - + diff --git a/src/config/urls.js b/src/config/urls.js index 4802926b..0094262d 100644 --- a/src/config/urls.js +++ b/src/config/urls.js @@ -4,7 +4,7 @@ export const urls = { liveHome: 'https://www.ledger.com/pages/ledger-live', // Social - twitter: 'https://twitter.com/LedgerHQ', + twitter: 'https://twitter.com/Ledger', github: 'https://github.com/LedgerHQ/ledger-live-desktop', reddit: 'https://www.reddit.com/r/ledgerwallet/', diff --git a/src/helpers/reset.js b/src/helpers/reset.js index 28439120..7a8e51ac 100644 --- a/src/helpers/reset.js +++ b/src/helpers/reset.js @@ -1,23 +1,21 @@ // @flow -import fs from 'fs' import { shell, remote } from 'electron' -import path from 'path' -import rimraf from 'rimraf' import resolveUserDataDirectory from 'helpers/resolveUserDataDirectory' import { disable as disableDBMiddleware } from 'middlewares/db' import db from 'helpers/db' import { delay } from 'helpers/promise' import killInternalProcess from 'commands/killInternalProcess' -import { DBNotReset } from '@ledgerhq/errors' +import libcoreReset from 'commands/libcoreReset' -async function resetLibcoreDatabase() { - await killInternalProcess.send().toPromise() - const dbpath = path.resolve(resolveUserDataDirectory(), 'sqlite/') - rimraf.sync(dbpath, { glob: false }) - if (fs.existsSync(dbpath)) { - throw new DBNotReset() - } +async function resetLibcore() { + // we need to stop everything that is happening right now, like syncs + await killInternalProcess + .send() + .toPromise() + .catch(() => {}) // this is a normal error due to the crash of the process, we ignore it + // we can now ask libcore to reset itself + await libcoreReset.send().toPromise() } function reload() { @@ -30,7 +28,7 @@ export async function hardReset() { disableDBMiddleware() db.resetAll() await delay(500) - await resetLibcoreDatabase() + await resetLibcore() reload() } @@ -38,7 +36,7 @@ export async function softReset({ cleanAccountsCache }: *) { cleanAccountsCache() await delay(500) await db.cleanCache() - await resetLibcoreDatabase() + await resetLibcore() reload() } diff --git a/src/icons/device/NanoX.js b/src/icons/device/NanoX.js index f72d05a4..e40572f2 100644 --- a/src/icons/device/NanoX.js +++ b/src/icons/device/NanoX.js @@ -2,29 +2,15 @@ import React from 'react' -export default ({ size = 30, ...p }: { size: number }) => ( - - - - - - - - - - - - - - - - +const path = ( + +) + +export default ({ size, ...p }: { size: number }) => ( + + {path} ) diff --git a/src/icons/device/NanoXBanner.js b/src/icons/device/NanoXBanner.js new file mode 100644 index 00000000..f72d05a4 --- /dev/null +++ b/src/icons/device/NanoXBanner.js @@ -0,0 +1,30 @@ +// @flow + +import React from 'react' + +export default ({ size = 30, ...p }: { size: number }) => ( + + + + + + + + + + + + + + + + + +) diff --git a/src/icons/device/index.js b/src/icons/device/index.js index f5282739..acfd1b3d 100644 --- a/src/icons/device/index.js +++ b/src/icons/device/index.js @@ -1,3 +1,4 @@ export Blue from './Blue' export NanoS from './NanoS' export NanoX from './NanoX' +export NanoXBanner from './NanoXBanner' diff --git a/src/reducers/onboarding.js b/src/reducers/onboarding.js index 14911cd5..3e4cea75 100644 --- a/src/reducers/onboarding.js +++ b/src/reducers/onboarding.js @@ -15,6 +15,8 @@ type Step = { }, } +export type DeviceModelId = 'nanoX' | 'nanoS' | 'blue' | '' + export type OnboardingState = { stepIndex: number, stepName: string, // TODO: specify that the string comes from Steps type @@ -27,7 +29,7 @@ export type OnboardingState = { genuineCheckUnavailable: ?Error, displayErrorScreen: boolean, }, - isLedgerNano: boolean | null, + deviceModelId: DeviceModelId, flowType: string, onboardingRelaunched?: boolean, } @@ -43,7 +45,7 @@ const initialState: OnboardingState = { genuineCheckUnavailable: null, displayErrorScreen: false, }, - isLedgerNano: null, + deviceModelId: '', flowType: '', onboardingRelaunched: false, steps: [ @@ -167,9 +169,9 @@ const handlers = { ...state, flowType, }), - ONBOARDING_SET_DEVICE_TYPE: (state: OnboardingState, { payload: isLedgerNano }) => ({ + ONBOARDING_SET_DEVICE_TYPE: (state: OnboardingState, { payload: deviceModelId }) => ({ ...state, - isLedgerNano, + deviceModelId, }), ONBOARDING_RELAUNCH: (state: OnboardingState, { payload: onboardingRelaunched }) => ({ ...initialState, @@ -187,5 +189,5 @@ export const nextStep = createAction('ONBOARDING_NEXT_STEP') export const prevStep = createAction('ONBOARDING_PREV_STEP') export const jumpStep = createAction('ONBOARDING_JUMP_STEP') export const updateGenuineCheck = createAction('UPDATE_GENUINE_CHECK') -export const isLedgerNano = createAction('ONBOARDING_SET_DEVICE_TYPE') +export const deviceModelId = createAction('ONBOARDING_SET_DEVICE_TYPE') export const flowType = createAction('ONBOARDING_SET_FLOW_TYPE') diff --git a/src/reducers/settings.js b/src/reducers/settings.js index 85141467..30ff551f 100644 --- a/src/reducers/settings.js +++ b/src/reducers/settings.js @@ -244,10 +244,12 @@ export const exportSettingsSelector = createSelector( counterValueCurrencySelector, counterValueExchangeSelector, state => state.settings.currenciesSettings, - (counterValueCurrency, counterValueExchange, currenciesSettings) => ({ + developerModeSelector, + (counterValueCurrency, counterValueExchange, currenciesSettings, developerModeEnabled) => ({ counterValue: counterValueCurrency.ticker, counterValueExchange, currenciesSettings, + developerModeEnabled, }), ) diff --git a/src/renderer/events.js b/src/renderer/events.js index f233913c..0ef0920c 100644 --- a/src/renderer/events.js +++ b/src/renderer/events.js @@ -51,14 +51,19 @@ export default ({ store }: { store: Object }) => { function syncDevices() { syncDeviceSub = listenDevices.send().subscribe( - ({ device, type }) => { + ({ device, deviceModel, type }) => { if (device) { + const stateDevice = { + path: device.path, + modelId: deviceModel ? deviceModel.id : 'nanoS', + type: 'hid', + } if (type === 'add') { d.device('Device - add') - store.dispatch(addDevice(device)) + store.dispatch(addDevice(stateDevice)) } else if (type === 'remove') { d.device('Device - remove') - store.dispatch(removeDevice(device)) + store.dispatch(removeDevice(stateDevice)) } } }, diff --git a/src/types/common.js b/src/types/common.js index 84ccc162..c219b115 100644 --- a/src/types/common.js +++ b/src/types/common.js @@ -1,11 +1,8 @@ // @flow export type Device = { - manufacturer: string, path: string, - product: string, - productId: string, - vendorId: string, + modelId: string, } // -------------------- Settings diff --git a/static/i18n/en/app.json b/static/i18n/en/app.json index f9f194cd..bdb8bfbf 100644 --- a/static/i18n/en/app.json +++ b/static/i18n/en/app.json @@ -146,10 +146,10 @@ "messageIfSkipped": "Your {{currencyName}} address has not been confirmed on your Ledger device. Please verify it for optimal security." }, "deviceConnect": { - "dashboard": "Navigate to the <1><0>{{managerAppName}} on your device", + "dashboard": "Navigate to the <1>Dashboard on your device", "step1": "Connect and unlock your <1>Ledger device", "step2": "Navigate to the <1><0>{{managerAppName}} app on your device", - "step3": "Allow <1><0>Ledger Manager on your device" + "step3": "Allow <1>Ledger Manager on your device" }, "emptyState": { "sidebar": { @@ -164,23 +164,27 @@ } } }, - "exchange": { + "partners": { "title": "Buy crypto", "desc": "Try a few services we've selected", "visitWebsite": "Visit website", - "coinhouse": "Coinhouse is a trusted platform for individuals and institutional investors looking to analyze, acquire, sell and securely store crypto assets.", + + "btcdirect": "Buy and sell with the greatest of ease at Europe's leading cryptocurrency broker. Sign up for free and receive your coins within minutes! Our users rate us with an 8,7 on Trustpilot", "changelly": "Changelly is a popular instant crypto asset exchange with 100+ coins and tokens listed.", + "changenow": "ChangeNOW is one of the leading custody-free instant exchange services", + "coinberry": "Coinberry is the most trusted crypto platform in Canada with better-than-bank security, best-in-class user interface and No Fee funding & withdraw. Pre-register to get early access to our platform if you are not a Canadian citizen.", + "coinhouse": "Coinhouse is a trusted platform for individuals and institutional investors looking to analyze, acquire, sell and securely store crypto assets.", "coinmama": "Coinmama is a financial service that makes it fast, safe and fun to buy digital assets, anywhere in the world.", - "simplex": "Simplex is a EU licensed financial institution, providing a fraudless credit card payment solution.", - "paybis": "it is safe and easy to Buy Bitcoin with credit card from PayBis. Service operates in US, Canada, Germany, Russia and Saudi Arabia.", + "genesis": "Genesis is an institutional trading firm offering liquidity and borrow for digital currencies, including bitcoin, bitcoin cash, ethereum, ethereum classic, litecoin, and XRP.", + "kyberswap": "Fast, simple and secure token swap platform. Powered by Kyber Network's on-chain liquidity protocol.", "luno": "Luno makes it safe and easy to buy, store and learn about cryptocurrencies like Bitcoin and Ethereum", + "paybis": "it is safe and easy to Buy Bitcoin with credit card from PayBis. Service operates in US, Canada, Germany, Russia and Saudi Arabia.", "shapeshift": "ShapeShift is an online marketplace where users can buy and sell digital assets. It is a fast and secure way for the world to buy and sell digital assets, with no lengthy signup process, no counterparty risk, and no friction.", - "genesis": "Genesis is an institutional trading firm offering liquidity and borrow for digital currencies, including bitcoin, bitcoin cash, ethereum, ethereum classic, litecoin, and XRP.", - "kyberSwap": "Fast, simple and secure token swap platform. Powered by Kyber Network's on-chain liquidity protocol.", - "thorSwap": "ThorSwap is an instant, safe and fair crypto asset market powered by decentralized atomic swap technologies. It is the most convenient way to start trading, no registration or KYC for small amount transactions.", - "changeNow": "ChangeNOW is one of the leading custody-free instant exchange services", - "coinberry": "Coinberry is the most trusted crypto platform in Canada with better-than-bank security, best-in-class user interface and No Fee funding & withdraw. Pre-register to get early access to our platform if you are not a Canadian citizen.", - "btcDirect": "Buy and sell with the greatest of ease at Europe's leading cryptocurrency broker. Sign up for free and receive your coins within minutes! Our users rate us with an 8,7 on Trustpilot" + "simplex": "Simplex is a EU licensed financial institution, providing a fraudless credit card payment solution.", + "taxtoken": "Automated cryptocurrency tax and cost basis software for US citizens.", + "thorswap": "ThorSwap is an instant, safe and fair crypto asset market powered by decentralized atomic swap technologies. It is the most convenient way to start trading, no registration or KYC for small amount transactions.", + "bitpanda": "Bitpanda is Europe’s leading retail broker for buying and selling Bitcoin, Ethereum, IOTA and many more. Deposit money using your credit card, SOFORT-Transfer, NETELLER, and other options.", + "exmo": "Founded in 2013 and based in London, Kiev, Barcelona, and Moscow, EXMO is #1 exchange in Eastern Europe, and one of the world's largest global exchanges in volume and liquidity." }, "genuinecheck": { "modal": { @@ -255,8 +259,6 @@ }, "firmware": { "installed": "Firmware version {{version}}", - "titleNano": "Ledger Nano S", - "titleBlue": "Ledger Blue", "update": "Update", "latest": "Firmware version {{version}} is available", "disclaimerTitle": "You are about to install <1><0>firmware version {{version}}.", @@ -566,13 +568,7 @@ } }, "selectDevice": { - "title": "Select your device", - "ledgerNanoCard": { - "title": "Ledger Nano S" - }, - "ledgerBlueCard": { - "title": "Ledger Blue" - } + "title": "Select your device" }, "selectPIN": { "disclaimer": { @@ -589,6 +585,12 @@ "step3": "Press the left or right button to select a digit. Press both buttons to validate.", "step4": "Select ✓ to confirm your PIN code. Select ⬅ to erase a digit." }, + "nanoX": { + "step1": "Connect the Ledger Nano X to your computer.", + "step2": "Press both buttons to choose Set up as new device.", + "step3": "Press the left or right button to select a digit.", + "step4": "Select ✓ to confirm your PIN code. Select ⬅ to erase a digit." + }, "blue": { "step1": "Connect the Ledger Blue to your computer.", "step2": "Tap on <1><0>Configure as new device.", @@ -605,6 +607,13 @@ "step3": "Press the left or right button to select a digit. Press both buttons to validate.", "step4": "Select ✓ to confirm your PIN code. Select ⬅ to erase a digit." }, + "nanoX": { + "step1": "Connect your Ledger Nano X to your computer.", + "step2": "Press both buttons as instructed on your Ledger Nano X screen.", + "step3": "Press the left button to cancel Configure as new device.", + "step4": "Press the right button to select Restore configuration.", + "step5": "Choose a PIN code between 4 and 8 digits long." + }, "blue": { "step1": "Connect the Ledger Blue to your computer.", "step2": "Tap on <1><0>Restore configuration.", @@ -934,4 +943,4 @@ "description": "Please contact Ledger Support" } } -} +} \ No newline at end of file diff --git a/static/images/ledger-nano-onb.svg b/static/images/ledger-nano-s-onb.svg similarity index 100% rename from static/images/ledger-nano-onb.svg rename to static/images/ledger-nano-s-onb.svg diff --git a/static/images/ledger-nano-x-onb.svg b/static/images/ledger-nano-x-onb.svg new file mode 100644 index 00000000..1b9d4405 --- /dev/null +++ b/static/images/ledger-nano-x-onb.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/static/images/logos/connectDevice.png b/static/images/logos/connectDevice.png index 2ef15562..5a5121e3 100755 Binary files a/static/images/logos/connectDevice.png and b/static/images/logos/connectDevice.png differ diff --git a/static/images/logos/exchanges/change-now.png b/static/images/logos/exchanges/change-now.png deleted file mode 100644 index fb3f8a77..00000000 Binary files a/static/images/logos/exchanges/change-now.png and /dev/null differ diff --git a/static/images/logos/exchanges/genesis.svg b/static/images/logos/exchanges/genesis.svg deleted file mode 100644 index 6aa3ea56..00000000 --- a/static/images/logos/exchanges/genesis.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/static/images/logos/exchanges/kyber-swap.png b/static/images/logos/exchanges/kyber-swap.png deleted file mode 100644 index 45472e4f..00000000 Binary files a/static/images/logos/exchanges/kyber-swap.png and /dev/null differ diff --git a/static/images/logos/exchanges/luno.svg b/static/images/logos/exchanges/luno.svg deleted file mode 100644 index e2ee1781..00000000 --- a/static/images/logos/exchanges/luno.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/static/images/logos/exchanges/shapeshift.svg b/static/images/logos/exchanges/shapeshift.svg deleted file mode 100644 index 5efd76eb..00000000 --- a/static/images/logos/exchanges/shapeshift.svg +++ /dev/null @@ -1,93 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/static/images/logos/exchanges/thor-swap.png b/static/images/logos/exchanges/thor-swap.png deleted file mode 100644 index f3851d66..00000000 Binary files a/static/images/logos/exchanges/thor-swap.png and /dev/null differ diff --git a/static/images/nano-x-error-onb.svg b/static/images/nano-x-error-onb.svg new file mode 100644 index 00000000..341a227b --- /dev/null +++ b/static/images/nano-x-error-onb.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/select-pin-nano-x-onb.svg b/static/images/select-pin-nano-x-onb.svg new file mode 100644 index 00000000..7693c90e --- /dev/null +++ b/static/images/select-pin-nano-x-onb.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/yarn.lock b/yarn.lock index 5e1b203b..f76aae9a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1689,7 +1689,7 @@ resolved "https://registry.yarnpkg.com/@ledgerhq/errors/-/errors-4.39.0.tgz#10b9889f78df94ce36a4b34d9a3a45aac77be0e9" integrity sha512-kBr2rnoYDACRCxTLtEufE4oCvYj6vx2oFWVVjwskBxYsF5LC9R8Mbg5C4GgvDweiWW4Io8HA9p9jCsOfdCDygg== -"@ledgerhq/hw-app-btc@^4.35.0", "@ledgerhq/hw-app-btc@^4.39.0": +"@ledgerhq/hw-app-btc@^4.39.0": version "4.39.0" resolved "https://registry.yarnpkg.com/@ledgerhq/hw-app-btc/-/hw-app-btc-4.39.0.tgz#5b564e683a43a50002579834ec54aa790fde35f1" integrity sha512-xPOtoIgsErycMFTKHb0yHLqlKn0C+9msLBsA1zRPNsWMdxEEBO5pzFVmn5ha1j3q/73yeICHlcB4KZcTb7CShA== @@ -1697,14 +1697,14 @@ "@ledgerhq/hw-transport" "^4.39.0" create-hash "^1.1.3" -"@ledgerhq/hw-app-eth@^4.35.0", "@ledgerhq/hw-app-eth@^4.39.0": +"@ledgerhq/hw-app-eth@^4.39.0": version "4.39.0" resolved "https://registry.yarnpkg.com/@ledgerhq/hw-app-eth/-/hw-app-eth-4.39.0.tgz#3cbba1f1650665c4c29c7b9fa246cb2360495867" integrity sha512-IKPcLTcGohh/S6Z1LaAfn2pGyxfT6xu958/xV+5H4a3Ej0CWKaxcno4FkhaxH4OiViF0F5SEFzxtH+UntH2jdg== dependencies: "@ledgerhq/hw-transport" "^4.39.0" -"@ledgerhq/hw-app-xrp@^4.35.0", "@ledgerhq/hw-app-xrp@^4.39.0": +"@ledgerhq/hw-app-xrp@^4.39.0": version "4.39.0" resolved "https://registry.yarnpkg.com/@ledgerhq/hw-app-xrp/-/hw-app-xrp-4.39.0.tgz#cc399649f17873778e34bcde16f488faef3117e5" integrity sha512-lbrG7AhQdJzt/zhu0G5yfC2t4zlytWuzbNLrPp/VQKJJPUKsC98H81pmfMzn1lFBdm8frmBVUW6reN5p7wDS2Q== @@ -1712,17 +1712,19 @@ "@ledgerhq/hw-transport" "^4.39.0" bip32-path "0.4.2" -"@ledgerhq/hw-transport-node-hid@^4.35.0": - version "4.35.0" - resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport-node-hid/-/hw-transport-node-hid-4.35.0.tgz#0eba08e5edd14a8c779ebaf73ec21976ee5f112e" - integrity sha512-Otnymk9B7qCEfjych/SvTvJsMM+DqyoB0saEwL80ukjuGFqMunecrG5w8nC4aCc169IVz70Spkg2uU90TBUCuw== +"@ledgerhq/hw-transport-node-hid@^4.40.0": + version "4.40.0" + resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport-node-hid/-/hw-transport-node-hid-4.40.0.tgz#7f35194f94e20374e1bd6a04e5299032c5e81b6c" + integrity sha512-Nxp2Ys5lxgWTUG/A+W7O3nemkIBigW0LSJI6QrCDdLDg7deU+Zp6QA/CDS95BLijqi5m0AqKE2U4IyQh4OFnhQ== dependencies: - "@ledgerhq/hw-transport" "^4.35.0" + "@ledgerhq/devices" "^4.39.0" + "@ledgerhq/errors" "^4.39.0" + "@ledgerhq/hw-transport" "^4.39.0" lodash "^4.17.11" - node-hid "^0.7.2" - usb "^1.3.3" + node-hid "^0.7.6" + usb "^1.5.0" -"@ledgerhq/hw-transport@^4.21.0", "@ledgerhq/hw-transport@^4.35.0", "@ledgerhq/hw-transport@^4.39.0": +"@ledgerhq/hw-transport@^4.21.0", "@ledgerhq/hw-transport@^4.39.0": version "4.39.0" resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport/-/hw-transport-4.39.0.tgz#602c6ea3fef56d1df205274ea742b4cf85613f6c" integrity sha512-XkVAy2SFRDdE3qQGGVxB7RQdsdIx1fcoRNReU7NQXK59fYqxue+ZoiGtynEoHq9RKMg8EBG2kBXSVEh1iPdOlA== @@ -1731,10 +1733,10 @@ "@ledgerhq/errors" "^4.39.0" events "^3.0.0" -"@ledgerhq/ledger-core@2.0.0-rc.16": - version "2.0.0-rc.16" - resolved "https://registry.yarnpkg.com/@ledgerhq/ledger-core/-/ledger-core-2.0.0-rc.16.tgz#51f141c0143edb020e38855bf2e2619e3446e74f" - integrity sha512-gmbeXRBg4NSqzH6+EajYTzaQlwN5ugaN1nH0SI6BvRqMfcorxNRE8byfh3F2u+7TNchBW72vOZnKPDShaR9/pQ== +"@ledgerhq/ledger-core@2.0.0-rc.21": + version "2.0.0-rc.21" + resolved "https://registry.yarnpkg.com/@ledgerhq/ledger-core/-/ledger-core-2.0.0-rc.21.tgz#f9e48cf162150ef3d5089ac19e9effcef4626697" + integrity sha512-DqBY1D95wz3a56k8bx7e8YhTsVake/4ZBxH5RgUnXl4OQYRQNKyrtqt94yKvYi+JkRqtU2z60XgB5mo58jaq+w== dependencies: bindings "^1.3.0" nan "^2.6.2" @@ -4186,6 +4188,13 @@ bindings@^1.3.0: resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.3.1.tgz#21fc7c6d67c18516ec5aaa2815b145ff77b26ea5" integrity sha512-i47mqjF9UbjxJhxGf+pZ6kSxrnI3wBLlnGI2ArWJ4r0VrvDS7ZYXkprq/pLaBWYq4GM0r4zdHY+NNRqEMU7uew== +bindings@^1.3.1: + version "1.4.0" + resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.4.0.tgz#909efa49f2ebe07ecd3cb136778f665052040127" + integrity sha512-7znEVX22Djn+nYjxCWKDne0RRloa9XfYa84yk3s+HkE3LpDYZmhArYr9O9huBoHY3/oXispx5LorIX7Sl2CgSQ== + dependencies: + file-uri-to-path "1.0.0" + bip32-path@0.4.2: version "0.4.2" resolved "https://registry.yarnpkg.com/bip32-path/-/bip32-path-0.4.2.tgz#5db0416ad6822712f077836e2557b8697c0c7c99" @@ -7866,6 +7875,11 @@ file-loader@^1.1.11: loader-utils "^1.0.2" schema-utils "^0.4.5" +file-uri-to-path@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" + integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== + filename-regex@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26" @@ -11352,16 +11366,21 @@ mute-stream@0.0.7: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s= -nan@^2.10.0, nan@^2.8.0: - version "2.12.0" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.12.0.tgz#9d443fdb5e13a20770cc5e602eee59760a685885" - integrity sha512-zT5nC0JhbljmyEf+Z456nvm7iO7XgRV2hYxoBtPpnyp+0Q4aCoP6uWNn76v/I6k2kCYNLWqWbwBWQcjsNI/bjw== +nan@^2.12.1: + version "2.12.1" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.12.1.tgz#7b1aa193e9aa86057e3c7bbd0ac448e770925552" + integrity sha512-JY7V6lRkStKcKTvHO5NVSQRv+RV+FIL5pvDoLiAtSL9pKlC5x9PKQcZDsq7m4FO4d57mkhC6Z+QhAh3Jdk5JFw== nan@^2.2.1, nan@^2.6.2, nan@^2.9.2: version "2.10.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.10.0.tgz#96d0cd610ebd58d4b4de9cc0c6828cda99c7548f" integrity sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA== +nan@^2.8.0: + version "2.12.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.12.0.tgz#9d443fdb5e13a20770cc5e602eee59760a685885" + integrity sha512-zT5nC0JhbljmyEf+Z456nvm7iO7XgRV2hYxoBtPpnyp+0Q4aCoP6uWNn76v/I6k2kCYNLWqWbwBWQcjsNI/bjw== + nanomatch@^1.2.9: version "1.2.13" resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" @@ -11439,6 +11458,13 @@ node-abi@^2.2.0: dependencies: semver "^5.4.1" +node-abi@^2.7.0: + version "2.7.1" + resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-2.7.1.tgz#a8997ae91176a5fbaa455b194976e32683cda643" + integrity sha512-OV8Bq1OrPh6z+Y4dqwo05HqrRL9YNF7QVMRfq1/pguwKLG+q9UB/Lk0x5qXjO23JjJg+/jqCHSTaG1P3tfKfuw== + dependencies: + semver "^5.4.1" + node-dir@0.1.8: version "0.1.8" resolved "https://registry.yarnpkg.com/node-dir/-/node-dir-0.1.8.tgz#55fb8deb699070707fb67f91a460f0448294c77d" @@ -11487,14 +11513,14 @@ node-gyp@^3.6.0: tar "^2.0.0" which "1" -node-hid@^0.7.2: - version "0.7.4" - resolved "https://registry.yarnpkg.com/node-hid/-/node-hid-0.7.4.tgz#2db109acee654b56bf518ffb3fd92cf9cd0647c1" - integrity sha512-gvgNDPoszObn7avIDYMUvVv1T0xQB4/CZFJWckra/LXAc0qHYho4M1LCnCKlLIocL2R5/3qGv0J4AjRMdwgjxg== +node-hid@^0.7.6: + version "0.7.6" + resolved "https://registry.yarnpkg.com/node-hid/-/node-hid-0.7.6.tgz#61523694f1111d209fca55bb704732dad029f3bc" + integrity sha512-cjbjL1CmOpImKQrqkvQKZUWuzvofwECZQ50zoih5vtPRowIZ4TFJDTN3tJYfVhbQYM9PGqDFXLcTILjx9EPvzw== dependencies: - bindings "^1.3.0" - nan "^2.10.0" - prebuild-install "^5.2.1" + bindings "^1.3.1" + nan "^2.12.1" + prebuild-install "^5.2.2" node-int64@^0.4.0: version "0.4.0" @@ -12791,10 +12817,10 @@ prebuild-install@^2.0.0: tunnel-agent "^0.6.0" which-pm-runs "^1.0.0" -prebuild-install@^5.2.1: - version "5.2.2" - resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-5.2.2.tgz#237888f21bfda441d0ee5f5612484390bccd4046" - integrity sha512-4e8VJnP3zJdZv/uP0eNWmr2r9urp4NECw7Mt1OSAi3rcLrbBRxGiAkfUFtre2MhQ5wfREAjRV+K1gubvs/GPsA== +prebuild-install@^5.2.2: + version "5.2.4" + resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-5.2.4.tgz#8cc41a217ef778a31d3a876fe6668d05406db750" + integrity sha512-CG3JnpTZXdmr92GW4zbcba4jkDha6uHraJ7hW4Fn8j0mExxwOKK20hqho8ZuBDCKYCHYIkFM1P2jhtG+KpP4fg== dependencies: detect-libc "^1.0.3" expand-template "^2.0.3" @@ -12802,7 +12828,7 @@ prebuild-install@^5.2.1: minimist "^1.2.0" mkdirp "^0.5.1" napi-build-utils "^1.0.1" - node-abi "^2.2.0" + node-abi "^2.7.0" noop-logger "^0.1.1" npmlog "^4.0.1" os-homedir "^1.0.1" @@ -16226,7 +16252,7 @@ url@^0.11.0, url@~0.11.0: punycode "1.3.2" querystring "0.2.0" -usb@^1.3.3: +usb@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/usb/-/usb-1.5.0.tgz#3e07b23c6dacf06a7c8801ae913926702a818218" integrity sha512-/0stiQEmweuO2BKv2avzQQ8ypDUjo4Osz5sSEi+d0F4Rc+ddX1xED3uf4Tkelc1eADlfn0JQZYHP0bI7CNDA0Q==