Browse Source

Merge pull request #258 from meriadec/common-countervalues

Use wallet-common countervalues helpers
master
Loëck Vézien 7 years ago
committed by GitHub
parent
commit
acdc045393
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      package.json
  2. 77
      src/actions/counterValues.js
  3. 4
      src/components/AccountPage/index.js
  4. 40
      src/components/CounterValue/index.js
  5. 18
      src/components/CounterValue/stories.js
  6. 4
      src/components/DashboardPage/index.js
  7. 4
      src/components/IsUnlocked.js
  8. 13
      src/components/OperationsList/index.js
  9. 301
      src/components/RequestAmount/index.js
  10. 2
      src/components/base/FormattedVal/__tests__/FormattedVal.test.js
  11. 5
      src/components/base/FormattedVal/index.js
  12. 2
      src/components/base/InputCurrency/index.js
  13. 2
      src/components/modals/AddAccount/index.js
  14. 3
      src/components/modals/OperationDetails.js
  15. 10
      src/components/modals/Send/01-step-amount.js
  16. 3
      src/components/modals/Send/Footer.js
  17. 26
      src/components/modals/Send/index.js
  18. 54
      src/helpers/__tests__/balance.test.js
  19. 7
      src/helpers/balance.js
  20. 11
      src/helpers/db.js
  21. 1
      src/main/bridge.js
  22. 24
      src/main/counterValuesSync.js
  23. 88
      src/reducers/counterValues.js
  24. 8
      src/reducers/settings.js
  25. 4
      src/renderer/createStore.js
  26. 27
      src/renderer/events.js
  27. 1
      src/renderer/index.js
  28. 6
      yarn.lock

3
package.json

@ -48,7 +48,7 @@
"@ledgerhq/hw-app-eth": "^4.7.3",
"@ledgerhq/hw-transport": "^4.7.3",
"@ledgerhq/hw-transport-node-hid": "^4.7.3",
"@ledgerhq/wallet-common": "^0.10.0",
"@ledgerhq/wallet-common": "^0.10.1",
"axios": "^0.18.0",
"bcryptjs": "^2.4.3",
"bitcoinjs-lib": "^3.3.2",
@ -112,6 +112,7 @@
"babel-loader": "^8.0.0-beta.2",
"babel-plugin-module-resolver": "^3.1.1",
"babel-plugin-styled-components": "^1.5.0",
"babel-polyfill": "^6.26.0",
"chalk": "^2.3.1",
"chance": "^1.0.13",
"concurrently": "^3.5.1",

77
src/actions/counterValues.js

