diff --git a/.storybook/config.js b/.storybook/config.js
index 6ee5a24c..4cdff471 100644
--- a/.storybook/config.js
+++ b/.storybook/config.js
@@ -4,22 +4,30 @@ import { withKnobs } from '@storybook/addon-knobs'
import { setOptions } from '@storybook/addon-options'
import { ThemeProvider } from 'styled-components'
import { I18nextProvider } from 'react-i18next'
+import { Provider } from 'react-redux'
import 'globals'
import 'styles/global'
import theme from 'styles/theme'
import i18n from 'renderer/i18n/storybook'
+import createStore from 'renderer/createStore'
+
+import state from '__mocks__/storybook-state'
const req = require.context('../src', true, /.stories.js$/)
function loadStories() {
req.keys().forEach(filename => req(filename))
}
+const store = createStore({ state })
+
addDecorator(story => (
- {story()}
+
+ {story()}
+
))
diff --git a/flow-defs/globals.js b/flow-defs/globals.js
index c35461a8..5e37fd59 100644
--- a/flow-defs/globals.js
+++ b/flow-defs/globals.js
@@ -11,5 +11,7 @@ declare var __APP_VERSION__: string
declare var __static: string
declare var describe: Function
declare var test: Function
+declare var it: Function
+declare var expect: Function
declare var ResizeObserver: Class
diff --git a/src/__mocks__/render.js b/src/__mocks__/render.js
new file mode 100644
index 00000000..7b945e49
--- /dev/null
+++ b/src/__mocks__/render.js
@@ -0,0 +1,19 @@
+import React from 'react'
+import { Provider } from 'react-redux'
+import renderer from 'react-test-renderer'
+import { ThemeProvider } from 'styled-components'
+
+import createStore from 'renderer/createStore'
+
+import theme from 'styles/theme'
+
+export default function render(component, state) {
+ const store = createStore({ state })
+ return renderer
+ .create(
+
+ {component}
+ ,
+ )
+ .toJSON()
+}
diff --git a/src/__mocks__/storybook-state.js b/src/__mocks__/storybook-state.js
new file mode 100644
index 00000000..47e81bd5
--- /dev/null
+++ b/src/__mocks__/storybook-state.js
@@ -0,0 +1,15 @@
+export default {
+ counterValues: {
+ BTC: {
+ USD: {
+ '2018-01-09': 0.00795978,
+ '2018-03-29': 0.007106619999999999,
+ '2018-03-30': 0.0068537599999999995,
+ '2018-03-31': 0.00694377,
+ '2018-04-01': 0.00683584,
+ '2018-04-02': 0.007061689999999999,
+ latest: 0.00706156,
+ },
+ },
+ },
+}
diff --git a/src/__mocks__/withStore.js b/src/__mocks__/withStore.js
new file mode 100644
index 00000000..8d4da041
--- /dev/null
+++ b/src/__mocks__/withStore.js
@@ -0,0 +1,8 @@
+import React from 'react'
+import { Provider } from 'react-redux'
+import createStore from 'renderer/createStore'
+
+export default function withStore(state, component) {
+ const store = createStore({ state })
+ return {component}
+}
diff --git a/src/components/CounterValue/__tests__/CounterValue.test.js b/src/components/CounterValue/__tests__/CounterValue.test.js
new file mode 100644
index 00000000..8984c8f1
--- /dev/null
+++ b/src/components/CounterValue/__tests__/CounterValue.test.js
@@ -0,0 +1,63 @@
+// @flow
+
+import React from 'react'
+
+import render from '__mocks__/render'
+import CounterValue from '..'
+
+describe('components', () => {
+ describe('CounterValue', () => {
+ it('basic', () => {
+ const state = { counterValues: { BTC: { USD: { latest: 10e2 } } } }
+ const component =
+ const tree = render(component, state)
+ expect(tree).toMatchSnapshot()
+ })
+
+ it('specifying ticker different from default', () => {
+ const state = { counterValues: { LOL: { USD: { latest: 5e2 } } } }
+ const component =
+ const tree = render(component, state)
+ expect(tree).toMatchSnapshot()
+ })
+
+ it('using countervalue different from default', () => {
+ const state = {
+ counterValues: { BTC: { EUR: { latest: 42 } } },
+ settings: {
+ counterValue: 'EUR',
+ },
+ }
+ const component =
+ const tree = render(component, state)
+ expect(tree).toMatchSnapshot()
+ })
+
+ it('without countervalues populated', () => {
+ const state = { counterValues: {} }
+ const component =
+ const tree = render(component, state)
+ expect(tree).toMatchSnapshot()
+ })
+
+ it('with time travel whith date in countervalues', () => {
+ const state = { counterValues: { BTC: { USD: { '2018-01-01': 20e2 } } } }
+
+ const date = new Date('2018-01-01')
+ const component =
+ const tree = render(component, state)
+ expect(tree).toMatchSnapshot()
+ })
+
+ it('with time travel whith date not in countervalues', () => {
+ const state = { counterValues: { BTC: { USD: { '2018-01-01': 20e2 } } } }
+ const date = new Date('2018-01-02')
+ const component =
+ const tree = render(component, state)
+
+ // TODO: actually it returns 0 when date is not in countervalues
+ // do we want to use closest past value instead?
+ expect(tree).toMatchSnapshot()
+ })
+ })
+})
diff --git a/src/components/CounterValue/__tests__/__snapshots__/CounterValue.test.js.snap b/src/components/CounterValue/__tests__/__snapshots__/CounterValue.test.js.snap
new file mode 100644
index 00000000..ccd2f43e
--- /dev/null
+++ b/src/components/CounterValue/__tests__/__snapshots__/CounterValue.test.js.snap
@@ -0,0 +1,55 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`components CounterValue basic 1`] = `
+
+ + USD 10.00
+
+`;
+
+exports[`components CounterValue specifying ticker different from default 1`] = `
+
+ + USD 5.00
+
+`;
+
+exports[`components CounterValue using countervalue different from default 1`] = `
+
+ + EUR 0.42
+
+`;
+
+exports[`components CounterValue with time travel whith date in countervalues 1`] = `
+
+ + USD 20.00
+
+`;
+
+exports[`components CounterValue with time travel whith date not in countervalues 1`] = `
+
+ + USD 0.00
+
+`;
+
+exports[`components CounterValue without countervalues populated 1`] = `
+
+ + USD 0.00
+
+`;
diff --git a/src/components/CounterValue/index.js b/src/components/CounterValue/index.js
index d180a2bc..e865a8c5 100644
--- a/src/components/CounterValue/index.js
+++ b/src/components/CounterValue/index.js
@@ -2,57 +2,83 @@
import React, { PureComponent } from 'react'
import { connect } from 'react-redux'
-import moment from 'moment'
-import type { Unit, Currency } from '@ledgerhq/currencies'
+import { getFiatUnit } from '@ledgerhq/currencies'
import { getCounterValueCode } from 'reducers/settings'
import { calculateCounterValueSelector } from 'reducers/counterValues'
import FormattedVal from 'components/base/FormattedVal'
-const mapStateToProps = state => ({
- counterValueCode: getCounterValueCode(state),
- getCounterValue: calculateCounterValueSelector(state),
-})
-
type Props = {
- formatValue: boolean,
+ // wich market to query
+ ticker: string,
+
+ // the value :)
+ value: number,
+
+ // when? if not given: take latest
+ date?: Date,
+
+ // from reducers
counterValueCode: string,
getCounterValue: Function,
- time?: Date | string | number,
- unit: Unit,
- currency: Currency,
- value: number,
}
-export class CounterValue extends PureComponent {
+const mapStateToProps = (state, props) => {
+ const { ticker } = props
+
+ // TODO: in wallet-common, stop using currency.
+ // always use ticker and remove that hack
+ let { currency } = props
+ if (!currency && ticker) {
+ currency = generateFakeCurrency(ticker)
+ } else if (currency) {
+ console.warn('`currency` is deprecated in CounterValue. use `ticker` instead.') // eslint-disable-line no-console
+ }
+
+ const counterValueCode = getCounterValueCode(state)
+ const counterValueUnit = getFiatUnit(counterValueCode)
+ const getCounterValue = calculateCounterValueSelector(state)(currency, counterValueUnit)
+
+ return {
+ counterValueCode,
+ getCounterValue,
+ }
+}
+
+class CounterValue extends PureComponent {
static defaultProps = {
- formatValue: true,
value: 0,
- time: undefined,
+ date: undefined,
}
render() {
- 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 ? (
-
- ) : (
- v
+ const { getCounterValue, counterValueCode, date, value, ...props } = this.props
+ const counterValue = getCounterValue(value, date)
+ return (
+
)
}
}
+function generateFakeCurrency(ticker) {
+ return {
+ units: [
+ {
+ code: ticker,
+
+ // unused
+ name: 'fake-unit',
+ magnitude: 0,
+ },
+ ],
+
+ // unused
+ coinType: 0,
+ color: '#000',
+ name: 'fake-coin',
+ scheme: 'bitcoin',
+ }
+}
+
export default connect(mapStateToProps)(CounterValue)
diff --git a/src/components/CounterValue/stories.js b/src/components/CounterValue/stories.js
index caa79ee0..8b48de26 100644
--- a/src/components/CounterValue/stories.js
+++ b/src/components/CounterValue/stories.js
@@ -1,40 +1,16 @@
// @flow
import React from 'react'
-import { getCurrencyByCoinType, getDefaultUnitByCoinType } from '@ledgerhq/currencies'
+import { getCurrencyByCoinType } from '@ledgerhq/currencies'
import { storiesOf } from '@storybook/react'
-import { boolean, text } from '@storybook/addon-knobs'
-import createHistory from 'history/createHashHistory'
+import { number } from '@storybook/addon-knobs'
-import { CounterValue } from 'components/CounterValue'
-import { calculateCounterValueSelector } from 'reducers/counterValues'
-import createStore from 'renderer/createStore'
+import CounterValue from 'components/CounterValue'
const stories = storiesOf('Components', module)
const currency = getCurrencyByCoinType(0)
-const unit = getDefaultUnitByCoinType(0)
-
-const counterValue = 'USD'
-const counterValues = {
- BTC: {
- USD: {
- '2018-01-09': 10000,
- },
- },
-}
-
-const store = createStore(createHistory(), { counterValues })
-const getCounterValue = calculateCounterValueSelector(store.getState())
stories.add('CounterValue', () => (
-
+
))
diff --git a/src/components/OperationsList/index.js b/src/components/OperationsList/index.js
index f9282fd0..3d8103d1 100644
--- a/src/components/OperationsList/index.js
+++ b/src/components/OperationsList/index.js
@@ -192,9 +192,8 @@ const Operation = ({
diff --git a/src/components/OperationsList/stories.js b/src/components/OperationsList/stories.js
index 5746c2cc..ebe1f11b 100644
--- a/src/components/OperationsList/stories.js
+++ b/src/components/OperationsList/stories.js
@@ -4,11 +4,10 @@ import React from 'react'
import { getCurrencyByCoinType, getDefaultUnitByCoinType } from '@ledgerhq/currencies'
import { storiesOf } from '@storybook/react'
import { boolean } from '@storybook/addon-knobs'
-import { translate } from 'react-i18next'
import { accounts } from 'components/SelectAccount/stories'
-import { OperationsList } from 'components/OperationsList'
+import OperationsList from 'components/OperationsList'
const stories = storiesOf('Components', module)
@@ -23,20 +22,12 @@ const account = ({ name }) => ({
unit,
})
-const counterValue = 'USD'
-const counterValues = {
- 'BTC-USD': {
- byDate: {
- '2018-01-09': 10000,
- },
- },
-}
-
const operations = [
{
address: '5c6ea1716520c7d6e038d36a3223faced3c',
hash: '5c6ea1716520c7d6e038d36a3223faced3c4b8f7ffb69d9fb5bd527d562fdb62',
- amount: 130000000,
+ id: '5c6ea1716520c7d6e038d36a3223faced3c4b8f7ffb69d9fb5bd527d562fdb62',
+ amount: 1.3e8,
date: new Date('2018-01-09T16:03:52Z'),
confirmations: 1,
account: account({
@@ -45,8 +36,9 @@ const operations = [
},
{
address: '5c6ea1716520c7d6e038d36a3223faced3c',
- hash: '5c6ea1716520c7d6e038d36a3223faced3c4b8f7ffb69d9fb5bd527d562fdb62',
- amount: 130000000,
+ hash: '26bdf265d725db5bf9d96bff7f8b4c3decaf3223a63d830e6d7c0256171ae6c5',
+ id: '26bdf265d725db5bf9d96bff7f8b4c3decaf3223a63d830e6d7c0256171ae6c5',
+ amount: 1.6e8,
date: new Date('2018-01-09T16:03:52Z'),
confirmations: 11,
account: account({
@@ -56,7 +48,8 @@ const operations = [
{
address: '27416a48caab90fab053b507b8b6b9d4',
hash: '27416a48caab90fab053b507b8b6b9d48fba75421d3bfdbae4b85f64024bc9c4',
- amount: -65000000,
+ id: '27416a48caab90fab053b507b8b6b9d48fba75421d3bfdbae4b85f64024bc9c4',
+ amount: -6.5e8,
date: new Date('2018-01-09T16:02:40Z'),
confirmations: 11,
account: account({
@@ -65,8 +58,9 @@ const operations = [
},
{
address: '27416a48caab90fab053b507b8b6b9d4',
- hash: '27416a48caab90fab053b507b8b6b9d48fba75421d3bfdbae4b85f64024bc9c4',
- amount: -65000000,
+ hash: '4c9cb42046f58b4eabdfb3d12457abf84d9b6b8b705b350baf09baac84a61472',
+ id: '4c9cb42046f58b4eabdfb3d12457abf84d9b6b8b705b350baf09baac84a61472',
+ amount: -4.2e8,
date: new Date('2018-01-09T16:02:40Z'),
confirmations: 1,
account: account({
@@ -75,12 +69,8 @@ const operations = [
},
]
-const OperationsListComp = translate()(OperationsList)
-
stories.add('OperationsList', () => (
- void,
+ onChange: number => void,
// used to determine the left input unit
account: Account,
@@ -66,83 +66,40 @@ type Props = {
getReverseCounterValue: CalculateCounterValue,
}
-type State = {
- leftUnit: Unit,
- rightUnit: Unit,
- leftValue: number,
- rightValue: number,
-}
-
-export class RequestAmount extends PureComponent {
- constructor(props: Props) {
- super(props)
-
- const { account, rightUnit, value, getCounterValue } = this.props
-
- const rawLeftValue = value * 10 ** account.unit.magnitude
- const rawRightValue = getCounterValue(account.currency, rightUnit)(rawLeftValue)
- const rightValue = rawRightValue / 10 ** rightUnit.magnitude
-
- this.state = {
- leftUnit: account.unit,
- rightUnit,
- leftValue: value,
- rightValue,
- }
- }
-
+export class RequestAmount extends PureComponent {
handleClickMax = () => {
- const leftValue = this.props.max / 10 ** this.props.account.unit.magnitude
- this.handleChangeAmount('left')(leftValue)
- this.setState({ leftValue })
+ this.props.onChange(this.props.max)
}
handleChangeAmount = (changedField: string) => (val: number) => {
- const { getCounterValue, getReverseCounterValue, account, max, onChange } = this.props
- const { rightUnit } = this.state
+ const { rightUnit, getReverseCounterValue, account, max, onChange } = this.props
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 })
+ onChange(val > max ? max : val)
} 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 })
+ const leftVal = getReverseCounterValue(account.currency, rightUnit)(val)
+ onChange(leftVal > max ? max : leftVal)
}
}
render() {
- const { t } = this.props
- const { leftUnit, rightUnit, leftValue, rightValue } = this.state
+ const { t, value, account, rightUnit, getCounterValue } = this.props
+ const right = getCounterValue(account.currency, rightUnit)(value)
return (
{leftUnit.code}}
+ renderRight={{account.unit.code}}
/>
=
{rightUnit.code}}
/>
diff --git a/src/components/RequestAmount/stories.js b/src/components/RequestAmount/stories.js
index 2e08839a..d4aa88a4 100644
--- a/src/components/RequestAmount/stories.js
+++ b/src/components/RequestAmount/stories.js
@@ -1,33 +1,39 @@
// @flow
-import React from 'react'
+import React, { PureComponent } from 'react'
import { storiesOf } from '@storybook/react'
import { action } from '@storybook/addon-actions'
-import { number } from '@storybook/addon-knobs'
-import { translate } from 'react-i18next'
import { accounts } from 'components/SelectAccount/stories'
-import { RequestAmount } from 'components/RequestAmount'
+import RequestAmount from 'components/RequestAmount'
const stories = storiesOf('Components', module)
-const props = {
- counterValue: 'USD',
- lastCounterValue: 9177.69,
- account: accounts[0],
+type State = {
+ value: number,
}
-const RequestAmountComp = translate()(RequestAmount)
+class Wrapper extends PureComponent {
+ state = {
+ value: 3e8,
+ }
+ handleChange = value => {
+ action('onChange')(value)
+ this.setState({ value })
+ }
+ render() {
+ const { value } = this.state
+ return (
+
+ )
+ }
+}
-stories.add('RequestAmount', () => (
- k}
- onChange={action('onChange')}
- value={{
- left: number('left value', 0),
- right: number('right value', 0),
- }}
- />
-))
+stories.add('RequestAmount', () => )
diff --git a/src/components/base/FormattedVal/__tests__/FormattedVal.test.js b/src/components/base/FormattedVal/__tests__/FormattedVal.test.js
index ca6d032f..4510fac3 100644
--- a/src/components/base/FormattedVal/__tests__/FormattedVal.test.js
+++ b/src/components/base/FormattedVal/__tests__/FormattedVal.test.js
@@ -1,7 +1,7 @@
import React from 'react'
import { getDefaultUnitByCoinType } from '@ledgerhq/currencies'
-import { render } from 'test-utils'
+import render from '__mocks__/render'
import FormattedVal from '..'
const bitcoinUnit = getDefaultUnitByCoinType(0)
diff --git a/src/components/base/InputCurrency/index.js b/src/components/base/InputCurrency/index.js
index 90242709..dde49cd6 100644
--- a/src/components/base/InputCurrency/index.js
+++ b/src/components/base/InputCurrency/index.js
@@ -17,10 +17,8 @@ function parseValue(value) {
return value.toString().replace(/,/g, '.')
}
-function format(unit: Unit, value: Value) {
- let v = value === '' ? 0 : Number(value)
- v *= 10 ** unit.magnitude
- return formatCurrencyUnit(unit, v, {
+function format(unit: Unit, value: number) {
+ return formatCurrencyUnit(unit, value, {
disableRounding: true,
showAllDigits: false,
})
@@ -28,13 +26,13 @@ function format(unit: Unit, value: Value) {
function unformat(unit, value) {
if (value === 0 || value === '') {
- return 0
+ return '0'
}
let v = parseCurrencyUnit(unit, value.toString())
v /= 10 ** unit.magnitude
- return v
+ return v.toString()
}
const Currencies = styled(Box)`
@@ -50,19 +48,18 @@ const Currency = styled(Box).attrs({
pr: 1,
})``
-type Value = string | number
-
type Props = {
onChange: Function,
renderRight: any,
unit: Unit,
units: Array,
- value: Value,
+ value: number,
}
type State = {
+ unit: Unit,
isFocus: boolean,
- value: Value,
+ displayValue: string,
}
class InputCurrency extends PureComponent {
@@ -75,66 +72,74 @@ class InputCurrency extends PureComponent {
state = {
isFocus: false,
- value: this.props.value,
+ displayValue: '0',
+ unit: this.props.unit,
+ }
+
+ componentWillMount() {
+ const { value } = this.props
+ const { unit } = this.state
+ const displayValue = format(unit, value)
+ this.setState({ displayValue })
}
componentWillReceiveProps(nextProps: Props) {
+ const { unit } = this.state
if (this.props.value !== nextProps.value) {
const { isFocus } = this.state
- const value = isFocus ? nextProps.value : format(nextProps.unit, nextProps.value)
- this.setState({
- value,
- })
+ const displayValue = isFocus
+ ? (nextProps.value / 10 ** unit.magnitude).toString()
+ : format(unit, nextProps.value)
+ this.setState({ displayValue })
}
}
- handleChange = (v: Value) => {
+ handleChange = (v: string) => {
+ // const { displayValue } = this.state
v = parseValue(v)
+ if (v.startsWith('00')) {
+ return
+ }
+
// Check if value is valid Number
if (isNaN(Number(v))) {
return
}
this.emitOnChange(v)
- this.setState({
- value: v,
- })
+ this.setState({ displayValue: v || '0' })
}
handleBlur = () => {
- const { unit } = this.props
- const { value } = this.state
-
+ const { value } = this.props
+ const { unit } = this.state
const v = format(unit, value)
-
- this.setState({
- isFocus: false,
- value: v,
- })
+ this.setState({ isFocus: false, displayValue: v })
}
handleFocus = () => {
- const { unit } = this.props
+ const { unit } = this.state
this.setState(prev => ({
isFocus: true,
- value: unformat(unit, prev.value),
+ displayValue: unformat(unit, prev.displayValue),
}))
}
- emitOnChange = (v: Value) => {
- const { onChange, unit } = this.props
- const { value } = this.state
+ emitOnChange = (v: string) => {
+ const { onChange } = this.props
+ const { displayValue, unit } = this.state
- if (value.toString() !== v.toString()) {
- onChange(v.toString(), unit)
+ if (displayValue.toString() !== v.toString()) {
+ const satoshiValue = Number(v) * 10 ** unit.magnitude
+ onChange(satoshiValue, unit)
}
}
renderListUnits = () => {
- const { unit, units, onChange } = this.props
- const { value } = this.state
+ const { units, value } = this.props
+ const { unit } = this.state
if (units.length <= 1) {
return null
@@ -146,7 +151,10 @@ class InputCurrency extends PureComponent {
bg="lightGraphite"
keyProp="code"
flatLeft
- onChange={item => onChange(unformat(item, value), item)}
+ onChange={item => {
+ this.setState({ unit: item, displayValue: format(item, value) })
+ // onChange(unformat(item, value), item)
+ }}
items={units}
value={unit}
renderItem={item => item.code}
@@ -158,13 +166,13 @@ class InputCurrency extends PureComponent {
render() {
const { renderRight } = this.props
- const { value } = this.state
+ const { displayValue } = this.state
return (
{
state = {
- value: 0,
+ value: 1e8,
unit: units[0],
}
- handleChange = (value, unit) => this.setState({ value, unit })
+ handleChange = (value, unit) => {
+ action('onChange')(value, unit)
+ this.setState({ value, unit })
+ }
render() {
const { render } = this.props
diff --git a/src/components/modals/OperationDetails.js b/src/components/modals/OperationDetails.js
index f9f1840c..1c003c95 100644
--- a/src/components/modals/OperationDetails.js
+++ b/src/components/modals/OperationDetails.js
@@ -90,9 +90,8 @@ const OperationDetails = ({ t }: { t: T }) => (
color="grey"
fontSize={5}
style={{ lineHeight: 1 }}
- time={date}
- unit={unit}
- currency={currency}
+ date={date}
+ ticker={currency.units[0].code}
value={amount}
/>
diff --git a/src/components/modals/Send/01-step-amount.js b/src/components/modals/Send/01-step-amount.js
index 1e47ca11..032f96a5 100644
--- a/src/components/modals/Send/01-step-amount.js
+++ b/src/components/modals/Send/01-step-amount.js
@@ -3,7 +3,6 @@
import React, { Fragment } from 'react'
import type { Account } from '@ledgerhq/wallet-common/lib/types'
-import type { Unit } from '@ledgerhq/currencies'
import type { T } from 'types/common'
import Box from 'components/base/Box'
@@ -22,11 +21,8 @@ type PropsStepAmount = {
account: Account | null,
onChange: Function,
recipientAddress: string,
- amount: { left: number, right: number },
- fees: {
- value: number,
- unit: Unit | null,
- },
+ amount: number,
+ fees: number,
isRBF: boolean,
t: T,
}
@@ -61,10 +57,10 @@ function StepAmount(props: PropsStepAmount) {
@@ -75,12 +71,7 @@ function StepAmount(props: PropsStepAmount) {
- onChange('fees')({ value, unit })}
- />
+ onChange('fees')(value)} />
@@ -121,11 +112,10 @@ type PropsFees = {
account: Account,
amount: number,
onChange: Function,
- unit: Unit | null,
}
function Fees(props: PropsFees) {
- const { onChange, account, unit, amount } = props
+ const { onChange, account, amount } = props
const { units } = account.currency
return (
@@ -135,11 +125,11 @@ function Fees(props: PropsFees) {
items={[{ key: 'custom', name: 'Custom' }]}
value={{ key: 'custom', name: 'Custom' }}
renderSelected={item => item.name}
- onChange={() => onChange(amount, unit)}
+ onChange={() => onChange(amount)}
/>
@@ -30,7 +31,7 @@ function Footer({ account, amount, t, onNext, canNext, counterValue }: Props) {
@@ -38,12 +39,12 @@ function Footer({ account, amount, t, onNext, canNext, counterValue }: Props) {
{'('}
-
diff --git a/src/components/modals/Send/index.js b/src/components/modals/Send/index.js
index 4638f1d4..06215b9f 100644
--- a/src/components/modals/Send/index.js
+++ b/src/components/modals/Send/index.js
@@ -1,19 +1,14 @@
// @flow
import React, { PureComponent } from 'react'
-import { compose } from 'redux'
-import { connect } from 'react-redux'
import { translate } from 'react-i18next'
import get from 'lodash/get'
import type { Account } from '@ledgerhq/wallet-common/lib/types'
-import type { Unit } from '@ledgerhq/currencies'
import type { T } from 'types/common'
import { MODAL_SEND } from 'config/constants'
-import { getCounterValueCode } from 'reducers/settings'
-
import Breadcrumb from 'components/Breadcrumb'
import Modal, { ModalBody, ModalTitle, ModalContent } from 'components/base/Modal'
@@ -24,25 +19,17 @@ import StepConnectDevice from './02-step-connect-device'
import StepVerification from './03-step-verification'
import StepConfirmation from './04-step-confirmation'
-const mapStateToProps = state => ({
- counterValue: getCounterValueCode(state),
-})
-
type Props = {
t: T,
- counterValue: string,
}
type State = {
stepIndex: number,
isDeviceReady: boolean,
- amount: { left: number, right: number },
+ amount: number,
+ fees: number,
account: Account | null,
recipientAddress: string,
- fees: {
- value: number,
- unit: Unit | null,
- },
isRBF: boolean,
}
@@ -58,14 +45,8 @@ const INITIAL_STATE = {
isDeviceReady: false,
account: null,
recipientAddress: '',
- amount: {
- left: 0,
- right: 0,
- },
- fees: {
- value: 0,
- unit: null,
- },
+ amount: 0,
+ fees: 0,
isRBF: false,
}
@@ -73,6 +54,7 @@ class SendModal extends PureComponent {
state = INITIAL_STATE
_steps = GET_STEPS(this.props.t)
+ _account: Account | null = null
canNext = account => {
const { stepIndex } = this.state
@@ -80,7 +62,7 @@ class SendModal extends PureComponent {
// informations
if (stepIndex === 0) {
const { amount, recipientAddress } = this.state
- return !!amount.left && !!recipientAddress && !!account
+ return !!amount && !!recipientAddress && !!account
}
// connect device
@@ -102,7 +84,25 @@ class SendModal extends PureComponent {
this.setState({ stepIndex: stepIndex + 1 })
}
- createChangeHandler = key => value => this.setState({ [key]: value })
+ createChangeHandler = key => value => {
+ const patch = { [key]: value }
+ // ensure max is always restecped when changing fees
+ if (key === 'fees') {
+ const { amount } = this.state
+ // if changing fees goes further than max, change amount
+ if (this._account && amount + value > this._account.balance) {
+ const diff = amount + value - this._account.balance
+ patch.amount = amount - diff
+ // if the user is a little joker, and try to put fees superior
+ // to the max, let's reset amount to 0 and put fees to max.
+ if (patch.amount < 0) {
+ patch.amount = 0
+ patch.fees = this._account.balance
+ }
+ }
+ }
+ this.setState(patch)
+ }
renderStep = acc => {
const { stepIndex, account, amount, ...othersState } = this.state
@@ -121,8 +121,8 @@ class SendModal extends PureComponent {
}
render() {
- const { t, counterValue } = this.props
- const { stepIndex, amount, account } = this.state
+ const { t } = this.props
+ const { stepIndex, amount, account, fees } = this.state
return (
{
render={({ data, onClose }) => {
const acc = account || get(data, 'account', null)
const canNext = this.canNext(acc)
+
+ // hack: access the selected account, living in modal data, outside
+ // of the modal render function
+ this._account = acc
+
return (
{t('send:title')}
@@ -140,11 +145,11 @@ class SendModal extends PureComponent {
{acc && (
)}
@@ -156,4 +161,4 @@ class SendModal extends PureComponent {
}
}
-export default compose(connect(mapStateToProps), translate())(SendModal)
+export default translate()(SendModal)
diff --git a/src/helpers/balance.js b/src/helpers/balance.js
index a211002e..340c5be2 100644
--- a/src/helpers/balance.js
+++ b/src/helpers/balance.js
@@ -69,6 +69,7 @@ export function getBalanceHistoryForAccount({
counterValues: Object,
interval: DateInterval,
}): Array {
+ const todayDate = moment().format('YYYY-MM-DD')
const unit = getDefaultUnitByCoinType(account.coinType)
const counterVals = get(counterValues, `${unit.code}.${counterValue}`)
let lastBalance = getBalanceAtIntervalStart(account, interval)
@@ -79,14 +80,19 @@ export function getBalanceHistoryForAccount({
return { balance, date }
}
+ const isToday = date === todayDate
+ const counterVal = isToday
+ ? counterVals.latest || counterVals[date] || 0
+ : counterVals[date] || 0
+
// 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 * counterVals[date]
+ balance = lastBalance === null ? 0 : lastBalance * counterVal
} else {
const b = account.balanceByDay[date]
lastBalance = b
- balance = b * counterVals[date]
+ balance = b * counterVal
}
if (isNaN(balance)) {
diff --git a/src/reducers/counterValues.js b/src/reducers/counterValues.js
index c662c04f..612a3f9e 100644
--- a/src/reducers/counterValues.js
+++ b/src/reducers/counterValues.js
@@ -16,7 +16,7 @@ export type CounterValuesState = {}
const state: CounterValuesState = {}
const handlers = {
- UPDATE_COUNTER_VALUES: (state, { payload: counterValues }) => merge(state, counterValues),
+ UPDATE_COUNTER_VALUES: (state, { payload: counterValues }) => merge({ ...state }, counterValues),
}
const getPairHistory = state => (coinTicker, fiat) => {
diff --git a/src/renderer/createStore.js b/src/renderer/createStore.js
index 18d11a7d..bd960576 100644
--- a/src/renderer/createStore.js
+++ b/src/renderer/createStore.js
@@ -1,20 +1,31 @@
// @flow
-import type { HashHistory } from 'history'
-
import { createStore, applyMiddleware, compose } from 'redux'
import { routerMiddleware } from 'react-router-redux'
import thunk from 'redux-thunk'
-
-import db from 'middlewares/db'
+import createHistory from 'history/createHashHistory'
+import type { HashHistory } from 'history'
import reducers from 'reducers'
-export default (history: HashHistory, initialState: any) => {
- const middlewares = [routerMiddleware(history), thunk, db]
+type Props = {
+ history: HashHistory,
+ state?: Object,
+ history?: any,
+ dbMiddleware?: Function,
+}
+
+export default ({ state, history, dbMiddleware }: Props) => {
+ if (!history) {
+ history = createHistory()
+ }
+ const middlewares = [routerMiddleware(history), thunk]
+ if (dbMiddleware) {
+ middlewares.push(dbMiddleware)
+ }
const enhancers = compose(
applyMiddleware(...middlewares),
window.devToolsExtension ? window.devToolsExtension() : f => f, // eslint-disable-line
)
- return createStore(reducers, initialState, enhancers)
+ return createStore(reducers, state, enhancers)
}
diff --git a/src/renderer/init.js b/src/renderer/init.js
index 9be31456..f7dae0a8 100644
--- a/src/renderer/init.js
+++ b/src/renderer/init.js
@@ -16,6 +16,7 @@ import { isLocked } from 'reducers/application'
import { getLanguage } from 'reducers/settings'
import db from 'helpers/db'
+import dbMiddleware from 'middlewares/db'
import App from 'components/App'
@@ -26,7 +27,7 @@ db.init('settings', {})
db.init('counterValues', {})
const history = createHistory()
-const store = createStore(history)
+const store = createStore({ history, dbMiddleware })
const rootNode = document.getElementById('app')
store.dispatch(fetchSettings())
diff --git a/src/test-utils.js b/src/test-utils.js
deleted file mode 100644
index 3288c903..00000000
--- a/src/test-utils.js
+++ /dev/null
@@ -1,9 +0,0 @@
-import React from 'react'
-import renderer from 'react-test-renderer'
-import { ThemeProvider } from 'styled-components'
-
-import theme from 'styles/theme'
-
-export function render(component) {
- return renderer.create({component}).toJSON()
-}