diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index bd18f9d3..3958350e 100755 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -7,8 +7,8 @@ about: Report a bug in Ledger Live Desktop or a regression. -- Ledger Live **version_here** -- Platform: **windows OR mac OR linux** +- tested on Ledger Live **version_here** +- Platform and version: **e.g. Mac 10.13.5 / Windows 10 / ..** #### Expected behavior diff --git a/.github/ISSUE_TEMPLATE/enhancement.md b/.github/ISSUE_TEMPLATE/enhancement.md index 6fe17a94..c876b7e8 100755 --- a/.github/ISSUE_TEMPLATE/enhancement.md +++ b/.github/ISSUE_TEMPLATE/enhancement.md @@ -3,6 +3,10 @@ name: πŸ—£ Start a Discussion about: Discuss to propose changes to improve the state of Ledger Live. --- + + + + #### Ledger Live Version @@ -12,7 +16,3 @@ about: Discuss to propose changes to improve the state of Ledger Live. #### Part of the application to improve - -#### Description - - diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 946065e3..76f5e6e6 100755 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -5,10 +5,10 @@ about: Any feature you find missing in Ledger Live? Discuss to suggest feature r - [ ] I have checked this feature was not yet requested. -#### Part of the application + - -#### Description - +#### Part of the application + + diff --git a/docs/screenshot.png b/docs/screenshot.png index 03d604e9..80322b25 100644 Binary files a/docs/screenshot.png and b/docs/screenshot.png differ diff --git a/electron-builder.yml b/electron-builder.yml index e91cb5b9..f5c1895c 100644 --- a/electron-builder.yml +++ b/electron-builder.yml @@ -36,9 +36,6 @@ win: - target: nsis arch: - x64 - - target: zip - arch: - - x64 nsis: oneClick: false diff --git a/package.json b/package.json index 06ad81ff..bac08f54 100644 --- a/package.json +++ b/package.json @@ -55,6 +55,7 @@ "debug": "^3.1.0", "downshift": "^1.31.16", "eip55": "^1.0.3", + "electron-context-menu": "^0.10.0", "electron-store": "^1.3.0", "electron-updater": "^2.21.8", "ethereumjs-tx": "^1.3.4", diff --git a/scripts/compile.sh b/scripts/compile.sh index 346cee44..a47eac75 100644 --- a/scripts/compile.sh +++ b/scripts/compile.sh @@ -2,7 +2,8 @@ set -e -export GIT_REVISION=`git rev-parse HEAD` +GIT_REVISION=$(git rev-parse HEAD) +export GIT_REVISION export SENTRY_URL=https://db8f5b9b021048d4a401f045371701cb@sentry.io/274561 export JOBS=max diff --git a/scripts/dist.sh b/scripts/dist.sh index 9ee8c68c..51f4fcee 100755 --- a/scripts/dist.sh +++ b/scripts/dist.sh @@ -4,13 +4,13 @@ # some context: # - https://github.com/electron-userland/electron-builder/issues/2577 # - https://github.com/electron-userland/electron-builder/issues/2269 -if [[ `uname` == 'Linux' ]]; then +if [[ $(uname) == 'Linux' ]]; then mv build/icon.png /tmp fi yarn compile && DEBUG=electron-builder electron-builder # hilarious fix continuation: put back the icon where it was -if [[ `uname` == 'Linux' ]]; then +if [[ $(uname) == 'Linux' ]]; then mv /tmp/icon.png build fi diff --git a/scripts/download-analytics.sh b/scripts/download-analytics.sh index daf8a3b1..16373bac 100755 --- a/scripts/download-analytics.sh +++ b/scripts/download-analytics.sh @@ -1,10 +1,10 @@ #!/bin/bash -if [ -z $ANALYTICS_KEY ]; then +if [ -z "$ANALYTICS_KEY" ]; then echo 'ANALYTICS_KEY must be set' exit 1 fi -cd `dirname $0`/.. +cd "$(dirname "$0")/.." || exit -wget https://cdn.segment.com/analytics.js/v1/$ANALYTICS_KEY/analytics.min.js -O static/analytics.min.js +wget https://cdn.segment.com/analytics.js/v1/"$ANALYTICS_KEY"/analytics.min.js -O static/analytics.min.js diff --git a/scripts/hash-utils.sh b/scripts/hash-utils.sh index 2458428d..07b1ab73 100644 --- a/scripts/hash-utils.sh +++ b/scripts/hash-utils.sh @@ -7,12 +7,12 @@ function GET_HASH_PATH { function GET_HASH { HASH_NAME=$1 - HASH_PATH=`GET_HASH_PATH $HASH_NAME` + HASH_PATH=$(GET_HASH_PATH "$HASH_NAME") if [ ! -e "$HASH_PATH" ]; then echo '' else - HASH_CONTENT=`cat "$HASH_PATH"` - echo $HASH_CONTENT + HASH_CONTENT=$(cat "$HASH_PATH") + echo "$HASH_CONTENT" fi } @@ -20,7 +20,7 @@ function SET_HASH { HASH_NAME=$1 HASH_CONTENT=$2 echo "setting hash $HASH_NAME to $HASH_CONTENT" - HASH_PATH=`GET_HASH_PATH $HASH_NAME` + HASH_PATH=$(GET_HASH_PATH "$HASH_NAME") mkdir -p ./node_modules/.cache - echo $HASH_CONTENT > $HASH_PATH + echo "$HASH_CONTENT" > "$HASH_PATH" } diff --git a/scripts/install-ci-deps.sh b/scripts/install-ci-deps.sh index 7b842885..3ed79367 100644 --- a/scripts/install-ci-deps.sh +++ b/scripts/install-ci-deps.sh @@ -1,13 +1,14 @@ #!/bin/bash +# shellcheck disable=SC1091 source scripts/hash-utils.sh -PACKAGE_JSON_HASH=`md5sum package.json | cut -d ' ' -f 1` -CACHED_PACKAGE_JSON_HASH=`GET_HASH 'package.json'` +PACKAGE_JSON_HASH=$(md5sum package.json | cut -d ' ' -f 1) +CACHED_PACKAGE_JSON_HASH=$(GET_HASH 'package.json') if [ "$CACHED_PACKAGE_JSON_HASH" == "$PACKAGE_JSON_HASH" ]; then echo "> Skipping yarn install" else yarn install - SET_HASH 'package.json' $PACKAGE_JSON_HASH + SET_HASH 'package.json' "$PACKAGE_JSON_HASH" fi diff --git a/scripts/postinstall.sh b/scripts/postinstall.sh index fe39dd1f..c33cf292 100755 --- a/scripts/postinstall.sh +++ b/scripts/postinstall.sh @@ -1,5 +1,6 @@ #!/bin/bash +# shellcheck disable=SC1091 source scripts/hash-utils.sh function MAIN { @@ -10,8 +11,8 @@ function MAIN { } function INSTALL_FLOW_TYPED { - LATEST_FLOW_TYPED_COMMIT_HASH=`curl --silent --header "Accept: application/vnd.github.VERSION.sha" https://api.github.com/repos/flowtype/flow-typed/commits/master` - CURRENT_FLOW_TYPED_HASH=`GET_HASH 'flow-typed'` + LATEST_FLOW_TYPED_COMMIT_HASH=$(curl --silent --header "Accept: application/vnd.github.VERSION.sha" https://api.github.com/repos/flowtype/flow-typed/commits/master) + CURRENT_FLOW_TYPED_HASH=$(GET_HASH 'flow-typed') if [ "$LATEST_FLOW_TYPED_COMMIT_HASH" == "$CURRENT_FLOW_TYPED_HASH" ]; then echo "> Flow-typed definitions are up to date. Skipping" else @@ -19,25 +20,25 @@ function INSTALL_FLOW_TYPED { flow-typed install -s --overwrite echo "> Removing broken flow definitions" rm flow-typed/npm/{react-i18next_v7.x.x.js,styled-components_v3.x.x.js,redux_*,winston*} - SET_HASH 'flow-typed' $LATEST_FLOW_TYPED_COMMIT_HASH + SET_HASH 'flow-typed' "$LATEST_FLOW_TYPED_COMMIT_HASH" fi } function REBUILD_ELECTRON_NATIVE_DEPS { # for strange/fancy os-es - if [[ `uname` == 'Darwin' ]]; then - PACKAGE_JSON_HASH=`md5 package.json | cut -d ' ' -f 1` + if [[ $(uname) == 'Darwin' ]]; then + PACKAGE_JSON_HASH=$(md5 package.json | cut -d ' ' -f 1) else # for normal os-es - PACKAGE_JSON_HASH=`md5sum package.json | cut -d ' ' -f 1` + PACKAGE_JSON_HASH=$(md5sum package.json | cut -d ' ' -f 1) fi - CACHED_PACKAGE_JSON_HASH=`GET_HASH 'package.json'` + CACHED_PACKAGE_JSON_HASH=$(GET_HASH 'package.json') if [ "$CACHED_PACKAGE_JSON_HASH" == "$PACKAGE_JSON_HASH" ]; then echo "> Electron native deps are up to date. Skipping" else echo "> Installing electron native deps" DEBUG=electron-builder electron-builder install-app-deps - SET_HASH 'package.json' $PACKAGE_JSON_HASH + SET_HASH 'package.json' "$PACKAGE_JSON_HASH" fi } diff --git a/scripts/reset-files.sh b/scripts/reset-files.sh index 29328651..8eb79d18 100644 --- a/scripts/reset-files.sh +++ b/scripts/reset-files.sh @@ -4,24 +4,24 @@ set -e echo "> Getting user data folder..." -TMP_FILE=`mktemp` -cat < $TMP_FILE +TMP_FILE=$(mktemp) +cat < "$TMP_FILE" const { app } = require('electron') console.log(app.getPath('userData')) EOF -USER_DATA_FOLDER=`timeout 0.5 electron $TMP_FILE || echo` # echo used to ensure status 0 +USER_DATA_FOLDER=$(timeout 0.5 electron "$TMP_FILE" || echo) # echo used to ensure status 0 if [ "$USER_DATA_FOLDER" == "" ]; then echo "You probably are on a slow computer. Be patient..." - USER_DATA_FOLDER=`timeout 3 electron $TMP_FILE || echo` # echo used to ensure status 0 + USER_DATA_FOLDER=$(timeout 3 electron "$TMP_FILE" || echo) # echo used to ensure status 0 fi if [ "$USER_DATA_FOLDER" == "" ]; then echo "Apparently, very very slow computer..." - USER_DATA_FOLDER=`timeout 6 electron $TMP_FILE || echo` # echo used to ensure status 0 + USER_DATA_FOLDER=$(timeout 6 electron "$TMP_FILE" || echo) # echo used to ensure status 0 fi -rm $TMP_FILE +rm "$TMP_FILE" if [ "$USER_DATA_FOLDER" == "" ]; then echo "Could not find the data folder. Bye" diff --git a/src/bridge/BridgeSyncContext.js b/src/bridge/BridgeSyncContext.js index e8df3923..99099fb0 100644 --- a/src/bridge/BridgeSyncContext.js +++ b/src/bridge/BridgeSyncContext.js @@ -17,12 +17,7 @@ import { setAccountSyncState } from 'actions/bridgeSync' import { bridgeSyncSelector, syncStateLocalSelector } from 'reducers/bridgeSync' import type { BridgeSyncState } from 'reducers/bridgeSync' import { accountsSelector } from 'reducers/accounts' -import { - SYNC_BOOT_DELAY, - SYNC_ALL_INTERVAL, - SYNC_MAX_CONCURRENT, - SYNC_TIMEOUT, -} from 'config/constants' +import { SYNC_MAX_CONCURRENT, SYNC_TIMEOUT } from 'config/constants' import { getBridgeForCurrency } from '.' type BridgeSyncProviderProps = { @@ -151,19 +146,6 @@ class Provider extends Component { this.api = sync } - componentDidMount() { - const syncLoop = async () => { - this.api({ type: 'BACKGROUND_TICK' }) - this.syncTimeout = setTimeout(syncLoop, SYNC_ALL_INTERVAL) - } - this.syncTimeout = setTimeout(syncLoop, SYNC_BOOT_DELAY) - } - - componentWillUnmount() { - clearTimeout(this.syncTimeout) - } - - syncTimeout: * api: Sync render() { diff --git a/src/bridge/EthereumJSBridge.js b/src/bridge/EthereumJSBridge.js index 618b8c75..48972922 100644 --- a/src/bridge/EthereumJSBridge.js +++ b/src/bridge/EthereumJSBridge.js @@ -176,6 +176,7 @@ const EthereumBridge: WalletBridge = { index, { address, path: freshAddressPath, publicKey }, isStandard, + mandatoryCount, ): { account?: Account, complete?: boolean } { const balance = await api.getAccountBalance(address) if (finished) return { complete: true } @@ -211,6 +212,10 @@ const EthereumBridge: WalletBridge = { } newAccountCount++ } + + if (index < mandatoryCount) { + return {} + } // NB for legacy addresses maybe we will continue at least for the first 10 addresses return { complete: true } } @@ -254,7 +259,13 @@ const EthereumBridge: WalletBridge = { const res = await getAddressCommand .send({ currencyId: currency.id, devicePath: deviceId, path: freshAddressPath }) .toPromise() - const r = await stepAddress(index, res, isStandard) + const r = await stepAddress( + index, + res, + isStandard, + // $FlowFixMe i know i know, not part of function + derivation.mandatoryCount || 0, + ) if (r.account) o.next(r.account) if (r.complete) { break diff --git a/src/bridge/LibcoreBridge.js b/src/bridge/LibcoreBridge.js index 55f021ce..6ea7ff24 100644 --- a/src/bridge/LibcoreBridge.js +++ b/src/bridge/LibcoreBridge.js @@ -138,7 +138,8 @@ const LibcoreBridge: WalletBridge = { (accountOps.length > 0 && syncedOps.length > 0 && (accountOps[0].accountId !== syncedOps[0].accountId || - accountOps[0].id !== syncedOps[0].id)) // if same size, only check if the last item has changed. + accountOps[0].id !== syncedOps[0].id || // if same size, only check if the last item has changed. + accountOps[0].blockHeight !== syncedOps[0].blockHeight)) if (hasChanged) { patch.operations = syncedAccount.operations diff --git a/src/bridge/RippleJSBridge.js b/src/bridge/RippleJSBridge.js index 727c1dca..17e9c24b 100644 --- a/src/bridge/RippleJSBridge.js +++ b/src/bridge/RippleJSBridge.js @@ -192,9 +192,11 @@ type Tx = { const txToOperation = (account: Account) => ({ id, sequence, + type: txType, outcome: { fee, deliveredAmount, ledgerVersion, timestamp }, specification: { source, destination }, -}: Tx): Operation => { +}: Tx): ?Operation => { + if (txType === 'trustline') return null const type = source.address === account.freshAddress ? 'OUT' : 'IN' let value = deliveredAmount ? parseAPICurrencyObject(deliveredAmount) : 0 const feeValue = parseAPIValue(fee) @@ -334,7 +336,7 @@ const RippleJSBridge: WalletBridge = { unit: currency.units[0], lastSyncDate: new Date(), } - account.operations = transactions.map(txToOperation(account)) + account.operations = transactions.map(txToOperation(account)).filter(Boolean) o.next(account) } } diff --git a/src/commands/libcoreScanAccounts.js b/src/commands/libcoreScanAccounts.js index 7044220d..b8b0e2e3 100644 --- a/src/commands/libcoreScanAccounts.js +++ b/src/commands/libcoreScanAccounts.js @@ -17,6 +17,7 @@ const cmd: Command = createCommand( 'libcoreScanAccounts', ({ devicePath, currencyId }) => Observable.create(o => { + let unsubscribed = false // TODO scanAccountsOnDevice should directly return a Observable so we just have to pass-in withLibcore(core => scanAccountsOnDevice({ @@ -26,6 +27,7 @@ const cmd: Command = createCommand( onAccountScanned: account => { o.next(account) }, + isUnsubscribed: () => unsubscribed, }).then( () => { o.complete() @@ -37,7 +39,7 @@ const cmd: Command = createCommand( ) function unsubscribe() { - // FIXME not implemented + unsubscribed = true } return unsubscribe diff --git a/src/commands/libcoreSignAndBroadcast.js b/src/commands/libcoreSignAndBroadcast.js index 409e809c..8b8e6bb2 100644 --- a/src/commands/libcoreSignAndBroadcast.js +++ b/src/commands/libcoreSignAndBroadcast.js @@ -19,13 +19,15 @@ type BitcoinLikeTransaction = { } type Input = { - account: AccountRaw, + account: AccountRaw, // FIXME there is no reason we send the whole AccountRaw transaction: BitcoinLikeTransaction, deviceId: string, } type Result = { type: 'signed' } | { type: 'broadcasted', operation: OperationRaw } +// FIXME this command should be unified with 'signTransaction' command + const cmd: Command = createCommand( 'libcoreSignAndBroadcast', ({ account, transaction, deviceId }) => diff --git a/src/commands/libcoreSyncAccount.js b/src/commands/libcoreSyncAccount.js index d2677b9e..380e8151 100644 --- a/src/commands/libcoreSyncAccount.js +++ b/src/commands/libcoreSyncAccount.js @@ -8,7 +8,7 @@ import { syncAccount } from 'helpers/libcore' import withLibcore from 'helpers/withLibcore' type Input = { - rawAccount: AccountRaw, + rawAccount: AccountRaw, // FIXME there is no reason we send the whole AccountRaw } type Result = AccountRaw diff --git a/src/components/AccountPage/EmptyStateAccount.js b/src/components/AccountPage/EmptyStateAccount.js index 98749e8f..bf8b672d 100644 --- a/src/components/AccountPage/EmptyStateAccount.js +++ b/src/components/AccountPage/EmptyStateAccount.js @@ -45,7 +45,7 @@ class EmptyStateAccount extends PureComponent { {'Make sure the'} - {account.currency.name} + {account.currency.managerAppName} {'app is installed to receive funds.'} diff --git a/src/components/BalanceSummary/index.js b/src/components/BalanceSummary/index.js index fc142a3a..c2a54702 100644 --- a/src/components/BalanceSummary/index.js +++ b/src/components/BalanceSummary/index.js @@ -73,6 +73,7 @@ const BalanceSummary = ({ } height={200} currency={counterValue} + cvCode={counterValue.ticker} tickXScale={selectedTimeRange} renderTickY={ isAvailable ? val => formatShort(counterValue.units[0], val) : () => '' diff --git a/src/components/CalculateBalance.js b/src/components/CalculateBalance.js index 792f8f17..b54fe189 100644 --- a/src/components/CalculateBalance.js +++ b/src/components/CalculateBalance.js @@ -79,7 +79,9 @@ const mapStateToProps = (state: State, props: OwnProps) => { balanceHistory, balanceStart: balanceHistory[0].value, balanceEnd, - hash: `${balanceHistory.length}_${balanceEnd}_${isAvailable.toString()}`, + hash: `${props.accounts.length > 0 ? props.accounts[0].id : ''}_${ + balanceHistory.length + }_${balanceEnd}_${isAvailable.toString()}`, } } diff --git a/src/components/DeviceBusyIndicator.js b/src/components/DeviceBusyIndicator.js index 47f45b99..7ecfbe27 100644 --- a/src/components/DeviceBusyIndicator.js +++ b/src/components/DeviceBusyIndicator.js @@ -14,10 +14,10 @@ const Indicator = styled.div` ` // NB this is done like this to be extremely performant. we don't want redux for this.. -const perPaths = {} +let globalBusy = false const instances = [] -export const onSetDeviceBusy = (path, busy) => { - perPaths[path] = busy +export const onSetDeviceBusy = busy => { + globalBusy = busy instances.forEach(i => i.forceUpdate()) } @@ -30,8 +30,7 @@ class DeviceBusyIndicator extends PureComponent<{}> { instances.splice(i, 1) } render() { - const busy = Object.values(perPaths).reduce((busy, b) => busy || b, false) - return + return } } diff --git a/src/components/EnsureDeviceApp.js b/src/components/EnsureDeviceApp.js index d001471d..6e1be5ac 100644 --- a/src/components/EnsureDeviceApp.js +++ b/src/components/EnsureDeviceApp.js @@ -24,7 +24,6 @@ import type { Device } from 'types/common' import { createCustomErrorClass } from 'helpers/errors' import { getCurrentDevice } from 'reducers/devices' -export const WrongAppOpened = createCustomErrorClass('WrongAppOpened') export const WrongDeviceForAccount = createCustomErrorClass('WrongDeviceForAccount') const usbIcon = @@ -77,20 +76,21 @@ class EnsureDeviceApp extends Component<{ const cur = account ? account.currency : currency invariant(cur, 'No currency given') return ( - + {'Open the '} - {cur.name} + {cur.managerAppName} {' app on your device'} ) } render() { - const { account, currency, ...props } = this.props + const { account, currency, device, ...props } = this.props const cur = account ? account.currency : currency const Icon = cur ? getCryptoCurrencyIcon(cur) : null return ( , }, { key: 'changelly', id: 'changelly', - url: 'https://changelly.com/?ref_id=aac789605a01', + url: urls.changelly, logo: , }, { key: 'coinmama', id: 'coinmama', - url: 'http://go.coinmama.com/visit/?bta=51801&nci=5343', + url: urls.coinmama, logo: , }, + { + key: 'simplex', + id: 'simplex', + url: urls.simplex, + logo: , + }, + { + key: 'paybis', + id: 'paybis', + url: urls.paybis, + logo: , + }, ] class ExchangePage extends PureComponent { diff --git a/src/components/ExchangePage/logos/paybis.js b/src/components/ExchangePage/logos/paybis.js new file mode 100644 index 00000000..2c6336ac --- /dev/null +++ b/src/components/ExchangePage/logos/paybis.js @@ -0,0 +1,46 @@ +// @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 new file mode 100644 index 00000000..94d68a37 --- /dev/null +++ b/src/components/ExchangePage/logos/simplex.js @@ -0,0 +1,27 @@ +// @flow + +import React, { Fragment } from 'react' + +const inner = ( + + + + + +) + +export default ({ width, height }: { width: number, height: number }) => ( + + {inner} + +) diff --git a/src/components/ExportLogsBtn.js b/src/components/ExportLogsBtn.js index e69dcb0d..199b2feb 100644 --- a/src/components/ExportLogsBtn.js +++ b/src/components/ExportLogsBtn.js @@ -6,15 +6,25 @@ import { webFrame, remote } from 'electron' import React, { Component } from 'react' import { translate } from 'react-i18next' import KeyHandler from 'react-key-handler' -import { getCurrentLogFile } from 'helpers/resolveLogsDirectory' import Button from './base/Button' +function writeToFile(file, data) { + return new Promise((resolve, reject) => { + fs.writeFile(file, data, error => { + if (error) { + reject(error) + } else { + resolve() + } + }) + }) +} + class ExportLogsBtn extends Component<{ t: *, hookToShortcut?: boolean, }> { - handleExportLogs = () => { - const srcLogFile = getCurrentLogFile() + export = async () => { const resourceUsage = webFrame.getResourceUsage() logger.log('exportLogsMeta', { resourceUsage, @@ -23,23 +33,39 @@ class ExportLogsBtn extends Component<{ environment: __DEV__ ? 'development' : 'production', userAgent: window.navigator.userAgent, }) + const date = new Date() // we don't want all the logs that happen after the Export was pressed ^^ const path = remote.dialog.showSaveDialog({ title: 'Export logs', defaultPath: `ledgerlive-export-${moment().format( 'YYYY.MM.DD-HH.mm.ss', - )}-${__GIT_REVISION__ || 'unversionned'}.log`, + )}-${__GIT_REVISION__ || 'unversionned'}.json`, filters: [ { name: 'All Files', - extensions: ['log'], + extensions: ['json'], }, ], }) if (path) { - fs.createReadStream(srcLogFile).pipe(fs.createWriteStream(path)) + const logs = await logger.queryAllLogs(date) + const json = JSON.stringify(logs) + await writeToFile(path, json) } } + exporting = false + handleExportLogs = () => { + if (this.exporting) return + this.exporting = true + this.export() + .catch(e => { + logger.critical(e) + }) + .then(() => { + this.exporting = false + }) + } + onKeyHandle = e => { if (e.ctrlKey) { this.handleExportLogs() diff --git a/src/components/FeesField/GenericContainer.js b/src/components/FeesField/GenericContainer.js index aa4a857d..af1d9c67 100644 --- a/src/components/FeesField/GenericContainer.js +++ b/src/components/FeesField/GenericContainer.js @@ -6,7 +6,7 @@ import Box from 'components/base/Box' import LabelWithExternalIcon from 'components/base/LabelWithExternalIcon' import { translate } from 'react-i18next' import { openURL } from 'helpers/linking' -import { urls } from 'config/support' +import { urls } from 'config/urls' import { track } from 'analytics/segment' export default translate()(({ children, t }: { children: React$Node, t: * }) => ( diff --git a/src/components/GenuineCheck.js b/src/components/GenuineCheck.js index dac98acc..eb599bd9 100644 --- a/src/components/GenuineCheck.js +++ b/src/components/GenuineCheck.js @@ -115,7 +115,7 @@ class GenuineCheck extends PureComponent { } render() { - const { onSuccess, ...props } = this.props + const { onSuccess, device, ...props } = this.props const steps = [ { id: 'device', @@ -156,6 +156,7 @@ class GenuineCheck extends PureComponent { return ( !step.external) .map(step => ({ ...step, label: t(step.label) })) const alreadyInitializedSteps = onboarding.steps - .filter(step => !step.external && step.name !== 'writeSeed' && step.name !== 'selectPIN') + .filter(step => !step.external && !step.options.alreadyInitSkip) .map(step => ({ ...step, label: t(step.label) })) - const filteredSteps = isInitializedFlow ? alreadyInitializedSteps : regularFilteredSteps + const onboardingRelaunchedSteps = onboarding.steps + .filter( + step => + isInitializedFlow + ? !step.options.alreadyInitSkip && !step.external && !step.options.relaunchSkip + : !step.external && !step.options.relaunchSkip, + ) + .map(step => ({ ...step, label: t(step.label) })) + + const filteredSteps = onboardingRelaunched + ? onboardingRelaunchedSteps + : isInitializedFlow + ? alreadyInitializedSteps + : regularSteps const stepIndex = findIndex(filteredSteps, s => s.name === stepName) const genuineStepIndex = findIndex(filteredSteps, s => s.name === 'genuineCheck') diff --git a/src/components/Onboarding/steps/Finish.js b/src/components/Onboarding/steps/Finish.js index 80b3c2a1..da9155be 100644 --- a/src/components/Onboarding/steps/Finish.js +++ b/src/components/Onboarding/steps/Finish.js @@ -4,7 +4,7 @@ import React, { Component } from 'react' import { openURL } from 'helpers/linking' import styled from 'styled-components' import { i } from 'helpers/staticPath' -import { urls } from 'config/support' +import { urls } from 'config/urls' import Box from 'components/base/Box' import Button from 'components/base/Button' diff --git a/src/components/Onboarding/steps/GenuineCheck/index.js b/src/components/Onboarding/steps/GenuineCheck/index.js index 0518d0ee..ae472f9c 100644 --- a/src/components/Onboarding/steps/GenuineCheck/index.js +++ b/src/components/Onboarding/steps/GenuineCheck/index.js @@ -5,7 +5,7 @@ import { openURL } from 'helpers/linking' import { connect } from 'react-redux' import styled from 'styled-components' import { colors } from 'styles/theme' -import { urls } from 'config/support' +import { urls } from 'config/urls' import { updateGenuineCheck } from 'reducers/onboarding' @@ -61,12 +61,12 @@ class GenuineCheck extends PureComponent { const { t } = this.props return [ { - label: t('app:common.yes'), + label: t('app:common.labelYes'), key: 'yes', pass: true, }, { - label: t('app:common.no'), + label: t('app:common.labelNo'), key: 'no', pass: false, }, @@ -144,14 +144,17 @@ class GenuineCheck extends PureComponent { } contactSupport = () => { - openURL(urls.genuineCheckContactSupport) + openURL(urls.contactSupport) } handlePrevStep = () => { const { prevStep, onboarding, jumpStep } = this.props onboarding.flowType === 'initializedDevice' ? jumpStep('selectDevice') : prevStep() } - + handleNextStep = () => { + const { onboarding, jumpStep, nextStep } = this.props + onboarding.onboardingRelaunched ? jumpStep('finish') : nextStep() + } renderGenuineFail = () => ( { ) : ( diff --git a/src/components/Onboarding/steps/NoDevice.js b/src/components/Onboarding/steps/NoDevice.js index abe35227..29c8cfbd 100644 --- a/src/components/Onboarding/steps/NoDevice.js +++ b/src/components/Onboarding/steps/NoDevice.js @@ -7,7 +7,7 @@ import { i } from 'helpers/staticPath' import Box from 'components/base/Box' import GrowScroll from 'components/base/GrowScroll' import TrackPage from 'analytics/TrackPage' -import { urls } from 'config/support' +import { urls } from 'config/urls' import IconCart from 'icons/Cart' import IconTruck from 'icons/Truck' import IconInfoCircle from 'icons/InfoCircle' diff --git a/src/components/OpenUserDataDirectoryBtn.js b/src/components/OpenUserDataDirectoryBtn.js new file mode 100644 index 00000000..bae4c3b1 --- /dev/null +++ b/src/components/OpenUserDataDirectoryBtn.js @@ -0,0 +1,30 @@ +// @flow + +import React, { Component } from 'react' +import logger from 'logger' +import { shell } from 'electron' +import { translate } from 'react-i18next' + +import resolveUserDataDirectory from 'helpers/resolveUserDataDirectory' +import Button from 'components/base/Button' + +class OpenUserDataDirectoryBtn extends Component<{ + t: *, +}> { + handleOpenUserDataDirectory = async () => { + const userDataDirectory = resolveUserDataDirectory() + logger.log(`Opening user data directory: ${userDataDirectory}`) + shell.openItem(userDataDirectory) + } + + render() { + const { t } = this.props + return ( + + ) + } +} + +export default translate()(OpenUserDataDirectoryBtn) diff --git a/src/components/RenderError.js b/src/components/RenderError.js index 0ed04df4..e20a7f9e 100644 --- a/src/components/RenderError.js +++ b/src/components/RenderError.js @@ -4,9 +4,9 @@ import React, { PureComponent } from 'react' import styled from 'styled-components' import { openURL } from 'helpers/linking' import { remote } from 'electron' -import qs from 'querystring' import { translate } from 'react-i18next' +import { urls } from 'config/urls' import { i } from 'helpers/staticPath' import hardReset from 'helpers/hardReset' @@ -47,21 +47,12 @@ class RenderError extends PureComponent< ) - handleCreateIssue = () => { - const { error } = this.props - if (!error) { - return - } - const q = qs.stringify({ - title: `Error: ${error.message}`, - body: `Error was thrown: - -\`\`\` -${error.stack} -\`\`\` -`, - }) - openURL(`https://github.com/LedgerHQ/ledger-live-desktop/issues/new?${q}`) + github = () => { + openURL(urls.githubIssues) + } + + contact = () => { + openURL(urls.contactSupport) } handleRestart = () => { @@ -105,8 +96,11 @@ ${error.stack} {t('app:crash.restart')} - +