Browse Source

Merge branch 'develop' into feature/getDeviceInfo

master
amougel 7 years ago
parent
commit
4957ab6a6d
  1. 3
      .npmrc
  2. 1
      .nvmrc
  3. 4
      electron-builder.yml
  4. 3
      package.json
  5. 12
      scripts/compile.sh
  6. 3
      scripts/dist-dir.sh
  7. 2
      scripts/start.sh
  8. 2
      src/api/Ethereum.js
  9. 2
      src/api/Fees.js
  10. 2
      src/api/network.js
  11. 2
      src/commands/libcoreGetFees.js
  12. 2
      src/commands/libcoreHardReset.js
  13. 2
      src/components/AccountPage/index.js
  14. 2
      src/components/AppError.js
  15. 6
      src/components/BalanceSummary/BalanceInfos.js
  16. 4
      src/components/EnsureDeviceApp.js
  17. 23
      src/components/ExportLogsBtn.js
  18. 2
      src/components/ManagerPage/AppsList.js
  19. 2
      src/components/Onboarding/helperComponents.js
  20. 9
      src/components/Onboarding/steps/GenuineCheck.js
  21. 9
      src/components/Onboarding/steps/SelectDevice.js
  22. 4
      src/components/Onboarding/steps/SelectPIN/SelectPINblue.js
  23. 4
      src/components/Onboarding/steps/SelectPIN/SelectPINnano.js
  24. 4
      src/components/Onboarding/steps/SelectPIN/SelectPINrestoreBlue.js
  25. 4
      src/components/Onboarding/steps/SelectPIN/SelectPINrestoreNano.js
  26. 4
      src/components/Onboarding/steps/Start.js
  27. 4
      src/components/Onboarding/steps/WriteSeed/WriteSeedBlue.js
  28. 4
      src/components/Onboarding/steps/WriteSeed/WriteSeedNano.js
  29. 4
      src/components/Onboarding/steps/WriteSeed/WriteSeedRestore.js
  30. 41
      src/components/RenderError.js
  31. 2
      src/components/RequestAmount/index.js
  32. 8
      src/components/SelectAccount/index.js
  33. 2
      src/components/SettingsPage/sections/Currencies.js
  34. 2
      src/components/base/Box/Box.js
  35. 2
      src/components/base/SideBar/SideBarListItem.js
  36. 81
      src/components/base/Stepper/index.js
  37. 50
      src/components/base/Stepper/stories.js
  38. 208
      src/components/modals/AddAccounts/index.js
  39. 15
      src/components/modals/AddAccounts/steps/01-step-choose-currency.js
  40. 6
      src/components/modals/AddAccounts/steps/02-step-connect-device.js
  41. 81
      src/components/modals/AddAccounts/steps/03-step-import.js
  42. 9
      src/components/modals/OperationDetails.js
  43. 2
      src/components/modals/Send/index.js
  44. 5
      src/config/constants.js
  45. 2
      src/helpers/apps/installApp.js
  46. 2
      src/helpers/apps/uninstallApp.js
  47. 14
      src/helpers/createCustomErrorClass.js
  48. 2
      src/helpers/devices/getNextMCU.js
  49. 79
      src/helpers/errors.js
  50. 2
      src/helpers/getAddressForCurrency/btc.js
  51. 7
      src/helpers/ipc.js
  52. 2
      src/helpers/libcore.js
  53. 4
      src/helpers/promise.js
  54. 4
      src/helpers/socket.js
  55. 5
      src/icons/Home.js
  56. 0
      src/icons/SensitiveOperationShield.js
  57. 79
      src/icons/illustrations/GetStartedLogo.js
  58. 32
      src/icons/illustrations/LedgerBlue.js
  59. 47
      src/icons/illustrations/LedgerBlueError.js
  60. 47
      src/icons/illustrations/LedgerBlueSelectPIN.js
  61. 44
      src/icons/illustrations/LedgerNano.js
  62. 78
      src/icons/illustrations/LedgerNanoError.js
  63. 60
      src/icons/illustrations/LedgerNanoSelectPIN.js
  64. 63
      src/icons/illustrations/SetPassword.js
  65. 40
      src/icons/illustrations/WriteSeed.js
  66. 7
      src/internals/index.js
  67. 16
      src/logger.js
  68. 41
      src/main/app.js
  69. 2
      src/main/bridge.js
  70. 32
      src/main/terminator.js
  71. 2
      src/stories/icons.stories.js
  72. 8
      static/i18n/en/app.yml
  73. 8
      static/i18n/fr/app.yml
  74. 9
      static/i18n/fr/errors.yml
  75. 19
      static/images/blue-error-onb.svg
  76. 13
      static/images/get-started-onb.svg
  77. 7
      static/images/ledger-blue-onb.svg
  78. 10
      static/images/ledger-nano-onb.svg
  79. 34
      static/images/nano-error-onb.svg
  80. 19
      static/images/select-pin-blue-onb.svg
  81. 25
      static/images/select-pin-nano-onb.svg
  82. 9
      static/images/write-seed-onb.svg
  83. 23
      webpack/renderer.config.js
  84. 17
      yarn.lock

3
.npmrc

@ -0,0 +1,3 @@
npm_config_target=1.8.7
npm_config_disturl=https://atom.io/download/electron
npm_config_runtime=electron

1
.nvmrc

