Browse Source

Merge pull request #291 from meriadec/types-and-pal

Handle recent types & settings changes on Operation and Account
master
Meriadec Pillet 7 years ago
committed by GitHub
parent
commit
526c5423a8
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      package.json
  2. 209
      src/components/OperationsList/Operation.js
  3. 199
      src/components/OperationsList/index.js
  4. 27
      src/components/SettingsPage/sections/Currencies.js
  5. 69
      src/components/modals/OperationDetails.js
  6. 10
      src/helpers/btc.js
  7. 11
      src/reducers/accounts.js
  8. 25
      src/reducers/settings.js
  9. 9
      src/types/common.js
  10. 131
      yarn.lock

2
package.json

@ -49,7 +49,7 @@
"@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": "^0.13.2", "@ledgerhq/wallet-common": "^1.2.0",
"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",

209
src/components/OperationsList/Operation.js

@ -0,0 +1,209 @@
// @flow
import React, { PureComponent } from 'react'
import { connect } from 'react-redux'
import styled from 'styled-components'
import moment from 'moment'
import noop from 'lodash/noop'
import { getIconByCoinType } from '@ledgerhq/currencies/react'
import type { Account, Operation as OperationType } from '@ledgerhq/wallet-common/lib/types'
import type { T } from 'types/common'
import { currencySettingsSelector, marketIndicatorSelector } from 'reducers/settings'
import { rgba, getMarketColor } from 'styles/helpers'
import Box from 'components/base/Box'
import Text from 'components/base/Text'
import CounterValue from 'components/CounterValue'
import FormattedVal from 'components/base/FormattedVal'
import ConfirmationCheck from './ConfirmationCheck'
const mapStateToProps = (state, props) => ({
minConfirmations: currencySettingsSelector(state, props.account.currency).confirmationsNb,
marketIndicator: marketIndicatorSelector(state),
})
const DATE_COL_SIZE = 100
const ACCOUNT_COL_SIZE = 150
const AMOUNT_COL_SIZE = 150
const CONFIRMATION_COL_SIZE = 44
const OperationRaw = styled(Box).attrs({
horizontal: true,
alignItems: 'center',
})`
cursor: pointer;
border-bottom: 1px solid ${p => p.theme.colors.lightGrey};
height: 68px;
&:last-child {
border-bottom: 0;
}
&:hover {
background: ${p => rgba(p.theme.colors.wallet, 0.04)};
}
`
const Address = ({ value }: { value: string }) => {
const addrSize = value.length / 2
const left = value.slice(0, 10)
const right = value.slice(-addrSize)
const middle = value.slice(10, -addrSize)
return (
<Box horizontal color="smoke" ff="Open Sans" fontSize={3}>
<div>{left}</div>
<AddressEllipsis>{middle}</AddressEllipsis>
<div>{right}</div>
</Box>
)
}
const AddressEllipsis = styled.div`
display: block;
flex-shrink: 1;
min-width: 20px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
`
const Day = styled(Text).attrs({
color: 'dark',
fontSize: 3,
ff: 'Open Sans',
})`
letter-spacing: 0.3px;
text-transform: uppercase;
`
const Hour = styled(Day).attrs({
color: 'grey',
})``
const Cell = styled(Box).attrs({
px: 4,
horizontal: true,
alignItems: 'center',
})`
width: ${p => (p.size ? `${p.size}px` : '')};
overflow: ${p => (p.noOverflow ? 'hidden' : '')};
`
type Props = {
account: Account,
minConfirmations: number,
onAccountClick: Function,
onOperationClick: Function,
marketIndicator: string,
t: T,
op: OperationType,
withAccount?: boolean,
}
class Operation extends PureComponent<Props> {
static defaultProps = {
onAccountClick: noop,
onOperationClick: noop,
withAccount: false,
}
render() {
const {
account,
minConfirmations,
onAccountClick,
onOperationClick,
t,
op,
withAccount,
marketIndicator,
} = this.props
const { unit, currency } = account
const time = moment(op.date)
const Icon = getIconByCoinType(account.currency.coinType)
const isNegative = op.amount < 0
const type = !isNegative ? 'from' : 'to'
const marketColor = getMarketColor({
marketIndicator,
isNegative,
})
return (
<OperationRaw onClick={() => onOperationClick({ operation: op, account, type, marketColor })}>
<Cell size={CONFIRMATION_COL_SIZE} align="center" justify="flex-start">
<ConfirmationCheck
type={type}
minConfirmations={minConfirmations}
confirmations={account.blockHeight - op.blockHeight}
marketColor={marketColor}
t={t}
/>
</Cell>
<Cell size={DATE_COL_SIZE} justifyContent="space-between" px={3}>
<Box>
<Box ff="Open Sans|SemiBold" fontSize={3} color="smoke">
{t(`operationsList:${type}`)}
</Box>
<Hour>{time.format('HH:mm')}</Hour>
</Box>
</Cell>
{withAccount &&
account && (
<Cell
noOverflow
size={ACCOUNT_COL_SIZE}
horizontal
flow={2}
style={{ cursor: 'pointer' }}
onClick={e => {
e.stopPropagation()
onAccountClick(account)
}}
>
<Box
alignItems="center"
justifyContent="center"
style={{ color: account.currency.color }}
>
{Icon && <Icon size={16} />}
</Box>
<Box ff="Open Sans|SemiBold" fontSize={3} color="dark">
{account.name}
</Box>
</Cell>
)}
<Cell grow shrink style={{ display: 'block' }}>
<Address value={op.address} />
</Cell>
<Cell size={AMOUNT_COL_SIZE} justify="flex-end">
<Box alignItems="flex-end">
<FormattedVal
val={op.amount}
unit={unit}
showCode
fontSize={4}
alwaysShowSign
color={op.amount < 0 ? 'smoke' : undefined}
/>
<CounterValue
color="grey"
fontSize={3}
date={time.toDate()}
ticker={currency.units[0].code}
value={op.amount}
/>
</Box>
</Cell>
</OperationRaw>
)
}
}
export default connect(mapStateToProps)(Operation)

