Merge pull request #72 from andrerfneves/feature/transactions-infinite-loader
Feature/transactions infinite loader
This commit is contained in:
commit
84df504703
|
@ -30,6 +30,7 @@ describe('Transactions Actions', () => {
|
||||||
const payload = {
|
const payload = {
|
||||||
list: [],
|
list: [],
|
||||||
zecPrice: 0,
|
zecPrice: 0,
|
||||||
|
hasNextPage: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
store.dispatch(loadTransactionsSuccess(payload));
|
store.dispatch(loadTransactionsSuccess(payload));
|
||||||
|
|
|
@ -14,7 +14,7 @@ describe('<LoadingScreen />', () => {
|
||||||
test('should render status pill correctly', () => {
|
test('should render status pill correctly', () => {
|
||||||
const { queryByTestId } = render(
|
const { queryByTestId } = render(
|
||||||
<ThemeProvider theme={appTheme}>
|
<ThemeProvider theme={appTheme}>
|
||||||
<LoadingScreen progress={83.0} />
|
<LoadingScreen progress={83.0} message='ZEC Wallet Starting' />
|
||||||
</ThemeProvider>,
|
</ThemeProvider>,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -3,8 +3,6 @@
|
||||||
import React, { PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { Transition, animated } from 'react-spring';
|
import { Transition, animated } from 'react-spring';
|
||||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
|
||||||
import { ipcRenderer } from 'electron';
|
|
||||||
|
|
||||||
import CircleProgressComponent from 'react-circle';
|
import CircleProgressComponent from 'react-circle';
|
||||||
import { TextComponent } from './text';
|
import { TextComponent } from './text';
|
||||||
|
@ -41,35 +39,27 @@ const Logo = styled.img`
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
progress: number,
|
progress: number,
|
||||||
|
message: string,
|
||||||
};
|
};
|
||||||
|
|
||||||
type State = {
|
type State = {
|
||||||
start: boolean,
|
start: boolean,
|
||||||
message: string,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const TIME_DELAY_ANIM = 100;
|
const TIME_DELAY_ANIM = 100;
|
||||||
|
|
||||||
export class LoadingScreen extends PureComponent<Props, State> {
|
export class LoadingScreen extends PureComponent<Props, State> {
|
||||||
state = { start: false, message: 'ZEC Wallet Starting' };
|
state = { start: false };
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.setState(() => ({ start: true }));
|
this.setState(() => ({ start: true }));
|
||||||
}, TIME_DELAY_ANIM);
|
}, TIME_DELAY_ANIM);
|
||||||
|
|
||||||
ipcRenderer.on('zcashd-params-download', (event: Object, message: string) => {
|
|
||||||
this.setState(() => ({ message }));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
ipcRenderer.removeAllListeners('zcashd-log');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { start, message } = this.state;
|
const { start } = this.state;
|
||||||
const { progress } = this.props;
|
const { progress, message } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Wrapper data-testid='LoadingScreen'>
|
<Wrapper data-testid='LoadingScreen'>
|
||||||
|
|
|
@ -188,7 +188,13 @@ export const TransactionDetailsComponent = ({
|
||||||
<InfoRow>
|
<InfoRow>
|
||||||
<ColumnComponent width='100%'>
|
<ColumnComponent width='100%'>
|
||||||
<Label value='TRANSACTION ID' />
|
<Label value='TRANSACTION ID' />
|
||||||
<TransactionId onClick={() => openExternal(ZCASH_EXPLORER_BASE_URL + transactionId)}>
|
<TransactionId
|
||||||
|
onClick={
|
||||||
|
from !== '(Shielded)'
|
||||||
|
? () => openExternal(ZCASH_EXPLORER_BASE_URL + transactionId)
|
||||||
|
: () => {}
|
||||||
|
}
|
||||||
|
>
|
||||||
<Ellipsis value={transactionId} />
|
<Ellipsis value={transactionId} />
|
||||||
</TransactionId>
|
</TransactionId>
|
||||||
</ColumnComponent>
|
</ColumnComponent>
|
||||||
|
|
|
@ -11,6 +11,7 @@ type Props = {};
|
||||||
type State = {
|
type State = {
|
||||||
isRunning: boolean,
|
isRunning: boolean,
|
||||||
progress: number,
|
progress: number,
|
||||||
|
message: string,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* eslint-disable max-len */
|
/* eslint-disable max-len */
|
||||||
|
@ -22,6 +23,7 @@ export const withDaemonStatusCheck = <PassedProps: {}>(
|
||||||
state = {
|
state = {
|
||||||
isRunning: false,
|
isRunning: false,
|
||||||
progress: 0,
|
progress: 0,
|
||||||
|
message: 'ZEC Wallet Starting',
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
@ -53,21 +55,23 @@ export const withDaemonStatusCheck = <PassedProps: {}>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch((error) => {
|
||||||
|
const statusMessage = error.message === 'Something went wrong' ? 'ZEC Wallet Starting' : error.message;
|
||||||
|
|
||||||
this.setState((state) => {
|
this.setState((state) => {
|
||||||
const newProgress = state.progress > 70 ? state.progress + 2.5 : state.progress + 5;
|
const newProgress = state.progress > 70 ? state.progress + 2.5 : state.progress + 5;
|
||||||
return { progress: newProgress > 95 ? 95 : newProgress };
|
return { progress: newProgress > 95 ? 95 : newProgress, message: statusMessage };
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { isRunning, progress } = this.state;
|
const { isRunning, progress, message } = this.state;
|
||||||
|
|
||||||
if (isRunning) {
|
if (isRunning) {
|
||||||
return <WrappedComponent {...this.props} {...this.state} />;
|
return <WrappedComponent {...this.props} {...this.state} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <LoadingScreen progress={progress} />;
|
return <LoadingScreen progress={progress} message={message} />;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,16 +2,15 @@
|
||||||
|
|
||||||
import eres from 'eres';
|
import eres from 'eres';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import flow from 'lodash.flow';
|
|
||||||
import groupBy from 'lodash.groupby';
|
|
||||||
import dateFns from 'date-fns';
|
|
||||||
import { BigNumber } from 'bignumber.js';
|
import { BigNumber } from 'bignumber.js';
|
||||||
|
import uuidv4 from 'uuid/v4';
|
||||||
|
|
||||||
import { TransactionsView } from '../views/transactions';
|
import { TransactionsView } from '../views/transactions';
|
||||||
import {
|
import {
|
||||||
loadTransactions,
|
loadTransactions,
|
||||||
loadTransactionsSuccess,
|
loadTransactionsSuccess,
|
||||||
loadTransactionsError,
|
loadTransactionsError,
|
||||||
|
resetTransactionsList,
|
||||||
} from '../redux/modules/transactions';
|
} from '../redux/modules/transactions';
|
||||||
import rpc from '../../services/api';
|
import rpc from '../../services/api';
|
||||||
import { listShieldedTransactions } from '../../services/shielded-transactions';
|
import { listShieldedTransactions } from '../../services/shielded-transactions';
|
||||||
|
@ -21,45 +20,64 @@ import { sortByDescend } from '../utils/sort-by-descend';
|
||||||
|
|
||||||
import type { AppState } from '../types/app-state';
|
import type { AppState } from '../types/app-state';
|
||||||
import type { Dispatch } from '../types/redux';
|
import type { Dispatch } from '../types/redux';
|
||||||
|
import type { Transaction } from '../components/transaction-item';
|
||||||
|
|
||||||
const mapStateToProps = ({ transactions }: AppState) => ({
|
const mapStateToProps = ({ transactions }: AppState) => ({
|
||||||
transactions: transactions.list,
|
transactions: transactions.list,
|
||||||
isLoading: transactions.isLoading,
|
isLoading: transactions.isLoading,
|
||||||
error: transactions.error,
|
error: transactions.error,
|
||||||
zecPrice: transactions.zecPrice,
|
zecPrice: transactions.zecPrice,
|
||||||
|
hasNextPage: transactions.hasNextPage,
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch: Dispatch) => ({
|
export type MapStateToProps = {
|
||||||
getTransactions: async () => {
|
transactions: Transaction[],
|
||||||
|
isLoading: boolean,
|
||||||
|
error: string | null,
|
||||||
|
zecPrice: number,
|
||||||
|
hasNextPage: boolean,
|
||||||
|
};
|
||||||
|
|
||||||
|
export type MapDispatchToProps = {|
|
||||||
|
getTransactions: ({
|
||||||
|
offset: number,
|
||||||
|
count: number,
|
||||||
|
shieldedTransactionsCount: number,
|
||||||
|
}) => Promise<void>,
|
||||||
|
resetTransactionsList: () => void,
|
||||||
|
|};
|
||||||
|
|
||||||
|
const mapDispatchToProps = (dispatch: Dispatch): MapDispatchToProps => ({
|
||||||
|
resetTransactionsList: () => dispatch(resetTransactionsList()),
|
||||||
|
getTransactions: async ({ offset, count, shieldedTransactionsCount }) => {
|
||||||
dispatch(loadTransactions());
|
dispatch(loadTransactions());
|
||||||
|
|
||||||
const [transactionsErr, transactions = []] = await eres(rpc.listtransactions('', 200));
|
const [transactionsErr, transactions = []] = await eres(
|
||||||
|
rpc.listtransactions('', count, offset),
|
||||||
|
);
|
||||||
|
|
||||||
if (transactionsErr) {
|
if (transactionsErr) {
|
||||||
return dispatch(loadTransactionsError({ error: transactionsErr.message }));
|
return dispatch(loadTransactionsError({ error: transactionsErr.message }));
|
||||||
}
|
}
|
||||||
|
|
||||||
const formattedTransactions = flow([
|
const formattedTransactions = sortByDescend('date')(
|
||||||
arr => arr.map(transaction => ({
|
[
|
||||||
transactionId: transaction.txid,
|
...transactions,
|
||||||
|
...listShieldedTransactions({ count, offset: shieldedTransactionsCount }),
|
||||||
|
].map(transaction => ({
|
||||||
|
transactionId: transaction.txid ? transaction.txid : uuidv4(),
|
||||||
type: transaction.category,
|
type: transaction.category,
|
||||||
date: new Date(transaction.time * 1000).toISOString(),
|
date: new Date(transaction.time * 1000).toISOString(),
|
||||||
address: transaction.address,
|
address: transaction.address,
|
||||||
amount: new BigNumber(transaction.amount).absoluteValue().toNumber(),
|
amount: new BigNumber(transaction.amount).absoluteValue().toNumber(),
|
||||||
})),
|
})),
|
||||||
arr => groupBy(arr, obj => dateFns.format(obj.date, 'MMM DD, YYYY')),
|
);
|
||||||
obj => Object.keys(obj).map(day => ({
|
|
||||||
day,
|
|
||||||
jsDay: new Date(day),
|
|
||||||
list: sortByDescend('date')(obj[day]),
|
|
||||||
})),
|
|
||||||
sortByDescend('jsDay'),
|
|
||||||
])([...transactions, ...listShieldedTransactions()]);
|
|
||||||
|
|
||||||
dispatch(
|
dispatch(
|
||||||
loadTransactionsSuccess({
|
loadTransactionsSuccess({
|
||||||
list: formattedTransactions,
|
list: formattedTransactions,
|
||||||
zecPrice: new BigNumber(store.get('ZEC_DOLLAR_PRICE')).toNumber(),
|
zecPrice: new BigNumber(store.get('ZEC_DOLLAR_PRICE')).toNumber(),
|
||||||
|
hasNextPage: Boolean(formattedTransactions.length),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
import uniqBy from 'lodash.uniqby';
|
||||||
import type { Action } from '../../types/redux';
|
import type { Action } from '../../types/redux';
|
||||||
import type { Transaction } from '../../components/transaction-item';
|
import type { Transaction } from '../../components/transaction-item';
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ import type { Transaction } from '../../components/transaction-item';
|
||||||
export const LOAD_TRANSACTIONS = 'LOAD_TRANSACTIONS';
|
export const LOAD_TRANSACTIONS = 'LOAD_TRANSACTIONS';
|
||||||
export const LOAD_TRANSACTIONS_SUCCESS = 'LOAD_TRANSACTIONS_SUCCESS';
|
export const LOAD_TRANSACTIONS_SUCCESS = 'LOAD_TRANSACTIONS_SUCCESS';
|
||||||
export const LOAD_TRANSACTIONS_ERROR = 'LOAD_TRANSACTIONS_ERROR';
|
export const LOAD_TRANSACTIONS_ERROR = 'LOAD_TRANSACTIONS_ERROR';
|
||||||
|
export const RESET_TRANSACTIONS_LIST = 'RESET_TRANSACTIONS_LIST';
|
||||||
|
|
||||||
export type TransactionsList = { day: string, list: Transaction[] }[];
|
export type TransactionsList = { day: string, list: Transaction[] }[];
|
||||||
|
|
||||||
|
@ -18,14 +19,17 @@ export const loadTransactions = () => ({
|
||||||
export const loadTransactionsSuccess = ({
|
export const loadTransactionsSuccess = ({
|
||||||
list,
|
list,
|
||||||
zecPrice,
|
zecPrice,
|
||||||
|
hasNextPage,
|
||||||
}: {
|
}: {
|
||||||
list: TransactionsList,
|
list: Transaction[],
|
||||||
zecPrice: number,
|
zecPrice: number,
|
||||||
|
hasNextPage: boolean,
|
||||||
}) => ({
|
}) => ({
|
||||||
type: LOAD_TRANSACTIONS_SUCCESS,
|
type: LOAD_TRANSACTIONS_SUCCESS,
|
||||||
payload: {
|
payload: {
|
||||||
list,
|
list,
|
||||||
zecPrice,
|
zecPrice,
|
||||||
|
hasNextPage,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -34,11 +38,17 @@ export const loadTransactionsError = ({ error }: { error: string }) => ({
|
||||||
payload: { error },
|
payload: { error },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const resetTransactionsList = () => ({
|
||||||
|
type: RESET_TRANSACTIONS_LIST,
|
||||||
|
payload: {},
|
||||||
|
});
|
||||||
|
|
||||||
export type State = {
|
export type State = {
|
||||||
isLoading: boolean,
|
isLoading: boolean,
|
||||||
error: string | null,
|
error: string | null,
|
||||||
list: TransactionsList,
|
list: Transaction[],
|
||||||
zecPrice: number,
|
zecPrice: number,
|
||||||
|
hasNextPage: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
const initialState = {
|
const initialState = {
|
||||||
|
@ -46,6 +56,7 @@ const initialState = {
|
||||||
list: [],
|
list: [],
|
||||||
error: null,
|
error: null,
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
|
hasNextPage: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
|
@ -61,6 +72,7 @@ export default (state: State = initialState, action: Action) => {
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
...action.payload,
|
...action.payload,
|
||||||
|
list: uniqBy(state.list.concat(action.payload.list), tr => tr.transactionId + tr.type),
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
error: null,
|
error: null,
|
||||||
};
|
};
|
||||||
|
@ -70,6 +82,13 @@ export default (state: State = initialState, action: Action) => {
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
error: action.payload.error,
|
error: action.payload.error,
|
||||||
};
|
};
|
||||||
|
case RESET_TRANSACTIONS_LIST:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
isLoading: false,
|
||||||
|
error: null,
|
||||||
|
list: [],
|
||||||
|
};
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,48 +1,198 @@
|
||||||
// @flow
|
// @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 { TextComponent } from '../components/text';
|
||||||
import { EmptyTransactionsComponent } from '../components/empty-transactions';
|
import { EmptyTransactionsComponent } from '../components/empty-transactions';
|
||||||
|
|
||||||
import type { TransactionsList } from '../redux/modules/transactions';
|
import type { MapDispatchToProps, MapStateToProps } from '../containers/transactions';
|
||||||
|
|
||||||
type Props = {
|
type Props = MapDispatchToProps & MapStateToProps;
|
||||||
error: string | null,
|
|
||||||
transactions: TransactionsList,
|
const PAGE_SIZE = 15;
|
||||||
zecPrice: number,
|
const ROW_HEIGHT = 60;
|
||||||
getTransactions: () => void,
|
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<Props> {
|
export class TransactionsView extends PureComponent<Props> {
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
// eslint-disable-next-line
|
const { getTransactions, resetTransactionsList } = this.props;
|
||||||
this.props.getTransactions();
|
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 <RoundedTransactionWrapper roundPosition='top'>{component}</RoundedTransactionWrapper>;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
nextTransactionDate
|
||||||
|
&& (nextTransactionDate && !dateFns.isSameDay(transactionDate, nextTransactionDate))
|
||||||
|
) {
|
||||||
|
return (
|
||||||
|
<RoundedTransactionWrapper roundPosition='bottom'>{component}</RoundedTransactionWrapper>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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: (
|
||||||
|
<TransactionItemComponent
|
||||||
|
address={transaction.address}
|
||||||
|
amount={transaction.amount}
|
||||||
|
date={transaction.date}
|
||||||
|
transactionId={transaction.transactionId}
|
||||||
|
type={transaction.type}
|
||||||
|
zecPrice={zecPrice}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
index,
|
||||||
|
});
|
||||||
|
if (
|
||||||
|
index === 0
|
||||||
|
|| (previousTransaction
|
||||||
|
&& !dateFns.isSameDay(new Date(previousTransaction.date), new Date(transaction.date)))
|
||||||
|
) {
|
||||||
|
return (
|
||||||
|
<Fragment>
|
||||||
|
<Day value={dateFns.format(new Date(transaction.date), 'MMM DD, YYYY')} />
|
||||||
|
{transactionItem}
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return transactionItem;
|
||||||
|
};
|
||||||
|
|
||||||
|
renderRow = ({ index, key, style }: { index: number, key: string, style: Object }) => (
|
||||||
|
<div key={key} style={style}>
|
||||||
|
{this.isRowLoaded({ index }) ? this.renderTransactions({ index }) : 'Loading...'}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
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() {
|
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) {
|
if (error) {
|
||||||
return <TextComponent value={error} />;
|
return <TextComponent value={error} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<InfiniteLoader
|
||||||
{transactions.length === 0 ? (
|
isRowLoaded={isRowLoaded}
|
||||||
<EmptyTransactionsComponent />
|
loadMoreRows={this.loadMoreRows}
|
||||||
) : (
|
rowCount={rowCount}
|
||||||
transactions.map(({ day, list }) => (
|
>
|
||||||
<TransactionDailyComponent
|
{({ onRowsRendered, registerChild }) => (
|
||||||
transactionsDate={day}
|
<AutoSizer>
|
||||||
transactions={list}
|
{({ width, height }) => (
|
||||||
zecPrice={zecPrice}
|
<List
|
||||||
key={day}
|
noRowsRenderer={EmptyTransactionsComponent}
|
||||||
/>
|
ref={registerChild}
|
||||||
))
|
onRowsRendered={onRowsRendered}
|
||||||
|
rowRenderer={this.renderRow}
|
||||||
|
rowHeight={this.getRowHeight}
|
||||||
|
rowCount={transactionsSize}
|
||||||
|
width={width}
|
||||||
|
height={height - 20}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</AutoSizer>
|
||||||
)}
|
)}
|
||||||
</Fragment>
|
</InfiniteLoader>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
declare module 'lodash.uniqby' {
|
||||||
|
declare module.exports: <T>(arr: T[], (T) => $Values<T>) => T[];
|
||||||
|
}
|
|
@ -108,6 +108,7 @@
|
||||||
"history": "^4.7.2",
|
"history": "^4.7.2",
|
||||||
"lodash.flow": "^3.5.0",
|
"lodash.flow": "^3.5.0",
|
||||||
"lodash.groupby": "^4.6.0",
|
"lodash.groupby": "^4.6.0",
|
||||||
|
"lodash.uniqby": "^4.7.0",
|
||||||
"p-queue": "^3.0.0",
|
"p-queue": "^3.0.0",
|
||||||
"process-exists": "^3.1.0",
|
"process-exists": "^3.1.0",
|
||||||
"qrcode.react": "^0.8.0",
|
"qrcode.react": "^0.8.0",
|
||||||
|
@ -119,6 +120,7 @@
|
||||||
"react-redux": "^5.0.7",
|
"react-redux": "^5.0.7",
|
||||||
"react-router-dom": "^4.2.2",
|
"react-router-dom": "^4.2.2",
|
||||||
"react-spring": "^7.2.10",
|
"react-spring": "^7.2.10",
|
||||||
|
"react-virtualized": "^9.21.0",
|
||||||
"redux": "^4.0.1",
|
"redux": "^4.0.1",
|
||||||
"redux-thunk": "^2.2.0",
|
"redux-thunk": "^2.2.0",
|
||||||
"styled-components": "^4.1.1",
|
"styled-components": "^4.1.1",
|
||||||
|
|
|
@ -12,7 +12,20 @@ type ShieldedTransaction = {|
|
||||||
|};
|
|};
|
||||||
|
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
export const listShieldedTransactions = (): Array<ShieldedTransaction> => electronStore.has(STORE_KEY) ? electronStore.get(STORE_KEY) : [];
|
export const listShieldedTransactions = (
|
||||||
|
pagination: ?{
|
||||||
|
offset: number,
|
||||||
|
count: number,
|
||||||
|
},
|
||||||
|
): Array<ShieldedTransaction> => {
|
||||||
|
const transactions = electronStore.has(STORE_KEY) ? electronStore.get(STORE_KEY) : [];
|
||||||
|
|
||||||
|
if (!pagination) return transactions;
|
||||||
|
|
||||||
|
const { offset = 0, count = 10 } = pagination;
|
||||||
|
|
||||||
|
return transactions.slice(offset - 1, offset + count);
|
||||||
|
};
|
||||||
|
|
||||||
export const saveShieldedTransaction = ({
|
export const saveShieldedTransaction = ({
|
||||||
category,
|
category,
|
||||||
|
|
Loading…
Reference in New Issue