@ -0,0 +1 @@
lts/*

4
electron-builder.yml

@ -28,6 +28,10 @@ linux:
win:
artifactName: ${name}-${version}-${os}-${arch}.${ext}
certificateSubjectName: Ledger SAS
certificateSha1: 7dd9acb2ef0402883c65901ebbafd06e5293d391
signingHashAlgorithms:
- sha256
target:
- target: nsis
arch:

3
package.json

@ -8,7 +8,7 @@
"license": "MIT",
"scripts": {
"compile": "bash ./scripts/compile.sh",
"dist:dir": "yarn dist --dir -c.compression=store -c.mac.identity=null",
"dist:dir": "bash ./scripts/dist-dir.sh",
"dist": "bash ./scripts/dist.sh",
"test": "jest",
"flow": "flow",
@ -156,6 +156,7 @@
"prettier": "^1.13.5",
"react-hot-loader": "^4.3.2",
"react-test-renderer": "^16.4.1",
"uglifyjs-webpack-plugin": "^1.2.6",
"webpack": "^4.6.0",
"webpack-bundle-analyzer": "^2.11.1",
"webpack-cli": "^2.0.14",

12
scripts/compile.sh

@ -2,11 +2,11 @@
set -e
export GIT_REVISION=`git rev-parse HEAD`
export SENTRY_URL=https://db8f5b9b021048d4a401f045371701cb@sentry.io/274561
GIT_REVISION=`git rev-parse HEAD`
SENTRY_URL=https://db8f5b9b021048d4a401f045371701cb@sentry.io/274561
NODE_ENV=production
rm -rf ./node_modules/.cache dist
yarn
rm -rf dist &&
NODE_ENV=production yarn run webpack-cli --mode production --config webpack/internals.config.js &&
NODE_ENV=production yarn run electron-webpack
JOBS=max yarn
yarn run webpack-cli --mode production --config webpack/internals.config.js
yarn run electron-webpack

3
scripts/dist-dir.sh

@ -0,0 +1,3 @@
#/bin/bash
yarn compile && DEBUG=electron-builder electron-builder --dir -c.compression=store -c.mac.identity=null

2
scripts/start.sh

@ -1,5 +1,5 @@
#/bin/bash
concurrently --raw \
concurrently --raw --kill-others \
"cross-env NODE_ENV=development webpack-cli --mode development --watch --config webpack/internals.config.js" \
"cross-env NODE_ENV=development electron-webpack dev"

2
src/api/Ethereum.js

@ -1,6 +1,6 @@
// @flow
import type { CryptoCurrency } from '@ledgerhq/live-common/lib/types'
import createCustomErrorClass from 'helpers/createCustomErrorClass'
import { createCustomErrorClass } from 'helpers/errors'
import network from './network'
import { blockchainBaseURL } from './Ledger'

2
src/api/Fees.js

@ -2,7 +2,7 @@
import invariant from 'invariant'
import LRU from 'lru-cache'
import type { Currency } from '@ledgerhq/live-common/lib/types'
import createCustomErrorClass from 'helpers/createCustomErrorClass'
import { createCustomErrorClass } from 'helpers/errors'
import { blockchainBaseURL } from './Ledger'
import network from './network'

2
src/api/network.js

@ -3,7 +3,7 @@ import axios from 'axios'
import { GET_CALLS_RETRY, GET_CALLS_TIMEOUT } from 'config/constants'
import { retry } from 'helpers/promise'
import logger from 'logger'
import createCustomErrorClass from 'helpers/createCustomErrorClass'
import { createCustomErrorClass } from 'helpers/errors'
export const LedgerAPIErrorWithMessage = createCustomErrorClass('LedgerAPIErrorWithMessage')
export const LedgerAPIError = createCustomErrorClass('LedgerAPIError')

2
src/commands/libcoreGetFees.js

@ -5,7 +5,7 @@ import withLibcore from 'helpers/withLibcore'
import { createCommand, Command } from 'helpers/ipc'
import * as accountIdHelper from 'helpers/accountId'
import { isValidAddress } from 'helpers/libcore'
import createCustomErrorClass from 'helpers/createCustomErrorClass'
import { createCustomErrorClass } from 'helpers/errors'
const InvalidAddress = createCustomErrorClass('InvalidAddress')

2
src/commands/libcoreHardReset.js

@ -3,7 +3,7 @@
import { createCommand } from 'helpers/ipc'
import { fromPromise } from 'rxjs/observable/fromPromise'
import withLibcore from 'helpers/withLibcore'
import createCustomErrorClass from 'helpers/createCustomErrorClass'
import { createCustomErrorClass } from 'helpers/errors'
const HardResetFail = createCustomErrorClass('HardResetFail')

2
src/components/AccountPage/index.js

@ -150,12 +150,14 @@ class AccountPage extends PureComponent<Props> {
<Box flow={4} mb={2}>
<Box horizontal>
<BalanceTotal
showCryptoEvenIfNotAvailable
isAvailable={isAvailable}
totalBalance={account.balance}
unit={account.unit}
>
<FormattedVal
animateTicker
disableRounding
alwaysShowSign={false}
color="warmGrey"
unit={counterValue.units[0]}

2
src/components/AppError.js

@ -13,7 +13,7 @@ import RenderError from './RenderError'
const App = ({ language, error }: { error: Error, language: string }) => (
<I18nextProvider i18n={i18n} initialLanguage={language}>
<ThemeProvider theme={theme}>
<RenderError disableExport error={error}>
<RenderError withoutAppData error={error}>
<TriggerAppReady />
</RenderError>
</ThemeProvider>

6
src/components/BalanceSummary/BalanceInfos.js

@ -32,6 +32,7 @@ type BalanceTotalProps = {
unit: Unit,
isAvailable: boolean,
totalBalance: number,
showCryptoEvenIfNotAvailable?: boolean,
}
type Props = {
@ -90,10 +91,10 @@ export function BalanceSinceDiff(props: Props) {
}
export function BalanceTotal(props: BalanceTotalProps) {
const { unit, totalBalance, isAvailable, children } = props
const { unit, totalBalance, isAvailable, showCryptoEvenIfNotAvailable, children } = props
return (
<Box grow {...props}>
{!isAvailable ? (
{!isAvailable && !showCryptoEvenIfNotAvailable ? (
<PlaceholderLine width={150} />
) : (
<FormattedVal
@ -101,6 +102,7 @@ export function BalanceTotal(props: BalanceTotalProps) {
color="dark"
unit={unit}
fontSize={8}
disableRounding
showCode
val={totalBalance}
/>

4
src/components/EnsureDeviceApp/index.js → src/components/EnsureDeviceApp.js

@ -13,7 +13,7 @@ import type { State as StoreState } from 'reducers/index'
import getAddress from 'commands/getAddress'
import { standardDerivation } from 'helpers/derivations'
import isDashboardOpen from 'commands/isDashboardOpen'
import createCustomErrorClass from 'helpers/createCustomErrorClass'
import { createCustomErrorClass } from 'helpers/errors'
import { CHECK_APP_INTERVAL_WHEN_VALID, CHECK_APP_INTERVAL_WHEN_INVALID } from 'config/constants'
@ -36,7 +36,7 @@ type OwnProps = {
deviceSelected: ?Device,
deviceStatus: DeviceStatus,
error: ?Error,
}) => React$Element<*>,
}) => React$Node,
}
type Props = OwnProps & {

23
src/components/ExportLogsBtn.js

@ -19,15 +19,24 @@ const mapStateToProps = createStructuredSelector({
class ExportLogsBtn extends Component<{
t: *,
settings: *,
accounts: *,
settings: ?*,
accounts: ?*,
hookToShortcut?: boolean,
}> {
handleExportLogs = () => {
const { accounts, settings } = this.props
const logs = logger.exportLogs()
const resourceUsage = webFrame.getResourceUsage()
const report = { resourceUsage, logs, accounts, settings, date: new Date() }
const report = {
resourceUsage,
logs,
accounts,
settings,
date: new Date(),
release: __APP_VERSION__,
git_commit: __GIT_REVISION__,
environment: __DEV__ ? 'development' : 'production',
}
console.log(report) // eslint-disable-line no-console
const reportJSON = JSON.stringify(report)
const path = remote.dialog.showSaveDialog({
@ -69,4 +78,10 @@ class ExportLogsBtn extends Component<{
}
}
export default translate()(connect(mapStateToProps)(ExportLogsBtn))
const WithAppData = connect(mapStateToProps)(ExportLogsBtn)
const WithoutAppData = ExportLogsBtn
const ExportLogsBtnDispatcher = ({ withAppData, ...rest }: *) =>
withAppData ? <WithAppData {...rest} /> : <WithoutAppData {...rest} />
export default translate()(ExportLogsBtnDispatcher)

2
src/components/ManagerPage/AppsList.js

@ -117,7 +117,7 @@ class AppsList extends PureComponent<Props, State> {
} = this.props
const data = { app, devicePath, targetId }
await uninstallApp.send(data).toPromise()
this.setState({ status: 'success', app: '' })
this.setState({ status: 'success' })
} catch (err) {
this.setState({ status: 'error', error: err, app: '', mode: 'home' })
}

2
src/components/Onboarding/helperComponents.js

@ -5,7 +5,7 @@ import { radii } from 'styles/theme'
import Box from 'components/base/Box'
import GrowScroll from 'components/base/GrowScroll'
import IconSensitiveOperationShield from 'icons/illustrations/SensitiveOperationShield'
import IconSensitiveOperationShield from 'icons/SensitiveOperationShield'
// GENERAL
export const Title = styled(Box).attrs({

9
src/components/Onboarding/steps/GenuineCheck.js

@ -5,6 +5,7 @@ import { shell } from 'electron'
import { connect } from 'react-redux'
import styled from 'styled-components'
import { colors } from 'styles/theme'
import { i } from 'helpers/staticPath'
import type { T } from 'types/common'
@ -17,8 +18,6 @@ import RadioGroup from 'components/base/RadioGroup'
import GenuineCheckModal from 'components/GenuineCheckModal'
import TranslatedError from 'components/TranslatedError'
import IconLedgerNanoError from 'icons/illustrations/LedgerNanoError'
import IconLedgerBlueError from 'icons/illustrations/LedgerBlueError'
import IconCheck from 'icons/Check'
import IconCross from 'icons/Cross'
@ -325,8 +324,8 @@ export function GenuineCheckFail({
<Fragment>
<Title>{t('onboarding:genuineCheck.errorPage.ledgerNano.title')}</Title>
<Description>{t('onboarding:genuineCheck.errorPage.ledgerNano.desc')}</Description>
<Box style={{ width: 550 }} mt={5} ml={100}>
<IconLedgerNanoError />
<Box mt={5} mr={7}>
<img alt="" src={i('nano-error-onb.svg')} />
</Box>
</Fragment>
) : (
@ -336,7 +335,7 @@ export function GenuineCheckFail({
{t('onboarding:genuineCheck.errorPage.ledgerBlue.desc')}
</Description>
<Box alignItems="center">
<IconLedgerBlueError />
<img alt="" src={i('blue-error-onb.svg')} />
</Box>
</Fragment>
)}

9
src/components/Onboarding/steps/SelectDevice.js

@ -3,14 +3,15 @@
import React, { PureComponent } from 'react'
import styled from 'styled-components'
import { connect } from 'react-redux'
import { i } from 'helpers/staticPath'
import { rgba } from 'styles/helpers'
import { isLedgerNano } from 'reducers/onboarding'
import Box from 'components/base/Box'
import IconCheckCirle from 'icons/Check'
import IconLedgerNano from 'icons/illustrations/LedgerNano'
import IconLedgerBlue from 'icons/illustrations/LedgerBlue'
import { Title, Inner, FixedTopContainer, StepContainerInner } from '../helperComponents'
import OnboardingFooter from '../OnboardingFooter'
@ -47,7 +48,7 @@ class SelectDevice extends PureComponent<StepProps, {}> {
>
{onboarding.isLedgerNano && <DeviceSelected />}
<DeviceIcon>
<IconLedgerNano />
<img alt="" src={i('ledger-nano-onb.svg')} />
</DeviceIcon>
<BlockTitle>{t('onboarding:selectDevice.ledgerNanoCard.title')}</BlockTitle>
</DeviceContainer>
@ -57,7 +58,7 @@ class SelectDevice extends PureComponent<StepProps, {}> {
>
{!onboarding.isLedgerNano && onboarding.isLedgerNano !== null && <DeviceSelected />}
<DeviceIcon>
<IconLedgerBlue />
<img alt="" src={i('ledger-blue-onb.svg')} />
</DeviceIcon>
<BlockTitle>{t('onboarding:selectDevice.ledgerBlueCard.title')}</BlockTitle>
</DeviceContainer>

4
src/components/Onboarding/steps/SelectPIN/SelectPINblue.js

@ -2,11 +2,11 @@
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 IconLedgerBlueSelectPIN from 'icons/illustrations/LedgerBlueSelectPIN'
import IconChevronRight from 'icons/ChevronRight'
@ -60,7 +60,7 @@ class SelectPIN extends PureComponent<Props, *> {
<Box align="center">
<Inner style={{ width: 550 }}>
<Box style={{ width: 180, justifyContent: 'center', alignItems: 'center' }}>
<IconLedgerBlueSelectPIN />
<img alt="" src={i('select-pin-blue-onb.svg')} />
</Box>
<Box>
<Box shrink grow flow={4}>

4
src/components/Onboarding/steps/SelectPIN/SelectPINnano.js

@ -2,11 +2,11 @@
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 IconLedgerNanoSelectPIN from 'icons/illustrations/LedgerNanoSelectPIN'
import IconChevronRight from 'icons/ChevronRight'
@ -63,7 +63,7 @@ class SelectPINnano extends PureComponent<Props, *> {
return (
<Box align="center" mt={3}>
<Inner style={{ width: 700 }}>
<IconLedgerNanoSelectPIN />
<img alt="" src={i('select-pin-nano-onb.svg')} />
<Box shrink grow flow={4} style={{ marginLeft: 40 }}>
{stepsLedgerNano.map(step => <OptionRow key={step.key} step={step} />)}
</Box>

4
src/components/Onboarding/steps/SelectPIN/SelectPINrestoreBlue.js

@ -2,11 +2,11 @@
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 IconLedgerBlueSelectPIN from 'icons/illustrations/LedgerBlueSelectPIN'
import IconChevronRight from 'icons/ChevronRight'
@ -60,7 +60,7 @@ class SelectPINrestoreBlue extends PureComponent<Props, *> {
<Box align="center">
<Inner style={{ width: 550 }}>
<Box style={{ width: 180, justifyContent: 'center', alignItems: 'center' }}>
<IconLedgerBlueSelectPIN />
<img alt="" src={i('select-pin-blue-onb.svg')} />
</Box>
<Box>
<Box shrink grow flow={4}>

4
src/components/Onboarding/steps/SelectPIN/SelectPINrestoreNano.js

@ -2,11 +2,11 @@
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 IconLedgerNanoSelectPIN from 'icons/illustrations/LedgerNanoSelectPIN'
import IconChevronRight from 'icons/ChevronRight'
@ -63,7 +63,7 @@ class SelectPINrestoreNano extends PureComponent<Props, *> {
return (
<Box align="center" mt={3}>
<Inner style={{ width: 700 }}>
<IconLedgerNanoSelectPIN />
<img alt="" src={i('select-pin-nano-onb.svg')} />
<Box shrink grow flow={4} style={{ marginLeft: 40 }}>
{stepsLedgerNano.map(step => <OptionRow key={step.key} step={step} />)}
</Box>

4
src/components/Onboarding/steps/Start.js

@ -1,11 +1,11 @@
// @flow
import React from 'react'
import { i } from 'helpers/staticPath'
import Box from 'components/base/Box'
import Button from 'components/base/Button'
import IconGetStarted from 'icons/illustrations/GetStartedLogo'
import type { StepProps } from '..'
import { Title } from '../helperComponents'
@ -14,7 +14,7 @@ export default (props: StepProps) => {
return (
<Box sticky justifyContent="center">
<Box alignItems="center">
<IconGetStarted />
<img alt="" src={i('get-started-onb.svg')} />
<Box my={4}>
<Title>{t('onboarding:start.title')}</Title>
</Box>

4
src/components/Onboarding/steps/WriteSeed/WriteSeedBlue.js

@ -2,11 +2,11 @@
import React, { PureComponent, Fragment } from 'react'
import { translate } from 'react-i18next'
import { colors } from 'styles/theme'
import { i } from 'helpers/staticPath'
import type { T } from 'types/common'
import Box from 'components/base/Box'
import IconWriteSeed from 'icons/illustrations/WriteSeed'
import IconChevronRight from 'icons/ChevronRight'
import {
@ -75,7 +75,7 @@ class WriteSeedBlue extends PureComponent<Props, *> {
<Box align="center">
<Inner style={{ width: 760 }}>
<Box style={{ width: 260, justifyContent: 'center', alignItems: 'center' }}>
<IconWriteSeed />
<img alt="" src={i('write-seed-onb.svg')} />
</Box>
<Box shrink flow={2} m={0}>
{steps.map(step => <OptionRow key={step.key} step={step} />)}

4
src/components/Onboarding/steps/WriteSeed/WriteSeedNano.js

@ -2,11 +2,11 @@
import React, { PureComponent, Fragment } from 'react'
import { translate } from 'react-i18next'
import { colors } from 'styles/theme'
import { i } from 'helpers/staticPath'
import type { T } from 'types/common'
import Box from 'components/base/Box'
import IconWriteSeed from 'icons/illustrations/WriteSeed'
import IconChevronRight from 'icons/ChevronRight'
import {
@ -75,7 +75,7 @@ class WriteSeedNano extends PureComponent<Props, *> {
<Box align="center" mt={3}>
<Inner style={{ width: 700 }}>
<Box style={{ width: 300 }} justifyContent="center" alignItems="center">
<IconWriteSeed />
<img alt="" src={i('write-seed-onb.svg')} />
</Box>
<Box shrink grow flow={4}>

4
src/components/Onboarding/steps/WriteSeed/WriteSeedRestore.js

@ -2,11 +2,11 @@
import React, { PureComponent, Fragment } 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 IconWriteSeed from 'icons/illustrations/WriteSeed'
import type { OnboardingState } from 'reducers/onboarding'
import IconChevronRight from 'icons/ChevronRight'
@ -100,7 +100,7 @@ class WriteSeedRestore extends PureComponent<Props, *> {
<Box align="center">
<Inner style={{ width: 760 }}>
<Box style={{ width: 260, justifyContent: 'center', alignItems: 'center' }}>
<IconWriteSeed />
<img alt="" src={i('write-seed-onb.svg')} />
</Box>
{onboarding.isLedgerNano ? (
<Box shrink flow={2} m={0}>

41
src/components/RenderError.js

@ -1,6 +1,7 @@
// @flow
import React, { PureComponent } from 'react'
import styled from 'styled-components'
import { shell, remote } from 'electron'
import qs from 'querystring'
import { translate } from 'react-i18next'
@ -14,14 +15,14 @@ import ExportLogsBtn from 'components/ExportLogsBtn'
import Box from 'components/base/Box'
import Space from 'components/base/Space'
import Button from 'components/base/Button'
import ConfirmModal from 'components/base/Modal/ConfirmModal'
import IconTriangleWarning from 'icons/TriangleWarning'
import ConfirmModal from './base/Modal/ConfirmModal'
import { IconWrapperCircle } from './SettingsPage/sections/Profile'
type Props = {
error: Error,
t: T,
disableExport?: boolean,
withoutAppData?: boolean,
children?: *,
}
@ -75,7 +76,7 @@ ${error.stack}
}
render() {
const { error, t, disableExport, children } = this.props
const { error, t, withoutAppData, children } = this.props
const { isHardResetting, isHardResetModalOpened } = this.state
return (
<Box align="center" grow>
@ -100,7 +101,7 @@ ${error.stack}
<Button primary onClick={this.handleRestart}>
{t('app:crash.restart')}
</Button>
{!disableExport ? <ExportLogsBtn /> : null}
<ExportLogsBtn withoutAppData={withoutAppData} />
<Button primary onClick={this.handleCreateIssue}>
{t('app:crash.createTicket')}
</Button>
@ -120,10 +121,8 @@ ${error.stack}
renderIcon={this.hardResetIconRender}
/>
<Box my={6}>
<ErrContainer>
<strong>{String(error)}</strong>
<div>{error.stack || 'no stacktrace'}</div>
</ErrContainer>
<ErrContainer>{`${String(error)}
${error.stack || 'no stacktrace'}`}</ErrContainer>
</Box>
<pre
style={{
@ -142,21 +141,15 @@ ${error.stack}
}
}
const ErrContainer = ({ children }: { children: any }) => (
<pre
style={{
margin: 'auto',
maxWidth: '80vw',
overflow: 'auto',
fontSize: 10,
fontFamily: 'monospace',
cursor: 'text',
userSelect: 'text',
opacity: 0.3,
}}
>
{children}
</pre>
)
const ErrContainer = styled.pre`
margin: auto;
max-width: 80vw;
overflow: auto;
font-size: 10px;
font-family: monospace;
cursor: text;
user-select: text;
opacity: 0.3;
`
export default translate()(RenderError)

2
src/components/RequestAmount/index.js

@ -21,7 +21,7 @@ import InputCurrency from 'components/base/InputCurrency'
import Button from 'components/base/Button'
import Box from 'components/base/Box'
import type { State } from 'reducers'
import createCustomErrorClass from 'helpers/createCustomErrorClass'
import { createCustomErrorClass } from 'helpers/errors'
const NotEnoughBalance = createCustomErrorClass('NotEnoughBalance')

8
src/components/SelectAccount/index.js

@ -43,7 +43,13 @@ const renderOption = a => {
</Text>
</Box>
<Box>
<FormattedVal color="grey" val={account.balance} unit={account.unit} showCode />
<FormattedVal
color="grey"
val={account.balance}
unit={account.unit}
showCode
disableRounding
/>
</Box>
</Box>
)

2
src/components/SettingsPage/sections/Currencies.js

@ -112,7 +112,7 @@ class TabCurrencies extends PureComponent<Props, State> {
<Body>
{currency !== intermediaryCurrency ? (
<Row
title={t('app:settings.display.exchange', {
title={t('app:settings.currencies.exchange', {
ticker: `${currency.ticker}${intermediaryCurrency.ticker}`,
})}
desc={t('app:settings.currencies.exchangeDesc')}

2
src/components/base/Box/Box.js

@ -17,6 +17,7 @@ import {
import fontFamily from 'styles/styled/fontFamily'
export const styledTextAlign = style({ prop: 'textAlign', cssProperty: 'textAlign' })
export const styledOverflow = style({ prop: 'overflow', cssProperty: 'overflow' })
export const styledCursor = style({ prop: 'cursor', cssProperty: 'cursor' })
export const styledTextTransform = style({ prop: 'textTransform', cssProperty: 'textTransform' })
@ -34,6 +35,7 @@ export default styled.div`
${styledTextAlign};
${styledCursor};
${styledTextTransform};
${styledOverflow};
display: flex;
flex-shrink: ${p => (p.noShrink === true ? '0' : p.shrink === true ? '1' : '')};

2
src/components/base/SideBar/SideBarListItem.js

@ -6,7 +6,7 @@ import styled from 'styled-components'
import Box, { Tabbable } from 'components/base/Box'
export type Props = {
label: string | (Props => React$Element<any>),
label: string | (Props => React$Node),
desc?: Props => any, // TODO: type should be more precise, but, eh ¯\_(ツ)_/¯
icon?: any, // TODO: type should be more precise, but, eh ¯\_(ツ)_/¯
disabled?: boolean,

81
src/components/base/Stepper/index.js

@ -0,0 +1,81 @@
// @flow
import React, { PureComponent } from 'react'
import invariant from 'invariant'
import { translate } from 'react-i18next'
import type { T } from 'types/common'
import { ModalContent, ModalTitle, ModalFooter, ModalBody } from 'components/base/Modal'
import Breadcrumb from 'components/Breadcrumb'
type Props = {
t: T,
title: string,
initialStepId: string,
onClose: void => void,
steps: Step[],
children: any,
}
export type Step = {
id: string,
label: string,
component: StepProps => React$Node,
footer: StepProps => React$Node,
preventClose?: boolean,
onBack?: StepProps => void,
}
type State = {
stepId: string,
}
export type StepProps = {
t: T,
transitionTo: string => void,
}
class Stepper extends PureComponent<Props, State> {
state = {
stepId: this.props.initialStepId,
}
transitionTo = stepId => this.setState({ stepId })
render() {
const { t, steps, title, onClose, children, ...props } = this.props
const { stepId } = this.state
const stepIndex = steps.findIndex(s => s.id === stepId)
const step = steps[stepIndex]
invariant(step, `Stepper: step ${stepId} doesn't exists`)
const { component: StepComponent, footer: StepFooter, onBack, preventClose } = step
const stepProps: StepProps = {
t,
transitionTo: this.transitionTo,
...props,
}
return (
<ModalBody onClose={preventClose ? undefined : onClose}>
<ModalTitle onBack={onBack ? () => onBack(stepProps) : undefined}>{title}</ModalTitle>
<ModalContent>
<Breadcrumb mb={6} currentStep={stepIndex} items={steps} />
<StepComponent {...stepProps} />
{children}
</ModalContent>
{StepFooter && (
<ModalFooter horizontal align="center" justify="flex-end">
<StepFooter {...stepProps} />
</ModalFooter>
)}
</ModalBody>
)
}
}
export default translate()(Stepper)

50
src/components/base/Stepper/stories.js

@ -0,0 +1,50 @@
// @flow
import React from 'react'
import { storiesOf } from '@storybook/react'
import { action } from '@storybook/addon-actions'
import Stepper from 'components/base/Stepper'
import Button from 'components/base/Button'
import type { StepProps, Step } from 'components/base/Stepper'
const stories = storiesOf('Components/base', module)
const steps: Step[] = [
{
id: 'first',
label: 'first step',
component: () => <div>first step</div>,
footer: ({ transitionTo }: StepProps) => (
<div>
<Button primary onClick={() => transitionTo('second')}>
Click to go next
</Button>
</div>
),
},
{
id: 'second',
label: 'second step',
preventClose: true,
onBack: ({ transitionTo }: StepProps) => transitionTo('first'),
component: () => <div>second step (you cant close on this one)</div>,
footer: ({ transitionTo }: StepProps) => (
<div>
<Button primary onClick={() => transitionTo('first')}>
Click to go prev
</Button>
</div>
),
},
]
stories.add('Stepper', () => (
<Stepper
onClose={action('onClose')}
title="Stepper component"
steps={steps}
initialStepId="first"
/>
))

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

@ -1,6 +1,5 @@
// @flow
import invariant from 'invariant'
import React, { PureComponent } from 'react'
import { compose } from 'redux'
import { connect } from 'react-redux'
@ -13,59 +12,66 @@ import type { Currency, Account } from '@ledgerhq/live-common/lib/types'
import { MODAL_ADD_ACCOUNTS } from 'config/constants'
import type { T, Device } from 'types/common'
import type { StepProps as DefaultStepProps, Step } from 'components/base/Stepper'
import { idleCallback } from 'helpers/promise'
import { getCurrentDevice } from 'reducers/devices'
import { accountsSelector } from 'reducers/accounts'
import { addAccount } from 'actions/accounts'
import { closeModal } from 'reducers/modals'
import Modal, { ModalContent, ModalTitle, ModalFooter, ModalBody } from 'components/base/Modal'
import Box from 'components/base/Box'
import Breadcrumb from 'components/Breadcrumb'
import Modal from 'components/base/Modal'
import Stepper from 'components/base/Stepper'
import StepChooseCurrency, { StepChooseCurrencyFooter } from './steps/01-step-choose-currency'
import StepConnectDevice, { StepConnectDeviceFooter } from './steps/02-step-connect-device'
import StepImport, { StepImportFooter } from './steps/03-step-import'
import StepFinish from './steps/04-step-finish'
const createSteps = ({ t }: { t: T }) => [
{
id: 'chooseCurrency',
label: t('app:addAccounts.breadcrumb.informations'),
component: StepChooseCurrency,
footer: StepChooseCurrencyFooter,
onBack: null,
hideFooter: false,
},
{
id: 'connectDevice',
label: t('app:addAccounts.breadcrumb.connectDevice'),
component: StepConnectDevice,
footer: StepConnectDeviceFooter,
onBack: ({ transitionTo }: StepProps) => transitionTo('chooseCurrency'),
hideFooter: false,
},
{
id: 'import',
label: t('app:addAccounts.breadcrumb.import'),
component: StepImport,
footer: StepImportFooter,
onBack: ({ transitionTo }: StepProps) => transitionTo('chooseCurrency'),
hideFooter: false,
},
{
id: 'finish',
label: t('app:addAccounts.breadcrumb.finish'),
component: StepFinish,
footer: null,
onBack: null,
hideFooter: true,
},
]
const createSteps = ({ t }: { t: T }) => {
const onBack = ({ transitionTo, resetScanState }: StepProps) => {
resetScanState()
transitionTo('chooseCurrency')
}
return [
{
id: 'chooseCurrency',
label: t('app:addAccounts.breadcrumb.informations'),
component: StepChooseCurrency,
footer: StepChooseCurrencyFooter,
onBack: null,
hideFooter: false,
},
{
id: 'connectDevice',
label: t('app:addAccounts.breadcrumb.connectDevice'),
component: StepConnectDevice,
footer: StepConnectDeviceFooter,
onBack,
hideFooter: false,
},
{
id: 'import',
label: t('app:addAccounts.breadcrumb.import'),
component: StepImport,
footer: StepImportFooter,
onBack,
hideFooter: false,
},
{
id: 'finish',
label: t('app:addAccounts.breadcrumb.finish'),
component: StepFinish,
footer: null,
onBack: null,
hideFooter: true,
},
]
}
type Props = {
t: T,
currentDevice: ?Device,
device: ?Device,
existingAccounts: Account[],
closeModal: string => void,
addAccount: Account => void,
@ -75,37 +81,39 @@ type StepId = 'chooseCurrency' | 'connectDevice' | 'import' | 'finish'
type ScanStatus = 'idle' | 'scanning' | 'error' | 'finished'
type State = {
stepId: StepId,
// TODO: I'm sure there will be always StepId and ScanStatus given,
// but I struggle making flow understand it. So I put string as fallback
stepId: StepId | string,
scanStatus: ScanStatus | string,
isAppOpened: boolean,
currency: ?Currency,
// scan process
scannedAccounts: Account[],
checkedAccountsIds: string[],
scanStatus: ScanStatus,
err: ?Error,
}
export type StepProps = {
export type StepProps = DefaultStepProps & {
t: T,
currency: ?Currency,
currentDevice: ?Device,
device: ?Device,
isAppOpened: boolean,
transitionTo: StepId => void,
setState: any => void,
onClickAdd: void => Promise<void>,
onCloseModal: void => void,
// scan process
scannedAccounts: Account[],
existingAccounts: Account[],
checkedAccountsIds: string[],
scanStatus: ScanStatus,
err: ?Error,
onClickAdd: void => Promise<void>,
onCloseModal: void => void,
resetScanState: void => void,
setCurrency: (?Currency) => void,
setAppOpened: boolean => void,
setScanStatus: (ScanStatus, ?Error) => string,
setScannedAccounts: ({ scannedAccounts?: Account[], checkedAccountsIds?: string[] }) => void,
}
const mapStateToProps = createStructuredSelector({
currentDevice: getCurrentDevice,
device: getCurrentDevice,
existingAccounts: accountsSelector,
})
@ -126,18 +134,7 @@ const INITIAL_STATE = {
class AddAccounts extends PureComponent<Props, State> {
state = INITIAL_STATE
STEPS = createSteps({
t: this.props.t,
})
transitionTo = stepId => {
const { currency } = this.state
let nextState = { stepId }
if (stepId === 'chooseCurrency') {
nextState = { ...INITIAL_STATE, currency }
}
this.setState(nextState)
}
STEPS = createSteps({ t: this.props.t })
handleClickAdd = async () => {
const { addAccount } = this.props
@ -151,16 +148,43 @@ class AddAccounts extends PureComponent<Props, State> {
await idleCallback()
addAccount(accountsToAdd[i])
}
this.transitionTo('finish')
}
handleCloseModal = () => {
const { closeModal } = this.props
closeModal(MODAL_ADD_ACCOUNTS)
handleCloseModal = () => this.props.closeModal(MODAL_ADD_ACCOUNTS)
handleStepChange = (step: Step) => this.setState({ stepId: step.id })
handleSetCurrency = (currency: ?Currency) => this.setState({ currency })
handleSetScanStatus = (scanStatus: string, err: ?Error = null) => {
this.setState({ scanStatus, err })
}
handleSetScannedAccounts = ({
checkedAccountsIds,
scannedAccounts,
}: {
checkedAccountsIds: string[],
scannedAccounts: Account[],
}) => {
this.setState({
...(checkedAccountsIds ? { checkedAccountsIds } : {}),
...(scannedAccounts ? { scannedAccounts } : {}),
})
}
handleResetScanState = () => {
this.setState({
scanStatus: 'idle',
err: null,
scannedAccounts: [],
checkedAccountsIds: [],
})
}
handleSetAppOpened = (isAppOpened: boolean) => this.setState({ isAppOpened })
render() {
const { t, currentDevice, existingAccounts } = this.props
const { t, device, existingAccounts } = this.props
const {
stepId,
currency,
@ -171,17 +195,9 @@ class AddAccounts extends PureComponent<Props, State> {
err,
} = this.state
const stepIndex = this.STEPS.findIndex(s => s.id === stepId)
const step = this.STEPS[stepIndex]
invariant(step, `AddAccountsModal: step ${stepId} doesn't exists`)
const { component: StepComponent, footer: StepFooter, hideFooter, onBack } = step
const stepProps: StepProps = {
t,
const addtionnalProps = {
currency,
currentDevice,
device,
existingAccounts,
scannedAccounts,
checkedAccountsIds,
@ -190,8 +206,11 @@ class AddAccounts extends PureComponent<Props, State> {
isAppOpened,
onClickAdd: this.handleClickAdd,
onCloseModal: this.handleCloseModal,
transitionTo: this.transitionTo,
setState: (...args) => this.setState(...args),
setScanStatus: this.handleSetScanStatus,
setCurrency: this.handleSetCurrency,
setScannedAccounts: this.handleSetScannedAccounts,
resetScanState: this.handleResetScanState,
setAppOpened: this.handleSetAppOpened,
}
return (
@ -200,21 +219,16 @@ class AddAccounts extends PureComponent<Props, State> {
refocusWhenChange={stepId}
onHide={() => this.setState({ ...INITIAL_STATE })}
render={({ onClose }) => (
<ModalBody onClose={onClose}>
<Stepper
title={t('app:addAccounts.title')}
initialStepId="chooseCurrency"
onStepChange={this.handleStepChange}
onClose={onClose}
steps={this.STEPS}
{...addtionnalProps}
>
<SyncSkipUnderPriority priority={100} />
<ModalTitle onBack={onBack ? () => onBack(stepProps) : void 0}>
{t('app:addAccounts.title')}
</ModalTitle>
<ModalContent>
<Breadcrumb mb={6} currentStep={stepIndex} items={this.STEPS} />
<StepComponent {...stepProps} />
</ModalContent>
{!hideFooter && (
<ModalFooter horizontal align="center" justify="flex-end" style={{ height: 80 }}>
{StepFooter ? <StepFooter {...stepProps} /> : <Box />}
</ModalFooter>
)}
</ModalBody>
</Stepper>
)}
/>
)
@ -228,7 +242,3 @@ export default compose(
),
translate(),
)(AddAccounts)
function idleCallback() {
return new Promise(resolve => window.requestIdleCallback(resolve))
}

15
src/components/modals/AddAccounts/steps/01-step-choose-currency.js

@ -1,7 +1,6 @@
// @flow
import React, { Fragment } from 'react'
import isArray from 'lodash/isArray'
import SelectCurrency from 'components/SelectCurrency'
import Button from 'components/base/Button'
@ -9,18 +8,8 @@ import CurrencyBadge from 'components/base/CurrencyBadge'
import type { StepProps } from '../index'
function StepChooseCurrency({ currency, setState }: StepProps) {
return (
<SelectCurrency
autoFocus
onChange={currency => {
setState({
currency: isArray(currency) && currency.length === 0 ? null : currency,
})
}}
value={currency}
/>
)
function StepChooseCurrency({ currency, setCurrency }: StepProps) {
return <SelectCurrency autoFocus onChange={setCurrency} value={currency} />
}
export function StepChooseCurrencyFooter({ transitionTo, currency, t }: StepProps) {

6
src/components/modals/AddAccounts/steps/02-step-connect-device.js

@ -11,7 +11,7 @@ import { CurrencyCircleIcon } from 'components/base/CurrencyBadge'
import type { StepProps } from '../index'
function StepConnectDevice({ t, currency, currentDevice, setState }: StepProps) {
function StepConnectDevice({ t, currency, device, setAppOpened }: StepProps) {
invariant(currency, 'No currency given')
return (
@ -30,11 +30,11 @@ function StepConnectDevice({ t, currency, currentDevice, setState }: StepProps)
</Box>
<ConnectDevice
t={t}
deviceSelected={currentDevice}
deviceSelected={device}
currency={currency}
onStatusChange={(deviceStatus, appStatus) => {
if (appStatus === 'success') {
setState({ isAppOpened: true })
setAppOpened(true)
}
}}
/>

81
src/components/modals/AddAccounts/steps/03-step-import.js

@ -18,19 +18,23 @@ import type { StepProps } from '../index'
class StepImport extends PureComponent<StepProps> {
componentDidMount() {
this.props.setState({ scanStatus: 'scanning' })
this.props.setScanStatus('scanning')
}
componentDidUpdate(prevProps: StepProps) {
// handle case when we click on stop sync
if (prevProps.scanStatus !== 'finished' && this.props.scanStatus === 'finished') {
this.unsub()
}
const didStartScan = prevProps.scanStatus !== 'scanning' && this.props.scanStatus === 'scanning'
const didFinishScan =
prevProps.scanStatus !== 'finished' && this.props.scanStatus === 'finished'
// handle case when we click on retry sync
if (prevProps.scanStatus !== 'scanning' && this.props.scanStatus === 'scanning') {
if (didStartScan) {
this.startScanAccountsDevice()
}
// handle case when we click on stop sync
if (didFinishScan) {
this.unsub()
}
}
componentWillUnmount() {
@ -63,15 +67,15 @@ class StepImport extends PureComponent<StepProps> {
startScanAccountsDevice() {
this.unsub()
const { currency, currentDevice, setState } = this.props
const { currency, device, setScanStatus, setScannedAccounts } = this.props
try {
invariant(currency, 'No currency to scan')
invariant(currentDevice, 'No device')
invariant(device, 'No device')
const bridge = getBridgeForCurrency(currency)
// TODO: use the real device
const devicePath = currentDevice.path
const devicePath = device.path
this.scanSubscription = bridge.scanAccountsOnDevice(currency, devicePath).subscribe({
next: account => {
@ -80,7 +84,7 @@ class StepImport extends PureComponent<StepProps> {
const hasAlreadyBeenImported = !!existingAccounts.find(a => account.id === a.id)
const isNewAccount = account.operations.length === 0
if (!hasAlreadyBeenScanned) {
setState({
setScannedAccounts({
scannedAccounts: [...scannedAccounts, this.translateName(account)],
checkedAccountsIds:
!hasAlreadyBeenImported && !isNewAccount
@ -89,43 +93,33 @@ class StepImport extends PureComponent<StepProps> {
})
}
},
complete: () => setState({ scanStatus: 'finished' }),
error: err => setState({ scanStatus: 'error', err }),
complete: () => setScanStatus('finished'),
error: err => setScanStatus('error', err),
})
} catch (err) {
setState({ scanStatus: 'error', err })
setScanStatus('error', err)
}
}
handleRetry = () => {
this.unsub()
this.handleResetState()
this.props.resetScanState()
this.startScanAccountsDevice()
}
handleResetState = () => {
const { setState } = this.props
setState({
scanStatus: 'idle',
err: null,
scannedAccounts: [],
checkedAccountsIds: [],
})
}
handleToggleAccount = (account: Account) => {
const { checkedAccountsIds, setState } = this.props
const { checkedAccountsIds, setScannedAccounts } = this.props
const isChecked = checkedAccountsIds.find(id => id === account.id) !== undefined
if (isChecked) {
setState({ checkedAccountsIds: checkedAccountsIds.filter(id => id !== account.id) })
setScannedAccounts({ checkedAccountsIds: checkedAccountsIds.filter(id => id !== account.id) })
} else {
setState({ checkedAccountsIds: [...checkedAccountsIds, account.id] })
setScannedAccounts({ checkedAccountsIds: [...checkedAccountsIds, account.id] })
}
}
handleUpdateAccount = (updatedAccount: Account) => {
const { scannedAccounts, setState } = this.props
setState({
const { scannedAccounts, setScannedAccounts } = this.props
setScannedAccounts({
scannedAccounts: scannedAccounts.map(account => {
if (account.id !== updatedAccount.id) {
return account
@ -136,19 +130,26 @@ class StepImport extends PureComponent<StepProps> {
}
handleSelectAll = () => {
const { scannedAccounts, setState } = this.props
setState({
const { scannedAccounts, setScannedAccounts } = this.props
setScannedAccounts({
checkedAccountsIds: scannedAccounts.filter(a => a.operations.length > 0).map(a => a.id),
})
}
handleUnselectAll = () => this.props.setState({ checkedAccountsIds: [] })
handleUnselectAll = () => this.props.setScannedAccounts({ checkedAccountsIds: [] })
renderError() {
const { err, t } = this.props
invariant(err, 'Trying to render inexisting error')
return (
<Box style={{ height: 200 }} align="center" justify="center" color="alertRed">
<Box
style={{ height: 200 }}
px={5}
textAlign="center"
align="center"
justify="center"
color="alertRed"
>
<IconExclamationCircleThin size={43} />
<Box mt={4}>{t('app:addAccounts.somethingWentWrong')}</Box>
<Box mt={4}>
@ -236,7 +237,8 @@ class StepImport extends PureComponent<StepProps> {
export default StepImport
export const StepImportFooter = ({
setState,
transitionTo,
setScanStatus,
scanStatus,
onClickAdd,
onCloseModal,
@ -274,18 +276,23 @@ export const StepImportFooter = ({
: t('app:common.close')
const willClose = !willCreateAccount && !willAddAccounts
const onClick = willClose ? onCloseModal : onClickAdd
const onClick = willClose
? onCloseModal
: async () => {
await onClickAdd()
transitionTo('finish')
}
return (
<Fragment>
{currency && <CurrencyBadge mr="auto" currency={currency} />}
{scanStatus === 'error' && (
<Button mr={2} onClick={() => setState({ scanStatus: 'scanning', err: null })}>
<Button mr={2} onClick={() => setScanStatus('scanning')}>
{t('app:common.retry')}
</Button>
)}
{scanStatus === 'scanning' && (
<Button mr={2} onClick={() => setState({ scanStatus: 'finished' })}>
<Button mr={2} onClick={() => setScanStatus('finished')}>
{t('app:common.stop')}
</Button>
)}

9
src/components/modals/OperationDetails.js

@ -126,7 +126,14 @@ const OperationDetails = connect(mapStateToProps)((props: Props) => {
/>
<Box my={4} alignItems="center">
<Box>
<FormattedVal unit={unit} alwaysShowSign showCode val={amount} fontSize={7} />
<FormattedVal
unit={unit}
alwaysShowSign
showCode
val={amount}
fontSize={7}
disableRounding
/>
</Box>
<Box mt={1}>
<CounterValue

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

@ -15,7 +15,7 @@ import { getBridgeForCurrency } from 'bridge'
import { accountsSelector } from 'reducers/accounts'
import { updateAccountWithUpdater } from 'actions/accounts'
import createCustomErrorClass from 'helpers/createCustomErrorClass'
import { createCustomErrorClass } from 'helpers/errors'
import { MODAL_SEND } from 'config/constants'
import Modal, { ModalBody, ModalContent, ModalTitle } from 'components/base/Modal'

5
src/config/constants.js

@ -12,6 +12,11 @@ const boolFromEnv = (key: string): boolean => {
const stringFromEnv = (key: string, def: string): string => process.env[key] || def
// Size
export const MIN_HEIGHT = intFromEnv('LEDGER_MIN_HEIGHT', 768)
export const MIN_WIDTH = intFromEnv('LEDGER_MIN_WIDTH', 1024)
// time and delays...
export const GET_CALLS_TIMEOUT = intFromEnv('GET_CALLS_TIMEOUT', 30 * 1000)

2
src/helpers/apps/installApp.js

@ -7,7 +7,7 @@ import { createDeviceSocket } from 'helpers/socket'
import type { LedgerScriptParams } from 'helpers/common'
import createCustomErrorClass from '../createCustomErrorClass'
import { createCustomErrorClass } from '../errors'
const ManagerUnexpectedError = createCustomErrorClass('ManagerUnexpected')
const ManagerNotEnoughSpaceError = createCustomErrorClass('ManagerNotEnoughSpace')

2
src/helpers/apps/uninstallApp.js

@ -6,7 +6,7 @@ import { BASE_SOCKET_URL_SECURE } from 'config/constants'
import { createDeviceSocket } from 'helpers/socket'
import type { LedgerScriptParams } from 'helpers/common'
import createCustomErrorClass from '../createCustomErrorClass'
import { createCustomErrorClass } from '../errors'
const ManagerUnexpectedError = createCustomErrorClass('ManagerUnexpectedError')
const ManagerDeviceLockedError = createCustomErrorClass('ManagerDeviceLocked')

14
src/helpers/createCustomErrorClass.js

@ -1,14 +0,0 @@
// @flow
export default (name: string): Class<any> => {
const C = function CustomError(message?: string, fields?: Object) {
this.name = name
this.message = message || name
this.stack = new Error().stack
Object.assign(this, fields)
}
// $FlowFixMe
C.prototype = new Error()
// $FlowFixMe we can't easily type a subset of Error for now...
return C
}

2
src/helpers/devices/getNextMCU.js

@ -2,7 +2,7 @@
import network from 'api/network'
import { GET_NEXT_MCU } from 'helpers/urls'
import createCustomErrorClass from 'helpers/createCustomErrorClass'
import { createCustomErrorClass } from 'helpers/errors'
const LatestMCUInstalledError = createCustomErrorClass('LatestMCUInstalledError')

79
src/helpers/errors.js

@ -1,3 +1,80 @@
// @flow
/* eslint-disable no-continue */
export const formatError = (e: Error) => e.message
const errorClasses = {}
export const createCustomErrorClass = (name: string): Class<any> => {
const C = function CustomError(message?: string, fields?: Object) {
this.name = name
this.message = message || name
this.stack = new Error().stack
Object.assign(this, fields)
}
// $FlowFixMe
C.prototype = new Error()
errorClasses[name] = C
// $FlowFixMe we can't easily type a subset of Error for now...
return C
}
// inspired from https://github.com/programble/errio/blob/master/index.js
export const deserializeError = (object: mixed): Error => {
if (typeof object === 'object' && object) {
const constructor = (typeof object.name === 'string' && errorClasses[object.name]) || Error
const error = Object.create(constructor.prototype)
for (const prop in object) {
if (object.hasOwnProperty(prop)) {
error[prop] = object[prop]
}
}
if (!error.stack && Error.captureStackTrace) {
Error.captureStackTrace(error, deserializeError)
}
return error
}
return new Error(String(object))
}
// inspired from https://github.com/sindresorhus/serialize-error/blob/master/index.js
export const serializeError = (value: mixed) => {
if (!value) return value
if (typeof value === 'object') {
return destroyCircular(value, [])
}
if (typeof value === 'function') {
return `[Function: ${value.name || 'anonymous'}]`
}
return value
}
// https://www.npmjs.com/package/destroy-circular
function destroyCircular(from: Object, seen) {
const to = {}
seen.push(from)
for (const key of Object.keys(from)) {
const value = from[key]
if (typeof value === 'function') {
continue
}
if (!value || typeof value !== 'object') {
to[key] = value
continue
}
if (seen.indexOf(from[key]) === -1) {
to[key] = destroyCircular(from[key], seen.slice(0))
continue
}
to[key] = '[Circular]'
}
if (typeof from.name === 'string') {
to.name = from.name
}
if (typeof from.message === 'string') {
to.message = from.message
}
if (typeof from.stack === 'string') {
to.stack = from.stack
}
return to
}

