diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 1c3fc764..bd18f9d3 100755 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -21,3 +21,4 @@ about: Report a bug in Ledger Live Desktop or a regression. #### Steps to reproduce the behavior + diff --git a/.github/ISSUE_TEMPLATE/discussion.md b/.github/ISSUE_TEMPLATE/discussion.md deleted file mode 100755 index 7faafbfa..00000000 --- a/.github/ISSUE_TEMPLATE/discussion.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -name: 🗣 Start a Discussion -about: Discuss to propose changes or suggest feature requests. ---- diff --git a/.github/ISSUE_TEMPLATE/enhancement.md b/.github/ISSUE_TEMPLATE/enhancement.md new file mode 100755 index 00000000..6fe17a94 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/enhancement.md @@ -0,0 +1,18 @@ +--- +name: 🗣 Start a Discussion +about: Discuss to propose changes to improve the state of Ledger Live. +--- + +#### Ledger Live Version + + + +- Ledger Live **version_here** + +#### Part of the application to improve + + + +#### Description + + diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100755 index 00000000..946065e3 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,14 @@ +--- +name: ✨ Feature Request +about: Any feature you find missing in Ledger Live? Discuss to suggest feature requests. +--- + +- [ ] I have checked this feature was not yet requested. + +#### Part of the application + + + +#### Description + + diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md index e65fa6e6..ccee0e68 100755 --- a/.github/ISSUE_TEMPLATE/question.md +++ b/.github/ISSUE_TEMPLATE/question.md @@ -4,4 +4,4 @@ about: If you need help using the app, please contact the support at https://sup --- - + diff --git a/README.md b/README.md index 3be65565..26a3af09 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,17 @@ -# Ledger Live - Desktop +# Ledger Live (desktop) [![CircleCI](https://circleci.com/gh/LedgerHQ/ledger-live-desktop.svg?style=svg)](https://circleci.com/gh/LedgerHQ/ledger-live-desktop) [![Crowdin](https://d322cqt584bo4o.cloudfront.net/ledger-wallet/localized.svg)](https://crowdin.com/project/ledger-wallet) -[![CircleCI](https://circleci.com/gh/LedgerHQ/ledger-live-desktop.svg?style=svg)](https://circleci.com/gh/LedgerHQ/ledger-live-desktop) -[![Crowdin](https://d322cqt584bo4o.cloudfront.net/ledger-wallet/localized.svg)](https://crowdin.com/project/ledger-wallet) +> Ledger Live is a new generation wallet desktop application providing a unique interface to maintain multiple cryptocurrencies for your Ledger Nano S / Blue. Manage your device, create accounts, receive and send cryptoassets, [...and many more](https://www.ledgerwallet.com/#LINK_TO_ANNOUNCEMENT). -:warning: Disclaimer: this project is under active development. Use at your own risks. - - - -> Ledger Live Desktop is a new generation Ledger Wallet application build with React, Redux and Electron to run natively on the web. The main goal of the app is to provide our users with a single wallet for all crypto currencies supported by our devices. To learn more check out [Ledger](https://www.ledgerwallet.com/?utm_source=redirection&utm_medium=variable) +

+ +

## Architecture -From one side Ledger Desktop app connected to the Blockchain via the in-house written C++ library - LibCore and from the other it communicates to the Ledger Hardware Device to securely sign all transactions. +Ledger Live is an hybrid desktop application built with Electron, React, Redux, RxJS,.. and highly optimized with [ledger-core](https://github.com/LedgerHQ/lib-ledger-core) C++ library to deal with blockchains (sync, broadcast,..) via [ledger-core-node-bindings](https://github.com/LedgerHQ/lib-ledger-core-node-bindings). It communicates to Ledger hardware wallet devices (Nano S / Blue) to verify address and sign transactions with [ledgerjs](https://github.com/LedgerHQ/ledgerjs). Some logic is shared with [live-common](https://github.com/LedgerHQ/ledger-live-common).

- +

