Browse Source

Group operations by day in Dashboard and account page

master
meriadec 7 years ago
parent
commit
ced75a12c2
No known key found for this signature in database GPG Key ID: 1D2FC2305E2CB399
  1. 6
      src/components/AccountPage/index.js
  2. 32
      src/components/DashboardPage/index.js
  3. 110
      src/components/OperationsList/index.js
  4. 76
      src/components/OperationsList/stories.js

6
src/components/AccountPage/index.js

@ -168,11 +168,7 @@ class AccountPage extends PureComponent<Props, State> {
)} )}
/> />
</Box> </Box>
<OperationsList <OperationsList account={account} title={t('account:lastOperations')} />
account={account}
operations={account.operations}
title={t('account:lastOperations')}
/>
</Box> </Box>
) )
} }

32
src/components/DashboardPage/index.js

@ -5,11 +5,9 @@ import { compose } from 'redux'
import { translate } from 'react-i18next' import { translate } from 'react-i18next'
import { connect } from 'react-redux' import { connect } from 'react-redux'
import { push } from 'react-router-redux' import { push } from 'react-router-redux'
import type { Account, Operation } from '@ledgerhq/wallet-common/lib/types' import type { Account } from '@ledgerhq/wallet-common/lib/types'
import chunk from 'lodash/chunk' import chunk from 'lodash/chunk'
import get from 'lodash/get'
import sortBy from 'lodash/sortBy'
import type { T } from 'types/common' import type { T } from 'types/common'
@ -49,33 +47,11 @@ type Props = {
type State = { type State = {
accountsChunk: Array<Array<Account | null>>, accountsChunk: Array<Array<Account | null>>,
allOperations: Operation[],
selectedTime: string, selectedTime: string,
daysCount: number, daysCount: number,
} }
const ACCOUNTS_BY_LINE = 3 const ACCOUNTS_BY_LINE = 3
const ALL_OPERATIONS_LIMIT = 10
const getAllOperations = accounts => {
const allOperations = accounts.reduce((result, account) => {
const operations = get(account, 'operations', [])
result = [
...result,
...operations.map(t => ({
...t,
account,
})),
]
return result
}, [])
return sortBy(allOperations, t => t.date)
.reverse()
.slice(0, ALL_OPERATIONS_LIMIT)
}
const getAccountsChunk = accounts => { const getAccountsChunk = accounts => {
// create shallow copy of accounts, to be mutated // create shallow copy of accounts, to be mutated
@ -89,7 +65,6 @@ const getAccountsChunk = accounts => {
class DashboardPage extends PureComponent<Props, State> { class DashboardPage extends PureComponent<Props, State> {
state = { state = {
accountsChunk: getAccountsChunk(this.props.accounts), accountsChunk: getAccountsChunk(this.props.accounts),
allOperations: getAllOperations(this.props.accounts),
selectedTime: 'week', selectedTime: 'week',
daysCount: 7, daysCount: 7,
} }
@ -98,7 +73,6 @@ class DashboardPage extends PureComponent<Props, State> {
if (nextProps.accounts !== this.props.accounts) { if (nextProps.accounts !== this.props.accounts) {
this.setState({ this.setState({
accountsChunk: getAccountsChunk(nextProps.accounts), accountsChunk: getAccountsChunk(nextProps.accounts),
allOperations: getAllOperations(nextProps.accounts),
}) })
} }
} }
@ -111,7 +85,7 @@ class DashboardPage extends PureComponent<Props, State> {
render() { render() {
const { push, accounts, t, counterValue } = this.props const { push, accounts, t, counterValue } = this.props
const { accountsChunk, allOperations, selectedTime, daysCount } = this.state const { accountsChunk, selectedTime, daysCount } = this.state
const totalAccounts = accounts.length const totalAccounts = accounts.length
@ -193,7 +167,7 @@ class DashboardPage extends PureComponent<Props, State> {
<OperationsList <OperationsList
canShowMore canShowMore
onAccountClick={account => push(`/account/${account.id}`)} onAccountClick={account => push(`/account/${account.id}`)}
operations={allOperations} accounts={accounts}
title={t('dashboard:recentActivity')} title={t('dashboard:recentActivity')}
withAccount withAccount
/> />

110
src/components/OperationsList/index.js

@ -7,10 +7,14 @@ 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 { getIconByCoinType } from '@ledgerhq/currencies/react'
import {
groupAccountOperationsByDay,
groupAccountsOperationsByDay,
} from '@ledgerhq/wallet-common/lib/helpers/account'
import type { Account, Operation as OperationType } from '@ledgerhq/wallet-common/lib/types' import type { Account, Operation as OperationType } from '@ledgerhq/wallet-common/lib/types'
import noop from 'lodash/noop' import noop from 'lodash/noop'
import isEqual from 'lodash/isEqual' import keyBy from 'lodash/keyBy'
import type { T } from 'types/common' import type { T } from 'types/common'
@ -22,12 +26,13 @@ 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 CounterValue from 'components/CounterValue'
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 Defer from 'components/base/Defer'
import ConfirmationCheck from './ConfirmationCheck' import ConfirmationCheck from './ConfirmationCheck'
const today = moment()
const DATE_COL_SIZE = 100 const DATE_COL_SIZE = 100
const ACCOUNT_COL_SIZE = 150 const ACCOUNT_COL_SIZE = 150
const AMOUNT_COL_SIZE = 150 const AMOUNT_COL_SIZE = 150
@ -69,6 +74,7 @@ const Cell = styled(Box).attrs({
alignItems: 'center', alignItems: 'center',
})` })`
width: ${p => (p.size ? `${p.size}px` : '')}; width: ${p => (p.size ? `${p.size}px` : '')};
overflow: ${p => (p.noOverflow ? 'hidden' : '')};
` `
const ShowMore = styled(Box).attrs({ const ShowMore = styled(Box).attrs({
@ -155,6 +161,7 @@ const Operation = ({
{withAccount && {withAccount &&
account && ( account && (
<Cell <Cell
noOverflow
size={ACCOUNT_COL_SIZE} size={ACCOUNT_COL_SIZE}
horizontal horizontal
flow={2} flow={2}
@ -214,21 +221,22 @@ const mapDispatchToProps = {
type Props = { type Props = {
account: Account, account: Account,
accounts: Account[],
canShowMore: boolean, canShowMore: boolean,
onAccountClick?: Function, onAccountClick?: Function,
openModal: Function, openModal: Function,
operations: OperationType[],
t: T, t: T,
title?: string,
withAccount?: boolean, withAccount?: boolean,
nbToShow: number,
title?: string,
} }
export class OperationsList extends Component<Props> { export class OperationsList extends Component<Props> {
static defaultProps = { static defaultProps = {
account: null,
onAccountClick: noop, onAccountClick: noop,
withAccount: false, withAccount: false,
canShowMore: false, canShowMore: false,
nbToShow: 20,
} }
shouldComponentUpdate(nextProps: Props) { shouldComponentUpdate(nextProps: Props) {
@ -236,55 +244,85 @@ export class OperationsList extends Component<Props> {
return true return true
} }
if (this.props.withAccount !== nextProps.withAccount) { if (this.props.accounts !== nextProps.accounts) {
return true return true
} }
if (this.props.canShowMore !== nextProps.canShowMore) { if (this.props.withAccount !== nextProps.withAccount) {
return true return true
} }
if (this._hashCache === null) { if (this.props.canShowMore !== nextProps.canShowMore) {
return true return true
} }
return !isEqual(this._hashCache, this.getHashCache(nextProps.operations)) return false
} }
getHashCache = (operations: OperationType[]) => operations.map(t => t.id)
handleClickOperation = (data: Object) => this.props.openModal(MODAL_OPERATION_DETAILS, data) handleClickOperation = (data: Object) => this.props.openModal(MODAL_OPERATION_DETAILS, data)
_hashCache = null
render() { render() {
const { account, canShowMore, onAccountClick, operations, t, title, withAccount } = this.props const {
account,
title,
accounts,
canShowMore,
onAccountClick,
t,
withAccount,
nbToShow,
} = this.props
if (!account && !accounts) {
console.warn('Preventing render OperationsList because not received account or accounts') // eslint-disable-line no-console
return null
}
const groupedOperations = accounts
? groupAccountsOperationsByDay(accounts, nbToShow || 20)
: groupAccountOperationsByDay(account, nbToShow)
this._hashCache = this.getHashCache(operations) const accountsMap = accounts ? keyBy(accounts, 'id') : { [account.id]: account }
return ( return (
<Defer> <Defer>
<Box> <Box flow={4}>
<Card flow={1} title={title} p={0}> {title && (
<Box> <Text color="dark" ff="Museo Sans" fontSize={6}>
{operations.map(op => { {title}
// $FlowFixMe </Text>
const acc = account || op.account )}
return ( {groupedOperations.map(group => {
<Operation const d = moment(group.day)
account={acc} const isToday = d.isSame(today, 'day')
key={`${op.id}${acc ? `-${acc.id}` : ''}`} const isYesterday = d.isSame(moment(today).subtract(1, 'day'), 'day')
minConfirmations={acc.minConfirmations} return (
onAccountClick={onAccountClick} <Box flow={2} key={group.day.toISOString()}>
onOperationClick={this.handleClickOperation} <Box ff="Open Sans|SemiBold" fontSize={4} color="grey">
t={t} {isToday ? 'Today' : isYesterday ? 'Yesterday' : d.format('D MMMM YYYY')}
op={op} </Box>
withAccount={withAccount} <Card p={0}>
/> {group.data.map(op => {
) const account = accountsMap[op.accountId]
})} if (!account) {
</Box> return null
</Card> }
return (
<Operation
key={op.hash}
account={account}
minConfirmations={account.minConfirmations}
onAccountClick={onAccountClick}
onOperationClick={this.handleClickOperation}
t={t}
op={op}
withAccount={withAccount}
/>
)
})}
</Card>
</Box>
)
})}
{canShowMore && ( {canShowMore && (
<ShowMore> <ShowMore>
<span>{t('operationsList:showMore')}</span> <span>{t('operationsList:showMore')}</span>

76
src/components/OperationsList/stories.js

@ -1,78 +1,24 @@
// @flow // @flow
import React from 'react' import React from 'react'
import { getCurrencyByCoinType, getDefaultUnitByCoinType } from '@ledgerhq/currencies' import { genAccount } from '@ledgerhq/wallet-common/lib/mock/account'
import { storiesOf } from '@storybook/react' import { storiesOf } from '@storybook/react'
import { boolean } from '@storybook/addon-knobs' import { boolean } from '@storybook/addon-knobs'
import { accounts } from 'components/SelectAccount/stories'
import OperationsList from 'components/OperationsList' import OperationsList from 'components/OperationsList'
import Box from 'components/base/Box'
const stories = storiesOf('Components', module) const stories = storiesOf('Components', module)
const unit = getDefaultUnitByCoinType(0) const account1 = genAccount('account1')
const account2 = genAccount('account2')
const account = ({ name }) => ({
...accounts[0],
minConfirmations: 10,
currency: getCurrencyByCoinType(0),
name,
coinType: 0,
unit,
})
const operations = [
{
address: '5c6ea1716520c7d6e038d36a3223faced3c',
hash: '5c6ea1716520c7d6e038d36a3223faced3c4b8f7ffb69d9fb5bd527d562fdb62',
id: '5c6ea1716520c7d6e038d36a3223faced3c4b8f7ffb69d9fb5bd527d562fdb62',
amount: 1.3e8,
date: new Date('2018-01-09T16:03:52Z'),
confirmations: 1,
account: account({
name: 'Account 1',
}),
},
{
address: '5c6ea1716520c7d6e038d36a3223faced3c',
hash: '26bdf265d725db5bf9d96bff7f8b4c3decaf3223a63d830e6d7c0256171ae6c5',
id: '26bdf265d725db5bf9d96bff7f8b4c3decaf3223a63d830e6d7c0256171ae6c5',
amount: 1.6e8,
date: new Date('2018-01-09T16:03:52Z'),
confirmations: 11,
account: account({
name: 'Account 1',
}),
},
{
address: '27416a48caab90fab053b507b8b6b9d4',
hash: '27416a48caab90fab053b507b8b6b9d48fba75421d3bfdbae4b85f64024bc9c4',
id: '27416a48caab90fab053b507b8b6b9d48fba75421d3bfdbae4b85f64024bc9c4',
amount: -6.5e8,
date: new Date('2018-01-09T16:02:40Z'),
confirmations: 11,
account: account({
name: 'Account 2',
}),
},
{
address: '27416a48caab90fab053b507b8b6b9d4',
hash: '4c9cb42046f58b4eabdfb3d12457abf84d9b6b8b705b350baf09baac84a61472',
id: '4c9cb42046f58b4eabdfb3d12457abf84d9b6b8b705b350baf09baac84a61472',
amount: -4.2e8,
date: new Date('2018-01-09T16:02:40Z'),
confirmations: 1,
account: account({
name: 'Account 2',
}),
},
]
stories.add('OperationsList', () => ( stories.add('OperationsList', () => (
<OperationsList <Box bg="lightGrey" p={6} m={-4}>
operations={operations} <OperationsList
canShowMore={boolean('canShowMore')} accounts={[account1, account2]}
withAccount={boolean('withAccount')} canShowMore={boolean('canShowMore')}
/> withAccount={boolean('withAccount')}
/>
</Box>
)) ))

Loading…
Cancel
Save