diff --git a/.circleci/config.yml b/.circleci/config.yml index 4ef83dc7..e590dbc9 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -9,7 +9,8 @@ jobs: build: <<: *defaults steps: - - run: sudo apt-get install -y libudev-dev libfuse-dev + - run: sudo apt-get update + - run: sudo apt-get install -y libudev-dev - run: name: Install latest yarn command: | @@ -20,14 +21,13 @@ jobs: - checkout - restore_cache: keys: - - v11-yarn-packages-{{ checksum "yarn.lock" }} + - v12-yarn-packages-{{ checksum "yarn.lock" }} - run: yarn install - save_cache: - key: v11-yarn-packages-{{ checksum "yarn.lock" }} + key: v12-yarn-packages-{{ checksum "yarn.lock" }} paths: - node_modules - run: yarn lint - run: ./node_modules/.bin/prettier -l "{src,webpack,.storybook,static/i18n}/**/*.js" - run: yarn flow --quiet - run: yarn test - - run: yarn release diff --git a/package.json b/package.json index c3c03da9..82192516 100644 --- a/package.json +++ b/package.json @@ -35,13 +35,13 @@ } }, "dependencies": { - "@ledgerhq/hw-app-btc": "^4.27.0", + "@ledgerhq/hw-app-btc": "^v4.30.0", "@ledgerhq/hw-app-eth": "^4.24.0", "@ledgerhq/hw-app-xrp": "^4.25.0", "@ledgerhq/hw-transport": "^4.24.0", "@ledgerhq/hw-transport-node-hid": "4.24.0", - "@ledgerhq/ledger-core": "2.0.0-rc.11", - "@ledgerhq/live-common": "4.4.2", + "@ledgerhq/ledger-core": "2.0.0-rc.12", + "@ledgerhq/live-common": "4.6.0", "animated": "^0.2.2", "async": "^2.6.1", "axios": "^0.18.0", diff --git a/scripts/cli/cli.sh b/scripts/cli/cli.sh index 3484d589..93a96030 100644 --- a/scripts/cli/cli.sh +++ b/scripts/cli/cli.sh @@ -6,4 +6,4 @@ export LEDGER_LOGS_DIRECTORY="$LEDGER_DATA_DIR/logs" export LEDGER_LIVE_SQLITE_PATH="$LEDGER_DATA_DIR/sqlite" export CLI=1 -node -r @babel/register scripts/cli/txBetweenAccounts.js +node -r @babel/register -r @babel/polyfill scripts/cli/txBetweenAccounts.js diff --git a/scripts/cli/txBetweenAccounts.js b/scripts/cli/txBetweenAccounts.js index bc7522ff..e6838653 100644 --- a/scripts/cli/txBetweenAccounts.js +++ b/scripts/cli/txBetweenAccounts.js @@ -4,17 +4,15 @@ import chalk from 'chalk' import path from 'path' import fs from 'fs' import inquirer from 'inquirer' -import { createAccountModel } from '@ledgerhq/live-common/lib/models/account' -import { formatCurrencyUnit } from '@ledgerhq/live-common/lib/helpers/currencies' +import { formatCurrencyUnit } from '@ledgerhq/live-common/lib/currencies' import 'globals' import withLibcore from 'helpers/withLibcore' +import accountModel from 'helpers/accountModel' import { doSignAndBroadcast } from 'commands/libcoreSignAndBroadcast' import getDevice from './getDevice' -const accountModel = createAccountModel() - async function main() { try { // GET ACCOUNTS diff --git a/src/actions/settings.js b/src/actions/settings.js index afd189d4..dae9f335 100644 --- a/src/actions/settings.js +++ b/src/actions/settings.js @@ -15,6 +15,7 @@ export const setDeveloperMode = (developerMode: boolean) => saveSettings({ devel export const setSentryLogs = (sentryLogs: boolean) => saveSettings({ sentryLogs }) export const setShareAnalytics = (shareAnalytics: boolean) => saveSettings({ shareAnalytics }) export const setMarketIndicator = (marketIndicator: *) => saveSettings({ marketIndicator }) +export const setAutoLockTimeout = (autoLockTimeout: *) => saveSettings({ autoLockTimeout }) export const setCounterValue = (counterValue: string) => saveSettings({ counterValue, diff --git a/src/commands/libcoreSignAndBroadcast.js b/src/commands/libcoreSignAndBroadcast.js index e91d35a9..c9413694 100644 --- a/src/commands/libcoreSignAndBroadcast.js +++ b/src/commands/libcoreSignAndBroadcast.js @@ -115,6 +115,9 @@ async function signTransaction({ if (blockHeight >= 419200) { additionals.push('sapling') } + } else if (currency.id === 'decred') { + expiryHeight = Buffer.from([0x00, 0x00, 0x00, 0x00]) + additionals.push('decred') } const rawInputs = transaction.getInputs() @@ -129,6 +132,7 @@ async function signTransaction({ true, // set to true allow both segwit AND non-segwit hasTimestamp, hasExtraData, + additionals, ) const outputIndex = input.getPreviousOutputIndex() const sequence = input.getSequence() diff --git a/src/components/CurrencyDownStatusAlert.js b/src/components/CurrencyDownStatusAlert.js index e39a5ff7..8271e96c 100644 --- a/src/components/CurrencyDownStatusAlert.js +++ b/src/components/CurrencyDownStatusAlert.js @@ -5,7 +5,6 @@ import { translate } from 'react-i18next' import styled from 'styled-components' import { connect } from 'react-redux' import { createStructuredSelector } from 'reselect' -import type { CryptoCurrency } from '@ledgerhq/live-common/lib/types' import type { CurrencyStatus } from 'reducers/currenciesStatus' import { currencyDownStatus } from 'reducers/currenciesStatus' diff --git a/src/components/EnsureDeviceApp.js b/src/components/EnsureDeviceApp.js index 1d300a98..e742a663 100644 --- a/src/components/EnsureDeviceApp.js +++ b/src/components/EnsureDeviceApp.js @@ -77,7 +77,7 @@ class EnsureDeviceApp extends Component<{ const cur = account ? account.currency : currency invariant(cur, 'No currency given') return ( - + {'Open the '} {cur.managerAppName} {' app on your device'} @@ -97,7 +97,7 @@ class EnsureDeviceApp extends Component<{ { id: 'device', title: ( - + {'Connect and unlock your '} {'Ledger device'} diff --git a/src/components/GenuineCheck.js b/src/components/GenuineCheck.js index 325f9dfa..aef771e9 100644 --- a/src/components/GenuineCheck.js +++ b/src/components/GenuineCheck.js @@ -124,7 +124,7 @@ class GenuineCheck extends PureComponent { { id: 'device', title: ( - + {'Connect and unlock your '} {'Ledger device'} @@ -135,10 +135,10 @@ class GenuineCheck extends PureComponent { { id: 'deviceInfo', title: ( - + {'Navigate to the '} {'dashboard'} - {' on your device'} + {' app on your device'} ), icon: homeIcon, @@ -147,7 +147,7 @@ class GenuineCheck extends PureComponent { { id: 'isGenuine', title: ( - + {'Allow '} {'Ledger Manager'} {' on your device'} diff --git a/src/components/Idler.js b/src/components/Idler.js new file mode 100644 index 00000000..12444110 --- /dev/null +++ b/src/components/Idler.js @@ -0,0 +1,66 @@ +// @flow + +import { PureComponent } from 'react' +import { createStructuredSelector } from 'reselect' +import { connect } from 'react-redux' +import { hasPasswordSelector, autoLockTimeoutSelector } from 'reducers/settings' +import debounce from 'lodash/debounce' +import { lock } from 'reducers/application' + +type Props = { + autoLockTimeout: number, + hasPassword: boolean, + lock: Function, +} + +const mapStateToProps = createStructuredSelector({ + autoLockTimeout: autoLockTimeoutSelector, + hasPassword: hasPasswordSelector, +}) + +const mapDispatchToProps = { + lock, +} + +class Idler extends PureComponent { + componentDidMount() { + window.addEventListener('keydown', this.debounceOnChange) + window.addEventListener('mouseover', this.debounceOnChange) + this.interval = setInterval(this.checkForAutoLock, 10000) + } + + componentWillUnmount() { + window.removeEventListener('keydown', this.debounceOnChange) + window.removeEventListener('mouseover', this.debounceOnChange) + clearInterval(this.interval) + this.debounceOnChange.cancel() + } + + interval: IntervalID + + lastAction: number = -1 + + debounceOnChange = debounce(_ => this.idleTimeHandler(), 1000) + + checkForAutoLock = _ => { + const timeout = this.props.autoLockTimeout + if (this.props.hasPassword && timeout && timeout !== -1) { + if (Date.now() - (this.lastAction + timeout * 60000) > 0) { + this.props.lock() + } + } + } + + idleTimeHandler = _ => { + this.lastAction = Date.now() + } + + render() { + return null + } +} + +export default connect( + mapStateToProps, + mapDispatchToProps, +)(Idler) diff --git a/src/components/MainSideBar/index.js b/src/components/MainSideBar/index.js index fbf843ed..8fa91bf5 100644 --- a/src/components/MainSideBar/index.js +++ b/src/components/MainSideBar/index.js @@ -1,7 +1,7 @@ // @flow import React, { PureComponent } from 'react' -import { translate } from 'react-i18next' +import { Trans, translate } from 'react-i18next' import { connect } from 'react-redux' import { compose } from 'redux' import { withRouter } from 'react-router' @@ -87,13 +87,6 @@ class MainSideBar extends PureComponent { push(to) } - ADD_ACCOUNT_EMPTY_STATE = ( - - - {this.props.t('emptyState.sidebar.text')} - - ) - handleClickDashboard = () => this.push('/') handleOpenSendModal = () => this.props.openModal(MODAL_SEND) handleOpenReceiveModal = () => this.props.openModal(MODAL_RECEIVE) @@ -110,6 +103,13 @@ class MainSideBar extends PureComponent { ) + const emptyState = ( + + + + + ) + return ( @@ -168,7 +168,7 @@ class MainSideBar extends PureComponent { {accounts.map(account => ( ) } +export function BulletRow({ step, ...p }: { step: StepType }) { + const { icon, desc } = step + return ( + + + {icon} + + + {desc} + + + ) +} export const OptionRowDesc = styled(Box).attrs({ ff: 'Open Sans|Regular', fontSize: 4, diff --git a/src/components/Onboarding/steps/SelectPIN/SelectPINnano.js b/src/components/Onboarding/steps/SelectPIN/SelectPINnano.js index 98af5d0e..2041a11c 100644 --- a/src/components/Onboarding/steps/SelectPIN/SelectPINnano.js +++ b/src/components/Onboarding/steps/SelectPIN/SelectPINnano.js @@ -30,14 +30,9 @@ class SelectPINnano extends PureComponent { { key: 'step2', icon: {'2.'}, - desc: t('onboarding.selectPIN.initialize.instructions.nano.step2'), - }, - { - key: 'step3', - icon: {'3.'}, desc: ( - + {'Press the right button to select'} {'Configure as new device'} @@ -46,6 +41,11 @@ class SelectPINnano extends PureComponent { ), }, + { + key: 'step3', + icon: {'3.'}, + desc: t('onboarding.selectPIN.initialize.instructions.nano.step3'), + }, { key: 'step4', icon: {'4.'}, diff --git a/src/components/Onboarding/steps/SelectPIN/SelectPINrestoreNano.js b/src/components/Onboarding/steps/SelectPIN/SelectPINrestoreNano.js index b0d839b4..d7e63124 100644 --- a/src/components/Onboarding/steps/SelectPIN/SelectPINrestoreNano.js +++ b/src/components/Onboarding/steps/SelectPIN/SelectPINrestoreNano.js @@ -30,19 +30,14 @@ class SelectPINrestoreNano extends PureComponent { { key: 'step2', icon: {'2.'}, - desc: t('onboarding.selectPIN.restore.instructions.nano.step2'), - }, - { - key: 'step3', - icon: {'3.'}, desc: ( - + {'Press the left button to cancel'} {'Initialize as new device?'} - {'Press the right button to select'} + {'Then press the right button to select'} {'Restore configuration?'} @@ -50,6 +45,11 @@ class SelectPINrestoreNano extends PureComponent { ), }, + { + key: 'step3', + icon: {'3.'}, + desc: t('onboarding.selectPIN.restore.instructions.nano.step3'), + }, { key: 'step4', icon: {'4.'}, diff --git a/src/components/PillsDaysCount.js b/src/components/PillsDaysCount.js index fe3519b5..16272467 100644 --- a/src/components/PillsDaysCount.js +++ b/src/components/PillsDaysCount.js @@ -10,7 +10,7 @@ import Track from 'analytics/Track' type Props = { selected: string, - onChange: ({ key: string, value: *, label: string }) => *, + onChange: ({ key: string, value: *, label: React$Node }) => *, t: T, } diff --git a/src/components/SettingsPage/PasswordAutoLockSelect.js b/src/components/SettingsPage/PasswordAutoLockSelect.js new file mode 100644 index 00000000..556fa845 --- /dev/null +++ b/src/components/SettingsPage/PasswordAutoLockSelect.js @@ -0,0 +1,63 @@ +// @flow + +import React, { PureComponent } from 'react' +import { translate } from 'react-i18next' +import { createStructuredSelector } from 'reselect' +import { connect } from 'react-redux' +import { setAutoLockTimeout, saveSettings } from 'actions/settings' +import Select from 'components/base/Select' +import type { T } from 'types/common' +import { autoLockTimeoutSelector } from 'reducers/settings' + +type Props = { + autoLockTimeout: string, + setAutoLockTimeout: (?number) => void, + t: T, +} + +const mapStateToProps = createStructuredSelector({ + autoLockTimeout: autoLockTimeoutSelector, +}) + +const mapDispatchToProps = { + saveSettings, + setAutoLockTimeout, +} + +class PasswordAutoLockSelect extends PureComponent { + handleChangeTimeout = ({ value: timeoutKey }: *) => { + this.props.setAutoLockTimeout(+timeoutKey) + } + + timeouts = [ + { value: 1, label: `1 ${this.props.t('app:time.minute')}` }, + { value: 10, label: `10 ${this.props.t('app:time.minute')}s` }, + { value: 30, label: `30 ${this.props.t('app:time.minute')}s` }, + { value: 60, label: `1 ${this.props.t('app:time.hour')}` }, + { value: -1, label: this.props.t(`app:common.never`) }, + ] + + render() { + const { autoLockTimeout } = this.props + const currentTimeout = this.timeouts.find(l => l.value === autoLockTimeout) + + return ( +