2
src/helpers/getAddressForCurrency/btc.js

@ -4,7 +4,7 @@ import type { CryptoCurrency } from '@ledgerhq/live-common/lib/types'
import Btc from '@ledgerhq/hw-app-btc'
import type Transport from '@ledgerhq/hw-transport'
import getBitcoinLikeInfo from '../devices/getBitcoinLikeInfo'
import createCustomErrorClass from '../createCustomErrorClass'
import { createCustomErrorClass } from '../errors'
const BtcUnmatchedApp = createCustomErrorClass('BtcUnmatchedApp')

7
src/helpers/ipc.js

@ -2,6 +2,7 @@
import logger from 'logger'
import { Observable } from 'rxjs'
import uuidv4 from 'uuid/v4'
import { deserializeError } from './errors'
export function createCommand<In, A>(id: string, impl: In => Observable<A>): Command<In, A> {
return new Command(id, impl)
@ -60,10 +61,12 @@ function ipcRendererSendCommand<In, A>(id: string, data: In): Observable<A> {
ipcRenderer.removeListener('command-event', handleCommandEvent)
break
case 'cmd.ERROR':
o.error(msg.data)
case 'cmd.ERROR': {
const error = deserializeError(msg.data)
o.error(error)
ipcRenderer.removeListener('command-event', handleCommandEvent)
break
}
default:
}

2
src/helpers/libcore.js

@ -11,7 +11,7 @@ import type { NJSAccount, NJSOperation } from '@ledgerhq/ledger-core/src/ledgerc
import { isSegwitAccount } from 'helpers/bip32'
import * as accountIdHelper from 'helpers/accountId'
import createCustomErrorClass from './createCustomErrorClass'
import { createCustomErrorClass } from './errors'
import { getAccountPlaceholderName, getNewAccountPlaceholderName } from './accountName'

4
src/helpers/promise.js

@ -27,3 +27,7 @@ export function retry<A>(f: () => Promise<A>, options?: $Shape<typeof defaults>)
})
}
}
export function idleCallback() {
return new Promise(resolve => window.requestIdleCallback(resolve))
}