@ -1,15 +1,11 @@
// @flow
import axios from 'axios'
import moment from 'moment'
import { getDefaultUnitByCoinType } from '@ledgerhq/currencies'
import { getFiatUnit } from '@ledgerhq/currencies'
import { fetchHistodayCounterValuesMultiple } from '@ledgerhq/wallet-common/lib/api/countervalue'
import type { Currency } from '@ledgerhq/currencies'
import type { Dispatch } from 'redux'
import { serializeCounterValues } from 'reducers/counterValues'
import get from 'lodash/get'
import db from 'helpers/db'
export type InitCounterValues = () => { type: string, payload: Object }
@ -24,60 +20,17 @@ export const updateCounterValues: UpdateCounterValues = payload => ({
payload,
})
export type UpdateLastCounterValueBySymbol = (string, Object) => { type: string, payload: Object }
export const updateLastCounterValueBySymbol: UpdateLastCounterValueBySymbol = (symbol, value) => ({
type: 'DB:UPDATE_LAST_COUNTER_VALUE',
payload: {
symbol,
value,
},
})
export type FetchCounterValues = (?number) => (Dispatch<*>, Function) => Promise<any>
export const fetchCounterValues: FetchCounterValues = coinType => (dispatch, getState) => {
const { accounts, counterValues, settings } = getState()
const { counterValue } = settings
let coinTypes = []
if (!coinType) {
coinTypes = [...new Set(accounts.map(a => a.coinType))]
} else {
coinTypes = [coinType]
export type FetchCounterValues = (?(Currency[])) => (Dispatch<*>, Function) => Promise<any>
export const fetchCounterValues: FetchCounterValues = (currencies: ?(Currency[])) => async (
dispatch,
getState,
) => {
const { accounts, settings } = getState()
if (!currencies) {
currencies = accounts.map(a => a.currency)
}
const today = moment().format('YYYY-MM-DD')
const fetchCounterValuesByCoinType = coinType => {
const { code } = getDefaultUnitByCoinType(coinType)
const todayCounterValues = get(counterValues, `${code}-${counterValue}.${today}`, null)
if (todayCounterValues !== null) {
return null
}
return axios
.get(
`https://min-api.cryptocompare.com/data/histoday?&extraParams=ledger-test&fsym=${code}&tsym=${counterValue}&allData=1`,
)
.then(({ data }) => ({
symbol: `${code}-${counterValue}`,
values: data.Data.map(d => [moment(d.time * 1000).format('YYYY-MM-DD'), d.close]),
}))
}
return Promise.all(coinTypes.map(fetchCounterValuesByCoinType)).then(results => {
const newCounterValues = serializeCounterValues(
results.reduce((r, v) => {
if (v !== null) {
r[v.symbol] = v.values
}
return r
}, {}),
)
if (Object.keys(newCounterValues).length !== 0) {
dispatch(updateCounterValues(newCounterValues))
}
})
const { counterValue } = settings
const fiatUnit = getFiatUnit(counterValue)
const counterValues = await fetchHistodayCounterValuesMultiple(currencies, fiatUnit)
dispatch(updateCounterValues(counterValues))
}

4
src/components/AccountPage/index.js

@ -15,7 +15,7 @@ import type { T } from 'types/common'
import { darken } from 'styles/helpers'
import { getAccountById } from 'reducers/accounts'
import { getCounterValue } from 'reducers/settings'
import { getCounterValueCode } from 'reducers/settings'
import { openModal } from 'reducers/modals'
import IconControls from 'icons/Controls'
@ -50,7 +50,7 @@ const ButtonSettings = styled(Button).attrs({
const mapStateToProps = (state, props) => ({
account: getAccountById(state, props.match.params.id),
counterValue: getCounterValue(state),
counterValue: getCounterValueCode(state),
})
const mapDispatchToProps = {

40
src/components/CounterValue/index.js

@ -3,26 +3,25 @@
import React, { PureComponent } from 'react'
import { connect } from 'react-redux'
import moment from 'moment'
import type { Unit, Currency } from '@ledgerhq/currencies'
import isNaN from 'lodash/isNaN'
import type { Unit } from '@ledgerhq/currencies'
import { getCounterValue } from 'reducers/settings'
import { getCounterValueCode } from 'reducers/settings'
import { calculateCounterValueSelector } from 'reducers/counterValues'
import FormattedVal from 'components/base/FormattedVal'
const mapStateToProps = state => ({
counterValue: getCounterValue(state),
counterValues: state.counterValues,
counterValueCode: getCounterValueCode(state),
getCounterValue: calculateCounterValueSelector(state),
})
type Props = {
formatValue: boolean,
counterValue: string,
counterValues: Object,
counterValueCode: string,
getCounterValue: Function,
time?: Date | string | number,
unit: Unit,
currency: Currency,
value: number,
}
@ -34,17 +33,22 @@ export class CounterValue extends PureComponent<Props> {
}
render() {
const { formatValue, value, unit, counterValue, counterValues, time, ...props } = this.props
const cValues = counterValues[`${unit.code}-${counterValue}`]
const v = isNaN(Number(value))
? 0
: (time ? cValues.byDate[moment(time).format('YYYY-MM-DD')] : cValues.list[0][1]) *
(value / 10 ** unit.magnitude)
const {
formatValue,
value,
currency,
unit,
counterValueCode,
time,
getCounterValue,
...props
} = this.props
const date = moment(time).format('YYYY-MM-DD')
const v = getCounterValue(currency, counterValueCode)(value, date)
return formatValue ? (
<FormattedVal val={v} fiat={counterValue} showCode alwaysShowSign {...props} />
<FormattedVal val={v} fiat={counterValueCode} showCode alwaysShowSign {...props} />
) : (
v
)

18
src/components/CounterValue/stories.js

@ -1,30 +1,38 @@
// @flow
import React from 'react'
import { getDefaultUnitByCoinType } from '@ledgerhq/currencies'
import { getCurrencyByCoinType, getDefaultUnitByCoinType } from '@ledgerhq/currencies'
import { storiesOf } from '@storybook/react'
import { boolean, text } from '@storybook/addon-knobs'
import createHistory from 'history/createHashHistory'
import { CounterValue } from 'components/CounterValue'
import { calculateCounterValueSelector } from 'reducers/counterValues'
import createStore from 'renderer/createStore'
const stories = storiesOf('Components', module)
const currency = getCurrencyByCoinType(0)
const unit = getDefaultUnitByCoinType(0)
const counterValue = 'USD'
const counterValues = {
'BTC-USD': {
byDate: {
BTC: {
USD: {
'2018-01-09': 10000,
},
list: [['2018-01-09', 10000]],
},
}
const store = createStore(createHistory(), { counterValues })
const getCounterValue = calculateCounterValueSelector(store.getState())
stories.add('CounterValue', () => (
<CounterValue
counterValue={counterValue}
getCounterValue={getCounterValue}
counterValueCode={counterValue}
counterValues={counterValues}
currency={currency}
unit={unit}
formatValue={boolean('formatValue', true)}
value={Number(text('value', '100000000'))}

4
src/components/DashboardPage/index.js

@ -14,7 +14,7 @@ import sortBy from 'lodash/sortBy'
import type { T } from 'types/common'
import { getVisibleAccounts } from 'reducers/accounts'
import { getCounterValue } from 'reducers/settings'
import { getCounterValueCode } from 'reducers/settings'
import { updateOrderAccounts } from 'actions/accounts'
import { saveSettings } from 'actions/settings'
@ -31,7 +31,7 @@ import AccountsOrder from './AccountsOrder'
const mapStateToProps = state => ({
accounts: getVisibleAccounts(state),
counterValue: getCounterValue(state),
counterValue: getCounterValueCode(state),
})
const mapDispatchToProps = {

4
src/components/IsUnlocked.js

@ -22,7 +22,7 @@ import { setEncryptionKey } from 'helpers/db'
import { fetchAccounts } from 'actions/accounts'
import { getAccounts } from 'reducers/accounts'
import { isLocked, unlock } from 'reducers/application'
import { getCounterValue } from 'reducers/settings'
import { getCounterValueCode } from 'reducers/settings'
import Box from 'components/base/Box'
import Input from 'components/base/Input'
@ -47,7 +47,7 @@ type State = {
const mapStateToProps = state => ({
accounts: getAccounts(state),
counterValue: getCounterValue(state),
counterValue: getCounterValueCode(state),
isLocked: isLocked(state),
settings: state.settings,
})

13
src/components/OperationsList/index.js

@ -129,7 +129,7 @@ const Operation = ({
op: OperationType,
withAccount?: boolean,
}) => {
const { unit } = account
const { unit, currency } = account
const time = moment(op.date)
const Icon = getIconByCoinType(account.currency.coinType)
const type = op.amount > 0 ? 'from' : 'to'
@ -179,7 +179,7 @@ const Operation = ({
<Cell grow shrink style={{ display: 'block' }}>
<Address value={op.address} />
</Cell>
<Cell size={AMOUNT_COL_SIZE}>
<Cell size={AMOUNT_COL_SIZE} justify="flex-end">
<Box alignItems="flex-end">
<FormattedVal
val={op.amount}
@ -189,7 +189,14 @@ const Operation = ({
alwaysShowSign
color={op.amount < 0 ? 'smoke' : 'positiveGreen'}
/>
<CounterValue color="grey" fontSize={3} time={time} unit={unit} value={op.amount} />
<CounterValue
color="grey"
fontSize={3}
time={time}
currency={currency}
unit={unit}
value={op.amount}
/>
</Box>
</Cell>
</OperationRaw>

301
src/components/RequestAmount/index.js

@ -5,16 +5,13 @@ import { compose } from 'redux'
import { translate } from 'react-i18next'
import styled from 'styled-components'
import { connect } from 'react-redux'
import { getDefaultUnitByCoinType, getFiatUnit } from '@ledgerhq/currencies'
import type { Account } from '@ledgerhq/wallet-common/lib/types'
import isNaN from 'lodash/isNaN'
import noop from 'lodash/noop'
import type { Account, CalculateCounterValue } from '@ledgerhq/wallet-common/lib/types'
import type { Unit } from '@ledgerhq/currencies'
import type { T } from 'types/common'
import { getCounterValue } from 'reducers/settings'
import { getLastCounterValueBySymbol } from 'reducers/counterValues'
import { getCounterValueFiatUnit } from 'reducers/settings'
import { calculateCounterValueSelector, reverseCounterValueSelector } from 'reducers/counterValues'
import InputCurrency from 'components/base/InputCurrency'
import Button from 'components/base/Button'
@ -27,6 +24,7 @@ const InputRight = styled(Box).attrs({
justifyContent: 'center',
pr: 3,
})``
const InputCenter = styled(Box).attrs({
ff: 'Rubik',
color: 'graphite',
@ -37,258 +35,119 @@ const InputCenter = styled(Box).attrs({
width: 30px;
`
const mapStateToProps = (state, { account }) => {
const counterValue = getCounterValue(state)
const unit = getDefaultUnitByCoinType(account.coinType)
const symbol = `${unit.code}-${counterValue}`
return {
counterValue,
lastCounterValue: getLastCounterValueBySymbol(symbol, state),
}
}
function maxUnitDigits(unit, value) {
const [leftDigits, rightDigits] = value.toString().split('.')
return Number(`${leftDigits}${rightDigits ? `.${rightDigits.slice(0, unit.magnitude)}` : ''}`)
}
const mapStateToProps = state => ({
rightUnit: getCounterValueFiatUnit(state),
getCounterValue: calculateCounterValueSelector(state),
getReverseCounterValue: reverseCounterValueSelector(state),
})
function calculateMax(props) {
const { account, counterValue, lastCounterValue } = props
const unit = getUnit({ account, counterValue })
const leftMax = account.balance / 10 ** unit.left.magnitude
return {
left: account.balance / 10 ** unit.left.magnitude,
right: maxUnitDigits(unit.right, leftMax * lastCounterValue),
}
}
function getUnit({ account, counterValue }) {
return {
left: getDefaultUnitByCoinType(account.coinType),
right: getFiatUnit(counterValue),
}
}
function calculateValues({
dir,
value,
max,
lastCounterValue,
}: {
dir: string,
value: Object,
max: Object,
lastCounterValue: number,
}) {
const v = value[dir]
const getMax = (d, v) => {
const result = v > max[d] ? max[d] : v
return isNaN(result) ? '0' : result.toString()
}
const newValue = {}
if (dir === 'left') {
newValue.left = v === '' ? v : getMax('left', v)
newValue.right = getMax('right', Number(v) * lastCounterValue)
}
type Props = {
// translation
t: T,
if (dir === 'right') {
newValue.left = getMax('left', Number(v) / lastCounterValue)
newValue.right = v === '' ? v : getMax('right', v)
}
// left value (always the one which is returned)
value: number,
return newValue
}
// max left value
max: number,
type Direction = 'left' | 'right'
// change handler
onChange: ({ left: number, right: number }) => void,
type Props = {
// used to determine the left input unit
account: Account,
counterValue: string,
lastCounterValue: number, // eslint-disable-line react/no-unused-prop-types
onChange: Function,
t: T,
value: Object,
}
export type DoubleVal = {
left: number,
right: number,
// used to determine the right input unit
// retrieved via selector (take the chosen countervalue unit)
rightUnit: Unit,
// used to calculate the opposite field value (right & left)
getCounterValue: CalculateCounterValue,
getReverseCounterValue: CalculateCounterValue,
}
type State = {
max: DoubleVal,
value: {
left: string | number,
right: string | number,
},
leftUnit: Unit,
rightUnit: Unit,
leftValue: number,
rightValue: number,
}
export class RequestAmount extends PureComponent<Props, State> {
static defaultProps = {
onChange: noop,
value: {},
}
constructor(props: Props) {
super()
this.props = props
super(props)
const max = calculateMax(props)
const { account, rightUnit, value, getCounterValue } = this.props
let v = {
left: 0,
right: 0,
}
if (props.value.left) {
v = calculateValues({
...props,
dir: 'left',
max,
})
}
if (props.value.right) {
v = calculateValues({
...props,
dir: 'right',
max,
})
}
const rawLeftValue = value * 10 ** account.unit.magnitude
const rawRightValue = getCounterValue(account.currency, rightUnit)(rawLeftValue)
const rightValue = rawRightValue / 10 ** rightUnit.magnitude
this.state = {
max,
value: v,
}
}
componentWillReceiveProps(nextProps: Props) {
if (this.props.account !== nextProps.account) {
const max = calculateMax(nextProps)
this.setState({
max,
value: calculateValues({
...nextProps,
dir: 'left',
max,
}),
})
leftUnit: account.unit,
rightUnit,
leftValue: value,
rightValue,
}
}
componentDidUpdate(prevProps: Props) {
this.updateValueWithProps(prevProps, this.props)
}
handleChangeAmount = (dir: Direction) => (v: number | string) => {
const { onChange, value, account, counterValue, ...otherProps } = this.props
const { max } = this.state
const otherDir = dir === 'left' ? 'right' : 'left'
const unit = getUnit({
account,
counterValue,
})
const newValue = calculateValues({
...otherProps,
dir,
value: {
[dir]: v.toString(),
},
max,
})
newValue[otherDir] = maxUnitDigits(unit[otherDir], newValue[otherDir]).toString()
this.setState({
value: newValue,
})
onChange({
values: newValue,
rawValues: {
left: Number(newValue.left) * 10 ** unit.left.magnitude,
right: Number(newValue.right),
},
})
}
handleClickMax = () => {
const { account } = this.props
this.handleChangeAmount('left')(account.balance)
const leftValue = this.props.max / 10 ** this.props.account.unit.magnitude
this.handleChangeAmount('left')(leftValue)
this.setState({ leftValue })
}
updateValueWithProps = (props: Props, nextProps: Props) => {
if (
props.value.left !== nextProps.value.left &&
nextProps.value.left !== this.state.value.left
) {
this.setState({
value: calculateValues({
...nextProps,
dir: 'left',
max: this.state.max,
}),
})
}
if (
props.value.right !== nextProps.value.right &&
nextProps.value.right !== this.state.value.right
) {
this.setState({
value: calculateValues({
...nextProps,
dir: 'right',
max: this.state.max,
}),
})
handleChangeAmount = (changedField: string) => (val: number) => {
const { getCounterValue, getReverseCounterValue, account, max, onChange } = this.props
const { rightUnit } = this.state
if (changedField === 'left') {
let rawLeftValue = val * 10 ** account.unit.magnitude
if (rawLeftValue > max) {
rawLeftValue = max
}
const leftValue = rawLeftValue / 10 ** account.unit.magnitude
const rawRightValue = getCounterValue(account.currency, rightUnit)(rawLeftValue)
const rightValue = rawRightValue / 10 ** rightUnit.magnitude
this.setState({ rightValue, leftValue })
onChange({ left: rawLeftValue, right: rawRightValue })
} else if (changedField === 'right') {
let rawRightValue = val * 10 ** rightUnit.magnitude
let rawLeftValue = getReverseCounterValue(account.currency, rightUnit)(rawRightValue)
if (rawLeftValue > max) {
rawLeftValue = max
rawRightValue = getCounterValue(account.currency, rightUnit)(rawLeftValue)
}
const rightValue = rawRightValue / 10 ** rightUnit.magnitude
const leftValue = rawLeftValue / 10 ** account.unit.magnitude
this.setState({ rightValue, leftValue })
onChange({ left: rawLeftValue, right: rawRightValue })
}
}
render() {
const { account, counterValue, t } = this.props
const { value } = this.state
const unit = getUnit({
account,
counterValue,
})
const { t } = this.props
const { leftUnit, rightUnit, leftValue, rightValue } = this.state
return (
<Box horizontal flow={5}>
<Box horizontal>
<Box horizontal flow="5">
<Box horizontal align="center">
<InputCurrency
containerProps={{
style: {
width: 156,
},
}}
unit={unit.left}
value={value.left}
containerProps={{ style: { width: 156 } }}
unit={leftUnit}
value={leftValue}
onChange={this.handleChangeAmount('left')}
renderRight={<InputRight>{unit.left.code}</InputRight>}
renderRight={<InputRight>{leftUnit.code}</InputRight>}
/>
<InputCenter>=</InputCenter>
<InputCurrency
containerProps={{
style: {
width: 156,
},
}}
unit={unit.right}
value={value.right}
containerProps={{ style: { width: 156 } }}
unit={rightUnit}
value={rightValue}
onChange={this.handleChangeAmount('right')}
renderRight={<InputRight>{unit.right.code}</InputRight>}
renderRight={<InputRight>{rightUnit.code}</InputRight>}
/>
</Box>
<Box grow justifyContent="flex-end">
<Box grow justify="flex-end">
<Button primary onClick={this.handleClickMax}>
{t('common:max')}
</Button>

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

@ -37,7 +37,7 @@ describe('components', () => {
})
it('renders a fiat', () => {
const component = <FormattedVal fiat="USD" val={20} />
const component = <FormattedVal fiat="USD" val={2000} />
const tree = render(component)
expect(tree).toMatchSnapshot()
})

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

@ -26,8 +26,8 @@ type Props = {
}
function FormattedVal(props: Props) {
const { disableRounding, fiat, isPercent, alwaysShowSign, showCode, ...p } = props
let { val, unit } = props
const { val, disableRounding, fiat, isPercent, alwaysShowSign, showCode, ...p } = props
let { unit } = props
if (isUndefined(val)) {
throw new Error('FormattedVal require a `val` prop. Received `undefined`')
@ -42,7 +42,6 @@ function FormattedVal(props: Props) {
} else {
if (fiat) {
unit = getFiatUnit(fiat)
val *= 10 ** unit.magnitude
} else if (!unit) {
return ''
}

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

@ -14,7 +14,7 @@ import Select from 'components/base/Select'
import type { Unit } from '@ledgerhq/currencies'
function parseValue(value) {
return value.toString().replace(/,/, '.')
return value.toString().replace(/,/g, '.')
}
function format(unit: Unit, value: Value) {

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

@ -306,7 +306,7 @@ class AddAccountModal extends PureComponent<Props, State> {
fetchingCounterValues: true,
})
await fetchCounterValues(currency.coinType)
await fetchCounterValues([currency])
this.setState({
fetchingCounterValues: false,

3
src/components/modals/OperationDetails.js

@ -54,7 +54,7 @@ const OperationDetails = ({ t }: { t: T }) => (
render={({ data, onClose }) => {
const { operation, account, type } = data
const { name, unit, minConfirmations } = account
const { name, unit, currency, minConfirmations } = account
const { id, amount, confirmations, date, from, to } = operation
const isConfirmed = confirmations >= minConfirmations
@ -92,6 +92,7 @@ const OperationDetails = ({ t }: { t: T }) => (
style={{ lineHeight: 1 }}
time={date}
unit={unit}
currency={currency}
value={amount}
/>
</Box>

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

@ -5,7 +5,6 @@ import type { Account } from '@ledgerhq/wallet-common/lib/types'
import type { Unit } from '@ledgerhq/currencies'
import type { T } from 'types/common'
import type { DoubleVal } from 'components/RequestAmount'
import Box from 'components/base/Box'
import CheckBox from 'components/base/CheckBox'
@ -23,7 +22,7 @@ type PropsStepAmount = {
account: Account | null,
onChange: Function,
recipientAddress: string,
amount: DoubleVal,
amount: { left: number, right: number },
fees: {
value: number,
unit: Unit | null,
@ -61,7 +60,12 @@ function StepAmount(props: PropsStepAmount) {
{/* AMOUNT */}
<Box flow={1}>
<Label>{t('send:steps.amount.amount')}</Label>
<RequestAmount account={account} onChange={onChange('amount')} value={amount} />
<RequestAmount
max={account.balance - 0}
account={account}
onChange={onChange('amount')}
value={amount.left}
/>
</Box>
{/* FEES */}

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

@ -4,7 +4,6 @@ import React from 'react'
import type { Account } from '@ledgerhq/wallet-common/lib/types'
import type { T } from 'types/common'
import type { DoubleVal } from 'components/RequestAmount'
import { ModalFooter } from 'components/base/Modal'
import Box from 'components/base/Box'
@ -16,7 +15,7 @@ import Text from 'components/base/Text'
type Props = {
t: T,
account: Account,
amount: DoubleVal,
amount: { left: number, right: number },
onNext: Function,
canNext: boolean,
counterValue: string,

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

@ -9,11 +9,10 @@ import type { Account } from '@ledgerhq/wallet-common/lib/types'
import type { Unit } from '@ledgerhq/currencies'
import type { T } from 'types/common'
import type { DoubleVal } from 'components/RequestAmount'
import { MODAL_SEND } from 'config/constants'
import { getCounterValue } from 'reducers/settings'
import { getCounterValueCode } from 'reducers/settings'
import Breadcrumb from 'components/Breadcrumb'
import Modal, { ModalBody, ModalTitle, ModalContent } from 'components/base/Modal'
@ -26,7 +25,7 @@ import StepVerification from './03-step-verification'
import StepConfirmation from './04-step-confirmation'
const mapStateToProps = state => ({
counterValue: getCounterValue(state),
counterValue: getCounterValueCode(state),
})
type Props = {
@ -37,10 +36,7 @@ type Props = {
type State = {
stepIndex: number,
isDeviceReady: boolean,
amount: {
values: DoubleVal,
rawValues: DoubleVal,
},
amount: { left: number, right: number },
account: Account | null,
recipientAddress: string,
fees: {
@ -63,14 +59,8 @@ const INITIAL_STATE = {
account: null,
recipientAddress: '',
amount: {
values: {
left: 0,
right: 0,
},
rawValues: {
left: 0,
right: 0,
},
left: 0,
right: 0,
},
fees: {
value: 0,
@ -90,7 +80,7 @@ class SendModal extends PureComponent<Props, State> {
// informations
if (stepIndex === 0) {
const { amount, recipientAddress } = this.state
return !!amount.rawValues.left && !!recipientAddress && !!account
return !!amount.left && !!recipientAddress && !!account
}
// connect device
@ -123,7 +113,7 @@ class SendModal extends PureComponent<Props, State> {
const { Comp } = step
const stepProps = {
...othersState,
amount: amount.values,
amount,
account: account || acc,
}
@ -154,7 +144,7 @@ class SendModal extends PureComponent<Props, State> {
canNext={canNext}
onNext={this.handleNextStep}
account={acc}
amount={amount.rawValues}
amount={amount}
t={t}
/>
)}

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

@ -1,13 +1,13 @@
import { getBalanceHistoryForAccount, getBalanceHistoryForAccounts } from 'helpers/balance'
const counterValues = {
'BTC-USD': {
byDate: {
'2018-01-01': 1000,
'2018-01-02': 2000,
'2018-01-03': 3000,
'2018-01-04': 4000,
'2018-01-05': 5000,
BTC: {
USD: {
'2018-01-01': 1,
'2018-01-02': 2,
'2018-01-03': 3,
'2018-01-04': 4,
'2018-01-05': 5,
},
},
}
@ -18,8 +18,8 @@ describe('helpers > balance', () => {
const account = {
coinType: 0,
balanceByDay: {
'2018-01-01': 100000000,
'2018-01-02': 200000000,
'2018-01-01': 1,
'2018-01-02': 2,
},
}
@ -36,8 +36,8 @@ describe('helpers > balance', () => {
})
expect(balances).toEqual([
{ date: '2018-01-01', balance: 1000 },
{ date: '2018-01-02', balance: 4000 },
{ date: '2018-01-01', balance: 1 },
{ date: '2018-01-02', balance: 4 },
])
})
@ -45,8 +45,8 @@ describe('helpers > balance', () => {
const account = {
coinType: 0,
balanceByDay: {
'2018-01-01': 100000000,
'2018-01-03': 200000000,
'2018-01-01': 1,
'2018-01-03': 2,
},
}
@ -63,9 +63,9 @@ describe('helpers > balance', () => {
})
expect(balances).toEqual([
{ date: '2018-01-01', balance: 1000 },
{ date: '2018-01-02', balance: 2000 },
{ date: '2018-01-03', balance: 6000 },
{ date: '2018-01-01', balance: 1 },
{ date: '2018-01-02', balance: 2 },
{ date: '2018-01-03', balance: 6 },
])
})
@ -73,7 +73,7 @@ describe('helpers > balance', () => {
const account = {
coinType: 0,
balanceByDay: {
'2018-01-01': 100000000,
'2018-01-01': 1,
},
}
@ -90,8 +90,8 @@ describe('helpers > balance', () => {
})
expect(balances).toEqual([
{ date: '2018-01-02', balance: 2000 },
{ date: '2018-01-03', balance: 3000 },
{ date: '2018-01-02', balance: 2 },
{ date: '2018-01-03', balance: 3 },
])
})
})
@ -101,16 +101,16 @@ describe('helpers > balance', () => {
const account1 = {
coinType: 0,
balanceByDay: {
'2018-01-01': 100000000,
'2018-01-02': 200000000,
'2018-01-01': 1,
'2018-01-02': 2,
},
}
const account2 = {
coinType: 0,
balanceByDay: {
'2018-01-02': 500000000,
'2018-01-04': 600000000,
'2018-01-02': 5,
'2018-01-04': 6,
},
}
@ -127,10 +127,10 @@ describe('helpers > balance', () => {
})
expect(balances).toEqual([
{ date: '2018-01-01', balance: 1000 },
{ date: '2018-01-02', balance: 14000 },
{ date: '2018-01-03', balance: 21000 },
{ date: '2018-01-04', balance: 32000 },
{ date: '2018-01-01', balance: 1 },
{ date: '2018-01-02', balance: 14 },
{ date: '2018-01-03', balance: 21 },
{ date: '2018-01-04', balance: 32 },
])
})
})

7
src/helpers/balance.js

@ -1,6 +1,7 @@
// @flow
import moment from 'moment'
import get from 'lodash/get'
import { getDefaultUnitByCoinType } from '@ledgerhq/currencies'
import type { Account } from '@ledgerhq/wallet-common/lib/types'
@ -69,7 +70,7 @@ export function getBalanceHistoryForAccount({
interval: DateInterval,
}): Array<BalanceHistoryDay> {
const unit = getDefaultUnitByCoinType(account.coinType)
const counterVals = counterValues[`${unit.code}-${counterValue}`].byDate
const counterVals = get(counterValues, `${unit.code}.${counterValue}`)
let lastBalance = getBalanceAtIntervalStart(account, interval)
return mapInterval(interval, date => {
let balance = 0
@ -81,11 +82,11 @@ export function getBalanceHistoryForAccount({
// if we don't have data on account balance for that day,
// we take the prev day
if (isUndefined(account.balanceByDay[date])) {
balance = lastBalance === null ? 0 : lastBalance / 10 ** unit.magnitude * counterVals[date]
balance = lastBalance === null ? 0 : lastBalance * counterVals[date]
} else {
const b = account.balanceByDay[date]
lastBalance = b
balance = b / 10 ** unit.magnitude * counterVals[date]
balance = b * counterVals[date]
}
if (isNaN(balance)) {

11
src/helpers/db.js

@ -5,7 +5,6 @@ import set from 'lodash/set'
import get from 'lodash/get'
import { serializeAccounts, deserializeAccounts } from 'reducers/accounts'
import { serializeCounterValues, deserializeCounterValues } from 'reducers/counterValues'
type DBKey = 'settings' | 'accounts' | 'counterValues'
@ -35,16 +34,6 @@ function middleware(type, key, data: any) {
}
}
if (key === 'counterValues') {
if (type === 'get') {
data = serializeCounterValues(data)
}
if (type === 'set') {
data = deserializeCounterValues(data)
}
}
return data
}

1
src/main/bridge.js

@ -1,5 +1,6 @@
// @flow
import 'babel-polyfill'
import { fork } from 'child_process'
import { BrowserWindow, ipcMain } from 'electron'
import objectPath from 'object-path'

24
src/main/counterValuesSync.js

@ -1,24 +1,14 @@
// @flow
import axios from 'axios'
import { fetchCurrentCounterValues } from '@ledgerhq/wallet-common/lib/api/countervalue'
type SendFunction = (type: string, data: *) => void
export default async (send: SendFunction, { counterValue, currencies }: Object) => {
const data = await axios
.get(
`https://min-api.cryptocompare.com/data/pricemulti?extraParams=ledger-test&fsyms=${currencies.join(
',',
)}&tsyms=${counterValue}`,
)
.then(({ data }) =>
currencies.reduce((result, code) => {
result.push({
symbol: `${code}-${counterValue}`,
value: data[code][counterValue],
})
return result
}, []),
)
send('counterValues.update', data)
try {
const data = await fetchCurrentCounterValues(currencies, counterValue)
send('counterValues.update', data)
} catch (err) {
console.error(err) // eslint-disable-line no-console
}
}

88
src/reducers/counterValues.js

@ -1,73 +1,41 @@
// @flow
import { handleActions } from 'redux-actions'
export type CounterValuesState = {
[string]: {
byDate: Object,
list: Array<[string, number]>,
},
}
import merge from 'lodash/merge'
import get from 'lodash/get'
import {
makeCalculateCounterValue,
makeReverseCounterValue,
formatCounterValueDay,
} from '@ledgerhq/wallet-common/lib/helpers/countervalue'
import type { CalculateCounterValue } from '@ledgerhq/wallet-common/lib/types'
import type { State } from 'reducers'
export type CounterValuesState = {}
const state: CounterValuesState = {}
const handlers = {
UPDATE_COUNTER_VALUES: (
state: CounterValuesState,
{ payload: counterValues }: { payload: CounterValuesState },
): CounterValuesState => ({
...state,
...counterValues,
}),
UPDATE_LAST_COUNTER_VALUE: (
state: CounterValuesState,
{ payload: { symbol, value } }: { payload: { symbol: string, value: number } },
): CounterValuesState => {
// We update only last value (newer)
if (state[symbol]) {
const [date] = state[symbol].list[0]
// [0] date, [1] value, only update value
state[symbol].list[0][1] = value
// Keep the same value for byDate object
state[symbol].byDate[date] = value
// Update reference for a proper update
return { ...state }
}
return state
},
UPDATE_COUNTER_VALUES: (state, { payload: counterValues }) => merge(state, counterValues),
}
export function getLastCounterValueBySymbol(
symbol: string,
state: { counterValues: CounterValuesState },
): number {
return state.counterValues[symbol].list[0][1]
}
export function serializeCounterValues(counterValues: Object) {
return Object.keys(counterValues).reduce((result, key) => {
const counterValue = counterValues[key].sort(([dateA], [dateB]) => (dateA < dateB ? 1 : -1))
result[key] = {
byDate: counterValue.reduce((r, [date, value]) => {
r[date] = value
return r
}, {}),
list: counterValue,
const getPairHistory = state => (coinTicker, fiat) => {
const byDate = get(state, `counterValues.${coinTicker}.${fiat}`)
return date => {
if (!byDate) {
return 0
}
return result
}, {})
if (!date) {
return byDate.latest || 0
}
return byDate[formatCounterValueDay(date)] || 0
}
}
export function deserializeCounterValues(counterValues: Object) {
return Object.keys(counterValues).reduce((result, key) => {
const counterValue = counterValues[key]
result[key] = counterValue.list
return result
}, {})
}
export const calculateCounterValueSelector = (state: State): CalculateCounterValue =>
makeCalculateCounterValue(getPairHistory(state))
export const reverseCounterValueSelector = (state: State): CalculateCounterValue =>
makeReverseCounterValue(getPairHistory(state))
export default handleActions(handlers, state)

8
src/reducers/settings.js

@ -1,6 +1,7 @@
// @flow
import { handleActions } from 'redux-actions'
import { getFiatUnit } from '@ledgerhq/currencies'
import get from 'lodash/get'
@ -34,9 +35,14 @@ const handlers: Object = {
export const hasPassword = (state: Object) =>
get(state.settings, 'password.state', defaultState.password.state)
export const getCounterValue = (state: Object) =>
export const getCounterValueCode = (state: Object) =>
get(state.settings, 'counterValue', defaultState.counterValue)
export const getCounterValueFiatUnit = (state: Object) => getFiatUnit(getCounterValueCode(state))
export const getLanguage = (state: Object) => get(state.settings, 'language', defaultState.language)
export const getOrderAccounts = (state: Object) =>
get(state.settings, 'orderAccounts', defaultState.orderAccounts)

4
src/renderer/createStore.js

@ -10,11 +10,11 @@ import db from 'middlewares/db'
import reducers from 'reducers'
export default (history: HashHistory) => {
export default (history: HashHistory, initialState: any) => {
const middlewares = [routerMiddleware(history), thunk, db]
const enhancers = compose(
applyMiddleware(...middlewares),
window.devToolsExtension ? window.devToolsExtension() : f => f, // eslint-disable-line
)
return createStore(reducers, undefined, enhancers)
return createStore(reducers, initialState, enhancers)
}

27
src/renderer/events.js

@ -3,17 +3,19 @@
import { ipcRenderer } from 'electron'
import objectPath from 'object-path'
import debug from 'debug'
import { getDefaultUnitByCoinType } from '@ledgerhq/currencies'
import uniqBy from 'lodash/uniqBy'
import { getFiatUnit } from '@ledgerhq/currencies'
import type { Account } from '@ledgerhq/wallet-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 { getAccounts, getAccountById } from 'reducers/accounts'
import { getCounterValue } from 'reducers/settings'
import { getCounterValueCode } from 'reducers/settings'
import { isLocked } from 'reducers/application'
import { setUpdateStatus } from 'reducers/update'
import { updateLastCounterValueBySymbol } from 'actions/counterValues'
import { updateCounterValues } from 'actions/counterValues'
import { updateAccount } from 'actions/accounts'
import { updateDevices, addDevice, removeDevice } from 'actions/devices'
@ -106,15 +108,11 @@ export function stopSyncAccounts() {
clearTimeout(syncAccountsTimeout)
}
export function startSyncCounterValues(counterValue: string, accounts: Account[]) {
export function startSyncCounterValues(counterValueCode: string, accounts: Account[]) {
d.sync('Sync counterValues - start')
sendEvent('msg', 'counterValues.sync', {
counterValue,
currencies: [
...new Set(accounts.map(account => getDefaultUnitByCoinType(account.coinType).code)),
],
})
const currencies: Currency[] = uniqBy(accounts.map(a => a.currency), 'code')
const counterValue: Unit = getFiatUnit(counterValueCode)
sendEvent('msg', 'counterValues.sync', { currencies, counterValue })
}
export function stopSyncCounterValues() {
@ -206,12 +204,11 @@ export default ({ store, locked }: { store: Object, locked: boolean }) => {
},
counterValues: {
update: counterValues => {
counterValues.map(c => store.dispatch(updateLastCounterValueBySymbol(c.symbol, c.value)))
store.dispatch(updateCounterValues(counterValues))
syncCounterValuesTimeout = setTimeout(() => {
const state = store.getState()
const accounts = getAccounts(state)
const counterValue = getCounterValue(state)
const counterValue = getCounterValueCode(state)
startSyncCounterValues(counterValue, accounts)
}, SYNC_COUNTER_VALUES_DELAY)
},
@ -245,7 +242,7 @@ export default ({ store, locked }: { store: Object, locked: boolean }) => {
if (!locked) {
const accounts = getAccounts(state)
const counterValue = getCounterValue(state)
const counterValue = getCounterValueCode(state)
startSyncCounterValues(counterValue, accounts)

1
src/renderer/index.js

@ -1,3 +1,4 @@
require('babel-polyfill')
const Raven = require('raven-js')
require('../env')

6
yarn.lock

@ -955,9 +955,9 @@
dependencies:
events "^2.0.0"
"@ledgerhq/wallet-common@^0.10.0":
version "0.10.0"
resolved "https://registry.yarnpkg.com/@ledgerhq/wallet-common/-/wallet-common-0.10.0.tgz#0275de0d0f73bdfb7aee7bbbaa324f2f3f993aee"
"@ledgerhq/wallet-common@^0.10.1":
version "0.10.1"
resolved "https://registry.yarnpkg.com/@ledgerhq/wallet-common/-/wallet-common-0.10.1.tgz#72e17329de02bebb5ed54bf797113266824e4af8"
dependencies:
"@ledgerhq/currencies" "^4.7.1"
axios "^0.18.0"

Loading…
Cancel
Save