Browse Source

Finally get rid of magnitude manipulation

master
meriadec 7 years ago
parent
commit
cbd607c223
No known key found for this signature in database GPG Key ID: 1D2FC2305E2CB399
  1. 77
      src/components/RequestAmount/index.js
  2. 39
      src/components/RequestAmount/stories.js
  3. 71
      src/components/base/InputCurrency/index.js
  4. 18
      src/components/base/InputCurrency/stories.js
  5. 4
      src/components/modals/Send/01-step-amount.js
  6. 20
      src/components/modals/Send/Footer.js
  7. 23
      src/components/modals/Send/index.js

77
src/components/RequestAmount/index.js

@ -52,7 +52,7 @@ type Props = {
max: number, max: number,
// change handler // change handler
onChange: ({ left: number, right: number }) => void, onChange: number => void,
// used to determine the left input unit // used to determine the left input unit
account: Account, account: Account,
@ -66,91 +66,40 @@ type Props = {
getReverseCounterValue: CalculateCounterValue, getReverseCounterValue: CalculateCounterValue,
} }
type State = { export class RequestAmount extends PureComponent<Props> {
leftUnit: Unit,
rightUnit: Unit,
leftValue: number,
rightValue: number,
}
export class RequestAmount extends PureComponent<Props, State> {
constructor(props: Props) {
super(props)
const { account, rightUnit, value, getCounterValue } = this.props
// @TODO forced to do those horrible and redondant calculations in order
// to make `getCounterValue` works. `getCounterValue` should take ticker code,
// and the both units in parameter. Not "a currency and a unit".
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,
}
}
handleClickMax = () => { handleClickMax = () => {
const leftValue = this.props.max / 10 ** this.props.account.unit.magnitude this.props.onChange(this.props.max)
this.handleChangeAmount('left')(leftValue)
this.setState({ leftValue })
} }
handleChangeAmount = (changedField: string) => (val: number) => { handleChangeAmount = (changedField: string) => (val: number) => {
const { getCounterValue, getReverseCounterValue, account, max, onChange } = this.props const { rightUnit, getReverseCounterValue, account, max, onChange } = this.props
const { rightUnit } = this.state
// @TODO forced to do those horrible and redondant calculations in order
// to make `getCounterValue` works. `getCounterValue` should take ticker code,
// and the both units in parameter. Not "a currency and a unit".
if (changedField === 'left') { if (changedField === 'left') {
let rawLeftValue = val * 10 ** account.unit.magnitude onChange(val > max ? max : val)
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') { } else if (changedField === 'right') {
let rawRightValue = val * 10 ** rightUnit.magnitude const leftVal = getReverseCounterValue(account.currency, rightUnit)(val)
let rawLeftValue = getReverseCounterValue(account.currency, rightUnit)(rawRightValue) onChange(leftVal > max ? max : leftVal)
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() { render() {
const { t } = this.props const { t, value, account, rightUnit, getCounterValue } = this.props
const { leftUnit, rightUnit, leftValue, rightValue } = this.state const right = getCounterValue(account.currency, rightUnit)(value)
return ( return (
<Box horizontal flow="5"> <Box horizontal flow="5">
<Box horizontal align="center"> <Box horizontal align="center">
<InputCurrency <InputCurrency
containerProps={{ style: { width: 156 } }} containerProps={{ style: { width: 156 } }}
unit={leftUnit} unit={account.unit}
value={leftValue} value={value}
onChange={this.handleChangeAmount('left')} onChange={this.handleChangeAmount('left')}
renderRight={<InputRight>{leftUnit.code}</InputRight>} renderRight={<InputRight>{account.unit.code}</InputRight>}
/> />
<InputCenter>=</InputCenter> <InputCenter>=</InputCenter>
<InputCurrency <InputCurrency
containerProps={{ style: { width: 156 } }} containerProps={{ style: { width: 156 } }}
unit={rightUnit} unit={rightUnit}
value={rightValue} value={right}
onChange={this.handleChangeAmount('right')} onChange={this.handleChangeAmount('right')}
renderRight={<InputRight>{rightUnit.code}</InputRight>} renderRight={<InputRight>{rightUnit.code}</InputRight>}
/> />

39
src/components/RequestAmount/stories.js

@ -1,6 +1,6 @@
// @flow // @flow
import React from 'react' import React, { PureComponent } from 'react'
import { storiesOf } from '@storybook/react' import { storiesOf } from '@storybook/react'
import { action } from '@storybook/addon-actions' import { action } from '@storybook/addon-actions'
@ -10,13 +10,30 @@ import RequestAmount from 'components/RequestAmount'
const stories = storiesOf('Components', module) const stories = storiesOf('Components', module)
stories.add('RequestAmount', () => ( type State = {
<RequestAmount value: number,
t={k => k} }
counterValue="USD"
account={accounts[0]} class Wrapper extends PureComponent<any, State> {
onChange={action('onChange')} state = {
value={3} value: 3e8,
max={400000000000} }
/> handleChange = value => {
)) action('onChange')(value)
this.setState({ value })
}
render() {
const { value } = this.state
return (
<RequestAmount
counterValue="USD"
account={accounts[0]}
onChange={this.handleChange}
value={value}
max={4e8}
/>
)
}
}
stories.add('RequestAmount', () => <Wrapper />)

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

@ -17,10 +17,8 @@ function parseValue(value) {
return value.toString().replace(/,/g, '.') return value.toString().replace(/,/g, '.')
} }
function format(unit: Unit, value: Value) { function format(unit: Unit, value: number) {
let v = value === '' ? 0 : Number(value) return formatCurrencyUnit(unit, value, {
v *= 10 ** unit.magnitude
return formatCurrencyUnit(unit, v, {
disableRounding: true, disableRounding: true,
showAllDigits: false, showAllDigits: false,
}) })
@ -28,13 +26,13 @@ function format(unit: Unit, value: Value) {
function unformat(unit, value) { function unformat(unit, value) {
if (value === 0 || value === '') { if (value === 0 || value === '') {
return 0 return '0'
} }
let v = parseCurrencyUnit(unit, value.toString()) let v = parseCurrencyUnit(unit, value.toString())
v /= 10 ** unit.magnitude v /= 10 ** unit.magnitude
return v return v.toString()
} }
const Currencies = styled(Box)` const Currencies = styled(Box)`
@ -50,19 +48,17 @@ const Currency = styled(Box).attrs({
pr: 1, pr: 1,
})`` })``
type Value = string | number
type Props = { type Props = {
onChange: Function, onChange: Function,
renderRight: any, renderRight: any,
unit: Unit, unit: Unit,
units: Array<Unit>, units: Array<Unit>,
value: Value, value: number,
} }
type State = { type State = {
isFocus: boolean, isFocus: boolean,
value: Value, displayValue: string,
} }
class InputCurrency extends PureComponent<Props, State> { class InputCurrency extends PureComponent<Props, State> {
@ -75,43 +71,46 @@ class InputCurrency extends PureComponent<Props, State> {
state = { state = {
isFocus: false, isFocus: false,
value: this.props.value, displayValue: '0',
}
componentWillMount() {
const { value, unit } = this.props
const displayValue = format(unit, value)
this.setState({ displayValue })
} }
componentWillReceiveProps(nextProps: Props) { componentWillReceiveProps(nextProps: Props) {
if (this.props.value !== nextProps.value) { if (this.props.value !== nextProps.value) {
const { isFocus } = this.state const { isFocus } = this.state
const value = isFocus ? nextProps.value : format(nextProps.unit, nextProps.value) const displayValue = isFocus
this.setState({ ? (nextProps.value / 10 ** nextProps.unit.magnitude).toString()
value, : format(nextProps.unit, nextProps.value)
}) this.setState({ displayValue })
} }
} }
handleChange = (v: Value) => { handleChange = (v: string) => {
// const { displayValue } = this.state
v = parseValue(v) v = parseValue(v)
if (v.startsWith('00')) {
return
}
// Check if value is valid Number // Check if value is valid Number
if (isNaN(Number(v))) { if (isNaN(Number(v))) {
return return
} }
this.emitOnChange(v) this.emitOnChange(v)
this.setState({ this.setState({ displayValue: v || '0' })
value: v,
})
} }
handleBlur = () => { handleBlur = () => {
const { unit } = this.props const { unit, value } = this.props
const { value } = this.state
const v = format(unit, value) const v = format(unit, value)
this.setState({ isFocus: false, displayValue: v })
this.setState({
isFocus: false,
value: v,
})
} }
handleFocus = () => { handleFocus = () => {
@ -119,22 +118,22 @@ class InputCurrency extends PureComponent<Props, State> {
this.setState(prev => ({ this.setState(prev => ({
isFocus: true, isFocus: true,
value: unformat(unit, prev.value), displayValue: unformat(unit, prev.displayValue),
})) }))
} }
emitOnChange = (v: Value) => { emitOnChange = (v: string) => {
const { onChange, unit } = this.props const { onChange, unit } = this.props
const { value } = this.state const { displayValue } = this.state
if (value.toString() !== v.toString()) { if (displayValue.toString() !== v.toString()) {
onChange(Number(v), unit) const satoshiValue = Number(v) * 10 ** unit.magnitude
onChange(satoshiValue, unit)
} }
} }
renderListUnits = () => { renderListUnits = () => {
const { unit, units, onChange } = this.props const { unit, units, onChange, value } = this.props
const { value } = this.state
if (units.length <= 1) { if (units.length <= 1) {
return null return null
@ -158,13 +157,13 @@ class InputCurrency extends PureComponent<Props, State> {
render() { render() {
const { renderRight } = this.props const { renderRight } = this.props
const { value } = this.state const { displayValue } = this.state
return ( return (
<Input <Input
{...this.props} {...this.props}
ff="Rubik" ff="Rubik"
value={value} value={displayValue}
onChange={this.handleChange} onChange={this.handleChange}
onFocus={this.handleFocus} onFocus={this.handleFocus}
onBlur={this.handleBlur} onBlur={this.handleBlur}

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

@ -2,28 +2,26 @@
import React, { Component } from 'react' import React, { Component } from 'react'
import { storiesOf } from '@storybook/react' import { storiesOf } from '@storybook/react'
import { action } from '@storybook/addon-actions'
import { getDefaultUnitByCoinType, getFiatUnit } from '@ledgerhq/currencies' import { getCurrencyByCoinType } from '@ledgerhq/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 = [ const { units } = getCurrencyByCoinType(1)
getDefaultUnitByCoinType(1),
getDefaultUnitByCoinType(2),
getDefaultUnitByCoinType(3),
getDefaultUnitByCoinType(6),
getFiatUnit('USD'),
]
class Wrapper extends Component<any, any> { class Wrapper extends Component<any, any> {
state = { state = {
value: 0, value: 1e8,
unit: units[0], unit: units[0],
} }
handleChange = (value, unit) => this.setState({ value, unit }) handleChange = (value, unit) => {
action('onChange')(value, unit)
this.setState({ value, unit })
}
render() { render() {
const { render } = this.props const { render } = this.props

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

@ -22,7 +22,7 @@ type PropsStepAmount = {
account: Account | null, account: Account | null,
onChange: Function, onChange: Function,
recipientAddress: string, recipientAddress: string,
amount: { left: number, right: number }, amount: number,
fees: { fees: {
value: number, value: number,
unit: Unit | null, unit: Unit | null,
@ -64,7 +64,7 @@ function StepAmount(props: PropsStepAmount) {
max={account.balance - 0} max={account.balance - 0}
account={account} account={account}
onChange={onChange('amount')} onChange={onChange('amount')}
value={amount.left} value={amount}
/> />
</Box> </Box>

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

@ -8,6 +8,7 @@ import type { T } from 'types/common'
import { ModalFooter } from 'components/base/Modal' import { ModalFooter } from 'components/base/Modal'
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 CounterValue from 'components/CounterValue'
import FormattedVal from 'components/base/FormattedVal' import FormattedVal from 'components/base/FormattedVal'
import Button from 'components/base/Button' import Button from 'components/base/Button'
import Text from 'components/base/Text' import Text from 'components/base/Text'
@ -15,35 +16,28 @@ import Text from 'components/base/Text'
type Props = { type Props = {
t: T, t: T,
account: Account, account: Account,
amount: { left: number, right: number }, amount: number,
onNext: Function, onNext: Function,
canNext: boolean, canNext: boolean,
counterValue: string,
} }
function Footer({ account, amount, t, onNext, canNext, counterValue }: Props) { function Footer({ account, amount, t, onNext, canNext }: Props) {
return ( return (
<ModalFooter horizontal alignItems="center"> <ModalFooter horizontal alignItems="center">
<Box grow> <Box grow>
<Label>{t('send:totalSpent')}</Label> <Label>{t('send:totalSpent')}</Label>
<Box horizontal flow={2} align="center"> <Box horizontal flow={2} align="center">
<FormattedVal <FormattedVal disableRounding color="dark" val={amount} unit={account.unit} showCode />
disableRounding
color="dark"
val={amount.left}
unit={account.unit}
showCode
/>
<Box horizontal align="center"> <Box horizontal align="center">
<Text ff="Rubik" fontSize={3}> <Text ff="Rubik" fontSize={3}>
{'('} {'('}
</Text> </Text>
<FormattedVal <CounterValue
ticker={account.currency.units[0].code}
value={amount}
disableRounding disableRounding
color="grey" color="grey"
fontSize={3} fontSize={3}
val={amount.right}
fiat={counterValue}
showCode showCode
/> />
<Text ff="Rubik" fontSize={3}> <Text ff="Rubik" fontSize={3}>

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

@ -1,8 +1,6 @@
// @flow // @flow
import React, { PureComponent } from 'react' import React, { PureComponent } from 'react'
import { compose } from 'redux'
import { connect } from 'react-redux'
import { translate } from 'react-i18next' import { translate } from 'react-i18next'
import get from 'lodash/get' import get from 'lodash/get'
import type { Account } from '@ledgerhq/wallet-common/lib/types' import type { Account } from '@ledgerhq/wallet-common/lib/types'
@ -12,8 +10,6 @@ import type { T } from 'types/common'
import { MODAL_SEND } from 'config/constants' import { MODAL_SEND } from 'config/constants'
import { getCounterValueCode } from 'reducers/settings'
import Breadcrumb from 'components/Breadcrumb' import Breadcrumb from 'components/Breadcrumb'
import Modal, { ModalBody, ModalTitle, ModalContent } from 'components/base/Modal' import Modal, { ModalBody, ModalTitle, ModalContent } from 'components/base/Modal'
@ -24,19 +20,14 @@ import StepConnectDevice from './02-step-connect-device'
import StepVerification from './03-step-verification' import StepVerification from './03-step-verification'
import StepConfirmation from './04-step-confirmation' import StepConfirmation from './04-step-confirmation'
const mapStateToProps = state => ({
counterValue: getCounterValueCode(state),
})
type Props = { type Props = {
t: T, t: T,
counterValue: string,
} }
type State = { type State = {
stepIndex: number, stepIndex: number,
isDeviceReady: boolean, isDeviceReady: boolean,
amount: { left: number, right: number }, amount: number,
account: Account | null, account: Account | null,
recipientAddress: string, recipientAddress: string,
fees: { fees: {
@ -58,10 +49,7 @@ const INITIAL_STATE = {
isDeviceReady: false, isDeviceReady: false,
account: null, account: null,
recipientAddress: '', recipientAddress: '',
amount: { amount: 0,
left: 0,
right: 0,
},
fees: { fees: {
value: 0, value: 0,
unit: null, unit: null,
@ -80,7 +68,7 @@ class SendModal extends PureComponent<Props, State> {
// informations // informations
if (stepIndex === 0) { if (stepIndex === 0) {
const { amount, recipientAddress } = this.state const { amount, recipientAddress } = this.state
return !!amount.left && !!recipientAddress && !!account return !!amount && !!recipientAddress && !!account
} }
// connect device // connect device
@ -121,7 +109,7 @@ class SendModal extends PureComponent<Props, State> {
} }
render() { render() {
const { t, counterValue } = this.props const { t } = this.props
const { stepIndex, amount, account } = this.state const { stepIndex, amount, account } = this.state
return ( return (
@ -140,7 +128,6 @@ class SendModal extends PureComponent<Props, State> {
</ModalContent> </ModalContent>
{acc && ( {acc && (
<Footer <Footer
counterValue={counterValue}
canNext={canNext} canNext={canNext}
onNext={this.handleNextStep} onNext={this.handleNextStep}
account={acc} account={acc}
@ -156,4 +143,4 @@ class SendModal extends PureComponent<Props, State> {
} }
} }
export default compose(connect(mapStateToProps), translate())(SendModal) export default translate()(SendModal)

Loading…
Cancel
Save