4
src/helpers/socket.js

@ -5,7 +5,7 @@ import logger from 'logger'
import Websocket from 'ws'
import type Transport from '@ledgerhq/hw-transport'
import { Observable } from 'rxjs'
import createCustomErrorClass from './createCustomErrorClass'
import { createCustomErrorClass } from './errors'
const WebsocketConnectionError = createCustomErrorClass('WebsocketConnectionError')
const WebsocketConnectionFailed = createCustomErrorClass('WebsocketConnectionFailed')
@ -74,7 +74,9 @@ export const createDeviceSocket = (transport: Transport<*>, url: string) =>
for (const apdu of data) {
const r: Buffer = await transport.exchange(Buffer.from(apdu, 'hex'))
lastStatus = r.slice(r.length - 2)
if (lastStatus.toString('hex') !== '9000') break
}
if (!lastStatus) {
throw new DeviceSocketNoBulkStatus()
}

5
src/icons/Home.js

@ -3,7 +3,10 @@
import React from 'react'
const path = (
<path d="m8.261 2.252a1.11 1.11 0 0 0-1.408 0l-6.772 5.545a0.224 0.224 0 0 0-0.03 0.313l0.563 0.69a0.224 0.224 0 0 0 0.314 0.03l0.408-0.333v5.502c0 0.245 0.2 0.445 0.445 0.445h4.667c0.122 0 0.222-0.1 0.222-0.222v-3.556h1.778v3.556c0 0.122 0.1 0.222 0.222 0.222h4.666c0.245 0 0.445-0.2 0.445-0.445v-5.505l0.408 0.333a0.224 0.224 0 0 0 0.314-0.03l0.564-0.69a0.227 0.227 0 0 0-0.036-0.31l-6.771-5.545zm4.184 10.858h-2.667v-3.555c0-0.122-0.1-0.222-0.222-0.222h-4c-0.122 0-0.222 0.1-0.222 0.222v3.555h-2.667v-5.708l4.747-3.889c0.08-0.066 0.2-0.066 0.28 0l4.748 3.89v5.707z" />
<path
fill="currentColor"
d="m8.261 2.252a1.11 1.11 0 0 0-1.408 0l-6.772 5.545a0.224 0.224 0 0 0-0.03 0.313l0.563 0.69a0.224 0.224 0 0 0 0.314 0.03l0.408-0.333v5.502c0 0.245 0.2 0.445 0.445 0.445h4.667c0.122 0 0.222-0.1 0.222-0.222v-3.556h1.778v3.556c0 0.122 0.1 0.222 0.222 0.222h4.666c0.245 0 0.445-0.2 0.445-0.445v-5.505l0.408 0.333a0.224 0.224 0 0 0 0.314-0.03l0.564-0.69a0.227 0.227 0 0 0-0.036-0.31l-6.771-5.545zm4.184 10.858h-2.667v-3.555c0-0.122-0.1-0.222-0.222-0.222h-4c-0.122 0-0.222 0.1-0.222 0.222v3.555h-2.667v-5.708l4.747-3.889c0.08-0.066 0.2-0.066 0.28 0l4.748 3.89v5.707z"
/>
)
export default ({ size, ...p }: { size: number }) => (

0
src/icons/illustrations/SensitiveOperationShield.js → src/icons/SensitiveOperationShield.js

79
src/icons/illustrations/GetStartedLogo.js

@ -1,79 +0,0 @@
// @flow
import React from 'react'
export default () => (
<svg width="113" height="109">
<g fill="none" fillRule="evenodd">
<rect
width="1.44"
height="5.6"
y="16.6"
fill="#1D2028"
rx=".72"
transform="matrix(-1 0 0 1 1.44 0)"
/>
<rect
width="1.44"
height="5.6"
y="34.8"
fill="#1D2028"
rx=".72"
transform="matrix(-1 0 0 1 1.44 0)"
/>
<path
fill="#6490F1"
fillOpacity=".1"
stroke="#1D2028"
strokeWidth="2"
d="M16.592 12c.225 0 .408.183.408.408v95.184a.408.408 0 0 1-.408.408H2.128a.408.408 0 0 1-.408-.408V12.408c0-.225.183-.408.408-.408h14.464z"
/>
<rect
width="7.64"
height="27"
x="5.513"
y="18.522"
fill="#FFF"
stroke="#6490F1"
rx=".704"
transform="matrix(-1 0 0 1 18.665 0)"
/>
<path
fill="#FFF"
stroke="#1D2028"
strokeWidth="2"
d="M9.36 54A7.64 7.64 0 0 1 17 61.64v45.952a.408.408 0 0 1-.408.408H2.128a.408.408 0 0 1-.408-.408V61.64A7.64 7.64 0 0 1 9.36 54z"
/>
<ellipse
cx="9.36"
cy="61.4"
fill="#FFF"
stroke="#6490F1"
rx="3.82"
ry="3.7"
transform="matrix(-1 0 0 1 18.72 0)"
/>
<rect width="3.137" height="9.306" x="109.863" y="13.959" fill="#1D2028" rx="1.569" />
<rect
width="76.431"
height="106.571"
x="34"
y="1"
fill="#6490F1"
fillOpacity=".1"
stroke="#1D2027"
strokeWidth="2"
rx="5.44"
/>
<rect
width="52.333"
height="79.653"
x="46.043"
y="15.235"
fill="#FFF"
stroke="#6490F1"
rx="4.08"
/>
</g>
</svg>
)

32
src/icons/illustrations/LedgerBlue.js

@ -1,32 +0,0 @@
// @flow
import React from 'react'
export default () => (
<svg width="52" height="72">
<g fill="none" fillRule="evenodd">
<rect width="2.039" height="6.171" x="49.961" y="9.257" fill="#1D2028" rx="1.02" />
<rect
width="49.48"
height="70.5"
x=".75"
y=".75"
fill="#6490F1"
fillOpacity=".1"
stroke="#1D2027"
strokeWidth="1.5"
rx="3.2"
/>
<rect
width="34.167"
height="52.986"
x="8.403"
y="10.021"
fill="#FFF"
stroke="#6490F1"
strokeWidth=".5"
rx="2.4"
/>
</g>
</svg>
)

47
src/icons/illustrations/LedgerBlueError.js

@ -1,47 +0,0 @@
// @flow
import React from 'react'
export default () => (
<svg width="92" height="188">
<defs>
<linearGradient id="a" x1="50%" x2="50%" y1="0%" y2="100%">
<stop offset="0%" />
<stop offset="100%" stopColor="#FFF" />
</linearGradient>
</defs>
<g fill="none" fillRule="evenodd">
<path
stroke="#1D2027"
strokeWidth="2"
d="M37 120.644a1 1 0 0 0-1 1v26.225a5 5 0 0 0 5 5h8.486a5 5 0 0 0 5-5v-26.225a1 1 0 0 0-1-1H37z"
/>
<path stroke="#142533" strokeWidth="2" d="M42.208 153.253v10.929h6.436v-10.93h-6.436z" />
<path
stroke="#1D2027"
strokeWidth="2"
d="M39.713 120.577h11.255l-.082-6.977a1 1 0 0 0-1-.988h-9.267a1 1 0 0 0-.988 1.012l.082 6.953z"
/>
<path
fill="url(#a)"
d="M6.836 53.925h1.616v22.65H6.836v-22.65zm5.657 0h1.616v22.65h-1.616v-22.65z"
transform="translate(35 111)"
/>
<path
fill="#1D2028"
d="M88.556 17.556c0-1.105.671-2 1.5-2 .828 0 1.5.895 1.5 2v6c0 1.104-.672 2-1.5 2-.829 0-1.5-.896-1.5-2"
/>
<rect
width="88"
height="118.635"
x="1"
y="1"
fill="#FCEAEC"
stroke="#1D2027"
strokeWidth="2"
rx="5.44"
/>
<rect width="59" height="88.615" x="15.5" y="16.5" fill="#FFF" stroke="#EA2E49" rx="4.08" />
</g>
</svg>
)

47
src/icons/illustrations/LedgerBlueSelectPIN.js

@ -1,47 +0,0 @@
// @flow
import React from 'react'
export default () => (
<svg width="92" height="188">
<defs>
<linearGradient id="a" x1="50%" x2="50%" y1="0%" y2="100%">
<stop offset="0%" />
<stop offset="100%" stopColor="#FFF" />
</linearGradient>
</defs>
<g fill="none" fillRule="evenodd">
<path
stroke="#1D2027"
strokeWidth="2"
d="M37 120.644a1 1 0 0 0-1 1v26.225a5 5 0 0 0 5 5h8.486a5 5 0 0 0 5-5v-26.225a1 1 0 0 0-1-1H37z"
/>
<path stroke="#142533" strokeWidth="2" d="M42.208 153.253v10.929h6.436v-10.93h-6.436z" />
<path
stroke="#1D2027"
strokeWidth="2"
d="M39.713 120.577h11.255l-.082-6.977a1 1 0 0 0-1-.988h-9.267a1 1 0 0 0-.988 1.012l.082 6.953z"
/>
<path
fill="url(#a)"
d="M6.836 53.925h1.616v22.65H6.836v-22.65zm5.657 0h1.616v22.65h-1.616v-22.65z"
transform="translate(35 111)"
/>
<path
fill="#1D2028"
d="M88.556 17.556c0-1.105.671-2 1.5-2 .828 0 1.5.895 1.5 2v6c0 1.104-.672 2-1.5 2-.829 0-1.5-.896-1.5-2"
/>
<rect
width="88"
height="118.635"
x="1"
y="1"
fill="#EFF3FD"
stroke="#1D2027"
strokeWidth="2"
rx="5.44"
/>
<rect width="59" height="88.615" x="15.5" y="16.5" fill="#FFF" stroke="#6490F1" rx="4.08" />
</g>
</svg>
)

44
src/icons/illustrations/LedgerNano.js

@ -1,44 +0,0 @@
// @flow
import React from 'react'
export default () => (
<svg width="13" height="72">
<g fill="none" fillRule="evenodd">
<rect width="1.04" height="4.114" x="11.96" y="4.114" fill="#1D2028" rx=".52" />
<rect width="1.04" height="4.114" x="11.96" y="17.486" fill="#1D2028" rx=".52" />
<path
fill="#6490F1"
fillOpacity=".1"
stroke="#1D2028"
strokeWidth="1.5"
d="M1.6.75a.85.85 0 0 0-.85.85v68.8c0 .47.38.85.85.85h9.28c.47 0 .85-.38.85-.85V1.6a.85.85 0 0 0-.85-.85H1.6z"
/>
<rect
width="5.74"
height="20.071"
x="3.39"
y="5.409"
fill="#FFF"
stroke="#6490F1"
strokeWidth=".5"
rx=".8"
/>
<path
fill="#FFF"
stroke="#1D2028"
strokeWidth="1.5"
d="M6.24 31.607a5.49 5.49 0 0 0-5.49 5.49V70.4c0 .47.38.85.85.85h9.28c.47 0 .85-.38.85-.85V37.097a5.49 5.49 0 0 0-5.49-5.49z"
/>
<ellipse
cx="6.24"
cy="37.029"
fill="#FFF"
stroke="#6490F1"
strokeWidth=".5"
rx="2.87"
ry="2.836"
/>
</g>
</svg>
)

78
src/icons/illustrations/LedgerNanoError.js

@ -1,78 +0,0 @@
// @flow
import React from 'react'
export default () => (
<svg>
<defs>
<linearGradient id="a" x1="50%" x2="50%" y1="0%" y2="100%">
<stop offset="0%" />
<stop offset="100%" stopColor="#FFF" />
</linearGradient>
<rect id="b" width="41.711" height="238.384" rx="4" />
<path
id="c"
d="M5.773 5l2.541-2.541a.235.235 0 0 0 0-.332l-.441-.441a.235.235 0 0 0-.332 0L5 4.226l-2.541-2.54a.235.235 0 0 0-.332 0l-.441.441a.235.235 0 0 0 0 .332L4.226 5l-2.54 2.541a.235.235 0 0 0 0 .332l.441.441c.092.092.24.092.332 0L5 5.774l2.541 2.54c.092.092.24.092.332 0l.441-.441a.235.235 0 0 0 0-.332L5.774 5z"
/>
</defs>
<g fill="none" fillRule="evenodd">
<path
stroke="#1D2027"
strokeWidth="2"
d="M127.356 30a1 1 0 0 1-1 1H100.13a5 5 0 0 1-5-5v-8.486a5 5 0 0 1 5-5h26.225a1 1 0 0 1 1 1V30z"
/>
<path stroke="#142533" strokeWidth="2" d="M94.747 24.792H83.818v-6.436h10.93v6.436z" />
<path
stroke="#1D2027"
strokeWidth="2"
d="M127.423 27.287V16.032l6.977.082a1 1 0 0 1 .988 1v9.267a1 1 0 0 1-1.012.988l-6.953-.082z"
/>
<path
fill="url(#a)"
d="M6.836 53.925h1.616v82.65H6.836v-82.65zm5.657 0h1.616v82.65h-1.616v-82.65z"
transform="matrix(0 -1 -1 0 137 32)"
/>
<g transform="rotate(-90 85 -42)">
<rect width="4.492" height="17.12" x="38.336" y="15.505" fill="#142533" rx="2" />
<rect width="4.492" height="17.12" x="38.336" y="70.094" fill="#142533" rx="2" />
<use fill="#FFF" />
<rect
width="39.711"
height="236.384"
x="1"
y="1"
fill="#FCE0E4"
stroke="#142533"
strokeLinejoin="square"
strokeWidth="2"
rx="4"
/>
<rect
width="20.176"
height="61.019"
x="10.767"
y="21.173"
fill="#FFF"
stroke="#EA2E49"
rx="1.6"
/>
<path
fill="#FFF"
stroke="#142533"
strokeWidth="2"
d="M20.856 95.966C9.89 95.966 1 104.856 1 115.822v118.562a3 3 0 0 0 3 3h33.711a3 3 0 0 0 3-3V115.822c0-10.966-8.89-19.856-19.855-19.856z"
/>
<ellipse cx="21.016" cy="116.123" stroke="#EA2E49" rx="10.57" ry="10.644" />
<g transform="translate(16.066 26.884)">
<mask id="d" fill="#fff">
<use xlinkHref="#c" />
</mask>
<use fill="#000" fillRule="nonzero" xlinkHref="#c" />
<g fill="#EA2E49" mask="url(#d)">
<path d="M0 0h10v10H0z" />
</g>
</g>
</g>
</g>
</svg>
)

60
src/icons/illustrations/LedgerNanoSelectPIN.js

@ -1,60 +0,0 @@
// @flow
import React from 'react'
export default () => (
<svg width="260" height="129">
<defs>
<linearGradient id="a" x1="50%" x2="50%" y1="0%" y2="100%">
<stop offset="0%" />
<stop offset="100%" stopColor="#FFF" />
</linearGradient>
<path
id="b"
d="M91 0h34a4 4 0 0 1 4 4v108.144c0 11.519-9.337 20.856-20.856 20.856h-.288C96.337 133 87 123.663 87 112.144V4a4 4 0 0 1 4-4z"
/>
</defs>
<g fill="none" fillRule="evenodd">
<path
stroke="#1D2027"
strokeWidth="2"
d="M127.856 31.44a1 1 0 0 1-1 1H100.63a5 5 0 0 1-5-5v-8.486a5 5 0 0 1 5-5h26.225a1 1 0 0 1 1 1v16.485z"
/>
<path stroke="#142533" strokeWidth="2" d="M95.247 26.231H84.318v-6.435h10.93v6.435z" />
<path
stroke="#1D2027"
strokeWidth="2"
d="M127.923 28.726V17.471l6.977.083a1 1 0 0 1 .988 1v9.266a1 1 0 0 1-1.012.988l-6.953-.083z"
/>
<path
fill="url(#a)"
d="M6.836 53.925h1.616v82.65H6.836v-82.65zm5.657 0h1.616v82.65h-1.616v-82.65z"
transform="matrix(0 -1 -1 0 137.5 33.44)"
/>
<g transform="rotate(-90 128.59 1.975)">
<rect width="4.492" height="17.12" x="125.336" y="15.505" fill="#142533" rx="2" />
<rect width="4.492" height="17.12" x="125.336" y="70.094" fill="#142533" rx="2" />
<use fill="#FFF" xlinkHref="#b" />
<path
fill="#6490F1"
fillOpacity=".15"
stroke="#142533"
strokeLinejoin="square"
strokeWidth="2"
d="M91 1a3 3 0 0 0-3 3v108.144C88 123.11 96.89 132 107.856 132h.288C119.11 132 128 123.11 128 112.144V4a3 3 0 0 0-3-3H91z"
/>
<rect width="21" height="62" x="97.5" y="21.5" fill="#FFF" stroke="#6490F1" rx="1.6" />
<path
fill="#6490F1"
d="M105.5 35h5a.5.5 0 0 1 .5.5v34a.5.5 0 0 1-.5.5h-5a.5.5 0 0 1-.5-.5v-34a.5.5 0 0 1 .5-.5zm1.238 3.042l.774.512v.013l-.774.505.341.466.722-.577h.013l.243.899.551-.177-.328-.88.932.053v-.597l-.932.046.328-.873-.551-.17-.243.892h-.013l-.722-.584-.34.472zm0 3.908l.774.512v.013l-.774.505.341.466.722-.578h.013l.243.9.551-.178-.328-.88.932.053v-.597l-.932.046.328-.872-.551-.17-.243.891h-.013l-.722-.584-.34.473zm0 3.907l.774.512v.013l-.774.505.341.466.722-.577h.013l.243.899.551-.178-.328-.879.932.053v-.597l-.932.046.328-.873-.551-.17-.243.892h-.013l-.722-.584-.34.472zm0 3.908l.774.511v.014l-.774.505.341.466.722-.578h.013l.243.899.551-.177-.328-.88.932.053v-.597l-.932.046.328-.872-.551-.171-.243.892h-.013l-.722-.584-.34.473zm0 3.907l.774.512v.013l-.774.505.341.466.722-.577h.013l.243.898.551-.177-.328-.879.932.053v-.597l-.932.046.328-.873-.551-.17-.243.892h-.013l-.722-.584-.34.472zm0 3.908l.774.511v.013l-.774.506.341.465.722-.577h.013l.243.899.551-.177-.328-.88.932.053v-.597l-.932.046.328-.873-.551-.17-.243.892h-.013l-.722-.584-.34.473zm0 3.907l.774.512v.013l-.774.505.341.466.722-.578h.013l.243.9.551-.178-.328-.879.932.052v-.597l-.932.046.328-.872-.551-.17-.243.891h-.013l-.722-.583-.34.472zm0 3.907l.774.512v.013l-.774.505.341.466.722-.577h.013l.243.899.551-.177-.328-.88.932.053v-.597l-.932.046.328-.873-.551-.17-.243.892h-.013l-.722-.584-.34.472z"
/>
<path
fill="#FFF"
stroke="#142533"
strokeWidth="2"
d="M123.166 125.105c7.049-8.4 5.953-20.925-2.447-27.974l-90.824-76.21a3 3 0 0 0-4.227.37L4 47.115a3 3 0 0 0 .37 4.227l90.824 76.21c8.4 7.049 20.924 5.953 27.973-2.447z"
/>
<ellipse cx="108.016" cy="111.123" stroke="#6490F1" rx="10.57" ry="10.644" />
</g>
</g>
</svg>
)

63
src/icons/illustrations/SetPassword.js

@ -1,63 +0,0 @@
// @flow
import React from 'react'
export default () => (
<svg width="136" height="52">
<g fill="none" fillRule="evenodd">
<g fillRule="nonzero">
<g transform="translate(36 1)">
<ellipse cx="37.488" cy="45.422" fill="#000" rx="1.037" ry="1.036" />
<ellipse cx="33.5" cy="45.422" fill="#000" rx="1.037" ry="1.036" />
<ellipse cx="29.512" cy="45.422" fill="#000" rx="1.037" ry="1.036" />
<path
fill="#142533"
d="M65.405 40.242h-2.792V9.562c0-7.251-5.982-9.164-9.173-9.164H13.56c-3.191 0-9.173 1.913-9.173 9.165v30.68H1.595C.957 40.242.4 40.8.4 41.436c0 3.188 1.914 9.165 9.172 9.165H57.43c7.258 0 9.172-5.977 9.172-9.164 0-.638-.558-1.196-1.196-1.196zM6.78 9.562c0-6.534 6.062-6.773 6.78-6.773h39.88c.718 0 6.78.24 6.78 6.774v30.68H6.78V9.562zm50.649 38.649H9.57c-5.025 0-6.3-3.586-6.7-5.578h61.18c-.32 1.912-1.595 5.578-6.62 5.578z"
/>
</g>
<g fill="#6490F1">
<path d="M74.189 20.125c0-.16.08-.319.08-.478V16.38c0-1.833-1.524-3.427-3.448-3.427h-2.005c-1.844 0-3.448 1.514-3.448 3.427v3.267c0 .16 0 .319.08.478-1.763.16-3.127 1.594-3.127 3.347v6.773c0 1.833 1.524 3.427 3.448 3.427h8.26c1.844 0 3.447-1.514 3.447-3.427v-6.773c-.08-1.753-1.523-3.188-3.287-3.347zM67.854 16.3c0-.558.481-1.036 1.043-1.036H70.9c.561 0 1.043.478 1.043 1.036v3.267c0 .16-.08.319-.16.478h-3.69c-.08-.16-.16-.318-.16-.478l-.08-3.267zm7.136 14.025c0 .558-.48 1.036-1.042 1.036H65.77a1.058 1.058 0 0 1-1.042-1.036v-6.773c0-.558.481-1.036 1.042-1.036h8.26c.56 0 1.042.478 1.042 1.036l-.08 6.773z" />
<path d="M69.899 24.11c-.638 0-1.197.548-1.197 1.175v2.43c0 .627.559 1.176 1.197 1.176s1.196-.549 1.196-1.176v-2.43c0-.705-.558-1.176-1.196-1.176z" />
</g>
</g>
<g transform="translate(109 4)">
<path
fill="#CCC"
fillRule="nonzero"
d="M6.325 38.352l.614-.636c.254-.263.712-.233 1.023.067.31.3.356.757.102 1.02l-.614.636.643.62c.267.258.242.715-.055 1.022-.296.307-.753.348-1.019.09l-.643-.62-.614.635c-.254.264-.712.234-1.023-.066-.31-.3-.356-.757-.102-1.02l.614-.636-.643-.62c-.266-.258-.242-.715.055-1.022.296-.308.753-.348 1.02-.09l.642.62z"
/>
<ellipse cx="1.224" cy="24.349" stroke="#E2E2E2" strokeWidth=".8" rx="1.146" ry="1.133" />
<ellipse cx="21.21" cy="23.216" stroke="#E2E2E2" strokeWidth=".8" rx="1.564" ry="1.546" />
<path
fill="#EEE"
fillRule="nonzero"
d="M11.558 12.189l.917-.95c.281-.291.677-.366.883-.167.206.2.145.597-.136.888l-.917.95.96.928c.295.284.375.68.178.884-.197.204-.596.139-.89-.146l-.961-.927-.917.95c-.281.29-.677.365-.883.166-.206-.2-.145-.597.136-.888l.917-.95-.96-.928c-.295-.284-.375-.68-.178-.884.197-.204.596-.139.89.146l.961.928z"
/>
<path
fill="#CCC"
fillRule="nonzero"
d="M25.03 3.01l.71-.736c.251-.26.657-.277.905-.037.249.24.246.646-.005.907l-.71.734.744.718c.263.255.285.66.047.906-.237.246-.643.239-.907-.016l-.743-.718-.71.735c-.251.26-.657.277-.906.037-.248-.24-.246-.646.006-.906l.71-.735-.744-.718c-.263-.255-.285-.66-.047-.906.237-.246.643-.238.907.016l.743.718z"
/>
</g>
<g transform="translate(1 -1)">
<path
fill="#CCC"
fillRule="nonzero"
d="M20.486 48.016l.621-.643c.257-.267.72-.238 1.033.065.314.303.36.764.102 1.03l-.621.644.648.626c.269.26.243.722-.057 1.033-.3.31-.761.352-1.03.093l-.648-.627-.622.644c-.257.266-.72.237-1.033-.066-.313-.302-.359-.764-.102-1.03l.622-.643-.649-.627c-.268-.26-.243-.721.057-1.032.3-.311.762-.353 1.03-.093l.649.626z"
/>
<ellipse cx="3.155" cy="37.389" stroke="#CCC" strokeWidth=".8" rx="3.155" ry="3.13" />
<ellipse cx="18.932" cy="29.564" stroke="#E2E2E2" strokeWidth=".8" rx="1.578" ry="1.565" />
<path
fill="#EEE"
fillRule="nonzero"
d="M9.447 16.156l1.118-1.158c.343-.356.825-.447 1.075-.205.251.242.176.726-.167 1.081l-1.118 1.158 1.167 1.128c.358.346.454.828.214 1.076-.24.249-.725.17-1.084-.176l-1.167-1.127-1.118 1.158c-.343.355-.825.447-1.075.205-.251-.242-.176-.727.167-1.082l1.118-1.158L7.41 15.93c-.358-.346-.454-.828-.214-1.077.24-.248.725-.17 1.084.176l1.167 1.128z"
/>
<path
fill="#CCC"
fillRule="nonzero"
d="M22.47 2.013l.699-.724c.214-.222.515-.28.672-.128.156.151.11.454-.105.676l-.699.724.73.704c.224.216.283.518.133.673-.15.156-.453.106-.677-.11l-.73-.704-.698.723c-.215.222-.515.28-.672.128-.157-.15-.11-.454.104-.676l.7-.723-.73-.705c-.224-.216-.284-.517-.134-.673.15-.155.453-.106.677.11l.73.705z"
/>
</g>
</g>
</svg>
)

40
src/icons/illustrations/WriteSeed.js

@ -1,40 +0,0 @@
// @flow
import React from 'react'
export default () => (
<svg width="145" height="109">
<g fill="none" fillRule="evenodd" transform="translate(-1)">
<path
fill="#6490F1"
fillOpacity=".1"
stroke="#142533"
strokeWidth="1.8"
d="M29.105 104.014l19.87-84.34L4.123 35.999a2.7 2.7 0 0 0-1.573 3.566l26.556 64.449zm89.265 1.336L97.73 34.29l44.752 16.288a2.7 2.7 0 0 1 1.49 3.748L118.37 105.35z"
/>
<rect
width="88.2"
height="107.2"
x="29.9"
y=".9"
fill="#FFF"
stroke="#142533"
strokeWidth="1.8"
rx="3.6"
/>
<rect width="50" height="4" x="49" y="14" fill="#6490F1" rx="2" />
<path
fill="#6490F1"
d="M83.18 91.378h24.01a1.81 1.81 0 0 1 0 3.622H83.18a1.81 1.81 0 1 1 0-3.622zm-42.37 0h24.01a1.81 1.81 0 1 1 0 3.622H40.81a1.81 1.81 0 1 1 0-3.622zm42.37-12.675h24.01a1.81 1.81 0 0 1 0 3.621H83.18a1.81 1.81 0 1 1 0-3.621zm-42.37 0h24.01a1.81 1.81 0 1 1 0 3.621H40.81a1.81 1.81 0 1 1 0-3.621zm42.37-12.676h24.01a1.81 1.81 0 0 1 0 3.622H83.18a1.81 1.81 0 1 1 0-3.622zm-42.37 0h24.01a1.81 1.81 0 1 1 0 3.622H40.81a1.81 1.81 0 1 1 0-3.622zm42.37-12.676h24.01a1.81 1.81 0 0 1 0 3.622H83.18a1.81 1.81 0 1 1 0-3.622zm-42.37 0h24.01a1.81 1.81 0 1 1 0 3.622H40.81a1.81 1.81 0 1 1 0-3.622zm42.37-12.675h24.01a1.81 1.81 0 0 1 0 3.621H83.18a1.81 1.81 0 1 1 0-3.621zm-42.37 0h24.01a1.81 1.81 0 1 1 0 3.621H40.81a1.81 1.81 0 1 1 0-3.621zM83.18 28h24.01a1.81 1.81 0 0 1 0 3.622H83.18a1.81 1.81 0 1 1 0-3.622zm-42.37 0h24.01a1.81 1.81 0 1 1 0 3.622H40.81a1.81 1.81 0 1 1 0-3.622z"
opacity=".5"
/>
<path
fill="#FFF"
fillRule="nonzero"
stroke="#6490F1"
strokeWidth="2"
d="M85.623 56.047a17.987 17.987 0 0 1 6.194 13.596c0 9.92-8.032 17.99-17.909 17.99-9.876 0-17.908-8.07-17.908-17.99a17.986 17.986 0 0 1 6.194-13.596v-3.281C62.194 46.278 67.447 41 73.908 41c6.462 0 11.715 5.278 11.715 11.766v3.28zM73.809 73.193zm.003-.018v4.55a.1.1 0 0 0 .096.102.1.1 0 0 0 .097-.102v-4.55a3.149 3.149 0 0 1-.006-.054l-.088-.899.885-.179a4.505 4.505 0 0 0 3.596-4.42c0-2.493-2.008-4.51-4.484-4.51-2.475 0-4.483 2.018-4.483 4.51a4.505 4.505 0 0 0 3.596 4.42l.885.18-.088.898a3.149 3.149 0 0 1-.006.054zm.196.018a.1.1 0 0 0 0 .004v-.004zm0 .004v-.002zm-7.62-19.876a17.719 17.719 0 0 1 7.52-1.668c2.633 0 5.186.577 7.521 1.668v-.555c0-4.17-3.375-7.562-7.52-7.562-4.146 0-7.521 3.392-7.521 7.562v.555z"
/>
</g>
</svg>
)

7
src/internals/index.js

@ -5,6 +5,7 @@ import uuid from 'uuid/v4'
import { setImplementation } from 'api/network'
import sentry from 'sentry/node'
import { DEBUG_NETWORK } from 'config/constants'
import { serializeError } from 'helpers/errors'
require('../env')
@ -75,11 +76,7 @@ process.on('message', m => {
process.send({
type: 'cmd.ERROR',
requestId,
data: {
...error,
name: error && error.name,
message: error && error.message,
},
data: serializeError(error),
})
},
})

