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. 88
      src/components/OperationsList/index.js
  4. 68
      src/components/OperationsList/stories.js

6
src/components/AccountPage/index.js

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

32
src/components/DashboardPage/index.js

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

88
src/components/OperationsList/index.js

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

68
src/components/OperationsList/stories.js

@ -1,78 +1,24 @@
// @flow
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 { boolean } from '@storybook/addon-knobs'
import { accounts } from 'components/SelectAccount/stories'
import OperationsList from 'components/OperationsList'
import Box from 'components/base/Box'
const stories = storiesOf('Components', module)
const unit = getDefaultUnitByCoinType(0)
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',
}),
},
]
const account1 = genAccount('account1')
const account2 = genAccount('account2')
stories.add('OperationsList', () => (
<Box bg="lightGrey" p={6} m={-4}>
<OperationsList
operations={operations}
accounts={[account1, account2]}
canShowMore={boolean('canShowMore')}
withAccount={boolean('withAccount')}
/>
</Box>
))

Loading…
Cancel
Save