Browse Source

Merge pull request #306 from gre/migrate-live-common

Integrate live-common library
master
Gaëtan Renaudeau 7 years ago
committed by GitHub
parent
commit
64087c87f6
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      .circleci/config.yml
  2. 1
      .eslintrc
  3. 8
      README.md
  4. 14
      package.json
  5. 2
      scripts/postinstall.sh
  6. 2
      src/actions/accounts.js
  7. 36
      src/actions/counterValues.js
  8. 13
      src/components/AccountPage/AccountHeader.js
  9. 17
      src/components/AccountPage/index.js
  10. 2
      src/components/BalanceSummary/BalanceInfos.js
  11. 6
      src/components/BalanceSummary/index.js
  12. 2
      src/components/CalculateBalance.js
  13. 36
      src/components/CounterValue/index.js
  14. 4
      src/components/CounterValue/stories.js
  15. 19
      src/components/CryptoCurrencyIcon.js
  16. 133
      src/components/DashboardPage/AccountCard.js
  17. 17
      src/components/DashboardPage/index.js
  18. 2
      src/components/DeviceCheckAddress.js
  19. 15
      src/components/DeviceConnect/index.js
  20. 2
      src/components/DeviceMonit/index.js
  21. 14
      src/components/DeviceMonitNew/index.js
  22. 2
      src/components/IsUnlocked.js
  23. 6
      src/components/OperationsList/Operation.js
  24. 4
      src/components/OperationsList/index.js
  25. 2
      src/components/OperationsList/stories.js
  26. 2
      src/components/QRCodeExporter.js
  27. 2
      src/components/ReceiveBox.js
  28. 19
      src/components/RequestAmount/index.js
  29. 6
      src/components/SelectAccount/index.js
  30. 26
      src/components/SelectAccount/stories.js
  31. 32
      src/components/SelectCurrency/index.js
  32. 18
      src/components/SettingsPage/sections/Currencies.js
  33. 15
      src/components/SettingsPage/sections/Display.js
  34. 6
      src/components/SideBar/index.js
  35. 2
      src/components/base/Chart/Tooltip.js
  36. 2
      src/components/base/Chart/index.js
  37. 2
      src/components/base/Chart/refreshDraw.js
  38. 4
      src/components/base/Chart/stories.js
  39. 7
      src/components/base/FlipTicker/stories.js
  40. 4
      src/components/base/FormattedVal/__tests__/FormattedVal.test.js
  41. 16
      src/components/base/FormattedVal/index.js
  42. 4
      src/components/base/InputCurrency/index.js
  43. 5
      src/components/base/InputCurrency/stories.js
  44. 2
      src/components/base/InputPassword/index.js
  45. 4
      src/components/base/Modal/ConfirmModal.js
  46. 26
      src/components/modals/AddAccount/01-step-currency.js
  47. 10
      src/components/modals/AddAccount/03-step-import.js
  48. 18
      src/components/modals/AddAccount/index.js
  49. 2
      src/components/modals/OperationDetails.js
  50. 2
      src/components/modals/Receive/01-step-account.js
  51. 2
      src/components/modals/Receive/03-step-confirm-address.js
  52. 2
      src/components/modals/Receive/04-step-receive-funds.js
  53. 2
      src/components/modals/Receive/index.js
  54. 2
      src/components/modals/Send/01-step-amount.js
  55. 2
      src/components/modals/Send/03-step-verification.js
  56. 2
      src/components/modals/Send/Footer.js
  57. 2
      src/components/modals/Send/index.js
  58. 2
      src/components/modals/SettingsAccount.js
  59. 17
      src/components/modals/StepConnectDevice.js
  60. 137
      src/helpers/__tests__/balance.test.js
  61. 5
      src/helpers/balance.js
  62. 19
      src/helpers/btc.js
  63. 1
      src/helpers/db.js
  64. 29
      src/internals/usb/wallet/accounts.js
  65. 24
      src/internals/usb/wallet/index.js
  66. 2
      src/main/counterValuesSync.js
  67. 30
      src/reducers/accounts.js
  68. 4
      src/reducers/counterValues.js
  69. 48
      src/reducers/settings.js
  70. 11
      src/renderer/events.js
  71. 2
      src/renderer/init.js
  72. 16
      src/stories/currencies.stories.js
  73. 2
      src/types/common.js
  74. 18
      yarn.lock

6
.circleci/config.yml

@ -15,12 +15,6 @@ jobs:
- run: - run:
name: Dependencies name: Dependencies
command: yarn command: yarn
- run:
name: Flow typed
command: yarn flow-typed
- run:
name: Temporary remove broken flow definitions
command: rm flow-typed/npm/{react-i18next_v7.x.x.js,react-redux_v5.x.x.js,styled-components_v3.x.x.js}
- run: - run:
name: Lint name: Lint
command: yarn lint command: yarn lint

1
.eslintrc