16
src/logger.js

@ -148,14 +148,16 @@ export default {
critical: (error: Error) => {
addLog('critical', error)
console.error(error)
try {
if (typeof window !== 'undefined') {
require('sentry/browser').captureException(error)
} else {
require('sentry/node').captureException(error)
if (!process.env.STORYBOOK_ENV) {
try {
if (typeof window !== 'undefined') {
require('sentry/browser').captureException(error)
} else {
require('sentry/node').captureException(error)
}
} catch (e) {
console.warn("Can't send to sentry", error, e)
}
} catch (e) {
console.warn("Can't send to sentry", error, e)
}
},

41
src/main/app.js

@ -2,16 +2,22 @@
import { app, BrowserWindow, Menu, screen } from 'electron'
import debounce from 'lodash/debounce'
import { MIN_HEIGHT, MIN_WIDTH } from 'config/constants'
import menu from 'main/menu'
import db from 'helpers/db'
import { setMainProcessPID, terminateAllTheThings } from './terminator'
setMainProcessPID(process.pid)
// necessary to prevent win from being garbage collected
let mainWindow = null
export const getMainWindow = () => mainWindow
let forceClose = false
// TODO put back OSX close behavior
// let forceClose = false
const { UPGRADE_EXTENSIONS, ELECTRON_WEBPACK_WDS_PORT, DEV_TOOLS, DEV_TOOLS_MODE } = process.env
@ -26,15 +32,16 @@ const getWindowPosition = (height, width, display = screen.getPrimaryDisplay())
}
}
const handleCloseWindow = w => e => {
if (!forceClose) {
e.preventDefault()
w.webContents.send('lock')
if (w !== null) {
w.hide()
}
}
}
// TODO put back OSX close behavior
// const handleCloseWindow = w => e => {
// if (!forceClose) {
// e.preventDefault()
// w.webContents.send('lock')
// if (w !== null) {
// w.hide()
// }
// }
// }
const getDefaultUrl = () =>
__DEV__ ? `http://localhost:${ELECTRON_WEBPACK_WDS_PORT || ''}` : `file://${__dirname}/index.html`
@ -67,9 +74,6 @@ const defaultWindowOptions = {
}
function createMainWindow() {
const MIN_HEIGHT = 768
const MIN_WIDTH = 1024
const savedDimensions = db.getIn('settings', 'window.MainWindow.dimensions', {})
const savedPositions = db.getIn('settings', 'window.MainWindow.positions', null)
@ -109,7 +113,9 @@ function createMainWindow() {
window.loadURL(url)
window.on('close', handleCloseWindow(window))
// TODO put back OSX close behavior
// window.on('close', handleCloseWindow(window))
window.on('close', terminateAllTheThings)
window.on('ready-to-show', () => {
window.show()
@ -128,9 +134,10 @@ function createMainWindow() {
return window
}
app.on('before-quit', () => {
forceClose = true
})
// TODO put back OSX close behavior
// app.on('before-quit', () => {
// forceClose = true
// })
app.on('window-all-closed', () => {
// On macOS it is common for applications to stay open

2
src/main/bridge.js

@ -11,6 +11,7 @@ import sentry from 'sentry/node'
import user from 'helpers/user'
import setupAutoUpdater, { quitAndInstall } from './autoUpdate'
import { setInternalProcessPID } from './terminator'
import { getMainWindow } from './app'
@ -49,6 +50,7 @@ const bootInternalProcess = () => {
SENTRY_USER_ID: userId,
},
})
setInternalProcessPID(internalProcess.pid)
internalProcess.on('message', handleGlobalInternalMessage)
internalProcess.on('exit', handleExit)
}

32
src/main/terminator.js

@ -0,0 +1,32 @@
// @flow
// <((((((\\\
// / . }\
// ;--..--._|}
// (\ '--/\--' )
// DISCLAIMER \\ | '-' :'|
// This is a dirty hack \\ . -==- .-|
// \\ \.__.' \--._
// [\\ __.--| // _/'--.
// \ \\ .'-._ ('-----'/ __/ \
// \ \\ / __>| | '--. |
// \ \\ | \ | / / /
// \ '\ / \ | | _/ /
// \ \ \ | | / /
// \ \ \ /
let MAIN_PROCESS_PID: ?number = null
let INTERNAL_PROCESS_PID: ?number = null
function kill(processType, pid) {
console.log(`-> Killing ${processType} process ${pid}`) // eslint-disable-line no-console
process.kill(pid, 'SIGTERM')
}
exports.setMainProcessPID = (pid: number) => (MAIN_PROCESS_PID = pid)
exports.setInternalProcessPID = (pid: number) => (INTERNAL_PROCESS_PID = pid)
exports.terminateAllTheThings = () => {
if (INTERNAL_PROCESS_PID) kill('internal', INTERNAL_PROCESS_PID)
if (MAIN_PROCESS_PID) kill('main', MAIN_PROCESS_PID)
}

2
src/stories/icons.stories.js

@ -6,7 +6,7 @@ import Box from 'components/base/Box'
const stories = storiesOf('Common', module)
const req = require.context('../icons', true, /^((?!illustrations).)*\.js$/)
const req = require.context('../icons', true, /.js$/)
const icons = req
.keys()
.map(file => {

8
static/i18n/en/app.yml

@ -286,9 +286,7 @@ settings:
language: Language
languageDesc: Choose the language to display.
counterValue: Base currency
counterValueDesc: Choose the currency to display next to your balance and operations.
exchange: Rate provider ({{ticker}})
exchangeDesc: Choose the provider of the base currency exchange rates.
counterValueDesc: Choose the currency to display next to your balance and operations. An exchange is used (via BTC pair).
region: Region
regionDesc: Choose the region in which you’re located to set the application’s time zone.
stock: Regional market indicator
@ -296,7 +294,7 @@ settings:
currencies:
desc: Select a cryptocurrency to edit its settings.
exchange: Rate provider ({{ticker}})
exchangeDesc: Choose the provider of the base currency exchange rates.
exchangeDesc: Choose the provider of the base currency exchange rates (via BTC).
confirmationsToSpend: Number of confirmations required to spend
confirmationsToSpendDesc: Set the number of confirmations required for your funds to be spendable. # A higher number of confirmations decreases the probability that a transaction is rejected.
confirmationsNb: Number of confirmations
@ -321,7 +319,7 @@ settings:
hardResetDesc: Erase all Ledger Live data stored on your computer, including your profile, accounts, transaction history and settings. The private keys that manage your crypto assets remain secure on your Ledger device.
hardReset: Reset
developerMode: Developer mode
developerModeDesc: Show developer apps in the Manager.
developerModeDesc: Show developer apps in the Manager and enable testnet currencies.
analytics: Analytics
analyticsDesc: Enable analytics of anonymous data to help Ledger improve the user experience. This includes the operating system, language, firmware versions and the number of added accounts.
reportErrors: Usage and diagnostics

8
static/i18n/fr/app.yml

@ -280,9 +280,7 @@ settings:
language: Language
languageDesc: Choose the language to display.
counterValue: Base currency
counterValueDesc: Choose the currency to display next to your balance and operations.
exchange: Rate provider ({{ticker}})
exchangeDesc: Choose the provider of the base currency exchange rates.
counterValueDesc: Choose the currency to display next to your balance and operations. An exchange is used (via BTC pair).
region: Region
regionDesc: Choose the region in which you’re located to set the application’s time zone.
stock: Regional market indicator
@ -290,7 +288,7 @@ settings:
currencies:
desc: Select a cryptocurrency to edit its settings.
exchange: Rate provider ({{ticker}})
exchangeDesc: Choose the provider of the base currency exchange rates.
exchangeDesc: Choose the provider of the base currency exchange rates (via BTC).
confirmationsToSpend: Number of confirmations required to spend
confirmationsToSpendDesc: Set the number of confirmations required for your funds to be spendable.
confirmationsNb: Number of confirmations
@ -315,7 +313,7 @@ settings:
hardResetDesc: Erase all Ledger Live data stored on your computer, including your profile, accounts, transaction history and settings. The private keys that manage your crypto assets remain secure on your Ledger device.
hardReset: Reset
developerMode: Developer mode
developerModeDesc: Show developer apps in the Manager.
developerModeDesc: Show developer apps in the Manager and enable testnet currencies.
analytics: Analytics
analyticsDesc: Enable analytics of anonymous data to help Ledger improve the user experience. This includes the operating system, language, firmware versions and the number of added accounts.
reportErrors: Usage and diagnostics

9
static/i18n/fr/errors.yml

@ -22,5 +22,10 @@ DeviceSocketNoBulkStatus: Oops, device connection failed. Please try again [bulk
DeviceSocketNoHandler: Oops, device connection failed (handler {{query}}). Please try again.
LatestMCUInstalledError: MCU on device already up to date.
HardResetFail: Reset failed. Please try again.
CannotUninstall: Cannot uninstall app.
CannotInstall: Not enough room left on your device. Please uninstall some apps and try again.
ManagerAPIsFail: Our services are currently unavailable. Please try again later.
ManagerUnexpected: Unexpected error occurred ({{msg}}). Please try again later.
ManagerNotEnoughSpace: Not enough room left on your device. Please uninstall some apps and try again.
ManagerDeviceLocked: Device is locked
ManagerAppAlreadyInstalled: App is already installed
ManagerAppRelyOnBTC: You must install Bitcoin application first
ManagerUninstallBTCDep: You must uninstall other altcoins first

19
static/images/blue-error-onb.svg

@ -0,0 +1,19 @@
<svg xmlns="http://www.w3.org/2000/svg" width="92" height="188" viewBox="0 0 92 188">
<defs>
<linearGradient id="a" x1="50%" x2="50%" y1="0%" y2="100%">
<stop offset="0%"/>
<stop offset="100%" stop-color="#FFF"/>
</linearGradient>
</defs>
<g fill="none" fill-rule="evenodd">
<path stroke="#1D2027" stroke-width="2" d="M37 120.644a1 1 0 0 0-1 1v26.225a5 5 0 0 0 5 5h8.486a5 5 0 0 0 5-5v-26.225a1 1 0 0 0-1-1H37z"/>
<path stroke="#142533" stroke-width="2" d="M42.208 153.253v10.929h6.436v-10.93h-6.436z"/>
<path stroke="#1D2027" stroke-width="2" d="M39.713 120.577h11.255l-.082-6.977a1 1 0 0 0-1-.988H40.619a1 1 0 0 0-.988 1.012l.082 6.953z"/>
<path fill="url(#a)" d="M6.836 53.925h1.616v22.65H6.836v-22.65zm5.657 0h1.616v22.65h-1.616v-22.65z" transform="translate(35 111)"/>
<g>
<path fill="#1D2028" d="M88.556 17.556c0-1.105.671-2 1.5-2 .828 0 1.5.895 1.5 2v6c0 1.104-.672 2-1.5 2-.829 0-1.5-.896-1.5-2"/>
<rect width="88" height="118.635" x="1" y="1" fill="#FCEAEC" stroke="#1D2027" stroke-width="2" rx="5.44"/>
<rect width="59" height="88.615" x="15.5" y="16.5" fill="#FFF" stroke="#EA2E49" rx="4.08"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

13
static/images/get-started-onb.svg

@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" width="113" height="109" viewBox="0 0 113 109">
<g fill="none" fill-rule="evenodd">
<rect width="1.44" height="5.6" y="16.6" fill="#1D2028" rx=".72" transform="matrix(-1 0 0 1 1.44 0)"/>
<rect width="1.44" height="5.6" y="34.8" fill="#1D2028" rx=".72" transform="matrix(-1 0 0 1 1.44 0)"/>
<path fill="#6490F1" fill-opacity=".1" stroke="#1D2028" stroke-width="2" d="M16.592 12c.225 0 .408.183.408.408v95.184a.408.408 0 0 1-.408.408H2.128a.408.408 0 0 1-.408-.408V12.408c0-.225.183-.408.408-.408h14.464z"/>
<rect width="7.64" height="27" x="5.513" y="18.522" fill="#FFF" stroke="#6490F1" rx=".704" transform="matrix(-1 0 0 1 18.665 0)"/>
<path fill="#FFF" stroke="#1D2028" stroke-width="2" d="M9.36 54A7.64 7.64 0 0 1 17 61.64v45.952a.408.408 0 0 1-.408.408H2.128a.408.408 0 0 1-.408-.408V61.64A7.64 7.64 0 0 1 9.36 54z"/>
<ellipse cx="9.36" cy="61.4" fill="#FFF" stroke="#6490F1" rx="3.82" ry="3.7" transform="matrix(-1 0 0 1 18.72 0)"/>
<rect width="3.137" height="9.306" x="109.863" y="13.959" fill="#1D2028" rx="1.569"/>
<rect width="76.431" height="106.571" x="34" y="1" fill="#6490F1" fill-opacity=".1" stroke="#1D2027" stroke-width="2" rx="5.44"/>
<rect width="52.333" height="79.653" x="46.043" y="15.235" fill="#FFF" stroke="#6490F1" rx="4.08"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

7
static/images/ledger-blue-onb.svg

@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" width="52" height="72" viewBox="0 0 52 72">
<g fill="none" fill-rule="evenodd">
<rect width="2.039" height="6.171" x="49.961" y="9.257" fill="#1D2028" rx="1.02"/>
<rect width="49.48" height="70.5" x=".75" y=".75" fill="#6490F1" fill-opacity=".1" stroke="#1D2027" stroke-width="1.5" rx="3.2"/>
<rect width="34.167" height="52.986" x="8.403" y="10.021" fill="#FFF" stroke="#6490F1" stroke-width=".5" rx="2.4"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 493 B

10
static/images/ledger-nano-onb.svg

@ -0,0 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg" width="13" height="72" viewBox="0 0 13 72">
<g fill="none" fill-rule="evenodd">
<rect width="1.04" height="4.114" x="11.96" y="4.114" fill="#1D2028" rx=".52"/>
<rect width="1.04" height="4.114" x="11.96" y="17.486" fill="#1D2028" rx=".52"/>
<path fill="#6490F1" fill-opacity=".1" stroke="#1D2028" stroke-width="1.5" d="M1.6.75a.85.85 0 0 0-.85.85v68.8c0 .47.38.85.85.85h9.28c.47 0 .85-.38.85-.85V1.6a.85.85 0 0 0-.85-.85H1.6z"/>
<rect width="5.74" height="20.071" x="3.39" y="5.409" fill="#FFF" stroke="#6490F1" stroke-width=".5" rx=".8"/>
<path fill="#FFF" stroke="#1D2028" stroke-width="1.5" d="M6.24 31.607a5.49 5.49 0 0 0-5.49 5.49V70.4c0 .47.38.85.85.85h9.28c.47 0 .85-.38.85-.85V37.097a5.49 5.49 0 0 0-5.49-5.49z"/>
<ellipse cx="6.24" cy="37.029" fill="#FFF" stroke="#6490F1" stroke-width=".5" rx="2.87" ry="2.836"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 931 B

34
static/images/nano-error-onb.svg

@ -0,0 +1,34 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="366" height="43" viewBox="0 0 366 43">
<defs>
<linearGradient id="a" x1="50%" x2="50%" y1="0%" y2="100%">
<stop offset="0%"/>
<stop offset="100%" stop-color="#FFF"/>
</linearGradient>
<rect id="b" width="41.711" height="238.384" rx="4"/>
<path id="c" d="M5.773 5l2.541-2.541a.235.235 0 0 0 0-.332l-.441-.441a.235.235 0 0 0-.332 0L5 4.226l-2.541-2.54a.235.235 0 0 0-.332 0l-.441.441a.235.235 0 0 0 0 .332L4.226 5l-2.54 2.541a.235.235 0 0 0 0 .332l.441.441c.092.092.24.092.332 0L5 5.774l2.541 2.54c.092.092.24.092.332 0l.441-.441a.235.235 0 0 0 0-.332L5.774 5z"/>
</defs>
<g fill="none" fill-rule="evenodd">
<path stroke="#1D2027" stroke-width="2" d="M127.356 30a1 1 0 0 1-1 1H100.13a5 5 0 0 1-5-5v-8.486a5 5 0 0 1 5-5h26.225a1 1 0 0 1 1 1V30z"/>
<path stroke="#142533" stroke-width="2" d="M94.747 24.792H83.818v-6.436h10.93v6.436z"/>
<path stroke="#1D2027" stroke-width="2" d="M127.423 27.287V16.032l6.977.082a1 1 0 0 1 .988 1V26.381a1 1 0 0 1-1.012.988l-6.953-.082z"/>
<path fill="url(#a)" d="M6.836 53.925h1.616v82.65H6.836v-82.65zm5.657 0h1.616v82.65h-1.616v-82.65z" transform="matrix(0 -1 -1 0 137 32)"/>
<g transform="rotate(-90 85 -42)">
<rect width="4.492" height="17.12" x="38.336" y="15.505" fill="#142533" rx="2"/>
<rect width="4.492" height="17.12" x="38.336" y="70.094" fill="#142533" rx="2"/>
<use fill="#FFF" xlink:href="#b"/>
<rect width="39.711" height="236.384" x="1" y="1" fill="#FCE0E4" stroke="#142533" stroke-linejoin="square" stroke-width="2" rx="4"/>
<rect width="20.176" height="61.019" x="10.767" y="21.173" fill="#FFF" stroke="#EA2E49" rx="1.6"/>
<path fill="#FFF" stroke="#142533" stroke-width="2" d="M20.856 95.966C9.89 95.966 1 104.856 1 115.822v118.562a3 3 0 0 0 3 3h33.711a3 3 0 0 0 3-3V115.822c0-10.966-8.89-19.856-19.855-19.856z"/>
<ellipse cx="21.016" cy="116.123" stroke="#EA2E49" rx="10.57" ry="10.644"/>
<g transform="translate(16.066 26.884)">
<mask id="d" fill="#fff">
<use xlink:href="#c"/>
</mask>
<use fill="#000" fill-rule="nonzero" xlink:href="#c"/>
<g fill="#EA2E49" mask="url(#d)">
<path d="M0 0h10v10H0z"/>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

19
static/images/select-pin-blue-onb.svg

@ -0,0 +1,19 @@
<svg xmlns="http://www.w3.org/2000/svg" width="92" height="188" viewBox="0 0 92 188">
<defs>
<linearGradient id="a" x1="50%" x2="50%" y1="0%" y2="100%">
<stop offset="0%"/>
<stop offset="100%" stop-color="#FFF"/>
</linearGradient>
</defs>
<g fill="none" fill-rule="evenodd">
<path stroke="#1D2027" stroke-width="2" d="M37 120.644a1 1 0 0 0-1 1v26.225a5 5 0 0 0 5 5h8.486a5 5 0 0 0 5-5v-26.225a1 1 0 0 0-1-1H37z"/>
<path stroke="#142533" stroke-width="2" d="M42.208 153.253v10.929h6.436v-10.93h-6.436z"/>
<path stroke="#1D2027" stroke-width="2" d="M39.713 120.577h11.255l-.082-6.977a1 1 0 0 0-1-.988H40.619a1 1 0 0 0-.988 1.012l.082 6.953z"/>
<path fill="url(#a)" d="M6.836 53.925h1.616v22.65H6.836v-22.65zm5.657 0h1.616v22.65h-1.616v-22.65z" transform="translate(35 111)"/>
<g>
<path fill="#1D2028" d="M88.556 17.556c0-1.105.671-2 1.5-2 .828 0 1.5.895 1.5 2v6c0 1.104-.672 2-1.5 2-.829 0-1.5-.896-1.5-2"/>
<rect width="88" height="118.635" x="1" y="1" fill="#EFF3FD" stroke="#1D2027" stroke-width="2" rx="5.44"/>
<rect width="59" height="88.615" x="15.5" y="16.5" fill="#FFF" stroke="#6490F1" rx="4.08"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

25
static/images/select-pin-nano-onb.svg

@ -0,0 +1,25 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="260" height="129" viewBox="0 0 260 129">
<defs>
<linearGradient id="a" x1="50%" x2="50%" y1="0%" y2="100%">
<stop offset="0%"/>
<stop offset="100%" stop-color="#FFF"/>
</linearGradient>
<path id="b" d="M91 0h34a4 4 0 0 1 4 4v108.144c0 11.519-9.337 20.856-20.856 20.856h-.288C96.337 133 87 123.663 87 112.144V4a4 4 0 0 1 4-4z"/>
</defs>
<g fill="none" fill-rule="evenodd">
<path stroke="#1D2027" stroke-width="2" d="M127.856 31.44a1 1 0 0 1-1 1H100.63a5 5 0 0 1-5-5v-8.486a5 5 0 0 1 5-5h26.225a1 1 0 0 1 1 1v16.485z"/>
<path stroke="#142533" stroke-width="2" d="M95.247 26.231H84.318v-6.435h10.93v6.435z"/>
<path stroke="#1D2027" stroke-width="2" d="M127.923 28.726V17.471l6.977.083a1 1 0 0 1 .988 1V27.82a1 1 0 0 1-1.012.988l-6.953-.083z"/>
<path fill="url(#a)" d="M6.836 53.925h1.616v82.65H6.836v-82.65zm5.657 0h1.616v82.65h-1.616v-82.65z" transform="matrix(0 -1 -1 0 137.5 33.44)"/>
<g transform="rotate(-90 128.59 1.975)">
<rect width="4.492" height="17.12" x="125.336" y="15.505" fill="#142533" rx="2"/>
<rect width="4.492" height="17.12" x="125.336" y="70.094" fill="#142533" rx="2"/>
<use fill="#FFF" xlink:href="#b"/>
<path fill="#6490F1" fill-opacity=".15" stroke="#142533" stroke-linejoin="square" stroke-width="2" d="M91 1a3 3 0 0 0-3 3v108.144C88 123.11 96.89 132 107.856 132h.288C119.11 132 128 123.11 128 112.144V4a3 3 0 0 0-3-3H91z"/>
<rect width="21" height="62" x="97.5" y="21.5" fill="#FFF" stroke="#6490F1" rx="1.6"/>
<path fill="#6490F1" d="M105.5 35h5a.5.5 0 0 1 .5.5v34a.5.5 0 0 1-.5.5h-5a.5.5 0 0 1-.5-.5v-34a.5.5 0 0 1 .5-.5zm1.238 3.042l.774.512v.013l-.774.505.341.466.722-.577h.013l.243.899.551-.177-.328-.88.932.053v-.597l-.932.046.328-.873-.551-.17-.243.892h-.013l-.722-.584-.34.472zm0 3.908l.774.512v.013l-.774.505.341.466.722-.578h.013l.243.9.551-.178-.328-.88.932.053v-.597l-.932.046.328-.872-.551-.17-.243.891h-.013l-.722-.584-.34.473zm0 3.907l.774.512v.013l-.774.505.341.466.722-.577h.013l.243.899.551-.178-.328-.879.932.053v-.597l-.932.046.328-.873-.551-.17-.243.892h-.013l-.722-.584-.34.472zm0 3.908l.774.511v.014l-.774.505.341.466.722-.578h.013l.243.899.551-.177-.328-.88.932.053v-.597l-.932.046.328-.872-.551-.171-.243.892h-.013l-.722-.584-.34.473zm0 3.907l.774.512v.013l-.774.505.341.466.722-.577h.013l.243.898.551-.177-.328-.879.932.053v-.597l-.932.046.328-.873-.551-.17-.243.892h-.013l-.722-.584-.34.472zm0 3.908l.774.511v.013l-.774.506.341.465.722-.577h.013l.243.899.551-.177-.328-.88.932.053v-.597l-.932.046.328-.873-.551-.17-.243.892h-.013l-.722-.584-.34.473zm0 3.907l.774.512v.013l-.774.505.341.466.722-.578h.013l.243.9.551-.178-.328-.879.932.052v-.597l-.932.046.328-.872-.551-.17-.243.891h-.013l-.722-.583-.34.472zm0 3.907l.774.512v.013l-.774.505.341.466.722-.577h.013l.243.899.551-.177-.328-.88.932.053v-.597l-.932.046.328-.873-.551-.17-.243.892h-.013l-.722-.584-.34.472z"/>
<path fill="#FFF" stroke="#142533" stroke-width="2" d="M123.166 125.105c7.049-8.4 5.953-20.925-2.447-27.974l-90.824-76.21a3 3 0 0 0-4.227.37L4 47.115a3 3 0 0 0 .37 4.227l90.824 76.21c8.4 7.049 20.924 5.953 27.973-2.447z"/>
<ellipse cx="108.016" cy="111.123" stroke="#6490F1" rx="10.57" ry="10.644"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.4 KiB

9
static/images/write-seed-onb.svg

@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" width="145" height="109" viewBox="0 0 145 109">
<g fill="none" fill-rule="evenodd" transform="translate(-1)">
<path fill="#6490F1" fill-opacity=".1" stroke="#142533" stroke-width="1.8" d="M29.105 104.014l19.87-84.34L4.123 35.999a2.7 2.7 0 0 0-1.573 3.566l26.556 64.449zM118.37 105.35L97.73 34.29l44.752 16.288a2.7 2.7 0 0 1 1.49 3.748L118.37 105.35z"/>
<rect width="88.2" height="107.2" x="29.9" y=".9" fill="#FFF" stroke="#142533" stroke-width="1.8" rx="3.6"/>
<rect width="50" height="4" x="49" y="14" fill="#6490F1" rx="2"/>
<path fill="#6490F1" d="M83.18 91.378h24.01a1.81 1.81 0 0 1 0 3.622H83.18a1.81 1.81 0 1 1 0-3.622zm-42.37 0h24.01a1.81 1.81 0 1 1 0 3.622H40.81a1.81 1.81 0 1 1 0-3.622zm42.37-12.675h24.01a1.81 1.81 0 0 1 0 3.621H83.18a1.81 1.81 0 1 1 0-3.621zm-42.37 0h24.01a1.81 1.81 0 1 1 0 3.621H40.81a1.81 1.81 0 1 1 0-3.621zm42.37-12.676h24.01a1.81 1.81 0 0 1 0 3.622H83.18a1.81 1.81 0 1 1 0-3.622zm-42.37 0h24.01a1.81 1.81 0 1 1 0 3.622H40.81a1.81 1.81 0 1 1 0-3.622zm42.37-12.676h24.01a1.81 1.81 0 0 1 0 3.622H83.18a1.81 1.81 0 1 1 0-3.622zm-42.37 0h24.01a1.81 1.81 0 1 1 0 3.622H40.81a1.81 1.81 0 1 1 0-3.622zm42.37-12.675h24.01a1.81 1.81 0 0 1 0 3.621H83.18a1.81 1.81 0 1 1 0-3.621zm-42.37 0h24.01a1.81 1.81 0 1 1 0 3.621H40.81a1.81 1.81 0 1 1 0-3.621zM83.18 28h24.01a1.81 1.81 0 0 1 0 3.622H83.18a1.81 1.81 0 1 1 0-3.622zm-42.37 0h24.01a1.81 1.81 0 1 1 0 3.622H40.81a1.81 1.81 0 1 1 0-3.622z" opacity=".5"/>
<path fill="#FFF" fill-rule="nonzero" stroke="#6490F1" stroke-width="2" d="M85.623 56.047a17.987 17.987 0 0 1 6.194 13.596c0 9.92-8.032 17.99-17.909 17.99-9.876 0-17.908-8.07-17.908-17.99a17.986 17.986 0 0 1 6.194-13.596v-3.281C62.194 46.278 67.447 41 73.908 41c6.462 0 11.715 5.278 11.715 11.766v3.28zM73.809 73.193zm.003-.018v4.55a.1.1 0 0 0 .096.102.1.1 0 0 0 .097-.102v-4.55a3.149 3.149 0 0 1-.006-.054l-.088-.899.885-.179a4.505 4.505 0 0 0 3.596-4.42c0-2.493-2.008-4.51-4.484-4.51-2.475 0-4.483 2.018-4.483 4.51a4.505 4.505 0 0 0 3.596 4.42l.885.18-.088.898a3.149 3.149 0 0 1-.006.054zm.196.018a.1.1 0 0 0 0 .004v-.003-.001zm0 .004v-.002zm-7.62-19.876a17.719 17.719 0 0 1 7.52-1.668c2.633 0 5.186.577 7.521 1.668v-.555c0-4.17-3.375-7.562-7.52-7.562-4.146 0-7.521 3.392-7.521 7.562v.555z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

23
webpack/renderer.config.js

@ -1,9 +1,29 @@
const HardSourceWebpackPlugin = require('hard-source-webpack-plugin')
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
const plugins = require('./plugins')
const resolve = require('./resolve')
const rules = require('./rules')
const getOptimization = env => {
const optimization = {
minimizer: [
// Default Config without mangling
new UglifyJsPlugin({
parallel: true,
sourceMap: true,
uglifyOptions: {
mangle: false,
compress: {
ecma: 7,
},
},
}),
],
}
return env === 'production' ? optimization : undefined
}
const config = {
mode: __ENV__,
plugins: [...plugins('renderer'), new HardSourceWebpackPlugin()],
@ -14,6 +34,9 @@ const config = {
devServer: {
historyApiFallback: true,
},
optimization: {
...getOptimization(__ENV__),
},
}
if (__DEV__) {

17
yarn.lock

@ -1477,8 +1477,8 @@
bip32-path "0.4.2"
"@ledgerhq/hw-transport-node-hid@^4.13.0":
version "4.15.0"
resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport-node-hid/-/hw-transport-node-hid-4.15.0.tgz#d25b1839230509235782884a5be3d56e791dad26"
version "4.16.0"
resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport-node-hid/-/hw-transport-node-hid-4.16.0.tgz#de8d7ffd37e01c77aed566d8c534fe3937c3c35c"
dependencies:
"@ledgerhq/hw-transport" "^4.15.0"
node-hid "^0.7.2"
@ -13797,6 +13797,19 @@ uglifyjs-webpack-plugin@^1.2.4, uglifyjs-webpack-plugin@^1.2.5:
webpack-sources "^1.1.0"
worker-farm "^1.5.2"
uglifyjs-webpack-plugin@^1.2.6:
version "1.2.6"
resolved "https://registry.yarnpkg.com/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.2.6.tgz#f4bb44f02431e82b301d8d4624330a6a35729381"
dependencies:
cacache "^10.0.4"
find-cache-dir "^1.0.0"
schema-utils "^0.4.5"
serialize-javascript "^1.4.0"
source-map "^0.6.1"
uglify-es "^3.3.4"
webpack-sources "^1.1.0"
worker-farm "^1.5.2"
uid-number@0.0.6:
version "0.0.6"
resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81"

Loading…
Cancel
Save