From 91f32e1a8d885cc56f95cab7f636fb7e92917ea9 Mon Sep 17 00:00:00 2001 From: George Lima Date: Tue, 12 Feb 2019 13:51:23 -0300 Subject: [PATCH] feat(transactions): add InfiniteScroll in transactions view --- app/views/transactions.js | 200 +++++++++++++++++++++++++++++++++----- 1 file changed, 175 insertions(+), 25 deletions(-) diff --git a/app/views/transactions.js b/app/views/transactions.js index 331a570..fb24948 100644 --- a/app/views/transactions.js +++ b/app/views/transactions.js @@ -1,48 +1,198 @@ // @flow -import React, { PureComponent, Fragment } from 'react'; +import React, { PureComponent, Fragment, type Element } from 'react'; +import { InfiniteLoader, AutoSizer, List } from 'react-virtualized'; +import styled from 'styled-components'; +import dateFns from 'date-fns'; -import { TransactionDailyComponent } from '../components/transaction-daily'; +import { TransactionItemComponent } from '../components/transaction-item'; import { TextComponent } from '../components/text'; import { EmptyTransactionsComponent } from '../components/empty-transactions'; -import type { TransactionsList } from '../redux/modules/transactions'; +import type { MapDispatchToProps, MapStateToProps } from '../containers/transactions'; -type Props = { - error: string | null, - transactions: TransactionsList, - zecPrice: number, - getTransactions: () => void, -}; +type Props = MapDispatchToProps & MapStateToProps; + +const PAGE_SIZE = 15; +const ROW_HEIGHT = 60; +const ROW_HEIGHT_WITH_HEADER = 88; + +const Day = styled(TextComponent)` + text-transform: uppercase; + color: ${props => props.theme.colors.transactionsDate}; + font-size: ${props => `${props.theme.fontSize.regular * 0.9}em`}; + font-weight: ${props => String(props.theme.fontWeight.bold)}; + margin-top: 10px; + margin-bottom: 5px; +`; + +const RoundedTransactionWrapper = styled.div` + overflow: hidden; + + ${props => (props.roundPosition === 'top' + ? ` + border-top-left-radius: ${props.theme.boxBorderRadius}; + border-top-right-radius: ${props.theme.boxBorderRadius};` + : `border-bottom-left-radius: ${props.theme.boxBorderRadius}; + border-bottom-right-radius: ${props.theme.boxBorderRadius};`)} +`; export class TransactionsView extends PureComponent { componentDidMount() { - // eslint-disable-next-line - this.props.getTransactions(); + const { getTransactions, resetTransactionsList } = this.props; + resetTransactionsList(); + getTransactions({ count: PAGE_SIZE, offset: 0, shieldedTransactionsCount: 0 }); } + isRowLoaded = ({ index }: { index: number }) => { + const { hasNextPage, transactions } = this.props; + const transactionsSize = transactions.length; + + return !hasNextPage || index < transactionsSize; + }; + + renderTransactionWrapper = ({ + index, + transactionDate, + previousTransactionDate, + nextTransactionDate, + component, + }: {| + index: number, + transactionDate: Date, + previousTransactionDate: ?Date, + nextTransactionDate: ?Date, + component: Element<*>, + |}) => { + if ( + index === 0 + || (previousTransactionDate && !dateFns.isSameDay(transactionDate, previousTransactionDate)) + ) { + return {component}; + } + + if ( + nextTransactionDate + && (nextTransactionDate && !dateFns.isSameDay(transactionDate, nextTransactionDate)) + ) { + return ( + {component} + ); + } + + return component; + }; + + renderTransactions = ({ index }: { index: number }) => { + const { transactions, zecPrice } = this.props; + + const transaction = transactions[index]; + const previousTransaction = transactions[index - 1]; + const nextTransaction = transactions[index + 1]; + + const transactionItem = this.renderTransactionWrapper({ + transactionDate: new Date(transaction.date), + previousTransactionDate: previousTransaction ? new Date(previousTransaction.date) : null, + nextTransactionDate: nextTransaction ? new Date(nextTransaction.date) : null, + component: ( + + ), + index, + }); + if ( + index === 0 + || (previousTransaction + && !dateFns.isSameDay(new Date(previousTransaction.date), new Date(transaction.date))) + ) { + return ( + + + {transactionItem} + + ); + } + + return transactionItem; + }; + + renderRow = ({ index, key, style }: { index: number, key: string, style: Object }) => ( +
+ {!this.isRowLoaded({ index }) ? 'Loading...' : this.renderTransactions({ index })} +
+ ); + + getRowHeight = ({ index }: { index: number }) => { + const { transactions } = this.props; + + const transaction = transactions[index]; + + if ( + index === 0 + || !dateFns.isSameDay(new Date(transactions[index - 1].date), new Date(transaction.date)) + ) { + return ROW_HEIGHT_WITH_HEADER; + } + + return ROW_HEIGHT; + }; + + loadNextPage = () => { + const { transactions, getTransactions } = this.props; + + const shieldedTransactionsCount = transactions.filter( + transaction => transaction.address === '(Shielded)', + ).length; + + getTransactions({ count: PAGE_SIZE, offset: transactions.length, shieldedTransactionsCount }); + }; + + loadMoreRows = async () => { + const { isLoading } = this.props; + + return isLoading ? Promise.resolve([]) : this.loadNextPage(); + }; + render() { - const { error, transactions, zecPrice } = this.props; + const { error, transactions, hasNextPage } = this.props; + + const transactionsSize = transactions.length; + const isRowLoaded = ({ index }) => !hasNextPage || index < transactionsSize; + const rowCount = transactionsSize ? transactionsSize + 1 : transactionsSize; if (error) { return ; } return ( - - {transactions.length === 0 ? ( - - ) : ( - transactions.map(({ day, list }) => ( - - )) + + {({ onRowsRendered, registerChild }) => ( + + {({ width, height }) => ( + + )} + )} - + ); } }