@ -51,6 +51,7 @@
"react/jsx-filename-extension": 0, "react/jsx-filename-extension": 0,
"react/jsx-no-target-blank": 0, "react/jsx-no-target-blank": 0,
"react/prefer-stateless-function": 0, "react/prefer-stateless-function": 0,
"react/require-default-props": 0,
}, },
"settings": { "settings": {
"import/resolver": { "import/resolver": {

8
README.md

@ -13,17 +13,13 @@ Project has been tested under [NodeJS](https://nodejs.org) v9.3.0, with [Yarn](h
#### Setup #### Setup
1. Install dependencies 1. Install dependencies
```bash ```bash
# app dependencies
yarn yarn
# npm packages flow definitions
yarn flow-typed
``` ```
2. Create `.env` file 2. Create `.env` file
```bash ```bash
# ENV VARIABLES # ENV VARIABLES

14
package.json

@ -12,10 +12,8 @@
"dist": "yarn compile && electron-builder", "dist": "yarn compile && electron-builder",
"test": "jest", "test": "jest",
"flow": "flow", "flow": "flow",
"flow-typed": "flow-typed install -s --overwrite",
"lint": "eslint src webpack .storybook", "lint": "eslint src webpack .storybook",
"postinstall": "bash ./scripts/postinstall.sh", "postinstall": "bash ./scripts/postinstall.sh",
"precommit": "lint-staged",
"prettier": "prettier --write \"{src,webpack,.storybook}/**/*.js\"", "prettier": "prettier --write \"{src,webpack,.storybook}/**/*.js\"",
"publish-storybook": "bash ./scripts/publish-storybook.sh", "publish-storybook": "bash ./scripts/publish-storybook.sh",
"release": "build", "release": "build",
@ -23,13 +21,6 @@
"storybook": "NODE_ENV=development STORYBOOK_ENV=1 start-storybook -s ./static -p 4444", "storybook": "NODE_ENV=development STORYBOOK_ENV=1 start-storybook -s ./static -p 4444",
"trans": "node scripts/trans" "trans": "node scripts/trans"
}, },
"lint-staged": {
"*.js": [
"eslint --fix",
"prettier --write",
"git add"
]
},
"electronWebpack": { "electronWebpack": {
"title": true, "title": true,
"renderer": { "renderer": {
@ -44,12 +35,11 @@
"webpack-sources": "1.0.1" "webpack-sources": "1.0.1"
}, },
"dependencies": { "dependencies": {
"@ledgerhq/currencies": "^4.10.1",
"@ledgerhq/hw-app-btc": "^4.7.3", "@ledgerhq/hw-app-btc": "^4.7.3",
"@ledgerhq/hw-app-eth": "^4.7.3", "@ledgerhq/hw-app-eth": "^4.7.3",
"@ledgerhq/hw-transport": "^4.7.3", "@ledgerhq/hw-transport": "^4.7.3",
"@ledgerhq/hw-transport-node-hid": "^4.7.6", "@ledgerhq/hw-transport-node-hid": "^4.7.6",
"@ledgerhq/wallet-common": "^1.2.0", "@ledgerhq/live-common": "^2.0.0-rc4",
"axios": "^0.18.0", "axios": "^0.18.0",
"babel-runtime": "^6.26.0", "babel-runtime": "^6.26.0",
"bcryptjs": "^2.4.3", "bcryptjs": "^2.4.3",
@ -89,6 +79,7 @@
"redux": "^4.0.0", "redux": "^4.0.0",
"redux-actions": "^2.3.0", "redux-actions": "^2.3.0",
"redux-thunk": "^2.2.0", "redux-thunk": "^2.2.0",
"reselect": "^3.0.1",
"smooth-scrollbar": "^8.2.7", "smooth-scrollbar": "^8.2.7",
"source-map": "0.7.2", "source-map": "0.7.2",
"source-map-support": "^0.5.4", "source-map-support": "^0.5.4",
@ -140,7 +131,6 @@
"husky": "^0.14.3", "husky": "^0.14.3",
"jest": "^22.4.3", "jest": "^22.4.3",
"js-yaml": "^3.10.0", "js-yaml": "^3.10.0",
"lint-staged": "^7.0.4",
"node-loader": "^0.6.0", "node-loader": "^0.6.0",
"prettier": "^1.12.1", "prettier": "^1.12.1",
"react-hot-loader": "^4.1.0", "react-hot-loader": "^4.1.0",

2
scripts/postinstall.sh

@ -1,3 +1,5 @@
#/bin/bash #/bin/bash
flow-typed install -s --overwrite
rm flow-typed/npm/{react-i18next_v7.x.x.js,react-redux_v5.x.x.js,styled-components_v3.x.x.js}
electron-builder install-app-deps electron-builder install-app-deps

2
src/actions/accounts.js

@ -1,7 +1,7 @@
// @flow // @flow
import sortBy from 'lodash/sortBy' import sortBy from 'lodash/sortBy'
import type { Account } from '@ledgerhq/wallet-common/lib/types' import type { Account } from '@ledgerhq/live-common/lib/types'
import db from 'helpers/db' import db from 'helpers/db'

36
src/actions/counterValues.js

@ -1,12 +1,13 @@
// @flow // @flow
import { getFiatUnit } from '@ledgerhq/currencies'
import { fetchHistodayRates } from '@ledgerhq/wallet-common/lib/api/countervalue'
import type { Currency } from '@ledgerhq/currencies'
import type { Dispatch } from 'redux' import type { Dispatch } from 'redux'
import { fetchHistodayRates } from '@ledgerhq/live-common/lib/api/countervalue'
import type { CryptoCurrency, Currency } from '@ledgerhq/live-common/lib/types'
import { counterValueCurrencySelector } from 'reducers/settings'
import { currenciesSelector } from 'reducers/accounts'
import db from 'helpers/db' import db from 'helpers/db'
import type { State } from 'reducers'
export type InitCounterValues = () => { type: string, payload: Object } export type InitCounterValues = () => { type: string, payload: Object }
export const initCounterValues: InitCounterValues = () => ({ export const initCounterValues: InitCounterValues = () => ({
@ -20,18 +21,23 @@ export const updateCounterValues: UpdateCounterValues = payload => ({
payload, payload,
}) })
export type FetchCounterValues = (?(Currency[])) => (Dispatch<*>, Function) => Promise<any> function cryptoCurrenciesToCurrencies(currencies: CryptoCurrency[]): Currency[] {
export const fetchCounterValues: FetchCounterValues = (currencies: ?(Currency[])) => async ( // $FlowFixMe this function is just to fix flow types. array contravariant issue.
dispatch, return currencies
getState, }
) => {
const { accounts, settings } = getState() export type FetchCounterValues = (
currencies?: Currency[],
) => (Dispatch<*>, () => State) => Promise<any>
export const fetchCounterValues: FetchCounterValues = currencies => async (dispatch, getState) => {
const state = getState()
const currency = counterValueCurrencySelector(state)
if (!currency) return
if (!currencies) { if (!currencies) {
currencies = accounts.map(a => a.currency) // TODO this should be default, there is no need to provide the currencies in parameter
currencies = cryptoCurrenciesToCurrencies(currenciesSelector(state))
} }
const counterValues = await fetchHistodayRates(currencies, currency)
const { counterValue } = settings
const fiatUnit = getFiatUnit(counterValue)
const counterValues = await fetchHistodayRates(currencies, fiatUnit)
dispatch(updateCounterValues(counterValues)) dispatch(updateCounterValues(counterValues))
} }

13
src/components/AccountPage/AccountHeader.js

@ -1,13 +1,13 @@
// @flow // @flow
import React, { PureComponent } from 'react' import React, { PureComponent } from 'react'
import { getIconByCoinType } from '@ledgerhq/currencies/react'
import styled from 'styled-components' import styled from 'styled-components'
import type { Account } from '@ledgerhq/wallet-common/lib/types' import type { Account } from '@ledgerhq/live-common/lib/types'
import Box from 'components/base/Box' import Box from 'components/base/Box'
import Text from 'components/base/Text' import Text from 'components/base/Text'
import CryptoCurrencyIcon from '../CryptoCurrencyIcon'
const CurName = styled(Text).attrs({ const CurName = styled(Text).attrs({
ff: 'Open Sans|SemiBold', ff: 'Open Sans|SemiBold',
@ -32,14 +32,11 @@ type Props = {
class AccountHeader extends PureComponent<Props> { class AccountHeader extends PureComponent<Props> {
render() { render() {
const { account } = this.props const { account } = this.props
const Icon = getIconByCoinType(account.currency.coinType)
return ( return (
<Box horizontal align="center" flow={2}> <Box horizontal align="center" flow={2}>
{Icon && ( <Box color={account.currency.color}>
<Box color={account.currency.color}> <CryptoCurrencyIcon currency={account.currency} size={24} />
<Icon size={24} /> </Box>
</Box>
)}
<Box> <Box>
<CurName>{account.currency.name}</CurName> <CurName>{account.currency.name}</CurName>
<AccountName>{account.name}</AccountName> <AccountName>{account.name}</AccountName>

17
src/components/AccountPage/index.js

@ -7,9 +7,12 @@ import { connect } from 'react-redux'
import { translate } from 'react-i18next' import { translate } from 'react-i18next'
import { Redirect } from 'react-router' import { Redirect } from 'react-router'
import styled from 'styled-components' import styled from 'styled-components'
import { formatCurrencyUnit, getFiatUnit } from '@ledgerhq/currencies' import {
formatCurrencyUnit,
getFiatCurrencyByTicker,
} from '@ledgerhq/live-common/lib/helpers/currencies'
import type { Account } from '@ledgerhq/wallet-common/lib/types' import type { Account } from '@ledgerhq/live-common/lib/types'
import { MODAL_SEND, MODAL_RECEIVE, MODAL_SETTINGS_ACCOUNT } from 'config/constants' import { MODAL_SEND, MODAL_RECEIVE, MODAL_SETTINGS_ACCOUNT } from 'config/constants'
@ -95,9 +98,13 @@ class AccountPage extends PureComponent<Props, State> {
currency: formatCurrencyUnit(account.unit, account.balance, { currency: formatCurrencyUnit(account.unit, account.balance, {
showCode: true, showCode: true,
}), }),
counterValue: formatCurrencyUnit(getFiatUnit(counterValue), data.totalBalance, { counterValue: formatCurrencyUnit(
showCode: true, getFiatCurrencyByTicker(counterValue).units[0],
}), data.totalBalance,
{
showCode: true,
},
),
}, },
}) })
} }

2
src/components/BalanceSummary/BalanceInfos.js

@ -3,7 +3,7 @@
import React from 'react' import React from 'react'
import styled from 'styled-components' import styled from 'styled-components'
import type { Unit } from '@ledgerhq/currencies' import type { Unit } from '@ledgerhq/live-common/lib/types'
import type { T } from 'types/common' import type { T } from 'types/common'
import Box from 'components/base/Box' import Box from 'components/base/Box'

6
src/components/BalanceSummary/index.js

@ -1,8 +1,8 @@
// @flow // @flow
import React, { Fragment } from 'react' import React, { Fragment } from 'react'
import type { Account } from '@ledgerhq/wallet-common/lib/types' import type { Account } from '@ledgerhq/live-common/lib/types'
import { getFiatUnit } from '@ledgerhq/currencies' import { getFiatCurrencyByTicker } from '@ledgerhq/live-common/lib/helpers/currencies'
import Chart from 'components/base/Chart' import Chart from 'components/base/Chart'
import Box, { Card } from 'components/base/Box' import Box, { Card } from 'components/base/Box'
@ -30,7 +30,7 @@ const BalanceSummary = ({
renderHeader, renderHeader,
selectedTime, selectedTime,
}: Props) => { }: Props) => {
const unit = getFiatUnit(counterValue) const unit = getFiatCurrencyByTicker(counterValue).units[0]
return ( return (
<Card p={0} py={6}> <Card p={0} py={6}>
<CalculateBalance <CalculateBalance

2
src/components/CalculateBalance.js

@ -5,7 +5,7 @@ import { connect } from 'react-redux'
import noop from 'lodash/noop' import noop from 'lodash/noop'
import type { Account } from '@ledgerhq/wallet-common/lib/types' import type { Account } from '@ledgerhq/live-common/lib/types'
import calculateBalance from 'helpers/balance' import calculateBalance from 'helpers/balance'

36
src/components/CounterValue/index.js

@ -2,9 +2,9 @@
import React, { PureComponent } from 'react' import React, { PureComponent } from 'react'
import { connect } from 'react-redux' import { connect } from 'react-redux'
import { getFiatUnit } from '@ledgerhq/currencies' import type { CryptoCurrency } from '@ledgerhq/live-common/lib/types'
import { getCounterValueCode } from 'reducers/settings' import { counterValueCurrencySelector } from 'reducers/settings'
import { calculateCounterValueSelector } from 'reducers/counterValues' import { calculateCounterValueSelector } from 'reducers/counterValues'
import FormattedVal from 'components/base/FormattedVal' import FormattedVal from 'components/base/FormattedVal'
@ -13,19 +13,16 @@ type Props = {
// wich market to query // wich market to query
ticker: string, ticker: string,
// the value :)
value: number,
// when? if not given: take latest // when? if not given: take latest
date?: Date, date?: Date,
// from reducers // from reducers
counterValueCode: string, counterValueCurrency: CryptoCurrency,
getCounterValue: Function, value: number,
} }
const mapStateToProps = (state, props) => { const mapStateToProps = (state, props) => {
const { ticker } = props const { ticker, value, date } = props
// TODO: in wallet-common, stop using currency. // TODO: in wallet-common, stop using currency.
// always use ticker and remove that hack // always use ticker and remove that hack
@ -36,13 +33,15 @@ const mapStateToProps = (state, props) => {
console.warn('`currency` is deprecated in CounterValue. use `ticker` instead.') // eslint-disable-line no-console console.warn('`currency` is deprecated in CounterValue. use `ticker` instead.') // eslint-disable-line no-console
} }
const counterValueCode = getCounterValueCode(state) const counterValueCurrency = counterValueCurrencySelector(state)
const counterValueUnit = getFiatUnit(counterValueCode) const counterValue =
const getCounterValue = calculateCounterValueSelector(state)(currency, counterValueUnit) !counterValueCurrency || !currency
? 0
: calculateCounterValueSelector(state)(currency, counterValueCurrency)(value, date)
return { return {
counterValueCode, counterValueCurrency,
getCounterValue, value: counterValue,
} }
} }
@ -53,10 +52,15 @@ class CounterValue extends PureComponent<Props> {
} }
render() { render() {
const { getCounterValue, counterValueCode, date, value, ...props } = this.props const { value, counterValueCurrency, date, ...props } = this.props
const counterValue = getCounterValue(value, date)
return ( return (
<FormattedVal val={counterValue} fiat={counterValueCode} showCode alwaysShowSign {...props} /> <FormattedVal
val={value}
fiat={counterValueCurrency.units[0].code}
showCode
alwaysShowSign
{...props}
/>
) )
} }
} }

4
src/components/CounterValue/stories.js

@ -1,7 +1,7 @@
// @flow // @flow
import React from 'react' import React from 'react'
import { getCurrencyByCoinType } from '@ledgerhq/currencies' import { getCryptoCurrencyById } from '@ledgerhq/live-common/lib/helpers/currencies'
import { storiesOf } from '@storybook/react' import { storiesOf } from '@storybook/react'
import { number } from '@storybook/addon-knobs' import { number } from '@storybook/addon-knobs'
@ -9,7 +9,7 @@ import CounterValue from 'components/CounterValue'
const stories = storiesOf('Components', module) const stories = storiesOf('Components', module)
const currency = getCurrencyByCoinType(0) const currency = getCryptoCurrencyById('bitcoin')
stories.add('CounterValue', () => ( stories.add('CounterValue', () => (
<CounterValue ticker={currency.units[0].code} value={Number(number('value', 100000000) || 0)} /> <CounterValue ticker={currency.units[0].code} value={Number(number('value', 100000000) || 0)} />

19
src/components/CryptoCurrencyIcon.js

@ -0,0 +1,19 @@
// @flow
import React, { PureComponent } from 'react'
import { getCryptoCurrencyIcon } from '@ledgerhq/live-common/lib/react'
import type { CryptoCurrency } from '@ledgerhq/live-common/lib/types'
type Props = {
currency: CryptoCurrency,
size: number,
}
class CryptoCurrencyIcon extends PureComponent<Props> {
render() {
const { currency, size } = this.props
const IconCurrency = getCryptoCurrencyIcon(currency)
return IconCurrency ? <IconCurrency size={size} /> : null
}
}
export default CryptoCurrencyIcon

133
src/components/DashboardPage/AccountCard.js

@ -2,15 +2,15 @@
import React from 'react' import React from 'react'
import styled from 'styled-components' import styled from 'styled-components'
import { getIconByCoinType } from '@ledgerhq/currencies/react'
import type { Account } from '@ledgerhq/wallet-common/lib/types' import type { Account } from '@ledgerhq/live-common/lib/types'
import Chart from 'components/base/Chart' import Chart from 'components/base/Chart'
import Bar from 'components/base/Bar' import Bar from 'components/base/Bar'
import Box, { Card } from 'components/base/Box' import Box, { Card } from 'components/base/Box'
import CalculateBalance from 'components/CalculateBalance' import CalculateBalance from 'components/CalculateBalance'
import FormattedVal from 'components/base/FormattedVal' import FormattedVal from 'components/base/FormattedVal'
import CryptoCurrencyIcon from 'components/CryptoCurrencyIcon'
const Wrapper = styled(Card).attrs({ const Wrapper = styled(Card).attrs({
p: 4, p: 4,
@ -30,81 +30,74 @@ const AccountCard = ({
account: Account, account: Account,
onClick?: Function, onClick?: Function,
daysCount: number, daysCount: number,
}) => { }) => (
const Icon = getIconByCoinType(account.currency.coinType) <Wrapper onClick={onClick} {...props}>
return ( <Box flow={4}>
<Wrapper onClick={onClick} {...props}> <Box horizontal ff="Open Sans|SemiBold" flow={3} alignItems="center">
<Box flow={4}> <Box alignItems="center" justifyContent="center" style={{ color: account.currency.color }}>
<Box horizontal ff="Open Sans|SemiBold" flow={3} alignItems="center"> <CryptoCurrencyIcon currency={account.currency} size={20} />
<Box </Box>
alignItems="center" <Box>
justifyContent="center" <Box style={{ textTransform: 'uppercase' }} fontSize={0} color="graphite">
style={{ color: account.currency.color }} {account.unit.code}
> </Box>
{Icon && <Icon size={20} />} <Box fontSize={4} color="dark">
{account.name}
</Box> </Box>
<Box> </Box>
<Box style={{ textTransform: 'uppercase' }} fontSize={0} color="graphite"> </Box>
{account.unit.code} <Bar size={1} color="fog" />
<Box justifyContent="center">
<FormattedVal
alwaysShowSign={false}
color="dark"
unit={account.unit}
showCode
val={account.balance}
/>
</Box>
</Box>
<CalculateBalance
counterValue={counterValue}
accounts={[account]}
daysCount={daysCount}
render={({ allBalances, totalBalance, refBalance }) => (
<Box flow={4}>
<Box flow={2} horizontal>
<Box justifyContent="center">
<FormattedVal
animateTicker
fiat={counterValue}
val={totalBalance}
alwaysShowSign={false}
showCode
fontSize={3}
color="graphite"
/>
</Box> </Box>
<Box fontSize={4} color="dark"> <Box grow justifyContent="center">
{account.name} <FormattedVal
isPercent
val={Math.floor((totalBalance - refBalance) / refBalance * 100)}
alwaysShowSign
fontSize={3}
/>
</Box> </Box>
</Box> </Box>
</Box> <Chart
<Bar size={1} color="fog" /> data={allBalances}
<Box justifyContent="center"> color={account.currency.color}
<FormattedVal height={52}
alwaysShowSign={false} hideAxis
color="dark" interactive={false}
id={`account-chart-${account.id}`}
unit={account.unit} unit={account.unit}
showCode
val={account.balance}
/> />
</Box> </Box>
</Box> )}
<CalculateBalance />
counterValue={counterValue} </Wrapper>
accounts={[account]} )
daysCount={daysCount}
render={({ allBalances, totalBalance, refBalance }) => (
<Box flow={4}>
<Box flow={2} horizontal>
<Box justifyContent="center">
<FormattedVal
animateTicker
fiat={counterValue}
val={totalBalance}
alwaysShowSign={false}
showCode
fontSize={3}
color="graphite"
/>
</Box>
<Box grow justifyContent="center">
<FormattedVal
isPercent
val={Math.floor((totalBalance - refBalance) / refBalance * 100)}
alwaysShowSign
fontSize={3}
/>
</Box>
</Box>
<Chart
data={allBalances}
color={account.currency.color}
height={52}
hideAxis
interactive={false}
id={`account-chart-${account.id}`}
unit={account.unit}
/>
</Box>
)}
/>
</Wrapper>
)
}
AccountCard.defaultProps = { AccountCard.defaultProps = {
onClick: undefined, onClick: undefined,

17
src/components/DashboardPage/index.js

@ -6,9 +6,12 @@ import { compose } from 'redux'
import { translate } from 'react-i18next' import { translate } from 'react-i18next'
import { connect } from 'react-redux' import { connect } from 'react-redux'
import { push } from 'react-router-redux' import { push } from 'react-router-redux'
import { formatCurrencyUnit, getFiatUnit } from '@ledgerhq/currencies' import {
formatCurrencyUnit,
getFiatCurrencyByTicker,
} from '@ledgerhq/live-common/lib/helpers/currencies'
import type { Account } from '@ledgerhq/wallet-common/lib/types' import type { Account } from '@ledgerhq/live-common/lib/types'
import chunk from 'lodash/chunk' import chunk from 'lodash/chunk'
@ -94,9 +97,13 @@ class DashboardPage extends PureComponent<Props, State> {
text: 'Total balance', text: 'Total balance',
color: colors.wallet, color: colors.wallet,
balance: { balance: {
counterValue: formatCurrencyUnit(getFiatUnit(counterValue), data.totalBalance, { counterValue: formatCurrencyUnit(
showCode: true, getFiatCurrencyByTicker(counterValue).units[0],
}), data.totalBalance,
{
showCode: true,
},
),
}, },
}) })
} }

2
src/components/DeviceCheckAddress.js

@ -3,7 +3,7 @@
import { PureComponent } from 'react' import { PureComponent } from 'react'
import { ipcRenderer } from 'electron' import { ipcRenderer } from 'electron'
import type { Account } from '@ledgerhq/wallet-common/lib/types' import type { Account } from '@ledgerhq/live-common/lib/types'
import type { Device } from 'types/common' import type { Device } from 'types/common'
import { sendEvent } from 'renderer/events' import { sendEvent } from 'renderer/events'

15
src/components/DeviceConnect/index.js

@ -3,14 +3,14 @@
import React, { PureComponent } from 'react' import React, { PureComponent } from 'react'
import styled from 'styled-components' import styled from 'styled-components'
import { Trans, translate } from 'react-i18next' import { Trans, translate } from 'react-i18next'
import { getCurrencyByCoinType } from '@ledgerhq/currencies' import type { CryptoCurrency } from '@ledgerhq/live-common/lib/types'
import { getIconByCoinType } from '@ledgerhq/currencies/react'
import type { T, Device, Devices } from 'types/common' import type { T, Device, Devices } from 'types/common'
import noop from 'lodash/noop' import noop from 'lodash/noop'
import Box from 'components/base/Box' import Box from 'components/base/Box'
import CryptoCurrencyIcon from 'components/CryptoCurrencyIcon'
import IconCheck from 'icons/Check' import IconCheck from 'icons/Check'
import IconExclamationCircle from 'icons/ExclamationCircle' import IconExclamationCircle from 'icons/ExclamationCircle'
@ -138,7 +138,7 @@ StepCheck.defaultProps = {
type Props = { type Props = {
accountName: null | string, accountName: null | string,
appOpened: null | 'success' | 'fail', appOpened: null | 'success' | 'fail',
coinType: number, currency: CryptoCurrency,
devices: Devices, devices: Devices,
deviceSelected: Device | null, deviceSelected: Device | null,
onChangeDevice: Function, onChangeDevice: Function,
@ -180,16 +180,13 @@ class DeviceConnect extends PureComponent<Props> {
} }
render() { render() {
const { deviceSelected, accountName, coinType, t, onChangeDevice, devices } = this.props const { deviceSelected, accountName, currency, t, onChangeDevice, devices } = this.props
const appState = this.getAppState() const appState = this.getAppState()
const hasDevice = devices.length > 0 const hasDevice = devices.length > 0
const hasMultipleDevices = devices.length > 1 const hasMultipleDevices = devices.length > 1
const { name: appName } = getCurrencyByCoinType(coinType)
const IconCurrency = getIconByCoinType(coinType)
return ( return (
<Box flow={4}> <Box flow={4}>
<Step validated={hasDevice}> <Step validated={hasDevice}>
@ -240,13 +237,13 @@ class DeviceConnect extends PureComponent<Props> {
<StepContent> <StepContent>
<StepIcon> <StepIcon>
<WrapperIconCurrency> <WrapperIconCurrency>
<IconCurrency size={12} /> <CryptoCurrencyIcon currency={currency} size={12} />
</WrapperIconCurrency> </WrapperIconCurrency>
</StepIcon> </StepIcon>
<Box grow shrink> <Box grow shrink>
<Trans i18nKey="deviceConnect:step2.open" parent="div"> <Trans i18nKey="deviceConnect:step2.open" parent="div">
{'Open '} {'Open '}
<strong>{appName}</strong> <strong>{currency.name}</strong>
{' App on your device'} {' App on your device'}
</Trans> </Trans>
</Box> </Box>

2
src/components/DeviceMonit/index.js

@ -3,7 +3,7 @@
import React, { PureComponent } from 'react' import React, { PureComponent } from 'react'
import { connect } from 'react-redux' import { connect } from 'react-redux'
import { ipcRenderer } from 'electron' import { ipcRenderer } from 'electron'
import type { Account } from '@ledgerhq/wallet-common/lib/types' import type { Account } from '@ledgerhq/live-common/lib/types'
import { sendEvent } from 'renderer/events' import { sendEvent } from 'renderer/events'
import { getCurrentDevice } from 'reducers/devices' import { getCurrentDevice } from 'reducers/devices'

14
src/components/DeviceMonitNew/index.js

@ -4,7 +4,7 @@ import { PureComponent } from 'react'
import { connect } from 'react-redux' import { connect } from 'react-redux'
import { ipcRenderer } from 'electron' import { ipcRenderer } from 'electron'
import type { Account } from '@ledgerhq/wallet-common/lib/types' import type { Account, CryptoCurrency } from '@ledgerhq/live-common/lib/types'
import type { Device, Devices } from 'types/common' import type { Device, Devices } from 'types/common'
import { sendEvent } from 'renderer/events' import { sendEvent } from 'renderer/events'
@ -19,7 +19,7 @@ type DeviceStatus = 'unconnected' | 'connected'
type AppStatus = 'success' | 'fail' | 'progress' type AppStatus = 'success' | 'fail' | 'progress'
type Props = { type Props = {
coinType: number, currency: ?CryptoCurrency,
devices: Devices, devices: Devices,
deviceSelected: Device | null, deviceSelected: Device | null,
account?: Account, account?: Account,
@ -78,7 +78,7 @@ class DeviceMonit extends PureComponent<Props, State> {
} }
checkAppOpened = () => { checkAppOpened = () => {
const { deviceSelected, account, coinType } = this.props const { deviceSelected, account, currency } = this.props
if (deviceSelected === null) { if (deviceSelected === null) {
return return
@ -93,9 +93,9 @@ class DeviceMonit extends PureComponent<Props, State> {
} }
} }
if (coinType) { if (currency) {
options = { options = {
coinType, currencyId: currency.id,
} }
} }
@ -134,13 +134,13 @@ class DeviceMonit extends PureComponent<Props, State> {
} }
render() { render() {
const { coinType, account, devices, deviceSelected, render } = this.props const { currency, account, devices, deviceSelected, render } = this.props
const { appStatus, deviceStatus } = this.state const { appStatus, deviceStatus } = this.state
if (render) { if (render) {
return render({ return render({
appStatus, appStatus,
coinType: account ? account.coinType : coinType, currency: account ? account.currency : currency,
devices, devices,
deviceSelected: deviceStatus === 'connected' ? deviceSelected : null, deviceSelected: deviceStatus === 'connected' ? deviceSelected : null,
deviceStatus, deviceStatus,

2
src/components/IsUnlocked.js

@ -6,7 +6,7 @@ import { connect } from 'react-redux'
import { compose } from 'redux' import { compose } from 'redux'
import styled from 'styled-components' import styled from 'styled-components'
import { translate } from 'react-i18next' import { translate } from 'react-i18next'
import type { Account } from '@ledgerhq/wallet-common/lib/types' import type { Account } from '@ledgerhq/live-common/lib/types'
import type { Settings, T } from 'types/common' import type { Settings, T } from 'types/common'
import IconLockScreen from 'icons/LockScreen' import IconLockScreen from 'icons/LockScreen'

6
src/components/OperationsList/Operation.js

@ -5,9 +5,9 @@ import { connect } from 'react-redux'
import styled from 'styled-components' import styled from 'styled-components'
import moment from 'moment' import moment from 'moment'
import noop from 'lodash/noop' import noop from 'lodash/noop'
import { getIconByCoinType } from '@ledgerhq/currencies/react' import { getCryptoCurrencyIcon } from '@ledgerhq/live-common/lib/react'
import type { Account, Operation as OperationType } from '@ledgerhq/wallet-common/lib/types' import type { Account, Operation as OperationType } from '@ledgerhq/live-common/lib/types'
import type { T } from 'types/common' import type { T } from 'types/common'
@ -130,7 +130,7 @@ class Operation extends PureComponent<Props> {
} = this.props } = this.props
const { unit, currency } = account const { unit, currency } = account
const time = moment(op.date) const time = moment(op.date)
const Icon = getIconByCoinType(account.currency.coinType) const Icon = getCryptoCurrencyIcon(account.currency)
const isNegative = op.amount < 0 const isNegative = op.amount < 0
const type = !isNegative ? 'from' : 'to' const type = !isNegative ? 'from' : 'to'

4
src/components/OperationsList/index.js

@ -9,9 +9,9 @@ import { translate } from 'react-i18next'
import { import {
groupAccountOperationsByDay, groupAccountOperationsByDay,
groupAccountsOperationsByDay, groupAccountsOperationsByDay,
} from '@ledgerhq/wallet-common/lib/helpers/account' } from '@ledgerhq/live-common/lib/helpers/account'
import type { Account } from '@ledgerhq/wallet-common/lib/types' import type { Account } from '@ledgerhq/live-common/lib/types'
import noop from 'lodash/noop' import noop from 'lodash/noop'
import keyBy from 'lodash/keyBy' import keyBy from 'lodash/keyBy'

2
src/components/OperationsList/stories.js

@ -1,7 +1,7 @@
// @flow // @flow
import React from 'react' import React from 'react'
import { genAccount } from '@ledgerhq/wallet-common/lib/mock/account' import { genAccount } from '@ledgerhq/live-common/lib/mock/account'
import { storiesOf } from '@storybook/react' import { storiesOf } from '@storybook/react'
import { boolean } from '@storybook/addon-knobs' import { boolean } from '@storybook/addon-knobs'

2
src/components/QRCodeExporter.js

@ -19,7 +19,7 @@ function makeChunks(state: State): Array<string> {
'account', 'account',
account.id, account.id,
account.name, account.name,
account.coinType, account.currency.id,
]), ]),
] ]
return data.map((arr, i) => JSON.stringify([data.length, i, ...arr])) return data.map((arr, i) => JSON.stringify([data.length, i, ...arr]))

2
src/components/ReceiveBox.js

@ -4,7 +4,7 @@ import React, { PureComponent } from 'react'
import { connect } from 'react-redux' import { connect } from 'react-redux'
import styled from 'styled-components' import styled from 'styled-components'
import { ipcRenderer } from 'electron' import { ipcRenderer } from 'electron'
import type { Account } from '@ledgerhq/wallet-common/lib/types' import type { Account } from '@ledgerhq/live-common/lib/types'
import type { Device } from 'types/common' import type { Device } from 'types/common'

19
src/components/RequestAmount/index.js

@ -5,12 +5,11 @@ import { compose } from 'redux'
import { translate } from 'react-i18next' import { translate } from 'react-i18next'
import styled from 'styled-components' import styled from 'styled-components'
import { connect } from 'react-redux' import { connect } from 'react-redux'
import type { Account, CalculateCounterValue } from '@ledgerhq/wallet-common/lib/types' import type { Currency, Account, CalculateCounterValue } from '@ledgerhq/live-common/lib/types'
import type { FiatUnit } from '@ledgerhq/currencies'
import type { T } from 'types/common' import type { T } from 'types/common'
import { getCounterValueFiatUnit } from 'reducers/settings' import { counterValueCurrencySelector } from 'reducers/settings'
import { calculateCounterValueSelector, reverseCounterValueSelector } from 'reducers/counterValues' import { calculateCounterValueSelector, reverseCounterValueSelector } from 'reducers/counterValues'
import InputCurrency from 'components/base/InputCurrency' import InputCurrency from 'components/base/InputCurrency'
@ -36,7 +35,7 @@ const InputCenter = styled(Box).attrs({
` `
const mapStateToProps = state => ({ const mapStateToProps = state => ({
rightUnit: getCounterValueFiatUnit(state), rightCurrency: counterValueCurrencySelector(state),
getCounterValue: calculateCounterValueSelector(state), getCounterValue: calculateCounterValueSelector(state),
getReverseCounterValue: reverseCounterValueSelector(state), getReverseCounterValue: reverseCounterValueSelector(state),
}) })
@ -59,7 +58,7 @@ type Props = {
// used to determine the right input unit // used to determine the right input unit
// retrieved via selector (take the chosen countervalue unit) // retrieved via selector (take the chosen countervalue unit)
rightUnit: FiatUnit, rightCurrency: Currency,
// used to calculate the opposite field value (right & left) // used to calculate the opposite field value (right & left)
getCounterValue: CalculateCounterValue, getCounterValue: CalculateCounterValue,
@ -80,19 +79,19 @@ export class RequestAmount extends PureComponent<Props> {
} }
handleChangeAmount = (changedField: string) => (val: number) => { handleChangeAmount = (changedField: string) => (val: number) => {
const { rightUnit, getReverseCounterValue, account, max, onChange } = this.props const { rightCurrency, getReverseCounterValue, account, max, onChange } = this.props
if (changedField === 'left') { if (changedField === 'left') {
onChange(val > max ? max : val) onChange(val > max ? max : val)
} else if (changedField === 'right') { } else if (changedField === 'right') {
const leftVal = getReverseCounterValue(account.currency, rightUnit)(val) const leftVal = getReverseCounterValue(account.currency, rightCurrency)(val)
onChange(leftVal > max ? max : leftVal) onChange(leftVal > max ? max : leftVal)
} }
} }
renderInputs(containerProps: Object) { renderInputs(containerProps: Object) {
const { value, account, rightUnit, getCounterValue } = this.props const { value, account, rightCurrency, getCounterValue } = this.props
const right = getCounterValue(account.currency, rightUnit)(value) const right = getCounterValue(account.currency, rightCurrency)(value)
const rightUnit = rightCurrency.units[0]
return ( return (
<Box horizontal grow shrink> <Box horizontal grow shrink>
<InputCurrency <InputCurrency

6
src/components/SelectAccount/index.js

@ -3,11 +3,11 @@
import React from 'react' import React from 'react'
import { connect } from 'react-redux' import { connect } from 'react-redux'
import { translate } from 'react-i18next' import { translate } from 'react-i18next'
import { getIconByCoinType } from '@ledgerhq/currencies/react' import { getCryptoCurrencyIcon } from '@ledgerhq/live-common/lib/react'
import noop from 'lodash/noop' import noop from 'lodash/noop'
import type { Account } from '@ledgerhq/wallet-common/lib/types' import type { Account } from '@ledgerhq/live-common/lib/types'
import type { T } from 'types/common' import type { T } from 'types/common'
import { getVisibleAccounts } from 'reducers/accounts' import { getVisibleAccounts } from 'reducers/accounts'
@ -22,7 +22,7 @@ const mapStateToProps = state => ({
}) })
const renderItem = a => { const renderItem = a => {
const Icon = getIconByCoinType(a.coinType) const Icon = getCryptoCurrencyIcon(a)
const { color } = a.currency const { color } = a.currency
return ( return (
<Box grow horizontal alignItems="center" flow={2}> <Box grow horizontal alignItems="center" flow={2}>

26
src/components/SelectAccount/stories.js

@ -2,35 +2,13 @@
import React, { Component } from 'react' import React, { Component } from 'react'
import { storiesOf } from '@storybook/react' import { storiesOf } from '@storybook/react'
import Chance from 'chance' import { genAccount } from '@ledgerhq/live-common/lib/mock/account'
import { getCurrencyByCoinType, getDefaultUnitByCoinType } from '@ledgerhq/currencies'
import { SelectAccount } from 'components/SelectAccount' import { SelectAccount } from 'components/SelectAccount'
const chance = new Chance()
const stories = storiesOf('Components', module) const stories = storiesOf('Components', module)
export const accounts = [...Array(20)].map(() => ({ export const accounts = [...Array(20)].map((_, i) => genAccount(i))
id: chance.string(),
address: chance.string({
pool: 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789',
length: 30,
}),
addresses: [],
balance: chance.integer({ min: 10000000000, max: 2000000000000 }),
balanceByDay: {},
coinType: 1,
currency: getCurrencyByCoinType(1),
index: chance.integer({ min: 0, max: 20 }),
name: chance.name(),
path: '',
rootPath: '',
operations: [],
unit: getDefaultUnitByCoinType(1),
settings: {
minConfirmations: 2,
},
}))
class Wrapper extends Component<any, any> { class Wrapper extends Component<any, any> {
state = { state = {

32
src/components/SelectCurrency/index.js

@ -2,27 +2,24 @@
import React from 'react' import React from 'react'
import { translate } from 'react-i18next' import { translate } from 'react-i18next'
import { getIconByCoinType } from '@ledgerhq/currencies/react' import { listCryptoCurrencies } from '@ledgerhq/live-common/lib/helpers/currencies'
import { listCurrencies } from '@ledgerhq/currencies'
import noop from 'lodash/noop' import noop from 'lodash/noop'
import type { Currency } from '@ledgerhq/currencies' import type { CryptoCurrency } from '@ledgerhq/live-common/lib/types'
import type { T } from 'types/common' import type { T } from 'types/common'
import CryptoCurrencyIcon from 'components/CryptoCurrencyIcon'
import Select from 'components/base/Select' import Select from 'components/base/Select'
import Box from 'components/base/Box' import Box from 'components/base/Box'
const renderItem = a => { const renderItem = (currency: CryptoCurrency) => {
const { color, name, coinType } = a const { color, name } = currency
const Icon = getIconByCoinType(coinType)
return ( return (
<Box grow horizontal alignItems="center" flow={2}> <Box grow horizontal alignItems="center" flow={2}>
{Icon && ( <Box style={{ width: 16, height: 16, color }}>
<Box style={{ width: 16, height: 16, color }}> <CryptoCurrencyIcon currency={currency} size={16} />
<Icon size={16} /> </Box>
</Box>
)}
<Box grow ff="Open Sans|SemiBold" color="dark" fontSize={4}> <Box grow ff="Open Sans|SemiBold" color="dark" fontSize={4}>
{name} {name}
</Box> </Box>
@ -30,23 +27,24 @@ const renderItem = a => {
) )
} }
const currencies = listCurrencies() const currencies = listCryptoCurrencies().sort((a, b) => a.name.localeCompare(b.name))
type Props = { type Props = {
onChange: Function, onChange: Function,
value?: Currency, value?: CryptoCurrency,
placeholder: string,
t: T, t: T,
} }
const SelectCurrency = ({ onChange, value, t, ...props }: Props) => ( const SelectCurrency = ({ onChange, value, t, placeholder, ...props }: Props) => (
<Select <Select
{...props} {...props}
value={value} value={value}
renderSelected={renderItem} renderSelected={renderItem}
renderItem={renderItem} renderItem={renderItem}
keyProp="coinType" keyProp="id"
items={currencies.sort((a, b) => (a.name < b.name ? -1 : 1))} items={currencies}
placeholder={t('common:selectCurrency')} placeholder={placeholder || t('common:selectCurrency')}
fontSize={4} fontSize={4}
onChange={onChange} onChange={onChange}
/> />

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

@ -2,9 +2,9 @@
import React, { PureComponent } from 'react' import React, { PureComponent } from 'react'
import { listCurrencies } from '@ledgerhq/currencies' import { listCryptoCurrencies } from '@ledgerhq/live-common/lib/helpers/currencies'
import type { Currency } from '@ledgerhq/currencies' import type { CryptoCurrency } from '@ledgerhq/live-common/lib/types'
import type { Settings, CurrencySettings, T } from 'types/common' import type { Settings, CurrencySettings, T } from 'types/common'
import SelectCurrency from 'components/SelectCurrency' import SelectCurrency from 'components/SelectCurrency'
@ -43,21 +43,21 @@ type Props = {
} }
type State = { type State = {
currency: Currency, currency: CryptoCurrency,
} }
class TabCurrencies extends PureComponent<Props, State> { class TabCurrencies extends PureComponent<Props, State> {
state = { state = {
currency: listCurrencies()[0], currency: listCryptoCurrencies()[0],
} }
getCurrencySettings() { getCurrencySettings() {
const { settings } = this.props const { settings } = this.props
const { currency } = this.state const { currency } = this.state
return settings.currenciesSettings[currency.coinType] return settings.currenciesSettings[currency.id]
} }
handleChangeCurrency = (currency: Currency) => this.setState({ currency }) handleChangeCurrency = (currency: CryptoCurrency) => this.setState({ currency })
handleChangeConfirmationsToSpend = (nb: number) => handleChangeConfirmationsToSpend = (nb: number) =>
this.updateCurrencySetting('confirmationsToSpend', nb) this.updateCurrencySetting('confirmationsToSpend', nb)
@ -72,7 +72,7 @@ class TabCurrencies extends PureComponent<Props, State> {
if (!currencySettings) { if (!currencySettings) {
newCurrenciesSettings = { newCurrenciesSettings = {
...settings.currenciesSettings, ...settings.currenciesSettings,
[currency.coinType]: { [currency.id]: {
...CURRENCY_DEFAULTS_SETTINGS, ...CURRENCY_DEFAULTS_SETTINGS,
[key]: val, [key]: val,
}, },
@ -80,7 +80,7 @@ class TabCurrencies extends PureComponent<Props, State> {
} else { } else {
newCurrenciesSettings = { newCurrenciesSettings = {
...settings.currenciesSettings, ...settings.currenciesSettings,
[currency.coinType]: { [currency.id]: {
...currencySettings, ...currencySettings,
[key]: val, [key]: val,
}, },
@ -95,7 +95,7 @@ class TabCurrencies extends PureComponent<Props, State> {
const { confirmationsToSpend, confirmationsNb } = const { confirmationsToSpend, confirmationsNb } =
this.getCurrencySettings() || CURRENCY_DEFAULTS_SETTINGS this.getCurrencySettings() || CURRENCY_DEFAULTS_SETTINGS
return ( return (
<Section key={currency.coinType}> <Section key={currency.id}>
<Header <Header
icon={<IconCurrencies size={16} />} icon={<IconCurrencies size={16} />}
title={t('settings:tabs.currencies')} title={t('settings:tabs.currencies')}

15
src/components/SettingsPage/sections/Display.js

@ -2,7 +2,7 @@
import React, { PureComponent } from 'react' import React, { PureComponent } from 'react'
import moment from 'moment' import moment from 'moment'
import { listFiats } from '@ledgerhq/currencies' import { listFiatCurrencies } from '@ledgerhq/live-common/lib/helpers/currencies'
import type { Settings, T } from 'types/common' import type { Settings, T } from 'types/common'
@ -19,11 +19,14 @@ import {
SettingsSectionRow as Row, SettingsSectionRow as Row,
} from '../SettingsSection' } from '../SettingsSection'
const fiats = listFiats().map(fiat => ({ const fiats = listFiatCurrencies()
key: fiat.code, .map(f => f.units[0])
fiat, // For now we take first unit, in the future we'll need to figure out something else
name: `${fiat.name} - ${fiat.code}${fiat.symbol ? ` (${fiat.symbol})` : ''}`, .map(fiat => ({
})) key: fiat.code,
fiat,
name: `${fiat.name} - ${fiat.code}${fiat.symbol ? ` (${fiat.symbol})` : ''}`,
}))
type Props = { type Props = {
t: T, t: T,

6
src/components/SideBar/index.js

@ -5,8 +5,8 @@ import { compose } from 'redux'
import { translate } from 'react-i18next' import { translate } from 'react-i18next'
import styled from 'styled-components' import styled from 'styled-components'
import { connect } from 'react-redux' import { connect } from 'react-redux'
import { getIconByCoinType } from '@ledgerhq/currencies/react' import { getCryptoCurrencyIcon } from '@ledgerhq/live-common/lib/react'
import type { Account } from '@ledgerhq/wallet-common/lib/types' import type { Account } from '@ledgerhq/live-common/lib/types'
import { MODAL_SEND, MODAL_RECEIVE, MODAL_ADD_ACCOUNT } from 'config/constants' import { MODAL_SEND, MODAL_RECEIVE, MODAL_ADD_ACCOUNT } from 'config/constants'
@ -104,7 +104,7 @@ class SideBar extends PureComponent<Props> {
</CapsSubtitle> </CapsSubtitle>
<GrowScroll pb={4} px={4} flow={2}> <GrowScroll pb={4} px={4} flow={2}>
{accounts.map(account => { {accounts.map(account => {
const Icon = getIconByCoinType(account.currency.coinType) const Icon = getCryptoCurrencyIcon(account.currency)
return ( return (
<Item <Item
big big

2
src/components/base/Chart/Tooltip.js

@ -2,7 +2,7 @@
import React from 'react' import React from 'react'
import type { Unit } from '@ledgerhq/currencies' import type { Unit } from '@ledgerhq/live-common/lib/types'
import { colors as themeColors } from 'styles/theme' import { colors as themeColors } from 'styles/theme'
import { TooltipContainer } from 'components/base/Tooltip' import { TooltipContainer } from 'components/base/Tooltip'

2
src/components/base/Chart/index.js

@ -37,7 +37,7 @@ import React, { PureComponent } from 'react'
import * as d3 from 'd3' import * as d3 from 'd3'
import noop from 'lodash/noop' import noop from 'lodash/noop'
import type { Unit } from '@ledgerhq/currencies' import type { Unit } from '@ledgerhq/live-common/lib/types'
import refreshNodes from './refreshNodes' import refreshNodes from './refreshNodes'
import refreshDraw from './refreshDraw' import refreshDraw from './refreshDraw'

2
src/components/base/Chart/refreshDraw.js

@ -2,7 +2,7 @@
import * as d3 from 'd3' import * as d3 from 'd3'
import moment from 'moment' import moment from 'moment'
import { formatShort } from '@ledgerhq/currencies' import { formatShort } from '@ledgerhq/live-common/lib/helpers/currencies'
import { colors as themeColors } from 'styles/theme' import { colors as themeColors } from 'styles/theme'

4
src/components/base/Chart/stories.js

@ -1,7 +1,7 @@
// @flow // @flow
import React, { Component, Fragment } from 'react' import React, { Component, Fragment } from 'react'
import { getDefaultUnitByCoinType } from '@ledgerhq/currencies' import { getCryptoCurrencyById } from '@ledgerhq/live-common/lib/helpers/currencies'
import Chance from 'chance' import Chance from 'chance'
import moment from 'moment' import moment from 'moment'
import { storiesOf } from '@storybook/react' import { storiesOf } from '@storybook/react'
@ -13,7 +13,7 @@ import Chart from 'components/base/Chart'
const stories = storiesOf('Components/base', module) const stories = storiesOf('Components/base', module)
const data = generateRandomData(365) const data = generateRandomData(365)
const unit = getDefaultUnitByCoinType(0) const unit = getCryptoCurrencyById('bitcoin').units[0]
type State = { type State = {
start: number, start: number,

7
src/components/base/FlipTicker/stories.js

@ -3,7 +3,10 @@
import React, { Component } from 'react' import React, { Component } from 'react'
import { storiesOf } from '@storybook/react' import { storiesOf } from '@storybook/react'
import { formatCurrencyUnit, getFiatUnit } from '@ledgerhq/currencies' import {
formatCurrencyUnit,
getFiatCurrencyByTicker,
} from '@ledgerhq/live-common/lib/helpers/currencies'
import Chance from 'chance' import Chance from 'chance'
import Box from 'components/base/Box' import Box from 'components/base/Box'
@ -12,7 +15,7 @@ import FlipTicker from 'components/base/FlipTicker'
const stories = storiesOf('Components/base', module) const stories = storiesOf('Components/base', module)
const unit = getFiatUnit('USD') const unit = getFiatCurrencyByTicker('USD').units[0]
const chance = new Chance() const chance = new Chance()
function getValue() { function getValue() {

4
src/components/base/FormattedVal/__tests__/FormattedVal.test.js

@ -1,10 +1,10 @@
import React from 'react' import React from 'react'
import { getDefaultUnitByCoinType } from '@ledgerhq/currencies' import { getCryptoCurrencyById } from '@ledgerhq/live-common/lib/helpers/currencies'
import render from '__mocks__/render' import render from '__mocks__/render'
import FormattedVal from '..' import FormattedVal from '..'
const bitcoinUnit = getDefaultUnitByCoinType(0) const bitcoinUnit = getCryptoCurrencyById('bitcoin').units[0]
describe('components', () => { describe('components', () => {
describe('FormattedVal', () => { describe('FormattedVal', () => {

16
src/components/base/FormattedVal/index.js

@ -7,9 +7,12 @@ import { connect } from 'react-redux'
import isUndefined from 'lodash/isUndefined' import isUndefined from 'lodash/isUndefined'
import type { Settings } from 'types/common' import type { Settings } from 'types/common'
import type { Unit } from '@ledgerhq/currencies' import type { Unit } from '@ledgerhq/live-common/lib/types'
import { formatCurrencyUnit, getFiatUnit } from '@ledgerhq/currencies' import {
formatCurrencyUnit,
findCurrencyByTicker,
} from '@ledgerhq/live-common/lib/helpers/currencies'
import { getMarketColor } from 'styles/helpers' import { getMarketColor } from 'styles/helpers'
@ -81,8 +84,13 @@ export function FormattedVal(props: Props) {
text = `${alwaysShowSign ? (isNegative ? '- ' : '+ ') : ''}${isNegative ? val * -1 : val} %` text = `${alwaysShowSign ? (isNegative ? '- ' : '+ ') : ''}${isNegative ? val * -1 : val} %`
} else { } else {
if (fiat) { if (fiat) {
unit = getFiatUnit(fiat) console.warn('FormattedVal: passing fiat prop is deprecated')
} else if (!unit) { const cur = findCurrencyByTicker(fiat)
if (cur) {
;[unit] = cur.units
}
}
if (!unit) {
return '' return ''
} }

4
src/components/base/InputCurrency/index.js

@ -2,7 +2,7 @@
import React, { PureComponent } from 'react' import React, { PureComponent } from 'react'
import styled from 'styled-components' import styled from 'styled-components'
import { formatCurrencyUnit } from '@ledgerhq/currencies' import { formatCurrencyUnit } from '@ledgerhq/live-common/lib/helpers/currencies'
import noop from 'lodash/noop' import noop from 'lodash/noop'
import isNaN from 'lodash/isNaN' import isNaN from 'lodash/isNaN'
@ -11,7 +11,7 @@ import Box from 'components/base/Box'
import Input from 'components/base/Input' import Input from 'components/base/Input'
import Select from 'components/base/Select' import Select from 'components/base/Select'
import type { Unit } from '@ledgerhq/currencies' import type { Unit } from '@ledgerhq/live-common/lib/types'
function parseValue(value) { function parseValue(value) {
return value.toString().replace(/,/g, '.') return value.toString().replace(/,/g, '.')

5
src/components/base/InputCurrency/stories.js

@ -5,13 +5,12 @@ import { storiesOf } from '@storybook/react'
import { action } from '@storybook/addon-actions' import { action } from '@storybook/addon-actions'
import { boolean } from '@storybook/addon-knobs' import { boolean } from '@storybook/addon-knobs'
import { getCurrencyByCoinType } from '@ledgerhq/currencies' import { getCryptoCurrencyById } from '@ledgerhq/live-common/lib/helpers/currencies'
import InputCurrency from 'components/base/InputCurrency' import InputCurrency from 'components/base/InputCurrency'
const stories = storiesOf('Components', module) const stories = storiesOf('Components', module)
const { units } = getCurrencyByCoinType(1) const { units } = getCryptoCurrencyById('bitcoin')
class Wrapper extends Component<any, any> { class Wrapper extends Component<any, any> {
state = { state = {

2
src/components/base/InputPassword/index.js

@ -50,7 +50,7 @@ type Props = {
onChange: Function, onChange: Function,
t: T, t: T,
value: string, value: string,
withStrength: boolean, withStrength?: boolean,
} }
class InputPassword extends PureComponent<Props, State> { class InputPassword extends PureComponent<Props, State> {

4
src/components/base/Modal/ConfirmModal.js

@ -16,8 +16,8 @@ type Props = {
title: string, title: string,
subTitle: string, subTitle: string,
desc: string, desc: string,
confirmText: string, confirmText?: string,
cancelText: string, cancelText?: string,
onReject: Function, onReject: Function,
onConfirm: Function, onConfirm: Function,
t: T, t: T,

26
src/components/modals/AddAccount/01-step-currency.js

@ -2,40 +2,26 @@
import React from 'react' import React from 'react'
import { listCurrencies } from '@ledgerhq/currencies' import type { CryptoCurrency } from '@ledgerhq/live-common/lib/types'
import type { Currency } from '@ledgerhq/currencies/lib/types'
import type { T } from 'types/common' import type { T } from 'types/common'
import get from 'lodash/get'
import Box from 'components/base/Box' import Box from 'components/base/Box'
import Label from 'components/base/Label' import Label from 'components/base/Label'
import Select from 'components/base/Select' import SelectCurrency from 'components/SelectCurrency'
const currencies = listCurrencies().map(currency => ({
key: currency.coinType,
name: currency.name,
data: currency,
}))
type Props = { type Props = {
onChangeCurrency: Function, onChangeCurrency: Function,
currency: Currency | null, currency?: ?CryptoCurrency,
t: T, t: T,
} }
export default (props: Props) => ( export default (props: Props) => (
<Box flow={1}> <Box flow={1}>
<Label>{props.t('common:currency')}</Label> <Label>{props.t('common:currency')}</Label>
<Select <SelectCurrency
placeholder={props.t('common:chooseWalletPlaceholder')} placeholder={props.t('common:chooseWalletPlaceholder')}
onChange={item => props.onChangeCurrency(item.data)} onChange={props.onChangeCurrency}
renderSelected={item => item.name} value={props.currency}
items={currencies}
value={
props.currency ? currencies.find(c => c.key === get(props, 'currency.coinType')) : null
}
/> />
</Box> </Box>
) )

10
src/components/modals/AddAccount/03-step-import.js

@ -2,10 +2,8 @@
import React, { Fragment } from 'react' import React, { Fragment } from 'react'
import styled from 'styled-components' import styled from 'styled-components'
import { getDefaultUnitByCoinType } from '@ledgerhq/currencies'
import type { Currency } from '@ledgerhq/currencies/lib/types' import type { CryptoCurrency, Account } from '@ledgerhq/live-common/lib/types'
import type { Account } from '@ledgerhq/wallet-common/lib/types'
import Box from 'components/base/Box' import Box from 'components/base/Box'
@ -29,7 +27,7 @@ const AccountItem = styled(AccountCard)`
type Props = { type Props = {
accountsImport: Object, accountsImport: Object,
archivedAccounts: Account[], archivedAccounts: Account[],
currency: Currency | null, currency?: ?CryptoCurrency,
importProgress: boolean, importProgress: boolean,
onSelectAccount?: Function, onSelectAccount?: Function,
selectedAccounts?: Array<number>, selectedAccounts?: Array<number>,
@ -37,7 +35,7 @@ type Props = {
function StepImport(props: Props) { function StepImport(props: Props) {
const hasAccountsImports = Object.keys(props.accountsImport).length > 0 const hasAccountsImports = Object.keys(props.accountsImport).length > 0
const unit = props.currency !== null && getDefaultUnitByCoinType(props.currency.coinType) const unit = props.currency && props.currency.units[0]
return ( return (
<Box> <Box>
{props.importProgress ? ( {props.importProgress ? (
@ -56,7 +54,7 @@ function StepImport(props: Props) {
onClick={props.onSelectAccount && props.onSelectAccount(a.id)} onClick={props.onSelectAccount && props.onSelectAccount(a.id)}
account={{ account={{
...a, ...a,
coinType: props.currency && props.currency.coinType, currencyId: props.currency && props.currency.id,
name: `Account ${a.accountIndex}`, name: `Account ${a.accountIndex}`,
currency: props.currency, currency: props.currency,
unit, unit,

18
src/components/modals/AddAccount/index.js

@ -5,10 +5,8 @@ import { connect } from 'react-redux'
import { compose } from 'redux' import { compose } from 'redux'
import { translate } from 'react-i18next' import { translate } from 'react-i18next'
import { ipcRenderer } from 'electron' import { ipcRenderer } from 'electron'
import { getDefaultUnitByCoinType } from '@ledgerhq/currencies'
import type { Account } from '@ledgerhq/wallet-common/lib/types' import type { Account, CryptoCurrency } from '@ledgerhq/live-common/lib/types'
import type { Currency } from '@ledgerhq/currencies'
import type { Device, T } from 'types/common' import type { Device, T } from 'types/common'
@ -63,11 +61,11 @@ type Props = {
type State = { type State = {
accountsImport: Object, accountsImport: Object,
currency: Currency | null, currency: ?CryptoCurrency,
deviceSelected: Device | null, deviceSelected: ?Device,
fetchingCounterValues: boolean, fetchingCounterValues: boolean,
selectedAccounts: Array<number>, selectedAccounts: Array<number>,
appStatus: null | string, appStatus: ?string,
stepIndex: number, stepIndex: number,
} }
@ -107,13 +105,13 @@ class AddAccountModal extends PureComponent<Props, State> {
const { accounts } = this.props const { accounts } = this.props
const { deviceSelected, currency } = this.state const { deviceSelected, currency } = this.state
if (deviceSelected === null || currency === null) { if (!deviceSelected || !currency) {
return return
} }
sendEvent('usb', 'wallet.getAccounts', { sendEvent('usb', 'wallet.getAccounts', {
pathDevice: deviceSelected.path, pathDevice: deviceSelected.path,
coinType: currency.coinType, currencyId: currency.id,
currentAccounts: accounts.map(acc => acc.id), currentAccounts: accounts.map(acc => acc.id),
}) })
} }
@ -201,7 +199,7 @@ class AddAccountModal extends PureComponent<Props, State> {
name: `Account ${accountIndex + 1}`, name: `Account ${accountIndex + 1}`,
archived: true, archived: true,
currency, currency,
unit: getDefaultUnitByCoinType(currency.coinType), unit: currency.units[0],
}) })
} }
} }
@ -223,7 +221,7 @@ class AddAccountModal extends PureComponent<Props, State> {
: [a, ...prev.selectedAccounts], : [a, ...prev.selectedAccounts],
})) }))
handleChangeCurrency = (currency: Currency) => this.setState({ currency }) handleChangeCurrency = (currency: CryptoCurrency) => this.setState({ currency })
handleChangeStatus = (deviceStatus, appStatus) => this.setState({ appStatus }) handleChangeStatus = (deviceStatus, appStatus) => this.setState({ appStatus })

2
src/components/modals/OperationDetails.js

@ -7,7 +7,7 @@ import { translate } from 'react-i18next'
import styled from 'styled-components' import styled from 'styled-components'
import moment from 'moment' import moment from 'moment'
import type { Account, Operation } from '@ledgerhq/wallet-common/lib/types' import type { Account, Operation } from '@ledgerhq/live-common/lib/types'
import type { T } from 'types/common' import type { T } from 'types/common'
import { MODAL_OPERATION_DETAILS } from 'config/constants' import { MODAL_OPERATION_DETAILS } from 'config/constants'

2
src/components/modals/Receive/01-step-account.js

@ -2,7 +2,7 @@
import React from 'react' import React from 'react'
import type { Account } from '@ledgerhq/wallet-common/lib/types' import type { Account } from '@ledgerhq/live-common/lib/types'
import type { T } from 'types/common' import type { T } from 'types/common'
import Box from 'components/base/Box' import Box from 'components/base/Box'

2
src/components/modals/Receive/03-step-confirm-address.js

@ -3,7 +3,7 @@
import React, { Fragment } from 'react' import React, { Fragment } from 'react'
import styled from 'styled-components' import styled from 'styled-components'
import type { Account } from '@ledgerhq/wallet-common/lib/types' import type { Account } from '@ledgerhq/live-common/lib/types'
import type { Device, T } from 'types/common' import type { Device, T } from 'types/common'
import Box from 'components/base/Box' import Box from 'components/base/Box'

2
src/components/modals/Receive/04-step-receive-funds.js

@ -2,7 +2,7 @@
import React from 'react' import React from 'react'
import type { Account } from '@ledgerhq/wallet-common/lib/types' import type { Account } from '@ledgerhq/live-common/lib/types'
import type { T } from 'types/common' import type { T } from 'types/common'
import Box from 'components/base/Box' import Box from 'components/base/Box'

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

@ -3,7 +3,7 @@
import React, { Fragment, PureComponent } from 'react' import React, { Fragment, PureComponent } from 'react'
import { translate } from 'react-i18next' import { translate } from 'react-i18next'
import type { Account } from '@ledgerhq/wallet-common/lib/types' import type { Account } from '@ledgerhq/live-common/lib/types'
import type { T, Device } from 'types/common' import type { T, Device } from 'types/common'
import { MODAL_RECEIVE } from 'config/constants' import { MODAL_RECEIVE } from 'config/constants'

2
src/components/modals/Send/01-step-amount.js

@ -1,7 +1,7 @@
// @flow // @flow
import React, { Fragment } from 'react' import React, { Fragment } from 'react'
import type { Account } from '@ledgerhq/wallet-common/lib/types' import type { Account } from '@ledgerhq/live-common/lib/types'
import type { T } from 'types/common' import type { T } from 'types/common'

2
src/components/modals/Send/03-step-verification.js

@ -9,7 +9,7 @@ import { multiline } from 'styles/helpers'
import DeviceCheckAddress from 'components/DeviceCheckAddress' import DeviceCheckAddress from 'components/DeviceCheckAddress'
import DeviceConfirm from 'components/DeviceConfirm' import DeviceConfirm from 'components/DeviceConfirm'
import type { Account } from '@ledgerhq/wallet-common/lib/types' import type { Account } from '@ledgerhq/live-common/lib/types'
import type { Device, T } from 'types/common' import type { Device, T } from 'types/common'
const Container = styled(Box).attrs({ const Container = styled(Box).attrs({

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

@ -1,7 +1,7 @@
// @flow // @flow
import React from 'react' import React from 'react'
import type { Account } from '@ledgerhq/wallet-common/lib/types' import type { Account } from '@ledgerhq/live-common/lib/types'
import type { T } from 'types/common' import type { T } from 'types/common'

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

@ -5,7 +5,7 @@ import { translate } from 'react-i18next'
import { connect } from 'react-redux' import { connect } from 'react-redux'
import { compose } from 'redux' import { compose } from 'redux'
import type { Account } from '@ledgerhq/wallet-common/lib/types' import type { Account } from '@ledgerhq/live-common/lib/types'
import type { T, Device } from 'types/common' import type { T, Device } from 'types/common'
import { getVisibleAccounts } from 'reducers/accounts' import { getVisibleAccounts } from 'reducers/accounts'

2
src/components/modals/SettingsAccount.js

@ -4,7 +4,7 @@ import React, { PureComponent } from 'react'
import { connect } from 'react-redux' import { connect } from 'react-redux'
import get from 'lodash/get' import get from 'lodash/get'
import { push } from 'react-router-redux' import { push } from 'react-router-redux'
import type { Account } from '@ledgerhq/wallet-common/lib/types' import type { Account } from '@ledgerhq/live-common/lib/types'
import { MODAL_SETTINGS_ACCOUNT } from 'config/constants' import { MODAL_SETTINGS_ACCOUNT } from 'config/constants'

17
src/components/modals/StepConnectDevice.js

@ -2,8 +2,7 @@
import React from 'react' import React from 'react'
import type { Account } from '@ledgerhq/wallet-common/lib/types' import type { Account, CryptoCurrency } from '@ledgerhq/live-common/lib/types'
import type { Currency } from '@ledgerhq/currencies/lib/types'
import type { Device } from 'types/common' import type { Device } from 'types/common'
import DeviceConnect from 'components/DeviceConnect' import DeviceConnect from 'components/DeviceConnect'
@ -12,7 +11,7 @@ import DeviceMonit from 'components/DeviceMonitNew'
type Props = { type Props = {
accountName?: string, accountName?: string,
account?: ?Account, account?: ?Account,
currency?: ?Currency, currency?: ?CryptoCurrency,
deviceSelected: ?Device, deviceSelected: ?Device,
onChangeDevice: Function, onChangeDevice: Function,
onStatusChange: Function, onStatusChange: Function,
@ -21,13 +20,13 @@ type Props = {
const StepConnectDevice = (props: Props) => ( const StepConnectDevice = (props: Props) => (
<DeviceMonit <DeviceMonit
account={props.account} account={props.account}
coinType={props.currency && props.currency.coinType} currency={props.currency}
deviceSelected={props.deviceSelected} deviceSelected={props.deviceSelected}
onStatusChange={props.onStatusChange} onStatusChange={props.onStatusChange}
render={({ coinType, appStatus, devices, deviceSelected }) => ( render={({ currency, appStatus, devices, deviceSelected }) => (
<DeviceConnect <DeviceConnect
accountName={props.accountName} accountName={props.accountName}
coinType={coinType} currency={currency}
appOpened={appStatus === 'success' ? 'success' : appStatus === 'fail' ? 'fail' : null} appOpened={appStatus === 'success' ? 'success' : appStatus === 'fail' ? 'fail' : null}
devices={devices} devices={devices}
deviceSelected={deviceSelected} deviceSelected={deviceSelected}
@ -37,10 +36,4 @@ const StepConnectDevice = (props: Props) => (
/> />
) )
StepConnectDevice.defaultProps = {
accountName: undefined,
account: undefined,
currency: undefined,
}
export default StepConnectDevice export default StepConnectDevice

137
src/helpers/__tests__/balance.test.js

@ -1,137 +0,0 @@
import { getBalanceHistoryForAccount, getBalanceHistoryForAccounts } from 'helpers/balance'
const counterValues = {
BTC: {
USD: {
'2018-01-01': 1,
'2018-01-02': 2,
'2018-01-03': 3,
'2018-01-04': 4,
'2018-01-05': 5,
},
},
}
describe('helpers > balance', () => {
describe('getBalanceHistoryForAccount', () => {
test('should handle a simple case', () => {
const account = {
coinType: 0,
balanceByDay: {
'2018-01-01': 1,
'2018-01-02': 2,
},
}
const interval = {
start: '2018-01-01',
end: '2018-01-02',
}
const balances = getBalanceHistoryForAccount({
counterValue: 'USD',
account,
counterValues,
interval,
})
expect(balances).toEqual([
{ date: '2018-01-01', balance: 1 },
{ date: '2018-01-02', balance: 4 },
])
})
test('should handle empty days', () => {
const account = {
coinType: 0,
balanceByDay: {
'2018-01-01': 1,
'2018-01-03': 2,
},
}
const interval = {
start: '2018-01-01',
end: '2018-01-03',
}
const balances = getBalanceHistoryForAccount({
counterValue: 'USD',
account,
counterValues,
interval,
})
expect(balances).toEqual([
{ date: '2018-01-01', balance: 1 },
{ date: '2018-01-02', balance: 2 },
{ date: '2018-01-03', balance: 6 },
])
})
test('should work if interval dont contain operations', () => {
const account = {
coinType: 0,
balanceByDay: {
'2018-01-01': 1,
},
}
const interval = {
start: '2018-01-02',
end: '2018-01-03',
}
const balances = getBalanceHistoryForAccount({
counterValue: 'USD',
account,
counterValues,
interval,
})
expect(balances).toEqual([
{ date: '2018-01-02', balance: 2 },
{ date: '2018-01-03', balance: 3 },
])
})
})
describe('getBalanceHistoryForAccounts', () => {
test('should merge multiple accounts balance', () => {
const account1 = {
coinType: 0,
balanceByDay: {
'2018-01-01': 1,
'2018-01-02': 2,
},
}
const account2 = {
coinType: 0,
balanceByDay: {
'2018-01-02': 5,
'2018-01-04': 6,
},
}
const interval = {
start: '2018-01-01',
end: '2018-01-04',
}
const balances = getBalanceHistoryForAccounts({
counterValue: 'USD',
accounts: [account1, account2],
counterValues,
interval,
})
expect(balances).toEqual([
{ date: '2018-01-01', balance: 1 },
{ date: '2018-01-02', balance: 14 },
{ date: '2018-01-03', balance: 21 },
{ date: '2018-01-04', balance: 32 },
])
})
})
})

5
src/helpers/balance.js

@ -2,8 +2,7 @@
import moment from 'moment' import moment from 'moment'
import get from 'lodash/get' import get from 'lodash/get'
import { getDefaultUnitByCoinType } from '@ledgerhq/currencies' import type { Account } from '@ledgerhq/live-common/lib/types'
import type { Account } from '@ledgerhq/wallet-common/lib/types'
import find from 'lodash/find' import find from 'lodash/find'
import first from 'lodash/first' import first from 'lodash/first'
@ -70,7 +69,7 @@ export function getBalanceHistoryForAccount({
interval: DateInterval, interval: DateInterval,
}): Array<BalanceHistoryDay> { }): Array<BalanceHistoryDay> {
const todayDate = moment().format('YYYY-MM-DD') const todayDate = moment().format('YYYY-MM-DD')
const unit = getDefaultUnitByCoinType(account.coinType) const { unit } = account
const counterVals = get(counterValues, `${unit.code}.${counterValue}`) const counterVals = get(counterValues, `${unit.code}.${counterValue}`)
let lastBalance = getBalanceAtIntervalStart(account, interval) let lastBalance = getBalanceAtIntervalStart(account, interval)
return mapInterval(interval, date => { return mapInterval(interval, date => {

19
src/helpers/btc.js

@ -1,9 +1,10 @@
// @flow // @flow
import { coinTypeForId } from 'internals/usb/wallet/accounts'
import ledger from 'ledger-test-library' import ledger from 'ledger-test-library'
import bitcoin from 'bitcoinjs-lib' import bitcoin from 'bitcoinjs-lib'
import axios from 'axios' import axios from 'axios'
import type { OperationRaw } from '@ledgerhq/wallet-common/lib/types' import type { OperationRaw } from '@ledgerhq/live-common/lib/types'
import groupBy from 'lodash/groupBy' import groupBy from 'lodash/groupBy'
import noop from 'lodash/noop' import noop from 'lodash/noop'
@ -11,16 +12,16 @@ import uniqBy from 'lodash/uniqBy'
const GAP_LIMIT_ADDRESSES = 20 const GAP_LIMIT_ADDRESSES = 20
export const networks = [ export const networks = {
{ bitcoin: {
...bitcoin.networks.bitcoin, ...bitcoin.networks.bitcoin,
family: 1, family: 1,
}, },
{ bitcoin_testnet: {
...bitcoin.networks.testnet, ...bitcoin.networks.testnet,
family: 1, family: 1,
}, },
] }
export function computeOperation(addresses: Array<string>, accountId: string) { export function computeOperation(addresses: Array<string>, accountId: string) {
return (t: Object) => { return (t: Object) => {
@ -78,7 +79,7 @@ export async function getAccount({
hdnode, hdnode,
segwit, segwit,
network, network,
coinType, currencyId,
accountId, accountId,
asyncDelay = 250, asyncDelay = 250,
onProgress = noop, onProgress = noop,
@ -89,7 +90,7 @@ export async function getAccount({
currentIndex?: number, currentIndex?: number,
hdnode: Object, hdnode: Object,
segwit: boolean, segwit: boolean,
coinType: number, currencyId: string,
accountId: string, accountId: string,
network: Object, network: Object,
asyncDelay?: number, asyncDelay?: number,
@ -156,7 +157,7 @@ export async function getAccount({
let txs = [] let txs = []
const operationsOpts = { coin_type: coinType } const operationsOpts = { coin_type: coinTypeForId(currencyId) }
try { try {
txs = await ledger.getTransactions(listAddresses, operationsOpts) txs = await ledger.getTransactions(listAddresses, operationsOpts)
@ -204,7 +205,7 @@ export async function getAccount({
const account = { const account = {
...nextAddress, ...nextAddress,
coinType, currencyId,
addresses: operations.length > 0 ? allAddresses : [], addresses: operations.length > 0 ? allAddresses : [],
balance, balance,
balanceByDay: getBalanceByDay(operations), balanceByDay: getBalanceByDay(operations),

1
src/helpers/db.js

@ -47,6 +47,7 @@ export default {
} }
}, },
// TODO flowtype this. we should be able to express all the possible entries and their expected type (with a union type)
get: (key: DBKey, defaults: any): any => { get: (key: DBKey, defaults: any): any => {
const db = store(key) const db = store(key)
const data = db.get('data', defaults) const data = db.get('data', defaults)

29
src/internals/usb/wallet/accounts.js

@ -9,8 +9,6 @@ import Btc from '@ledgerhq/hw-app-btc'
import { getAccount, getHDNode, networks } from 'helpers/btc' import { getAccount, getHDNode, networks } from 'helpers/btc'
import { serializeAccounts } from 'reducers/accounts' import { serializeAccounts } from 'reducers/accounts'
type CoinType = number
async function sleep(delay, callback) { async function sleep(delay, callback) {
if (delay !== 0) { if (delay !== 0) {
await new Promise(resolve => setTimeout(resolve, delay)) await new Promise(resolve => setTimeout(resolve, delay))
@ -56,16 +54,24 @@ function encodeBase58Check(vchIn) {
return bs58check.encode(Buffer.from(vchIn)) return bs58check.encode(Buffer.from(vchIn))
} }
export function coinTypeForId(id: string) {
if (id === 'bitcoin_testnet') return 1
if (id === 'bitcoin') return 0
throw new Error('coinTypeForId is a hack and will disappear with libcore')
}
export function getPath({ export function getPath({
coinType, currencyId,
account, account,
segwit = true, segwit = true,
}: { }: {
coinType: CoinType, currencyId: string,
account?: any, account?: any,
segwit: boolean, segwit: boolean,
}) { }) {
return `${segwit ? 49 : 44}'/${coinType}'${account !== undefined ? `/${account}'` : ''}` return `${segwit ? 49 : 44}'/${coinTypeForId(currencyId)}'${
account !== undefined ? `/${account}'` : ''
}`
} }
export function verifyAddress({ export function verifyAddress({
@ -86,20 +92,20 @@ export default async ({
transport, transport,
currentAccounts, currentAccounts,
onProgress, onProgress,
coinType = 1, currencyId = 'bitcoin_testnet',
segwit = true, segwit = true,
nextAccountDelay = 1e3, nextAccountDelay = 1e3,
}: { }: {
transport: Object, transport: Object,
currentAccounts: Array<*>, currentAccounts: Array<*>,
onProgress: Function, onProgress: Function,
coinType?: CoinType, currencyId?: string,
segwit?: boolean, segwit?: boolean,
nextAccountDelay?: number, nextAccountDelay?: number,
}) => { }) => {
const btc = new Btc(transport) const btc = new Btc(transport)
const network = networks[coinType] const network = networks[currencyId]
const [p2pkh, p2sh, fam] = [network.pubKeyHash, network.scriptHash, network.family].map(v => const [p2pkh, p2sh, fam] = [network.pubKeyHash, network.scriptHash, network.family].map(v =>
v.toString(16).padStart(4, 0), v.toString(16).padStart(4, 0),
@ -110,7 +116,7 @@ export default async ({
const getPublicKey = path => btc.getWalletPublicKey(path) const getPublicKey = path => btc.getWalletPublicKey(path)
let result = bitcoin.crypto.sha256( let result = bitcoin.crypto.sha256(
await getPublicKey(getPath({ segwit, coinType })).then( await getPublicKey(getPath({ segwit, currencyId })).then(
({ publicKey }) => new Uint8Array(parseHexString(getCompressPublicKey(publicKey))), ({ publicKey }) => new Uint8Array(parseHexString(getCompressPublicKey(publicKey))),
), ),
) )
@ -139,7 +145,7 @@ export default async ({
} }
const getAllAccounts = async (currentAccount = 0, accounts = []) => { const getAllAccounts = async (currentAccount = 0, accounts = []) => {
const path = getPath({ segwit, coinType, account: currentAccount }) const path = getPath({ segwit, currencyId, account: currentAccount })
const xpub58 = await getXpub58ByPath({ path, account: currentAccount, network }) const xpub58 = await getXpub58ByPath({ path, account: currentAccount, network })
if (currentAccounts.includes(xpub58)) { if (currentAccounts.includes(xpub58)) {
@ -149,7 +155,7 @@ export default async ({
const hdnode = getHDNode({ xpub58, network }) const hdnode = getHDNode({ xpub58, network })
const account = await getAccount({ const account = await getAccount({
asyncDelay: 0, asyncDelay: 0,
coinType, currencyId,
accountId: xpub58, accountId: xpub58,
hdnode, hdnode,
network, network,
@ -164,7 +170,6 @@ export default async ({
accounts.push({ accounts.push({
id: xpub58, id: xpub58,
coinType,
...account, ...account,
}) })

24
src/internals/usb/wallet/index.js

@ -5,12 +5,11 @@ import Btc from '@ledgerhq/hw-app-btc'
import getAllAccounts, { getPath, verifyAddress } from './accounts' import getAllAccounts, { getPath, verifyAddress } from './accounts'
async function getAllAccountsByCoinType({ pathDevice, coinType, currentAccounts, onProgress }) { async function getAllAccountsByCurrencyId({ pathDevice, currencyId, currentAccounts, onProgress }) {
const transport = await CommNodeHid.open(pathDevice) const transport = await CommNodeHid.open(pathDevice)
// 1: BTC Testnet if (currencyId === 'bitcoin_testnet') {
if (coinType === 1) { return getAllAccounts({ currencyId, transport, currentAccounts, onProgress })
return getAllAccounts({ coinType, transport, currentAccounts, onProgress })
} }
throw new Error('Invalid coinType') throw new Error('Invalid coinType')
@ -19,11 +18,11 @@ async function getAllAccountsByCoinType({ pathDevice, coinType, currentAccounts,
export default (sendEvent: Function) => ({ export default (sendEvent: Function) => ({
getAccounts: async ({ getAccounts: async ({
pathDevice, pathDevice,
coinType, currencyId,
currentAccounts, currentAccounts,
}: { }: {
pathDevice: string, pathDevice: string,
coinType: number, currencyId: string,
currentAccounts: Array<string>, currentAccounts: Array<string>,
}) => { }) => {
sendEvent( sendEvent(
@ -35,9 +34,9 @@ export default (sendEvent: Function) => ({
) )
try { try {
const data = await getAllAccountsByCoinType({ const data = await getAllAccountsByCurrencyId({
pathDevice, pathDevice,
coinType, currencyId,
currentAccounts, currentAccounts,
onProgress: progress => sendEvent('wallet.getAccounts.progress', progress, { kill: false }), onProgress: progress => sendEvent('wallet.getAccounts.progress', progress, { kill: false }),
}) })
@ -59,13 +58,13 @@ export default (sendEvent: Function) => ({
} }
}, },
checkIfAppOpened: async ({ checkIfAppOpened: async ({
coinType, currencyId,
devicePath, devicePath,
accountPath, accountPath,
accountAddress, accountAddress,
segwit = true, segwit = true,
}: { }: {
coinType?: number, currencyId?: string,
devicePath: string, devicePath: string,
accountPath: string, accountPath: string,
accountAddress: string, accountAddress: string,
@ -82,9 +81,8 @@ export default (sendEvent: Function) => ({
throw new Error('Address is different') throw new Error('Address is different')
} }
} }
if (currencyId) {
if (coinType) { await btc.getWalletPublicKey(getPath({ currencyId, segwit }), false, segwit)
await btc.getWalletPublicKey(getPath({ coinType, segwit }), false, segwit)
sendEvent('wallet.checkIfAppOpened.success', { devicePath }) sendEvent('wallet.checkIfAppOpened.success', { devicePath })
} }
} catch (err) { } catch (err) {

2
src/main/counterValuesSync.js

@ -1,6 +1,6 @@
// @flow // @flow
import { fetchCurrentRates } from '@ledgerhq/wallet-common/lib/api/countervalue' import { fetchCurrentRates } from '@ledgerhq/live-common/lib/api/countervalue'
type SendFunction = (type: string, data: *) => void type SendFunction = (type: string, data: *) => void

30
src/reducers/accounts.js

@ -1,12 +1,13 @@
// @flow // @flow
import { createSelector } from 'reselect'
import { handleActions } from 'redux-actions' import { handleActions } from 'redux-actions'
import { createAccountModel } from '@ledgerhq/wallet-common/lib/models/account' import { createAccountModel } from '@ledgerhq/live-common/lib/models/account'
import every from 'lodash/every' import every from 'lodash/every'
import get from 'lodash/get' import get from 'lodash/get'
import reduce from 'lodash/reduce' import reduce from 'lodash/reduce'
import type { Account } from '@ledgerhq/wallet-common/lib/types' import type { Account } from '@ledgerhq/live-common/lib/types'
import type { State } from 'reducers' import type { State } from 'reducers'
@ -58,7 +59,32 @@ const handlers: Object = {
// Selectors // Selectors
export function accountsSelector(state: { accounts: AccountsState }): Account[] {
return state.accounts
}
export const archivedAccountsSelector = createSelector(accountsSelector, accounts =>
accounts.filter(acc => acc.archived),
)
export const visibleAccountsSelector = createSelector(accountsSelector, accounts =>
accounts.filter(acc => !acc.archived),
)
export const currenciesSelector = createSelector(visibleAccountsSelector, accounts =>
[...new Set(accounts.map(a => a.currency))].sort((a, b) => a.name.localeCompare(b.name)),
)
export const accountSelector = createSelector(
accountsSelector,
(_, { accountId }: { accountId: string }) => accountId,
(accounts, accountId) => accounts.find(a => a.id === accountId),
)
// TODO remove deprecated selectors
export function getTotalBalance(state: { accounts: AccountsState }) { export function getTotalBalance(state: { accounts: AccountsState }) {
// TODO we will have it using utility functions
return reduce( return reduce(
state.accounts, state.accounts,
(result, account) => { (result, account) => {

4
src/reducers/counterValues.js

@ -7,9 +7,9 @@ import {
makeCalculateCounterValue, makeCalculateCounterValue,
makeReverseCounterValue, makeReverseCounterValue,
formatCounterValueDay, formatCounterValueDay,
} from '@ledgerhq/wallet-common/lib/helpers/countervalue' } from '@ledgerhq/live-common/lib/helpers/countervalue'
import type { CalculateCounterValue } from '@ledgerhq/wallet-common/lib/types' import type { CalculateCounterValue } from '@ledgerhq/live-common/lib/types'
import type { State } from 'reducers' import type { State } from 'reducers'
export type CounterValuesState = {} export type CounterValuesState = {}

48
src/reducers/settings.js

@ -1,15 +1,28 @@
// @flow // @flow
import { handleActions } from 'redux-actions' import { handleActions } from 'redux-actions'
import { getFiatUnit } from '@ledgerhq/currencies' import { findCurrencyByTicker } from '@ledgerhq/live-common/lib/helpers/currencies'
import type { Currency } from '@ledgerhq/currencies' import type { CryptoCurrency, Currency } from '@ledgerhq/live-common/lib/types'
import get from 'lodash/get'
import type { Settings, CurrencySettings } from 'types/common' import type { Settings, CurrencySettings } from 'types/common'
import type { State } from 'reducers' import type { State } from 'reducers'
export type SettingsState = Object export type SettingsState = {
hasCompletedOnboarding: boolean,
username: string,
counterValue: string,
language: string,
orderAccounts: string,
password: {
isEnabled: boolean,
value: string,
},
marketIndicator: string,
currenciesSettings: {
[currencyId: string]: CurrencySettings,
},
region: string,
}
const defaultState: SettingsState = { const defaultState: SettingsState = {
hasCompletedOnboarding: false, hasCompletedOnboarding: false,
@ -53,22 +66,25 @@ const handlers: Object = {
}), }),
} }
export const hasPassword = (state: Object) => // TODO refactor selectors to *Selector naming convention
get(state.settings, 'password.isEnabled', defaultState.password.isEnabled)
export const hasPassword = (state: State): boolean => state.settings.password.isEnabled
export const getCounterValueCode = (state: Object) => export const getCounterValueCode = (state: State) => state.settings.counterValue
get(state.settings, 'counterValue', defaultState.counterValue)
export const getCounterValueFiatUnit = (state: Object) => getFiatUnit(getCounterValueCode(state)) export const counterValueCurrencySelector = (state: State): ?Currency =>
findCurrencyByTicker(getCounterValueCode(state))
export const getLanguage = (state: Object) => get(state.settings, 'language', defaultState.language) export const getLanguage = (state: State) => state.settings.language
export const getOrderAccounts = (state: Object) => export const getOrderAccounts = (state: State) => state.settings.orderAccounts
get(state.settings, 'orderAccounts', defaultState.orderAccounts)
export const currencySettingsSelector = (state: State, currency: Currency): CurrencySettings => { export const currencySettingsSelector = (
const currencySettings = state.settings.currenciesSettings[currency.coinType] state: State,
return currencySettings || CURRENCY_DEFAULTS_SETTINGS currency: CryptoCurrency,
): CurrencySettings => {
const currencySettings = state.settings.currenciesSettings[currency.id]
return { ...CURRENCY_DEFAULTS_SETTINGS, ...currencySettings }
} }
export const marketIndicatorSelector = (state: State) => state.settings.marketIndicator export const marketIndicatorSelector = (state: State) => state.settings.marketIndicator

11
src/renderer/events.js

@ -4,9 +4,8 @@ import { ipcRenderer } from 'electron'
import objectPath from 'object-path' import objectPath from 'object-path'
import debug from 'debug' import debug from 'debug'
import uniqBy from 'lodash/uniqBy' import uniqBy from 'lodash/uniqBy'
import { getFiatUnit } from '@ledgerhq/currencies' import { getFiatCurrencyByTicker } from '@ledgerhq/live-common/lib/helpers/currencies'
import type { Account } from '@ledgerhq/wallet-common/lib/types' import type { Currency, Account } from '@ledgerhq/live-common/lib/types'
import type { Currency, Unit } from '@ledgerhq/currencies'
import { CHECK_UPDATE_DELAY, SYNC_ACCOUNT_DELAY, SYNC_COUNTER_VALUES_DELAY } from 'config/constants' import { CHECK_UPDATE_DELAY, SYNC_ACCOUNT_DELAY, SYNC_COUNTER_VALUES_DELAY } from 'config/constants'
@ -89,10 +88,10 @@ export function startSyncAccounts(accounts: Account[]) {
syncAccountsInProgress = true syncAccountsInProgress = true
sendEvent('accounts', 'sync.all', { sendEvent('accounts', 'sync.all', {
accounts: accounts.map(account => { accounts: accounts.map(account => {
const { id, coinType, rootPath, addresses, index, operations } = account const { id, currency, rootPath, addresses, index, operations } = account
return { return {
id, id,
coinType, currencyId: currency.id,
allAddresses: addresses, allAddresses: addresses,
currentIndex: index, currentIndex: index,
rootPath, rootPath,
@ -111,7 +110,7 @@ export function stopSyncAccounts() {
export function startSyncCounterValues(counterValueCode: string, accounts: Account[]) { export function startSyncCounterValues(counterValueCode: string, accounts: Account[]) {
d.sync('Sync counterValues - start') d.sync('Sync counterValues - start')
const currencies: Currency[] = uniqBy(accounts.map(a => a.currency), 'code') const currencies: Currency[] = uniqBy(accounts.map(a => a.currency), 'code')
const counterValue: Unit = getFiatUnit(counterValueCode) const counterValue = getFiatCurrencyByTicker(counterValueCode)
sendEvent('msg', 'counterValues.sync', { currencies, counterValue }) sendEvent('msg', 'counterValues.sync', { currencies, counterValue })
} }

2
src/renderer/init.js

@ -34,7 +34,7 @@ const rootNode = document.getElementById('app')
store.dispatch(fetchSettings()) store.dispatch(fetchSettings())
store.dispatch(initCounterValues()) store.dispatch(initCounterValues())
const state = store.getState() || {} const state = store.getState()
const language = getLanguage(state) const language = getLanguage(state)
const locked = isLocked(state) const locked = isLocked(state)

16
src/stories/currencies.stories.js

@ -2,21 +2,21 @@
import React, { Fragment } from 'react' import React, { Fragment } from 'react'
import { storiesOf } from '@storybook/react' import { storiesOf } from '@storybook/react'
import { listCurrencies } from '@ledgerhq/currencies' import { listCryptoCurrencies } from '@ledgerhq/live-common/lib/helpers/currencies'
import { getIconByCoinType } from '@ledgerhq/currencies/react' import { getCryptoCurrencyIcon } from '@ledgerhq/live-common/lib/react'
import type { Currency } from '@ledgerhq/currencies' import type { CryptoCurrency } from '@ledgerhq/live-common/lib/types'
const stories = storiesOf('Common', module) const stories = storiesOf('Common', module)
const currencies: Array<Currency> = listCurrencies() const currencies: Array<CryptoCurrency> = listCryptoCurrencies()
stories.add('Currencies', () => ( stories.add('Currencies', () => (
<div> <div>
<table border="1"> <table border="1">
<thead> <thead>
<tr> <tr>
<td>{'coin type'}</td> <td>{'id'}</td>
<td>{'name'}</td> <td>{'name'}</td>
<td>{'color'}</td> <td>{'color'}</td>
<td>{'icon'}</td> <td>{'icon'}</td>
@ -25,10 +25,10 @@ stories.add('Currencies', () => (
</thead> </thead>
<tbody> <tbody>
{currencies.map(cur => { {currencies.map(cur => {
const Icon = getIconByCoinType(cur.coinType) const Icon = getCryptoCurrencyIcon(cur)
return ( return (
<tr key={cur.coinType}> <tr key={cur.id}>
<td>{cur.coinType}</td> <td>{cur.id}</td>
<td>{cur.name}</td> <td>{cur.name}</td>
<td> <td>
{cur.color ? ( {cur.color ? (

2
src/types/common.js

@ -25,7 +25,7 @@ export type CurrencySettings = {
} }
export type CurrenciesSettings = { export type CurrenciesSettings = {
[coinType: number]: CurrencySettings, [id: string]: CurrencySettings,
} }
export type Settings = { export type Settings = {

18
yarn.lock

@ -956,14 +956,6 @@
lodash "^4.2.0" lodash "^4.2.0"
to-fast-properties "^2.0.0" to-fast-properties "^2.0.0"
"@ledgerhq/currencies@^4.10.1":
version "4.10.1"
resolved "https://registry.yarnpkg.com/@ledgerhq/currencies/-/currencies-4.10.1.tgz#462081005e3e37e0737bad9aba189eef1663e96a"
dependencies:
lodash "^4.17.5"
numeral "^2.0.6"
querystring "^0.2.0"
"@ledgerhq/hw-app-btc@^4.7.3": "@ledgerhq/hw-app-btc@^4.7.3":
version "4.7.3" version "4.7.3"
resolved "https://registry.yarnpkg.com/@ledgerhq/hw-app-btc/-/hw-app-btc-4.7.3.tgz#5a7d365f893f5a72bbb6b639d1738edced712c5f" resolved "https://registry.yarnpkg.com/@ledgerhq/hw-app-btc/-/hw-app-btc-4.7.3.tgz#5a7d365f893f5a72bbb6b639d1738edced712c5f"
@ -990,14 +982,14 @@
dependencies: dependencies:
events "^2.0.0" events "^2.0.0"
"@ledgerhq/wallet-common@^1.2.0": "@ledgerhq/live-common@^2.0.0-rc4":
version "1.2.0" version "2.0.0-rc4"
resolved "https://registry.yarnpkg.com/@ledgerhq/wallet-common/-/wallet-common-1.2.0.tgz#5e648829b6dff0fe33f31da2ee4a974df405642e" resolved "https://registry.yarnpkg.com/@ledgerhq/live-common/-/live-common-2.0.0-rc4.tgz#5f1028ff3e52fc6bf7e42c5ea64d71be9b843f82"
dependencies: dependencies:
"@ledgerhq/currencies" "^4.10.1"
axios "^0.18.0" axios "^0.18.0"
invariant "^2.2.2" invariant "^2.2.2"
lodash "^4.17.4" lodash "^4.17.4"
numeral "^2.0.6"
prando "^3.0.1" prando "^3.0.1"
react "^16.0.0" react "^16.0.0"
@ -5018,7 +5010,7 @@ electron-webpack@^2.0.1:
webpack-merge "^4.1.2" webpack-merge "^4.1.2"
yargs "^11.1.0" yargs "^11.1.0"
electron@1.8.4: electron@1.8.4, electron@^1.8.2:
version "1.8.4" version "1.8.4"
resolved "https://registry.yarnpkg.com/electron/-/electron-1.8.4.tgz#cca8d0e6889f238f55b414ad224f03e03b226a38" resolved "https://registry.yarnpkg.com/electron/-/electron-1.8.4.tgz#cca8d0e6889f238f55b414ad224f03e03b226a38"
dependencies: dependencies:

Loading…
Cancel
Save