## Setup @@ -32,23 +29,15 @@ From one side Ledger Desktop app connected to the Blockchain via the in-house wr ## Install -1. Clone or fork the repo - -```bash -git clone git@github.com:LedgerHQ/ledger-live-desktop.git -``` - -2. Install dependencies - ```bash +# install dependencies yarn ``` ## Run -Launch the app - ```bash +# launch the app yarn start ``` diff --git a/static/docs/architecture.png b/docs/architecture.png similarity index 100% rename from static/docs/architecture.png rename to docs/architecture.png diff --git a/docs/screenshot.png b/docs/screenshot.png new file mode 100644 index 00000000..03d604e9 Binary files /dev/null and b/docs/screenshot.png differ diff --git a/package.json b/package.json index 6edff9ed..e8464aa6 100644 --- a/package.json +++ b/package.json @@ -40,8 +40,8 @@ "@ledgerhq/hw-app-xrp": "^4.13.0", "@ledgerhq/hw-transport": "^4.13.0", "@ledgerhq/hw-transport-node-hid": "^4.13.0", - "@ledgerhq/ledger-core": "2.0.0-rc.3", - "@ledgerhq/live-common": "2.31.0", + "@ledgerhq/ledger-core": "2.0.0-rc.4", + "@ledgerhq/live-common": "^2.32.0", "animated": "^0.2.2", "async": "^2.6.1", "axios": "^0.18.0", diff --git a/src/analytics/Track.js b/src/analytics/Track.js index 997fa783..1e2ad085 100644 --- a/src/analytics/Track.js +++ b/src/analytics/Track.js @@ -7,6 +7,7 @@ class Track extends PureComponent<{ onUnmount?: boolean, onUpdate?: boolean, event: string, + mandatory?: boolean, }> { componentDidMount() { if (typeof this.props.event !== 'string') { @@ -21,8 +22,8 @@ class Track extends PureComponent<{ if (this.props.onUnmount) this.track() } track = () => { - const { event, onMount, onUnmount, onUpdate, ...properties } = this.props - track(event, properties) + const { event, onMount, onUnmount, onUpdate, mandatory, ...properties } = this.props + track(event, properties, mandatory) } render() { return null diff --git a/src/analytics/segment.js b/src/analytics/segment.js index 9be99887..f028204f 100644 --- a/src/analytics/segment.js +++ b/src/analytics/segment.js @@ -72,9 +72,9 @@ export const stop = () => { analytics.reset() } -export const track = (event: string, properties: ?Object) => { +export const track = (event: string, properties: ?Object, mandatory: ?boolean) => { logger.analyticsTrack(event, properties) - if (!storeInstance || !shareAnalyticsSelector(storeInstance.getState())) { + if (!storeInstance || (!mandatory && !shareAnalyticsSelector(storeInstance.getState()))) { return } const { analytics } = window diff --git a/src/api/Ledger.js b/src/api/Ledger.js index f84d0f7c..f5c5e9a6 100644 --- a/src/api/Ledger.js +++ b/src/api/Ledger.js @@ -3,4 +3,4 @@ import type { Currency } from '@ledgerhq/live-common/lib/types' import { LEDGER_REST_API_BASE } from 'config/constants' export const blockchainBaseURL = ({ ledgerExplorerId }: Currency): ?string => - ledgerExplorerId ? `${LEDGER_REST_API_BASE}blockchain/v2/${ledgerExplorerId}` : null + ledgerExplorerId ? `${LEDGER_REST_API_BASE}/blockchain/v2/${ledgerExplorerId}` : null diff --git a/src/bridge/RippleJSBridge.js b/src/bridge/RippleJSBridge.js index a1664336..727c1dca 100644 --- a/src/bridge/RippleJSBridge.js +++ b/src/bridge/RippleJSBridge.js @@ -351,7 +351,12 @@ const RippleJSBridge: WalletBridge = { return unsubscribe }), - synchronize: ({ endpointConfig, freshAddress, blockHeight }) => + synchronize: ({ + endpointConfig, + freshAddress, + blockHeight, + operations: { length: currentOpsLength }, + }) => Observable.create(o => { let finished = false const unsubscribe = () => { @@ -394,7 +399,10 @@ const RippleJSBridge: WalletBridge = { o.next(a => ({ ...a, balance })) const transactions = await api.getTransactions(freshAddress, { - minLedgerVersion: Math.max(blockHeight, minLedgerVersion), + minLedgerVersion: Math.max( + currentOpsLength === 0 ? 0 : blockHeight, // if there is no ops, it might be after a clear and we prefer to pull from the oldest possible history + minLedgerVersion, + ), maxLedgerVersion, }) diff --git a/src/components/AccountPage/AccountHeaderActions.js b/src/components/AccountPage/AccountHeaderActions.js index eb60494b..c510c9cb 100644 --- a/src/components/AccountPage/AccountHeaderActions.js +++ b/src/components/AccountPage/AccountHeaderActions.js @@ -62,7 +62,7 @@ class AccountHeaderActions extends PureComponent { const { account, openModal, t } = this.props return ( - {account.operations.length > 0 && ( + {account.operations.length > 0 || account.balance > 0 ? ( - )} + ) : null} t('app:account.settings.title')}> openModal(MODAL_SETTINGS_ACCOUNT, { account })}> diff --git a/src/components/AccountPage/index.js b/src/components/AccountPage/index.js index e79620e9..19d19b47 100644 --- a/src/components/AccountPage/index.js +++ b/src/components/AccountPage/index.js @@ -82,7 +82,7 @@ class AccountPage extends PureComponent { - {account.operations.length > 0 ? ( + {account.operations.length > 0 || account.balance > 0 ? ( - {d.date.toISOString().substr(0, 10)} + {moment(d.date).format('LL')} ) diff --git a/src/components/SelectExchange.js b/src/components/SelectExchange.js index a0ebbce5..635c5a2c 100644 --- a/src/components/SelectExchange.js +++ b/src/components/SelectExchange.js @@ -37,12 +37,14 @@ class SelectExchange extends Component< prevFromTo: string, exchanges: ?(Exchange[]), error: ?Error, + isLoading: boolean, }, > { state = { prevFromTo: '', // eslint-disable-line exchanges: null, error: null, + isLoading: false, } static getDerivedStateFromProps(nextProps: *, prevState: *) { @@ -75,25 +77,25 @@ class SelectExchange extends Component< async _load() { this._loadId++ if (this._unmounted) return - this.setState({ exchanges: [] }) + this.setState({ exchanges: [], isLoading: true }) const { _loadId } = this const { from, to } = this.props try { const exchanges = await getExchanges(from, to) if (!this._unmounted && this._loadId === _loadId) { - this.setState({ exchanges }) + this.setState({ exchanges, isLoading: false }) } } catch (error) { logger.error(error) if (!this._unmounted && this._loadId === _loadId) { - this.setState({ error }) + this.setState({ error, isLoading: false }) } } } render() { const { onChange, exchangeId, style, t, from, to, ...props } = this.props - const { exchanges, error } = this.state + const { exchanges, error, isLoading } = this.state const options = exchanges ? exchanges.map(e => ({ value: e.id, label: e.name, ...e })) : [] const value = options.find(e => e.id === exchangeId) @@ -115,10 +117,12 @@ class SelectExchange extends Component< value={value} options={options} onChange={onChange} - isLoading={options.length === 0} + isLoading={isLoading} placeholder={t('app:common.selectExchange')} noOptionsMessage={({ inputValue }) => - t('app:common.selectExchangeNoOption', { exchangeName: inputValue }) + inputValue + ? t('app:common.selectExchangeNoOption', { exchangeName: inputValue }) + : t('app:common.selectExchangeNoOptionAtAll') } {...props} /> diff --git a/src/components/base/Chart/Tooltip.js b/src/components/base/Chart/Tooltip.js index 900afa8e..f38e403a 100644 --- a/src/components/base/Chart/Tooltip.js +++ b/src/components/base/Chart/Tooltip.js @@ -1,6 +1,7 @@ // @flow import React, { Fragment } from 'react' +import moment from 'moment' import styled from 'styled-components' import type { Unit, Currency } from '@ledgerhq/live-common/lib/types' @@ -68,7 +69,7 @@ const Tooltip = ({ /> )} - {item.date.toISOString().substr(0, 10)} + {moment(item.date).format('LL')} )} diff --git a/src/components/base/Markdown/index.js b/src/components/base/Markdown/index.js index a70e180d..5e02c5b9 100644 --- a/src/components/base/Markdown/index.js +++ b/src/components/base/Markdown/index.js @@ -79,6 +79,10 @@ export const Notes = styled(Box).attrs({ color: #6a737d; } + strong { + font-weight: bold; + } + img { max-width: 100%; } diff --git a/src/components/layout/Default.js b/src/components/layout/Default.js index 964d7c32..d304af06 100644 --- a/src/components/layout/Default.js +++ b/src/components/layout/Default.js @@ -13,6 +13,7 @@ import type { Location } from 'react-router' import * as modals from 'components/modals' import Box from 'components/base/Box' import GrowScroll from 'components/base/GrowScroll' +import Track from 'analytics/Track' import AccountPage from 'components/AccountPage' import DashboardPage from 'components/DashboardPage' @@ -84,6 +85,7 @@ class Default extends Component { {process.platform === 'darwin' && } + diff --git a/src/components/modals/Receive/index.js b/src/components/modals/Receive/index.js index 8e68c04e..77d4e1ca 100644 --- a/src/components/modals/Receive/index.js +++ b/src/components/modals/Receive/index.js @@ -194,7 +194,7 @@ class ReceiveModal extends PureComponent { ? [verifyAddressError.name === 'UserRefusedAddress' ? 2 : 3] : [] - const isModalLocked = stepId === 'confirm' && isAddressVerified === null + const isModalLocked = stepId === 'receive' && isAddressVerified === null return ( , { totalSpent: number, - canBeSpent: boolean, + canNext: boolean, isSyncing: boolean, }, > { state = { isSyncing: false, totalSpent: 0, - canBeSpent: true, + canNext: false, } componentDidMount() { @@ -127,6 +128,7 @@ export class StepAmountFooter extends PureComponent< const syncId = ++this.syncId if (!account || !transaction || !bridge) { + this.setState({ canNext: false, isSyncing: false }) return } @@ -135,21 +137,26 @@ export class StepAmountFooter extends PureComponent< try { const totalSpent = await bridge.getTotalSpent(account, transaction) if (syncId !== this.syncId) return + const isRecipientValid = await bridge.isRecipientValid( + account.currency, + bridge.getTransactionRecipient(account, transaction), + ) + if (syncId !== this.syncId) return const canBeSpent = await bridge .checkCanBeSpent(account, transaction) .then(() => true, () => false) if (syncId !== this.syncId) return - this.setState({ totalSpent, canBeSpent, isSyncing: false }) + const canNext = isRecipientValid && canBeSpent + this.setState({ totalSpent, canNext, isSyncing: false }) } catch (err) { - this.setState({ isSyncing: false }) + logger.critical(err) + this.setState({ totalSpent: 0, canNext: false, isSyncing: false }) } } render() { - const { t, transitionTo, account, transaction, bridge } = this.props - const { totalSpent, canBeSpent, isSyncing } = this.state - const canNext = - account && transaction && bridge && bridge.isValidTransaction(account, transaction) + const { t, transitionTo, account } = this.props + const { isSyncing, totalSpent, canNext } = this.state return ( @@ -186,7 +193,7 @@ export class StepAmountFooter extends PureComponent< {isSyncing && } - diff --git a/src/config/constants.js b/src/config/constants.js index e8b8f3b5..1fd94965 100644 --- a/src/config/constants.js +++ b/src/config/constants.js @@ -43,15 +43,15 @@ export const SYNC_TIMEOUT = intFromEnv('SYNC_TIMEOUT', 30 * 1000) export const LEDGER_COUNTERVALUES_API = stringFromEnv( 'LEDGER_COUNTERVALUES_API', - 'https://beta.manager.live.ledger.fr/countervalues', + 'https://countervalues.api.live.ledger.com', ) export const LEDGER_REST_API_BASE = stringFromEnv( 'LEDGER_REST_API_BASE', - 'https://explorers.api.live.ledger.com/', + 'https://explorers.api.live.ledger.com', ) export const MANAGER_API_BASE = stringFromEnv( 'MANAGER_API_BASE', - 'https://beta.manager.live.ledger.fr/api', + 'https://manager.api.live.ledger.com/api', ) export const BASE_SOCKET_URL = stringFromEnv('BASE_SOCKET_URL', 'wss://api.ledgerwallet.com/update') diff --git a/src/helpers/apps/listAppVersions.js b/src/helpers/apps/listAppVersions.js index c340a200..b29ba127 100644 --- a/src/helpers/apps/listAppVersions.js +++ b/src/helpers/apps/listAppVersions.js @@ -7,25 +7,19 @@ import getDeviceVersion from 'helpers/devices/getDeviceVersion' import getCurrentFirmware from 'helpers/devices/getCurrentFirmware' export default async (deviceInfo: DeviceInfo) => { - try { - const deviceData = await getDeviceVersion(deviceInfo.targetId, deviceInfo.providerId) - const firmwareData = await getCurrentFirmware({ - deviceId: deviceData.id, - fullVersion: deviceInfo.fullVersion, - provider: deviceInfo.providerId, - }) - const params = { - provider: deviceInfo.providerId, - current_se_firmware_final_version: firmwareData.id, - device_version: deviceData.id, - } - const { - data: { application_versions }, - } = await network({ method: 'POST', url: APPLICATIONS_BY_DEVICE, data: params }) - return application_versions.length > 0 ? application_versions : [] - } catch (err) { - const error = Error(err.message) - error.stack = err.stack - throw err + const deviceData = await getDeviceVersion(deviceInfo.targetId, deviceInfo.providerId) + const firmwareData = await getCurrentFirmware({ + deviceId: deviceData.id, + fullVersion: deviceInfo.fullVersion, + provider: deviceInfo.providerId, + }) + const params = { + provider: deviceInfo.providerId, + current_se_firmware_final_version: firmwareData.id, + device_version: deviceData.id, } + const { + data: { application_versions }, + } = await network({ method: 'POST', url: APPLICATIONS_BY_DEVICE, data: params }) + return application_versions.length > 0 ? application_versions : [] } diff --git a/src/helpers/apps/listApps.js b/src/helpers/apps/listApps.js index 7c85e3ee..372d5ad1 100644 --- a/src/helpers/apps/listApps.js +++ b/src/helpers/apps/listApps.js @@ -4,12 +4,6 @@ import network from 'api/network' import { GET_APPLICATIONS } from 'helpers/urls' export default async () => { - try { - const { data } = await network({ method: 'GET', url: GET_APPLICATIONS }) - return data.length > 0 ? data : [] - } catch (err) { - const error = Error(err.message) - error.stack = err.stack - throw err - } + const { data } = await network({ method: 'GET', url: GET_APPLICATIONS }) + return data.length > 0 ? data : [] } diff --git a/src/helpers/apps/listCategories.js b/src/helpers/apps/listCategories.js index 494b5cab..59836572 100644 --- a/src/helpers/apps/listCategories.js +++ b/src/helpers/apps/listCategories.js @@ -4,12 +4,6 @@ import network from 'api/network' import { GET_CATEGORIES } from 'helpers/urls' export default async () => { - try { - const { data } = await network({ method: 'GET', url: GET_CATEGORIES }) - return data.length > 0 ? data : [] - } catch (err) { - const error = Error(err.message) - error.stack = err.stack - throw err - } + const { data } = await network({ method: 'GET', url: GET_CATEGORIES }) + return data.length > 0 ? data : [] } diff --git a/src/helpers/common.js b/src/helpers/common.js index 562709ca..c8d8eaf0 100644 --- a/src/helpers/common.js +++ b/src/helpers/common.js @@ -27,44 +27,38 @@ export type LedgerScriptParams = { * Retrieve targetId and firmware version from device */ export async function getFirmwareInfo(transport: Transport<*>) { - try { - const res = await transport.send(...APDUS.GET_FIRMWARE) - const byteArray = [...res] - const data = byteArray.slice(0, byteArray.length - 2) - const targetIdStr = Buffer.from(data.slice(0, 4)) - const targetId = targetIdStr.readUIntBE(0, 4) - const seVersionLength = data[4] - const seVersion = Buffer.from(data.slice(5, 5 + seVersionLength)).toString() - const flagsLength = data[5 + seVersionLength] - const flags = Buffer.from( - data.slice(5 + seVersionLength + 1, 5 + seVersionLength + 1 + flagsLength), - ).toString() + const res = await transport.send(...APDUS.GET_FIRMWARE) + const byteArray = [...res] + const data = byteArray.slice(0, byteArray.length - 2) + const targetIdStr = Buffer.from(data.slice(0, 4)) + const targetId = targetIdStr.readUIntBE(0, 4) + const seVersionLength = data[4] + const seVersion = Buffer.from(data.slice(5, 5 + seVersionLength)).toString() + const flagsLength = data[5 + seVersionLength] + const flags = Buffer.from( + data.slice(5 + seVersionLength + 1, 5 + seVersionLength + 1 + flagsLength), + ).toString() - const mcuVersionLength = data[5 + seVersionLength + 1 + flagsLength] - let mcuVersion = Buffer.from( - data.slice( - 7 + seVersionLength + flagsLength, - 7 + seVersionLength + flagsLength + mcuVersionLength, - ), - ) - if (mcuVersion[mcuVersion.length - 1] === 0) { - mcuVersion = mcuVersion.slice(0, mcuVersion.length - 1) - } - mcuVersion = mcuVersion.toString() + const mcuVersionLength = data[5 + seVersionLength + 1 + flagsLength] + let mcuVersion = Buffer.from( + data.slice( + 7 + seVersionLength + flagsLength, + 7 + seVersionLength + flagsLength + mcuVersionLength, + ), + ) + if (mcuVersion[mcuVersion.length - 1] === 0) { + mcuVersion = mcuVersion.slice(0, mcuVersion.length - 1) + } + mcuVersion = mcuVersion.toString() - if (!seVersionLength) { - return { - targetId, - seVersion: '0.0.0', - flags: '', - mcuVersion: '', - } + if (!seVersionLength) { + return { + targetId, + seVersion: '0.0.0', + flags: '', + mcuVersion: '', } - - return { targetId, seVersion, flags, mcuVersion } - } catch (err) { - const error = new Error(err.message) - error.stack = err.stack - throw error } + + return { targetId, seVersion, flags, mcuVersion } } diff --git a/src/helpers/devices/getCurrentFirmware.js b/src/helpers/devices/getCurrentFirmware.js index 1aa48a3f..a98c01d5 100644 --- a/src/helpers/devices/getCurrentFirmware.js +++ b/src/helpers/devices/getCurrentFirmware.js @@ -9,22 +9,15 @@ type Input = { provider: number, } -let error export default async (input: Input): Promise<*> => { - try { - const { data } = await network({ - method: 'POST', - url: GET_CURRENT_FIRMWARE, - data: { - device_version: input.deviceId, - version_name: input.fullVersion, - provider: input.provider, - }, - }) - return data - } catch (err) { - error = Error(err.message) - error.stack = err.stack - throw error - } + const { data } = await network({ + method: 'POST', + url: GET_CURRENT_FIRMWARE, + data: { + device_version: input.deviceId, + version_name: input.fullVersion, + provider: input.provider, + }, + }) + return data } diff --git a/src/helpers/devices/getLatestFirmwareForDevice.js b/src/helpers/devices/getLatestFirmwareForDevice.js index 016249a6..9bbf7296 100644 --- a/src/helpers/devices/getLatestFirmwareForDevice.js +++ b/src/helpers/devices/getLatestFirmwareForDevice.js @@ -10,53 +10,47 @@ import getCurrentFirmware from './getCurrentFirmware' import getDeviceVersion from './getDeviceVersion' export default async (deviceInfo: DeviceInfo) => { - try { - // Get device infos from targetId - const deviceVersion = await getDeviceVersion(deviceInfo.targetId, deviceInfo.providerId) - - // Get firmware infos with firmware name and device version - const seFirmwareVersion = await getCurrentFirmware({ - fullVersion: deviceInfo.fullVersion, - deviceId: deviceVersion.id, + // Get device infos from targetId + const deviceVersion = await getDeviceVersion(deviceInfo.targetId, deviceInfo.providerId) + + // Get firmware infos with firmware name and device version + const seFirmwareVersion = await getCurrentFirmware({ + fullVersion: deviceInfo.fullVersion, + deviceId: deviceVersion.id, + provider: deviceInfo.providerId, + }) + + // Fetch next possible firmware + const { data } = await network({ + method: 'POST', + url: GET_LATEST_FIRMWARE, + data: { + current_se_firmware_final_version: seFirmwareVersion.id, + device_version: deviceVersion.id, provider: deviceInfo.providerId, - }) - - // Fetch next possible firmware - const { data } = await network({ - method: 'POST', - url: GET_LATEST_FIRMWARE, - data: { - current_se_firmware_final_version: seFirmwareVersion.id, - device_version: deviceVersion.id, - provider: deviceInfo.providerId, - }, - }) - - if (data.result === 'null') { - return null - } + }, + }) - const { se_firmware_osu_version } = data - const { next_se_firmware_final_version } = se_firmware_osu_version - const seFirmwareFinalVersion = await getFinalFirmwareById(next_se_firmware_final_version) + if (data.result === 'null') { + return null + } - const mcus = await getMcus() + const { se_firmware_osu_version } = data + const { next_se_firmware_final_version } = se_firmware_osu_version + const seFirmwareFinalVersion = await getFinalFirmwareById(next_se_firmware_final_version) - const currentMcuVersionId = mcus - .filter(mcu => mcu.name === deviceInfo.mcuVersion) - .map(mcu => mcu.id) + const mcus = await getMcus() - if (!seFirmwareFinalVersion.mcu_versions.includes(...currentMcuVersionId)) { - return { - ...se_firmware_osu_version, - shouldFlashMcu: true, - } - } + const currentMcuVersionId = mcus + .filter(mcu => mcu.name === deviceInfo.mcuVersion) + .map(mcu => mcu.id) - return { ...se_firmware_osu_version, shouldFlashMcu: false } - } catch (err) { - const error = Error(err.message) - error.stack = err.stack - throw error + if (!seFirmwareFinalVersion.mcu_versions.includes(...currentMcuVersionId)) { + return { + ...se_firmware_osu_version, + shouldFlashMcu: true, + } } + + return { ...se_firmware_osu_version, shouldFlashMcu: false } } diff --git a/src/helpers/devices/isDashboardOpen.js b/src/helpers/devices/isDashboardOpen.js index e3663e5f..a10c76fd 100644 --- a/src/helpers/devices/isDashboardOpen.js +++ b/src/helpers/devices/isDashboardOpen.js @@ -7,16 +7,10 @@ import { getFirmwareInfo } from 'helpers/common' type Result = boolean export default async (transport: Transport<*>): Promise => { - try { - const { targetId, seVersion } = await getFirmwareInfo(transport) - if (targetId && seVersion) { - return true - } - - return false - } catch (err) { - const error = Error(err.message) - error.stack = err.stack - throw error + const { targetId, seVersion } = await getFirmwareInfo(transport) + if (targetId && seVersion) { + return true } + + return false } diff --git a/src/helpers/devices/shouldFlashMcu.js b/src/helpers/devices/shouldFlashMcu.js index be793c48..dba76d75 100644 --- a/src/helpers/devices/shouldFlashMcu.js +++ b/src/helpers/devices/shouldFlashMcu.js @@ -10,46 +10,40 @@ import getOsuFirmware from './getOsuFirmware' import getDeviceVersion from './getDeviceVersion' export default async (deviceInfo: DeviceInfo): Promise => { - try { - // Get device infos from targetId - const deviceVersion = await getDeviceVersion(deviceInfo.targetId, deviceInfo.providerId) - - // Get firmware infos with firmware name and device version - const seFirmwareVersion = await getOsuFirmware({ - version: deviceInfo.fullVersion, - deviceId: deviceVersion.id, + // Get device infos from targetId + const deviceVersion = await getDeviceVersion(deviceInfo.targetId, deviceInfo.providerId) + + // Get firmware infos with firmware name and device version + const seFirmwareVersion = await getOsuFirmware({ + version: deviceInfo.fullVersion, + deviceId: deviceVersion.id, + provider: deviceInfo.providerId, + }) + + // Fetch next possible firmware + const { data } = await network({ + method: 'POST', + url: GET_LATEST_FIRMWARE, + data: { + current_se_firmware_final_version: seFirmwareVersion.id, + device_version: deviceVersion.id, provider: deviceInfo.providerId, - }) - - // Fetch next possible firmware - const { data } = await network({ - method: 'POST', - url: GET_LATEST_FIRMWARE, - data: { - current_se_firmware_final_version: seFirmwareVersion.id, - device_version: deviceVersion.id, - provider: deviceInfo.providerId, - }, - }) - - if (data.result === 'null') { - return false - } - - const { se_firmware_osu_version } = data - const { next_se_firmware_final_version } = se_firmware_osu_version - const seFirmwareFinalVersion = await getFinalFirmwareById(next_se_firmware_final_version) - - const mcus = await getMcus() - - const currentMcuVersionId = mcus - .filter(mcu => mcu.name === deviceInfo.mcuVersion) - .map(mcu => mcu.id) - - return !seFirmwareFinalVersion.mcu_versions.includes(...currentMcuVersionId) - } catch (err) { - const error = Error(err.message) - error.stack = err.stack - throw error + }, + }) + + if (data.result === 'null') { + return false } + + const { se_firmware_osu_version } = data + const { next_se_firmware_final_version } = se_firmware_osu_version + const seFirmwareFinalVersion = await getFinalFirmwareById(next_se_firmware_final_version) + + const mcus = await getMcus() + + const currentMcuVersionId = mcus + .filter(mcu => mcu.name === deviceInfo.mcuVersion) + .map(mcu => mcu.id) + + return !seFirmwareFinalVersion.mcu_versions.includes(...currentMcuVersionId) } diff --git a/src/helpers/firmware/getMcus.js b/src/helpers/firmware/getMcus.js index 3f0b0399..9f0057f4 100644 --- a/src/helpers/firmware/getMcus.js +++ b/src/helpers/firmware/getMcus.js @@ -4,16 +4,10 @@ import network from 'api/network' import { GET_MCUS } from 'helpers/urls' export default async (): Promise<*> => { - try { - const { data } = await network({ - method: 'GET', - url: GET_MCUS, - }) + const { data } = await network({ + method: 'GET', + url: GET_MCUS, + }) - return data - } catch (err) { - const error = Error(err.message) - error.stack = err.stack - throw err - } + return data } diff --git a/src/helpers/firmware/getNextMCU.js b/src/helpers/firmware/getNextMCU.js index 20d3d020..7565e379 100644 --- a/src/helpers/firmware/getNextMCU.js +++ b/src/helpers/firmware/getNextMCU.js @@ -7,24 +7,18 @@ import { createCustomErrorClass } from 'helpers/errors' const LatestMCUInstalledError = createCustomErrorClass('LatestMCUInstalledError') export default async (bootloaderVersion: string): Promise<*> => { - try { - const { data } = await network({ - method: 'POST', - url: GET_NEXT_MCU, - data: { - bootloader_version: bootloaderVersion, - }, - }) + const { data } = await network({ + method: 'POST', + url: GET_NEXT_MCU, + data: { + bootloader_version: bootloaderVersion, + }, + }) - // FIXME: nextVersion will not be able to "default" when - // Error handling is standardize on the API side - if (data === 'default' || !data.name) { - throw new LatestMCUInstalledError('there is no next mcu version to install') - } - return data - } catch (err) { - const error = Error(err.message) - error.stack = err.stack - throw err + // FIXME: nextVersion will not be able to "default" when + // Error handling is standardize on the API side + if (data === 'default' || !data.name) { + throw new LatestMCUInstalledError('there is no next mcu version to install') } + return data } diff --git a/src/helpers/urls.js b/src/helpers/urls.js index 7c338763..c40da8eb 100644 --- a/src/helpers/urls.js +++ b/src/helpers/urls.js @@ -14,6 +14,8 @@ const wsURLBuilder = (endpoint: string) => (params?: Object) => // const wsURLBuilderProxy = (endpoint: string) => (params?: Object) => // `ws://manager.ledger.fr:3501/${endpoint}${params ? `?${qs.stringify(params)}` : ''}` +// FIXME we shouldn't do this here. we should just collocate these where it's used. + export const GET_FINAL_FIRMWARE: string = managerUrlbuilder('firmware_final_versions') export const GET_DEVICE_VERSION: string = managerUrlbuilder('get_device_version') export const APPLICATIONS_BY_DEVICE: string = managerUrlbuilder('get_apps') diff --git a/static/docs/ledgerLogo.png b/static/docs/ledgerLogo.png deleted file mode 100644 index 2a45816b..00000000 Binary files a/static/docs/ledgerLogo.png and /dev/null differ diff --git a/static/i18n/en/app.yml b/static/i18n/en/app.yml index 20be05e7..13c73942 100644 --- a/static/i18n/en/app.yml +++ b/static/i18n/en/app.yml @@ -7,7 +7,7 @@ common: cancel: Cancel delete: Delete continue: Continue - learnMore: Learn More + learnMore: Learn more skipThisStep: Skip this step needHelp: Need help? chooseWalletPlaceholder: Choose a wallet... @@ -18,6 +18,7 @@ common: selectCurrencyNoOption: 'No crypto asset "{{currencyName}}"' selectExchange: Select an exchange selectExchangeNoOption: 'No exchange matching "{{exchangeName}}"' + selectExchangeNoOptionAtAll: 'No exchange found' sortBy: Sort by search: Search save: Save @@ -137,7 +138,7 @@ deviceConnect: dashboard: Not used. # This key is not used. Still managed in JS. emptyState: sidebar: - text: Press the + button to add an account to your portfolio + text: Press this button to add accounts to your portfolio dashboard: title: 'Add accounts to your portfolio' desc: Your portfolio has no accounts the first time Ledger Live is launched. Open the Manager to install apps on your Ledger device before you start adding accounts to your portfolio. @@ -273,7 +274,7 @@ send: totalSpent: Total to debit steps: amount: - title: crypto assets + title: Details selectAccountDebit: Select an account to debit recipientAddress: Recipient address # can't control the tooltip! amount: Amount @@ -360,7 +361,7 @@ settings: # Always ensure descriptions carry full stops (.) reportErrors: Report bugs reportErrorsDesc: Share anonymous usage and diagnostics data to help improve Ledger products, services and security features. about: - desc: Learn about Ledger Live features + desc: Information about Ledger Live, terms and conditions, and privacy policy. help: desc: Learn about Ledger Live features or get help. version: Version @@ -370,7 +371,7 @@ settings: # Always ensure descriptions carry full stops (.) terms: Terms and conditions termsDesc: By using Ledger Live you are deemed to have accepted our terms and conditions. privacy: Privacy policy - privacyDesc: Refer to our privacy policy to learn what personal data we collect, and why and how we use it. + privacyDesc: Refer to our privacy policy to learn what personal data we collect, why and how we use it. hardResetModal: title: Reset Ledger Live desc: Erase all Ledger Live data stored on your computer, including your accounts, transaction history and settings. The private keys to access your crypto assets in the blockchain remain secure on your Ledger device and on your Recovery sheet. diff --git a/static/i18n/en/onboarding.yml b/static/i18n/en/onboarding.yml index a7eb3e21..5c599621 100644 --- a/static/i18n/en/onboarding.yml +++ b/static/i18n/en/onboarding.yml @@ -132,19 +132,19 @@ analytics: title: Share analytics desc: Enable analytics to help Ledger understand how to improve the user experience. mandatoryContextual: - item1: Page visits - item2: Actions (send, receive, logout) + item1: In-app page visits + item2: Actions (send, receive, lock) item3: Clicks item4: Redirections to webpages item5: Scrolled to end of page item6: Install/Uninstall item7: Number of accounts, currencies and operations item8: Overall and page session duration - item9: Device product ID - item10: Device firmware and app versions + item9: Type of Ledger device + item10: Device firmware and app version numbers sentryLogs: title: Report bugs - desc: Automatically send reports to help Ledger fix bugs + desc: Automatically send reports to help Ledger fix bugs. technicalData: title: Technical data * desc: Ledger will automatically collect technical information to get basic feedback on usage. This information is anonymous and does not contain personal data. @@ -155,7 +155,7 @@ analytics: item2: OS name and version item3: Ledger Live version item4: Application language or region - item5: OS Language/Region + item5: OS language or region finish: title: Your device is ready! desc: Proceed to your portfolio and start adding your accounts... diff --git a/static/i18n/fr/app.yml b/static/i18n/fr/app.yml index af21db9a..17ec3a0f 100644 --- a/static/i18n/fr/app.yml +++ b/static/i18n/fr/app.yml @@ -8,7 +8,7 @@ common: cancel: Cancel delete: Delete continue: Continue - learnMore: Learn More + learnMore: Learn more skipThisStep: Skip this step needHelp: Need help? chooseWalletPlaceholder: Choose a wallet... @@ -19,6 +19,7 @@ common: selectCurrencyNoOption: 'No crypto asset "{{currencyName}}"' selectExchange: Select an exchange selectExchangeNoOption: 'No exchange matching "{{exchangeName}}"' + selectExchangeNoOptionAtAll: 'No exchange found' sortBy: Sort by search: Search save: Save @@ -134,7 +135,7 @@ deviceConnect: dashboard: Not used. emptyState: sidebar: - text: Press the + button to add an account to your portfolio + text: Press this button to add accounts to your portfolio dashboard: title: 'Add accounts to your portfolio' desc: Your portfolio has no accounts the first time Ledger Live is launched. Open the Manager to install apps on your Ledger device before you start adding accounts to your portfolio. @@ -270,7 +271,7 @@ send: totalSpent: Total to debit steps: amount: - title: crypto assets + title: Details selectAccountDebit: Select an account to debit recipientAddress: Recipient address amount: Amount @@ -357,7 +358,7 @@ settings: reportErrors: Report bugs reportErrorsDesc: Share anonymous usage and diagnostics data to help improve Ledger products, services and security features. about: - desc: Learn about Ledger Live features + desc: Information about Ledger Live, terms and conditions, and privacy policy. help: desc: Learn about Ledger Live features or get help. version: Version @@ -367,7 +368,7 @@ settings: terms: Terms and conditions termsDesc: By using Ledger Live you are deemed to have accepted our terms and conditions. privacy: Privacy policy - privacyDesc: Refer to our privacy policy to learn what personal data we collect, and why and how we use it. + privacyDesc: Refer to our privacy policy to learn what personal data we collect, why and how we use it. hardResetModal: title: Reset Ledger Live desc: Erase all Ledger Live data stored on your computer, including your accounts, transaction history and settings. The private keys to access your crypto assets in the blockchain remain secure on your Ledger device and on your Recovery sheet. diff --git a/static/i18n/fr/onboarding.yml b/static/i18n/fr/onboarding.yml index ec7fa42f..79ea4507 100644 --- a/static/i18n/fr/onboarding.yml +++ b/static/i18n/fr/onboarding.yml @@ -133,19 +133,19 @@ analytics: title: Share analytics desc: Enable analytics to help Ledger understand how to improve the user experience. mandatoryContextual: - item1: Page visits - item2: Actions (send, receive, logout) + item1: In-app page visits + item2: Actions (send, receive, lock) item3: Clicks item4: Redirections to webpages item5: Scrolled to end of page item6: Install/Uninstall item7: Number of accounts, currencies and operations item8: Overall and page session duration - item9: Device product ID - item10: Device firmware and app versions + item9: Type of Ledger device + item10: Device firmware and app version numbers sentryLogs: title: Report bugs - desc: Automatically send reports to help Ledger fix bugs + desc: Automatically send reports to help Ledger fix bugs. technicalData: title: Technical data * desc: Ledger will automatically collect technical information to get basic feedback on usage. This information is anonymous and does not contain personal data. @@ -156,7 +156,7 @@ analytics: item2: OS name and version item3: Ledger Live version item4: Application language or region - item5: OS Language/Region + item5: OS language or region finish: title: Your device is ready! desc: Proceed to your portfolio and start adding your accounts... diff --git a/yarn.lock b/yarn.lock index 3186ed48..c6e72999 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1521,9 +1521,9 @@ dependencies: events "^2.0.0" -"@ledgerhq/ledger-core@2.0.0-rc.3": - version "2.0.0-rc.3" - resolved "https://registry.yarnpkg.com/@ledgerhq/ledger-core/-/ledger-core-2.0.0-rc.3.tgz#21b04239e9ba6b7fdcb89958eea8ad47a4a28a88" +"@ledgerhq/ledger-core@2.0.0-rc.4": + version "2.0.0-rc.4" + resolved "https://registry.yarnpkg.com/@ledgerhq/ledger-core/-/ledger-core-2.0.0-rc.4.tgz#0ec80a763c666658bea94bd38b86aa90d5a24906" dependencies: "@ledgerhq/hw-app-btc" "^4.7.3" "@ledgerhq/hw-transport-node-hid" "^4.7.6" @@ -1534,9 +1534,9 @@ npm "^5.7.1" prebuild-install "^2.2.2" -"@ledgerhq/live-common@2.31.0": - version "2.31.0" - resolved "https://registry.yarnpkg.com/@ledgerhq/live-common/-/live-common-2.31.0.tgz#0f599a1e23b64d9ed74a845d3a9c82f0696f1df3" +"@ledgerhq/live-common@^2.32.0": + version "2.32.0" + resolved "https://registry.yarnpkg.com/@ledgerhq/live-common/-/live-common-2.32.0.tgz#6c2108d58ec44335077d87442a6418e0ec4d3372" dependencies: axios "^0.18.0" invariant "^2.2.2"