199
src/components/OperationsList/index.js

@ -6,19 +6,17 @@ import moment from 'moment'
import { connect } from 'react-redux' import { connect } from 'react-redux'
import { compose } from 'redux' import { compose } from 'redux'
import { translate } from 'react-i18next' import { translate } from 'react-i18next'
import { getIconByCoinType } from '@ledgerhq/currencies/react'
import { import {
groupAccountOperationsByDay, groupAccountOperationsByDay,
groupAccountsOperationsByDay, groupAccountsOperationsByDay,
} from '@ledgerhq/wallet-common/lib/helpers/account' } from '@ledgerhq/wallet-common/lib/helpers/account'
import type { Account, Operation as OperationType } from '@ledgerhq/wallet-common/lib/types'
import type { Account } from '@ledgerhq/wallet-common/lib/types'
import noop from 'lodash/noop' import noop from 'lodash/noop'
import keyBy from 'lodash/keyBy' import keyBy from 'lodash/keyBy'
import { getMarketColor, rgba } from 'styles/helpers' import type { T } from 'types/common'
import type { Settings, T } from 'types/common'
import { MODAL_OPERATION_DETAILS } from 'config/constants' import { MODAL_OPERATION_DETAILS } from 'config/constants'
@ -27,17 +25,10 @@ import { openModal } from 'reducers/modals'
import IconAngleDown from 'icons/AngleDown' import IconAngleDown from 'icons/AngleDown'
import Box, { Card } from 'components/base/Box' import Box, { Card } from 'components/base/Box'
import CounterValue from 'components/CounterValue'
import FormattedVal from 'components/base/FormattedVal'
import Text from 'components/base/Text' import Text from 'components/base/Text'
import Defer from 'components/base/Defer' import Defer from 'components/base/Defer'
import ConfirmationCheck from './ConfirmationCheck' import Operation from './Operation'
const DATE_COL_SIZE = 100
const ACCOUNT_COL_SIZE = 150
const AMOUNT_COL_SIZE = 150
const CONFIRMATION_COL_SIZE = 44
const calendarOpts = { const calendarOpts = {
sameDay: 'LL – [Today]', sameDay: 'LL – [Today]',
@ -47,45 +38,6 @@ const calendarOpts = {
sameElse: 'LL', sameElse: 'LL',
} }
const Day = styled(Text).attrs({
color: 'dark',
fontSize: 3,
ff: 'Open Sans',
})`
letter-spacing: 0.3px;
text-transform: uppercase;
`
const Hour = styled(Day).attrs({
color: 'grey',
})``
const OperationRaw = styled(Box).attrs({
horizontal: true,
alignItems: 'center',
})`
cursor: pointer;
border-bottom: 1px solid ${p => p.theme.colors.lightGrey};
height: 68px;
&:last-child {
border-bottom: 0;
}
&:hover {
background: ${p => rgba(p.theme.colors.wallet, 0.04)};
}
`
const Cell = styled(Box).attrs({
px: 4,
horizontal: true,
alignItems: 'center',
})`
width: ${p => (p.size ? `${p.size}px` : '')};
overflow: ${p => (p.noOverflow ? 'hidden' : '')};
`
const ShowMore = styled(Box).attrs({ const ShowMore = styled(Box).attrs({
horizontal: true, horizontal: true,
flow: 1, flow: 1,
@ -102,141 +54,6 @@ const ShowMore = styled(Box).attrs({
} }
` `
const AddressEllipsis = styled.div`
display: block;
flex-shrink: 1;
min-width: 20px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
`
const Address = ({ value }: { value: string }) => {
const addrSize = value.length / 2
const left = value.slice(0, 10)
const right = value.slice(-addrSize)
const middle = value.slice(10, -addrSize)
return (
<Box horizontal color="smoke" ff="Open Sans" fontSize={3}>
<div>{left}</div>
<AddressEllipsis>{middle}</AddressEllipsis>
<div>{right}</div>
</Box>
)
}
const Operation = ({
account,
minConfirmations,
onAccountClick,
onOperationClick,
t,
op,
withAccount,
marketIndicator,
}: {
account: Account,
minConfirmations: number,
onAccountClick: Function,
onOperationClick: Function,
t: T,
op: OperationType,
withAccount?: boolean,
marketIndicator: string,
}) => {
const { unit, currency } = account
const time = moment(op.date)
const Icon = getIconByCoinType(account.currency.coinType)
const isNegative = op.amount < 0
const type = !isNegative ? 'from' : 'to'
const marketColor = getMarketColor({
marketIndicator,
isNegative,
})
return (
<OperationRaw onClick={() => onOperationClick({ operation: op, account, type, marketColor })}>
<Cell size={CONFIRMATION_COL_SIZE} align="center" justify="flex-start">
<ConfirmationCheck
confirmations={op.confirmations}
marketColor={marketColor}
minConfirmations={minConfirmations}
t={t}
type={type}
/>
</Cell>
<Cell size={DATE_COL_SIZE} justifyContent="space-between" px={3}>
<Box>
<Box ff="Open Sans|SemiBold" fontSize={3} color="smoke">
{t(`operationsList:${type}`)}
</Box>
<Hour>{time.format('HH:mm')}</Hour>
</Box>
</Cell>
{withAccount &&
account && (
<Cell
noOverflow
size={ACCOUNT_COL_SIZE}
horizontal
flow={2}
style={{ cursor: 'pointer' }}
onClick={e => {
e.stopPropagation()
onAccountClick(account)
}}
>
<Box
alignItems="center"
justifyContent="center"
style={{ color: account.currency.color }}
>
{Icon && <Icon size={16} />}
</Box>
<Box ff="Open Sans|SemiBold" fontSize={3} color="dark">
{account.name}
</Box>
</Cell>
)}
<Cell grow shrink style={{ display: 'block' }}>
<Address value={op.address} />
</Cell>
<Cell size={AMOUNT_COL_SIZE} justify="flex-end">
<Box alignItems="flex-end">
<FormattedVal
val={op.amount}
unit={unit}
showCode
fontSize={4}
alwaysShowSign
color={op.amount < 0 ? 'smoke' : undefined}
/>
<CounterValue
color="grey"
fontSize={3}
date={time.toDate()}
ticker={currency.units[0].code}
value={op.amount}
/>
</Box>
</Cell>
</OperationRaw>
)
}
Operation.defaultProps = {
onAccountClick: noop,
onOperationClick: noop,
withAccount: false,
}
const mapStateToProps = state => ({
settings: state.settings,
})
const mapDispatchToProps = { const mapDispatchToProps = {
openModal, openModal,
} }
@ -251,7 +68,6 @@ type Props = {
withAccount?: boolean, withAccount?: boolean,
nbToShow: number, nbToShow: number,
title?: string, title?: string,
settings: Settings,
} }
export class OperationsList extends PureComponent<Props> { export class OperationsList extends PureComponent<Props> {
@ -271,7 +87,6 @@ export class OperationsList extends PureComponent<Props> {
canShowMore, canShowMore,
nbToShow, nbToShow,
onAccountClick, onAccountClick,
settings,
t, t,
title, title,
withAccount, withAccount,
@ -295,7 +110,7 @@ export class OperationsList extends PureComponent<Props> {
{title} {title}
</Text> </Text>
)} )}
{groupedOperations.map(group => { {groupedOperations.sections.map(group => {
const d = moment(group.day) const d = moment(group.day)
return ( return (
<Box flow={2} key={group.day.toISOString()}> <Box flow={2} key={group.day.toISOString()}>
@ -312,8 +127,6 @@ export class OperationsList extends PureComponent<Props> {
<Operation <Operation
account={account} account={account}
key={`${account.id}-${op.hash}`} key={`${account.id}-${op.hash}`}
marketIndicator={settings.marketIndicator}
minConfirmations={account.minConfirmations}
onAccountClick={onAccountClick} onAccountClick={onAccountClick}
onOperationClick={this.handleClickOperation} onOperationClick={this.handleClickOperation}
op={op} op={op}
@ -338,4 +151,4 @@ export class OperationsList extends PureComponent<Props> {
} }
} }
export default compose(translate(), connect(mapStateToProps, mapDispatchToProps))(OperationsList) export default compose(translate(), connect(null, mapDispatchToProps))(OperationsList)

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

@ -25,9 +25,6 @@ import {
// instead of using same default for all. // instead of using same default for all.
// //
const CURRENCY_DEFAULTS_SETTINGS: CurrencySettings = { const CURRENCY_DEFAULTS_SETTINGS: CurrencySettings = {
// will be overwritten
coinType: 0,
confirmationsToSpend: 10, confirmationsToSpend: 10,
minConfirmationsToSpend: 10, minConfirmationsToSpend: 10,
maxConfirmationsToSpend: 50, maxConfirmationsToSpend: 50,
@ -57,7 +54,7 @@ class TabCurrencies extends PureComponent<Props, State> {
getCurrencySettings() { getCurrencySettings() {
const { settings } = this.props const { settings } = this.props
const { currency } = this.state const { currency } = this.state
return settings.currencies.find(c => c.coinType === currency.coinType) return settings.currenciesSettings[currency.coinType]
} }
handleChangeCurrency = (currency: Currency) => this.setState({ currency }) handleChangeCurrency = (currency: Currency) => this.setState({ currency })
@ -73,23 +70,23 @@ class TabCurrencies extends PureComponent<Props, State> {
const currencySettings = this.getCurrencySettings() const currencySettings = this.getCurrencySettings()
let newCurrenciesSettings = [] let newCurrenciesSettings = []
if (!currencySettings) { if (!currencySettings) {
newCurrenciesSettings = [ newCurrenciesSettings = {
...settings.currencies, ...settings.currenciesSettings,
{ [currency.coinType]: {
...CURRENCY_DEFAULTS_SETTINGS, ...CURRENCY_DEFAULTS_SETTINGS,
coinType: currency.coinType,
[key]: val, [key]: val,
}, },
] }
} else { } else {
newCurrenciesSettings = settings.currencies.map(c => { newCurrenciesSettings = {
if (c.coinType !== currency.coinType) { ...settings.currenciesSettings,
return c [currency.coinType]: {
...currencySettings,
[key]: val,
},
} }
return { ...c, [key]: val }
})
} }
saveSettings({ currencies: newCurrenciesSettings }) saveSettings({ currenciesSettings: newCurrenciesSettings })
} }
render() { render() {

69
src/components/modals/OperationDetails.js

@ -1,11 +1,13 @@
// @flow // @flow
import React from 'react' import React from 'react'
import { connect } from 'react-redux'
import { shell } from 'electron' import { shell } from 'electron'
import { translate } from 'react-i18next' 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 { T } from 'types/common' import type { T } from 'types/common'
import { MODAL_OPERATION_DETAILS } from 'config/constants' import { MODAL_OPERATION_DETAILS } from 'config/constants'
@ -16,6 +18,8 @@ import Bar from 'components/base/Bar'
import FormattedVal from 'components/base/FormattedVal' import FormattedVal from 'components/base/FormattedVal'
import Modal, { ModalBody, ModalTitle, ModalFooter, ModalContent } from 'components/base/Modal' import Modal, { ModalBody, ModalTitle, ModalFooter, ModalContent } from 'components/base/Modal'
import { currencySettingsSelector } from 'reducers/settings'
import CounterValue from 'components/CounterValue' import CounterValue from 'components/CounterValue'
import ConfirmationCheck from 'components/OperationsList/ConfirmationCheck' import ConfirmationCheck from 'components/OperationsList/ConfirmationCheck'
@ -48,17 +52,30 @@ const B = styled(Bar).attrs({
size: 1, size: 1,
})`` })``
const OperationDetails = ({ t }: { t: T }) => ( const mapStateToProps = (state, props) => ({
<Modal minConfirmations: currencySettingsSelector(state, props.account.currency).confirmationsNb,
name={MODAL_OPERATION_DETAILS} })
render={({ data, onClose }) => {
const { marketColor, operation, account, type } = data
const { name, unit, currency, minConfirmations } = account type Props = {
const { id, amount, confirmations, date, from, to } = operation t: T,
operation: Operation,
account: Account,
type: 'from' | 'to',
onClose: Function,
minConfirmations: number,
marketColor: string,
}
const isConfirmed = confirmations >= minConfirmations const OperationDetails = connect(mapStateToProps)((props: Props) => {
const { t, type, onClose, minConfirmations, operation, account, marketColor } = props
const { id, amount, date } = operation
// $FlowFixMe YEAH, I know those fields should not be present in operation
const { from, to } = operation
const { name, unit, currency } = account
const confirmations = account.blockHeight - operation.blockHeight
const isConfirmed = confirmations >= minConfirmations
return ( return (
<ModalBody onClose={onClose}> <ModalBody onClose={onClose}>
<ModalTitle>Operation details</ModalTitle> <ModalTitle>Operation details</ModalTitle>
@ -104,11 +121,9 @@ const OperationDetails = ({ t }: { t: T }) => (
<ColLeft>Status</ColLeft> <ColLeft>Status</ColLeft>
<ColRight color={isConfirmed ? 'positiveGreen' : null} horizontal flow={1}> <ColRight color={isConfirmed ? 'positiveGreen' : null} horizontal flow={1}>
<Box> <Box>
{isConfirmed {isConfirmed ? t('operationDetails:confirmed') : t('operationDetails:notConfirmed')}
? t('operationDetails:confirmed')
: t('operationDetails:notConfirmed')}
</Box> </Box>
<Box>({confirmations})</Box> <Box>{`(${confirmations})`}</Box>
</ColRight> </ColRight>
</Line> </Line>
<B /> <B />
@ -156,8 +171,36 @@ const OperationDetails = ({ t }: { t: T }) => (
</ModalFooter> </ModalFooter>
</ModalBody> </ModalBody>
) )
})
type ModalRenderProps = {
data: {
account: Account,
operation: Operation,
type: 'from' | 'to',
marketColor: string,
},
onClose: Function,
}
const OperationDetailsWrapper = ({ t }: { t: T }) => (
<Modal
name={MODAL_OPERATION_DETAILS}
render={(props: ModalRenderProps) => {
const { data, onClose } = props
const { operation, account, type, marketColor } = data
return (
<OperationDetails
t={t}
operation={operation}
account={account}
type={type}
onClose={onClose}
marketColor={marketColor}
/>
)
}} }}
/> />
) )
export default translate()(OperationDetails) export default translate()(OperationDetailsWrapper)

10
src/helpers/btc.js

@ -2,6 +2,7 @@
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 type { OperationRaw } from '@ledgerhq/wallet-common/lib/types' import type { OperationRaw } from '@ledgerhq/wallet-common/lib/types'
import groupBy from 'lodash/groupBy' import groupBy from 'lodash/groupBy'
@ -40,7 +41,7 @@ export function computeOperation(addresses: Array<string>, accountId: string) {
confirmations: t.confirmations, confirmations: t.confirmations,
date: t.received_at, date: t.received_at,
accountId, accountId,
blockHeight: 0, blockHeight: t.block.height,
} }
} }
} }
@ -196,6 +197,11 @@ export async function getAccount({
}) })
: getAddress({ type: 'external', index: 0 }) : getAddress({ type: 'external', index: 0 })
// TODO: in the future, it should be done with the libc call
const {
data: { height: blockHeight },
} = await axios.get('https://api.ledgerwallet.com/blockchain/v2/btc_testnet/blocks/current')
const account = { const account = {
...nextAddress, ...nextAddress,
coinType, coinType,
@ -204,6 +210,8 @@ export async function getAccount({
balanceByDay: getBalanceByDay(operations), balanceByDay: getBalanceByDay(operations),
rootPath, rootPath,
operations, operations,
lastSyncDate: new Date(),
blockHeight,
} }
onProgress({ onProgress({

11
src/reducers/accounts.js

@ -101,7 +101,16 @@ export function serializeAccounts(accounts: any): Account[] {
} }
export function deserializeAccounts(accounts: Account[]) { export function deserializeAccounts(accounts: Account[]) {
return accounts.map(accountModel.encode) return accounts.map(account => {
// as account can be passed by main process, the Date types
// can be converted to string. we ensure here that we have real
// date
if (typeof account.lastSyncDate === 'string') {
account.lastSyncDate = new Date(account.lastSyncDate)
}
return accountModel.encode(account)
})
} }
export default handleActions(handlers, state) export default handleActions(handlers, state)

25
src/reducers/settings.js

@ -2,10 +2,12 @@
import { handleActions } from 'redux-actions' import { handleActions } from 'redux-actions'
import { getFiatUnit } from '@ledgerhq/currencies' import { getFiatUnit } from '@ledgerhq/currencies'
import type { Currency } from '@ledgerhq/currencies'
import get from 'lodash/get' import get from 'lodash/get'
import type { Settings } from 'types/common' import type { Settings, CurrencySettings } from 'types/common'
import type { State } from 'reducers'
export type SettingsState = Object export type SettingsState = Object
@ -18,8 +20,20 @@ const defaultState: SettingsState = {
isEnabled: false, isEnabled: false,
value: '', value: '',
}, },
currencies: [],
marketIndicator: 'eastern', marketIndicator: 'eastern',
currenciesSettings: {},
}
const CURRENCY_DEFAULTS_SETTINGS: CurrencySettings = {
confirmationsToSpend: 10,
minConfirmationsToSpend: 10,
maxConfirmationsToSpend: 50,
confirmationsNb: 10,
minConfirmationsNb: 10,
maxConfirmationsNb: 50,
transactionFees: 10,
} }
const state: SettingsState = { const state: SettingsState = {
@ -50,4 +64,11 @@ export const getLanguage = (state: Object) => get(state.settings, 'language', de
export const getOrderAccounts = (state: Object) => export const getOrderAccounts = (state: Object) =>
get(state.settings, 'orderAccounts', defaultState.orderAccounts) get(state.settings, 'orderAccounts', defaultState.orderAccounts)
export const currencySettingsSelector = (state: State, currency: Currency): CurrencySettings => {
const currencySettings = state.settings.currenciesSettings[currency.coinType]
return currencySettings || CURRENCY_DEFAULTS_SETTINGS
}
export const marketIndicatorSelector = (state: State) => state.settings.marketIndicator
export default handleActions(handlers, state) export default handleActions(handlers, state)

9
src/types/common.js

@ -13,8 +13,6 @@ export type Devices = Array<Device>
// -------------------- Settings // -------------------- Settings
export type CurrencySettings = { export type CurrencySettings = {
coinType: number,
confirmationsToSpend: number, confirmationsToSpend: number,
minConfirmationsToSpend: number, minConfirmationsToSpend: number,
maxConfirmationsToSpend: number, maxConfirmationsToSpend: number,
@ -26,16 +24,21 @@ export type CurrencySettings = {
transactionFees: number, transactionFees: number,
} }
export type CurrenciesSettings = {
[coinType: number]: CurrencySettings,
}
export type Settings = { export type Settings = {
language: string, language: string,
orderAccounts: string,
username: string, username: string,
counterValue: string, counterValue: string,
password: { password: {
isEnabled: boolean, isEnabled: boolean,
value: string, value: string,
}, },
currencies: CurrencySettings[],
marketIndicator: 'eastern' | 'western', marketIndicator: 'eastern' | 'western',
currenciesSettings: CurrenciesSettings,
} }
export type T = (?string, ?Object) => string export type T = (?string, ?Object) => string

131
yarn.lock

@ -983,9 +983,9 @@
dependencies: dependencies:
events "^2.0.0" events "^2.0.0"
"@ledgerhq/wallet-common@^0.13.2": "@ledgerhq/wallet-common@^1.2.0":
version "0.13.2" version "1.2.0"
resolved "https://registry.yarnpkg.com/@ledgerhq/wallet-common/-/wallet-common-0.13.2.tgz#55bff35179acb8e4c12836eeea7885ebaa85e6c4" resolved "https://registry.yarnpkg.com/@ledgerhq/wallet-common/-/wallet-common-1.2.0.tgz#5e648829b6dff0fe33f31da2ee4a974df405642e"
dependencies: dependencies:
"@ledgerhq/currencies" "^4.10.1" "@ledgerhq/currencies" "^4.10.1"
axios "^0.18.0" axios "^0.18.0"
@ -1419,38 +1419,18 @@ anymatch@^2.0.0:
micromatch "^3.1.4" micromatch "^3.1.4"
normalize-path "^2.1.1" normalize-path "^2.1.1"
app-builder-bin-linux@1.8.4:
version "1.8.4"
resolved "https://registry.yarnpkg.com/app-builder-bin-linux/-/app-builder-bin-linux-1.8.4.tgz#9fa4f4f6af21f147cdedc69279940134c77d297f"
app-builder-bin-linux@1.8.5: app-builder-bin-linux@1.8.5:
version "1.8.5" version "1.8.5"
resolved "https://registry.yarnpkg.com/app-builder-bin-linux/-/app-builder-bin-linux-1.8.5.tgz#bf001d3ef347e1179680a1760fea83d0832fc344" resolved "https://registry.yarnpkg.com/app-builder-bin-linux/-/app-builder-bin-linux-1.8.5.tgz#bf001d3ef347e1179680a1760fea83d0832fc344"
app-builder-bin-mac@1.8.4:
version "1.8.4"
resolved "https://registry.yarnpkg.com/app-builder-bin-mac/-/app-builder-bin-mac-1.8.4.tgz#abd35353167b037a15353fe44c84b0b17045d12f"
app-builder-bin-mac@1.8.5: app-builder-bin-mac@1.8.5:
version "1.8.5" version "1.8.5"
resolved "https://registry.yarnpkg.com/app-builder-bin-mac/-/app-builder-bin-mac-1.8.5.tgz#31282504d232081a9de94d377736bf904bbfbd53" resolved "https://registry.yarnpkg.com/app-builder-bin-mac/-/app-builder-bin-mac-1.8.5.tgz#31282504d232081a9de94d377736bf904bbfbd53"
app-builder-bin-win@1.8.4:
version "1.8.4"
resolved "https://registry.yarnpkg.com/app-builder-bin-win/-/app-builder-bin-win-1.8.4.tgz#ba5f7a7d8ae48d32c400691b3c45f6f746c27748"
app-builder-bin-win@1.8.5: app-builder-bin-win@1.8.5:
version "1.8.5" version "1.8.5"
resolved "https://registry.yarnpkg.com/app-builder-bin-win/-/app-builder-bin-win-1.8.5.tgz#d7eefc1dff6052e137a3c5d68cd6e68ba862054b" resolved "https://registry.yarnpkg.com/app-builder-bin-win/-/app-builder-bin-win-1.8.5.tgz#d7eefc1dff6052e137a3c5d68cd6e68ba862054b"
app-builder-bin@1.8.4:
version "1.8.4"
resolved "https://registry.yarnpkg.com/app-builder-bin/-/app-builder-bin-1.8.4.tgz#ca8fd02209c2e0681de97fdb4c559d93381cc812"
optionalDependencies:
app-builder-bin-linux "1.8.4"
app-builder-bin-mac "1.8.4"
app-builder-bin-win "1.8.4"
app-builder-bin@1.8.5: app-builder-bin@1.8.5:
version "1.8.5" version "1.8.5"
resolved "https://registry.yarnpkg.com/app-builder-bin/-/app-builder-bin-1.8.5.tgz#ac0c0fad3c348ff3bde367e49b25cf5edc414407" resolved "https://registry.yarnpkg.com/app-builder-bin/-/app-builder-bin-1.8.5.tgz#ac0c0fad3c348ff3bde367e49b25cf5edc414407"
@ -3131,26 +3111,7 @@ builder-util-runtime@4.2.0, builder-util-runtime@^4.2.0, builder-util-runtime@~4
fs-extra-p "^4.5.2" fs-extra-p "^4.5.2"
sax "^1.2.4" sax "^1.2.4"
builder-util@5.7.5: builder-util@5.7.6, builder-util@^5.7.6:
version "5.7.5"
resolved "https://registry.yarnpkg.com/builder-util/-/builder-util-5.7.5.tgz#58f8d2b7a35445c5fb45bff50b39bbed554c9863"
dependencies:
"7zip-bin" "~3.1.0"
app-builder-bin "1.8.4"
bluebird-lst "^1.0.5"
builder-util-runtime "^4.2.0"
chalk "^2.3.2"
debug "^3.1.0"
fs-extra-p "^4.5.2"
is-ci "^1.1.0"
js-yaml "^3.11.0"
lazy-val "^1.0.3"
semver "^5.5.0"
source-map-support "^0.5.4"
stat-mode "^0.2.2"
temp-file "^3.1.1"
builder-util@5.7.6, builder-util@^5.7.4, builder-util@^5.7.5, builder-util@^5.7.6:
version "5.7.6" version "5.7.6"
resolved "https://registry.yarnpkg.com/builder-util/-/builder-util-5.7.6.tgz#2480c8a233ab7ab05a4f1689892cc39819e768bb" resolved "https://registry.yarnpkg.com/builder-util/-/builder-util-5.7.6.tgz#2480c8a233ab7ab05a4f1689892cc39819e768bb"
dependencies: dependencies:
@ -4565,19 +4526,6 @@ dijkstrajs@^1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/dijkstrajs/-/dijkstrajs-1.0.1.tgz#d3cd81221e3ea40742cfcde556d4e99e98ddc71b" resolved "https://registry.yarnpkg.com/dijkstrajs/-/dijkstrajs-1.0.1.tgz#d3cd81221e3ea40742cfcde556d4e99e98ddc71b"
dmg-builder@4.1.4:
version "4.1.4"
resolved "https://registry.yarnpkg.com/dmg-builder/-/dmg-builder-4.1.4.tgz#bca05298febebca81eb5a4686c222d1ef72b3542"
dependencies:
bluebird-lst "^1.0.5"
builder-util "^5.7.5"
electron-builder-lib "~20.8.2"
fs-extra-p "^4.5.2"
iconv-lite "^0.4.19"
js-yaml "^3.11.0"
parse-color "^1.0.0"
sanitize-filename "^1.6.1"
dmg-builder@4.1.5: dmg-builder@4.1.5:
version "4.1.5" version "4.1.5"
resolved "https://registry.yarnpkg.com/dmg-builder/-/dmg-builder-4.1.5.tgz#f8dc24cd911e0e4a8cdcf9c8a2c829317403985a" resolved "https://registry.yarnpkg.com/dmg-builder/-/dmg-builder-4.1.5.tgz#f8dc24cd911e0e4a8cdcf9c8a2c829317403985a"
@ -4768,35 +4716,6 @@ ejs@^2.5.7:
version "2.5.7" version "2.5.7"
resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.5.7.tgz#cc872c168880ae3c7189762fd5ffc00896c9518a" resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.5.7.tgz#cc872c168880ae3c7189762fd5ffc00896c9518a"
electron-builder-lib@20.8.2, electron-builder-lib@~20.8.2:
version "20.8.2"
resolved "https://registry.yarnpkg.com/electron-builder-lib/-/electron-builder-lib-20.8.2.tgz#c080cc9bf04cb3a55771b36a958680d59cfffbe5"
dependencies:
"7zip-bin" "~3.1.0"
app-builder-bin "1.8.4"
async-exit-hook "^2.0.1"
bluebird-lst "^1.0.5"
builder-util "5.7.5"
builder-util-runtime "4.2.0"
chromium-pickle-js "^0.2.0"
debug "^3.1.0"
ejs "^2.5.8"
electron-osx-sign "0.4.10"
electron-publish "20.8.1"
fs-extra-p "^4.5.2"
hosted-git-info "^2.6.0"
is-ci "^1.1.0"
isbinaryfile "^3.0.2"
js-yaml "^3.11.0"
lazy-val "^1.0.3"
minimatch "^3.0.4"
normalize-package-data "^2.4.0"
plist "^3.0.1"
read-config-file "3.0.0"
sanitize-filename "^1.6.1"
semver "^5.5.0"
temp-file "^3.1.1"
electron-builder-lib@20.9.0, electron-builder-lib@~20.9.0: electron-builder-lib@20.9.0, electron-builder-lib@~20.9.0:
version "20.9.0" version "20.9.0"
resolved "https://registry.yarnpkg.com/electron-builder-lib/-/electron-builder-lib-20.9.0.tgz#bb4725a796e80d15c5512e50a1ed0be08fea0e4f" resolved "https://registry.yarnpkg.com/electron-builder-lib/-/electron-builder-lib-20.9.0.tgz#bb4725a796e80d15c5512e50a1ed0be08fea0e4f"
@ -4826,25 +4745,6 @@ electron-builder-lib@20.9.0, electron-builder-lib@~20.9.0:
semver "^5.5.0" semver "^5.5.0"
temp-file "^3.1.1" temp-file "^3.1.1"
electron-builder@^20.0.4:
version "20.8.2"
resolved "https://registry.yarnpkg.com/electron-builder/-/electron-builder-20.8.2.tgz#0b5d0596997023329a7dddcebe167c5f773818cc"
dependencies:
bluebird-lst "^1.0.5"
builder-util "5.7.5"
builder-util-runtime "4.2.0"
chalk "^2.3.2"
dmg-builder "4.1.4"
electron-builder-lib "20.8.2"
electron-download-tf "4.3.4"
fs-extra-p "^4.5.2"
is-ci "^1.1.0"
lazy-val "^1.0.3"
read-config-file "3.0.0"
sanitize-filename "^1.6.1"
update-notifier "^2.4.0"
yargs "^11.0.0"
electron-builder@^20.9.0: electron-builder@^20.9.0:
version "20.9.0" version "20.9.0"
resolved "https://registry.yarnpkg.com/electron-builder/-/electron-builder-20.9.0.tgz#3010d5fbd927c0e1d5c4db6984d797f4b87cd239" resolved "https://registry.yarnpkg.com/electron-builder/-/electron-builder-20.9.0.tgz#3010d5fbd927c0e1d5c4db6984d797f4b87cd239"
@ -4916,18 +4816,6 @@ electron-osx-sign@0.4.10:
minimist "^1.2.0" minimist "^1.2.0"
plist "^2.1.0" plist "^2.1.0"
electron-publish@20.8.1:
version "20.8.1"
resolved "https://registry.yarnpkg.com/electron-publish/-/electron-publish-20.8.1.tgz#ec5730efbda88c6566a47395d433d7b122782675"
dependencies:
bluebird-lst "^1.0.5"
builder-util "^5.7.4"
builder-util-runtime "^4.2.0"
chalk "^2.3.2"
fs-extra-p "^4.5.2"
lazy-val "^1.0.3"
mime "^2.2.0"
electron-publish@20.9.0: electron-publish@20.9.0:
version "20.9.0" version "20.9.0"
resolved "https://registry.yarnpkg.com/electron-publish/-/electron-publish-20.9.0.tgz#095c02fe39674079d90a29eb404dbc894188ca16" resolved "https://registry.yarnpkg.com/electron-publish/-/electron-publish-20.9.0.tgz#095c02fe39674079d90a29eb404dbc894188ca16"
@ -5025,7 +4913,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.2: electron@1.8.4:
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:
@ -6783,7 +6671,7 @@ iconv-lite@0.4, iconv-lite@0.4.19, iconv-lite@^0.4.17, iconv-lite@~0.4.13:
version "0.4.19" version "0.4.19"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b"
iconv-lite@^0.4.19, iconv-lite@^0.4.21: iconv-lite@^0.4.21:
version "0.4.21" version "0.4.21"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.21.tgz#c47f8733d02171189ebc4a400f3218d348094798" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.21.tgz#c47f8733d02171189ebc4a400f3218d348094798"
dependencies: dependencies:
@ -8050,9 +7938,6 @@ ledger-test-library@KhalilBellakrid/ledger-test-library-nodejs#7d37482:
dependencies: dependencies:
axios "^0.17.1" axios "^0.17.1"
bindings "^1.3.0" bindings "^1.3.0"
electron "^1.8.2"
electron-builder "^20.0.4"
electron-rebuild "^1.7.3"
nan "^2.6.2" nan "^2.6.2"
prebuild-install "^2.2.2" prebuild-install "^2.2.2"
@ -8559,7 +8444,7 @@ mime@^2.0.3, mime@^2.1.0:
version "2.2.0" version "2.2.0"
resolved "https://registry.yarnpkg.com/mime/-/mime-2.2.0.tgz#161e541965551d3b549fa1114391e3a3d55b923b" resolved "https://registry.yarnpkg.com/mime/-/mime-2.2.0.tgz#161e541965551d3b549fa1114391e3a3d55b923b"
mime@^2.2.0, mime@^2.3.1: mime@^2.3.1:
version "2.3.1" version "2.3.1"
resolved "https://registry.yarnpkg.com/mime/-/mime-2.3.1.tgz#b1621c54d63b97c47d3cfe7f7215f7d64517c369" resolved "https://registry.yarnpkg.com/mime/-/mime-2.3.1.tgz#b1621c54d63b97c47d3cfe7f7215f7d64517c369"
@ -12345,7 +12230,7 @@ upath@^1.0.0:
version "1.0.4" version "1.0.4"
resolved "https://registry.yarnpkg.com/upath/-/upath-1.0.4.tgz#ee2321ba0a786c50973db043a50b7bcba822361d" resolved "https://registry.yarnpkg.com/upath/-/upath-1.0.4.tgz#ee2321ba0a786c50973db043a50b7bcba822361d"
update-notifier@^2.4.0, update-notifier@^2.5.0: update-notifier@^2.5.0:
version "2.5.0" version "2.5.0"
resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-2.5.0.tgz#d0744593e13f161e406acb1d9408b72cad08aff6" resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-2.5.0.tgz#d0744593e13f161e406acb1d9408b72cad08aff6"
dependencies: dependencies:

Loading…
Cancel
Save