Browse Source

Merge pull request #172 from meriadec/master

Display confirmation check on TransactionsList, and ability to adjust min confirmations
master
Loëck Vézien 7 years ago
committed by GitHub
parent
commit
9b77426b83
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      src/components/AccountPage.js
  2. 3
      src/components/SelectAccount/stories.js
  3. 41
      src/components/TransactionsList/ConfirmationCheck.js
  4. 31
      src/components/TransactionsList/index.js
  5. 33
      src/components/modals/SettingsAccount.js
  6. 1
      src/helpers/btc.js
  7. 10
      src/icons/Check.js
  8. 10
      src/icons/Clock.js
  9. 11
      src/reducers/accounts.js
  10. 6
      src/types/common.js

2
src/components/AccountPage.js

@ -25,7 +25,7 @@ import TransactionsList from 'components/TransactionsList'
type Props = { type Props = {
t: T, t: T,
account: Account, account?: Account,
openModal: Function, openModal: Function,
} }

3
src/components/SelectAccount/stories.js

@ -22,6 +22,9 @@ const accounts = [...Array(20)].map(() => ({
path: '', path: '',
transactions: [], transactions: [],
unit: getDefaultUnitByCoinType(0), unit: getDefaultUnitByCoinType(0),
settings: {
minConfirmations: 2,
},
})) }))
type State = { type State = {

41
src/components/TransactionsList/ConfirmationCheck.js

@ -0,0 +1,41 @@
// @flow
import React from 'react'
import styled from 'styled-components'
import { rgba } from 'styles/helpers'
import Box from 'components/base/Box'
import Tooltip from 'components/base/Tooltip'
import IconCheck from 'icons/Check'
import IconClock from 'icons/Clock'
const Container = styled(Box).attrs({
bg: p => rgba(p.isConfirmed ? p.theme.colors.positiveGreen : p.theme.colors.grey, 0.1),
color: p => (p.isConfirmed ? p.theme.colors.positiveGreen : p.theme.colors.grey),
align: 'center',
justify: 'center',
})`
width: 24px;
height: 24px;
border-radius: 50%;
`
const ConfirmationCheck = ({
confirmations,
minConfirmations,
}: {
confirmations: number,
minConfirmations: number,
}) => {
const isConfirmed = confirmations >= minConfirmations
return (
<Tooltip render={() => (isConfirmed ? 'Confirmed' : 'Not confirmed')}>
<Container isConfirmed={isConfirmed}>
{isConfirmed ? <IconCheck width={12} /> : <IconClock width={12} />}
</Container>
</Tooltip>
)
}
export default ConfirmationCheck

31
src/components/TransactionsList/index.js

@ -15,10 +15,12 @@ import Box from 'components/base/Box'
import Defer from 'components/base/Defer' import Defer from 'components/base/Defer'
import FormattedVal from 'components/base/FormattedVal' import FormattedVal from 'components/base/FormattedVal'
import Text from 'components/base/Text' import Text from 'components/base/Text'
import ConfirmationCheck from './ConfirmationCheck'
const DATE_COL_SIZE = 80 const DATE_COL_SIZE = 80
const ACCOUNT_COL_SIZE = 150 const ACCOUNT_COL_SIZE = 150
const AMOUNT_COL_SIZE = 150 const AMOUNT_COL_SIZE = 150
const CONFIRMATION_COL_SIZE = 30
const Cap = styled(Text).attrs({ const Cap = styled(Text).attrs({
fontSize: 2, fontSize: 2,
@ -41,7 +43,7 @@ const Hour = styled(Day).attrs({
color: 'graphite', color: 'graphite',
})`` })``
const HeaderCol = ({ size, children, ...props }: { size?: number, children: any }) => ( const HeaderCol = ({ size, children, ...props }: { size?: number, children?: any }) => (
<Cell size={size} {...props}> <Cell size={size} {...props}>
<Cap>{children}</Cap> <Cap>{children}</Cap>
</Cell> </Cell>
@ -49,6 +51,7 @@ const HeaderCol = ({ size, children, ...props }: { size?: number, children: any
HeaderCol.defaultProps = { HeaderCol.defaultProps = {
size: undefined, size: undefined,
children: undefined,
} }
const TransactionRaw = styled(Box).attrs({ const TransactionRaw = styled(Box).attrs({
@ -76,11 +79,13 @@ const Transaction = ({
onAccountClick, onAccountClick,
tx, tx,
withAccounts, withAccounts,
minConfirmations,
}: { }: {
t: T, t: T,
onAccountClick?: Function, onAccountClick?: Function,
tx: TransactionType, tx: TransactionType,
withAccounts?: boolean, withAccounts?: boolean,
minConfirmations: number,
}) => { }) => {
const time = moment(tx.receivedAt) const time = moment(tx.receivedAt)
const Icon = getIconByCoinType(get(tx, 'account.currency.coinType')) const Icon = getIconByCoinType(get(tx, 'account.currency.coinType'))
@ -117,16 +122,23 @@ const Transaction = ({
grow grow
shrink shrink
style={{ style={{
overflow: 'hidden',
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
display: 'block', display: 'block',
}} }}
> >
<Box ff="Open Sans" fontSize={3} color="graphite"> <Box ff="Open Sans" fontSize={3} color="graphite">
{tx.balance > 0 ? t('transactionsList.from') : t('transactionsList.to')} {tx.balance > 0 ? t('transactionsList.from') : t('transactionsList.to')}
</Box> </Box>
<Box color="dark" ff="Open Sans" fontSize={3}> <Box
color="dark"
ff="Open Sans"
fontSize={3}
style={{
overflow: 'hidden',
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
display: 'block',
}}
>
{tx.address} {tx.address}
</Box> </Box>
</Cell> </Cell>
@ -139,6 +151,9 @@ const Transaction = ({
alwaysShowSign alwaysShowSign
/> />
</Cell> </Cell>
<Cell size={CONFIRMATION_COL_SIZE} px={0} align="center" justify="center">
<ConfirmationCheck minConfirmations={minConfirmations} confirmations={tx.confirmations} />
</Cell>
</TransactionRaw> </TransactionRaw>
) )
} }
@ -153,12 +168,14 @@ type Props = {
onAccountClick?: Function, onAccountClick?: Function,
transactions: Array<TransactionType>, transactions: Array<TransactionType>,
withAccounts?: boolean, withAccounts?: boolean,
minConfirmations: number,
} }
class TransactionsList extends Component<Props> { class TransactionsList extends Component<Props> {
static defaultProps = { static defaultProps = {
onAccountClick: noop, onAccountClick: noop,
withAccounts: false, withAccounts: false,
minConfirmations: 2,
} }
shouldComponentUpdate(nextProps: Props) { shouldComponentUpdate(nextProps: Props) {
@ -174,7 +191,7 @@ class TransactionsList extends Component<Props> {
_hashCache = null _hashCache = null
render() { render() {
const { transactions, withAccounts, onAccountClick, t } = this.props const { transactions, withAccounts, onAccountClick, minConfirmations, t } = this.props
this._hashCache = this.getHashCache(transactions) this._hashCache = this.getHashCache(transactions)
@ -189,6 +206,7 @@ class TransactionsList extends Component<Props> {
<HeaderCol size={AMOUNT_COL_SIZE} justifyContent="flex-end"> <HeaderCol size={AMOUNT_COL_SIZE} justifyContent="flex-end">
{t('transactionsList.amount')} {t('transactionsList.amount')}
</HeaderCol> </HeaderCol>
<HeaderCol size={CONFIRMATION_COL_SIZE} px={0} />
</Box> </Box>
<Defer> <Defer>
<Box> <Box>
@ -198,6 +216,7 @@ class TransactionsList extends Component<Props> {
key={`{${trans.hash}-${trans.account ? trans.account.id : ''}`} key={`{${trans.hash}-${trans.account ? trans.account.id : ''}`}
withAccounts={withAccounts} withAccounts={withAccounts}
onAccountClick={onAccountClick} onAccountClick={onAccountClick}
minConfirmations={minConfirmations}
tx={trans} tx={trans}
/> />
))} ))}

33
src/components/modals/SettingsAccount.js

@ -18,9 +18,11 @@ import Input from 'components/base/Input'
import Modal, { ModalBody } from 'components/base/Modal' import Modal, { ModalBody } from 'components/base/Modal'
import Text from 'components/base/Text' import Text from 'components/base/Text'
import Icon from 'components/base/Icon' import Icon from 'components/base/Icon'
import Label from 'components/base/Label'
type State = { type State = {
accountName: string | null, accountName: string | null,
minConfirmations: number | null,
editName: boolean, editName: boolean,
nameHovered: boolean, nameHovered: boolean,
} }
@ -44,6 +46,7 @@ const mapDispatchToProps = {
const defaultState = { const defaultState = {
editName: false, editName: false,
accountName: null, accountName: null,
minConfirmations: null,
nameHovered: false, nameHovered: false,
} }
@ -57,7 +60,7 @@ class SettingsAccount extends PureComponent<Props, State> {
} }
getAccount(data: Object) { getAccount(data: Object) {
const { accountName } = this.state const { accountName, minConfirmations } = this.state
const account = get(data, 'account', {}) const account = get(data, 'account', {})
@ -68,6 +71,10 @@ class SettingsAccount extends PureComponent<Props, State> {
name: accountName, name: accountName,
} }
: {}), : {}),
settings: {
...account.settings,
minConfirmations: minConfirmations || account.settings.minConfirmations,
},
} }
} }
@ -76,6 +83,20 @@ class SettingsAccount extends PureComponent<Props, State> {
nameHovered: state, nameHovered: state,
}) })
handleChangeMinConfirmations = account => minConfirmations => {
const { updateAccount } = this.props
this.setState({ minConfirmations })
window.requestAnimationFrame(() => {
updateAccount({
...account,
settings: {
...account.settings,
minConfirmations,
},
})
})
}
handleEditName = (state: boolean) => () => handleEditName = (state: boolean) => () =>
this.setState({ this.setState({
nameHovered: false, nameHovered: false,
@ -179,6 +200,16 @@ class SettingsAccount extends PureComponent<Props, State> {
</Box> </Box>
)} )}
</Box> </Box>
<Box>
<Label>{'Minimum confirmations'}</Label>
<Input
type="number"
min={1}
max={100}
value={account.settings.minConfirmations}
onChange={this.handleChangeMinConfirmations(account)}
/>
</Box>
<Box horizontal grow alignItems="flex-end" flow={2}> <Box horizontal grow alignItems="flex-end" flow={2}>
<Box grow> <Box grow>
<Button onClick={this.handleArchiveAccount(account)}> <Button onClick={this.handleArchiveAccount(account)}>

1
src/helpers/btc.js

@ -145,6 +145,7 @@ export async function getAccount({
index: currentAddress.index, index: currentAddress.index,
path: `${path}/${getPath('external', currentAddress.index + 1)}`, path: `${path}/${getPath('external', currentAddress.index + 1)}`,
transactions: transactions.map(t => ({ transactions: transactions.map(t => ({
confirmations: t.confirmations,
address: t.balance > 0 ? t.inputs[0].address : t.outputs[0].address, address: t.balance > 0 ? t.inputs[0].address : t.outputs[0].address,
balance: t.balance, balance: t.balance,
hash: t.hash, hash: t.hash,

10
src/icons/Check.js

@ -0,0 +1,10 @@
import React from 'react'
export default props => (
<svg viewBox="0 0 16 16" {...props}>
<path
fill="currentColor"
d="M13.62 2.608l-8.22 8.22-3.02-3.02a.375.375 0 0 0-.53 0l-.884.884a.375.375 0 0 0 0 .53l4.169 4.17a.375.375 0 0 0 .53 0l9.37-9.37a.375.375 0 0 0 0-.53l-.884-.884a.375.375 0 0 0-.53 0z"
/>
</svg>
)

10
src/icons/Clock.js

@ -0,0 +1,10 @@
import React from 'react'
export default props => (
<svg viewBox="0 0 16 16" {...props}>
<path
fill="currentColor"
d="M8 .583a7.417 7.417 0 1 1 0 14.834A7.417 7.417 0 0 1 8 .583zm0 1.5a5.917 5.917 0 1 0 0 11.834A5.917 5.917 0 0 0 8 2.083zm.75 5.606l1.78 1.78a.75.75 0 0 1-1.06 1.061l-2-2A.75.75 0 0 1 7.25 8V4a.75.75 0 0 1 1.5 0v3.69z"
/>
</svg>
)

11
src/reducers/accounts.js

@ -5,6 +5,7 @@ import { handleActions } from 'redux-actions'
import every from 'lodash/every' import every from 'lodash/every'
import get from 'lodash/get' import get from 'lodash/get'
import reduce from 'lodash/reduce' import reduce from 'lodash/reduce'
import defaultsDeep from 'lodash/defaultsDeep'
import { getDefaultUnitByCoinType, getCurrencyByCoinType } from '@ledgerhq/currencies' import { getDefaultUnitByCoinType, getCurrencyByCoinType } from '@ledgerhq/currencies'
@ -24,11 +25,19 @@ function orderAccountsTransactions(account: Account) {
} }
} }
function applyDefaults(account) {
return defaultsDeep(account, {
settings: {
minConfirmations: 2,
},
})
}
const handlers: Object = { const handlers: Object = {
SET_ACCOUNTS: ( SET_ACCOUNTS: (
state: AccountsState, state: AccountsState,
{ payload: accounts }: { payload: Accounts }, { payload: accounts }: { payload: Accounts },
): AccountsState => accounts, ): AccountsState => accounts.map(applyDefaults),
ADD_ACCOUNT: ( ADD_ACCOUNT: (
state: AccountsState, state: AccountsState,

6
src/types/common.js

@ -18,10 +18,15 @@ export type Transaction = {
balance: number, balance: number,
hash: string, hash: string,
receivedAt: string, receivedAt: string,
confirmations: number,
} }
// -------------------- Accounts // -------------------- Accounts
export type AccountSettings = {
minConfirmations: number,
}
export type Account = { export type Account = {
address: string, address: string,
addresses: Array<string>, addresses: Array<string>,
@ -35,6 +40,7 @@ export type Account = {
path: string, path: string,
transactions: Array<Transaction>, transactions: Array<Transaction>,
unit: Unit, unit: Unit,
settings: AccountSettings,
} }
export type Accounts = Array<Account> export type Accounts = Array<Account>

Loading…
Cancel
Save