feat(@cockpit/explorer): implement pagination for accounts explorer

Display two accounts per page in the explorer overview. Display ten accounts
per page in the accounts explorer page.

Sort accounts by their api-supplied index numbers with the lowest index coming
first.

Change the `initBlockHeader` saga so that it triggers a re-fetch of accounts
and therefore the "Tx Count" numbers of displayed accounts will reflect
increased counts.
This commit is contained in:
Michael Bradley, Jr 2019-04-01 14:40:16 -05:00 committed by Iuri Matias
parent 5a502b379a
commit 745edafee4
5 changed files with 92 additions and 12 deletions

View File

@ -2,10 +2,11 @@ import React from 'react';
import {Row, Col, Card, CardHeader, CardBody} from 'reactstrap'; import {Row, Col, Card, CardHeader, CardBody} from 'reactstrap';
import {Link} from 'react-router-dom'; import {Link} from 'react-router-dom';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import Pagination from './Pagination';
import CardTitleIdenticon from './CardTitleIdenticon'; import CardTitleIdenticon from './CardTitleIdenticon';
const Accounts = ({accounts}) => ( const Accounts = ({accounts, changePage, currentPage, numberOfPages}) => (
<Row> <Row>
<Col> <Col>
<Card> <Card>
@ -13,6 +14,7 @@ const Accounts = ({accounts}) => (
<h2>Accounts</h2> <h2>Accounts</h2>
</CardHeader> </CardHeader>
<CardBody> <CardBody>
{!accounts.length && "No accounts to display"}
{accounts.map(account => ( {accounts.map(account => (
<div className="explorer-row border-top" key={account.address}> <div className="explorer-row border-top" key={account.address}>
<CardTitleIdenticon id={account.address}>Account&nbsp; <CardTitleIdenticon id={account.address}>Account&nbsp;
@ -34,6 +36,7 @@ const Accounts = ({accounts}) => (
</Row> </Row>
</div> </div>
))} ))}
{numberOfPages > 1 && <Pagination changePage={changePage} currentPage={currentPage} numberOfPages={numberOfPages}/>}
</CardBody> </CardBody>
</Card> </Card>
</Col> </Col>
@ -41,7 +44,10 @@ const Accounts = ({accounts}) => (
); );
Accounts.propTypes = { Accounts.propTypes = {
accounts: PropTypes.arrayOf(PropTypes.object) accounts: PropTypes.arrayOf(PropTypes.object),
changePage: PropTypes.func,
currentPage: PropTypes.number,
numberOfPages: PropTypes.number
}; };
export default Accounts; export default Accounts;

View File

@ -17,7 +17,7 @@ const ExplorerDashboardLayout = () => (
<div className="explorer-overview"> <div className="explorer-overview">
<Row> <Row>
<Col> <Col>
<AccountsContainer overridePageHead={false} /> <AccountsContainer numAccountsToDisplay={2} overridePageHead={false} />
</Col> </Col>
</Row> </Row>
<Row> <Row>

View File

@ -1,37 +1,105 @@
import React, {Component} from 'react'; import React, {Component} from 'react';
import {connect} from 'react-redux'; import {connect} from 'react-redux';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import {accounts as accountsAction,
import {accounts as accountsAction} from '../actions'; initBlockHeader,
stopBlockHeader} from '../actions';
import Accounts from '../components/Accounts'; import Accounts from '../components/Accounts';
import DataWrapper from "../components/DataWrapper";
import {getAccounts} from "../reducers/selectors"; import {getAccounts} from "../reducers/selectors";
import PageHead from "../components/PageHead"; import PageHead from "../components/PageHead";
const MAX_ACCOUNTS = 10;
class AccountsContainer extends Component { class AccountsContainer extends Component {
constructor(props) {
super(props);
this.numAccountsToDisplay = this.props.numAccountsToDisplay || MAX_ACCOUNTS;
this.state = {currentPage: 1};
}
componentDidMount() { componentDidMount() {
this.props.fetchAccounts(); this.props.fetchAccounts();
this.props.initBlockHeader();
}
componentWillUnmount() {
this.props.stopBlockHeader();
}
get numberOfAccounts() {
if (this._numberOfAccounts === undefined) {
this._numberOfAccounts = this.props.accounts.length;
}
return this._numberOfAccounts;
}
get numberOfPages() {
if (this._numberOfPages === undefined) {
this._numberOfPages = Math.ceil(
this.numberOfAccounts / this.numAccountsToDisplay
);
}
return this._numberOfPages;
}
resetNums() {
this._numberOfAccounts = undefined;
this._numberOfPages = undefined;
}
changePage(newPage) {
if (newPage <= 0) {
newPage = 1;
} else if (newPage > this.numberOfPages) {
newPage = this.numberOfPages;
}
this.setState({ currentPage: newPage });
this.props.fetchAccounts();
}
get currentAccounts() {
return this.props.accounts
.filter((account) => {
const index = (
(account.index + 1) -
(this.numAccountsToDisplay * (this.state.currentPage - 1))
);
return index <= this.numAccountsToDisplay && index > 0;
});
} }
render() { render() {
this.resetNums();
return ( return (
<React.Fragment> <React.Fragment>
<PageHead title="Accounts" enabled={this.props.overridePageHead} description="Summary view of the accounts configured for Embark" /> <PageHead
<DataWrapper shouldRender={this.props.accounts.length > 0} {...this.props} render={({accounts}) => ( title="Accounts"
<Accounts accounts={accounts} /> enabled={this.props.overridePageHead}
)} /> description="Summary view of the accounts configured for Embark" />
<Accounts accounts={this.currentAccounts}
numberOfPages={this.numberOfPages}
changePage={(newPage) => this.changePage(newPage)}
currentPage={this.state.currentPage} />
</React.Fragment> </React.Fragment>
); );
} }
} }
function mapStateToProps(state) { function mapStateToProps(state) {
return {accounts: getAccounts(state), error: state.errorMessage, loading: state.loading}; return {
accounts: getAccounts(state),
error: state.errorMessage,
loading: state.loading
};
} }
AccountsContainer.propTypes = { AccountsContainer.propTypes = {
accounts: PropTypes.arrayOf(PropTypes.object), accounts: PropTypes.arrayOf(PropTypes.object),
fetchAccounts: PropTypes.func, fetchAccounts: PropTypes.func,
numAccountsToDisplay: PropTypes.number,
initBlockHeader: PropTypes.func,
stopBlockHeader: PropTypes.func,
error: PropTypes.string, error: PropTypes.string,
loading: PropTypes.bool, loading: PropTypes.bool,
overridePageHead: PropTypes.bool overridePageHead: PropTypes.bool
@ -40,6 +108,8 @@ AccountsContainer.propTypes = {
export default connect( export default connect(
mapStateToProps, mapStateToProps,
{ {
fetchAccounts: accountsAction.request fetchAccounts: accountsAction.request,
initBlockHeader,
stopBlockHeader
}, },
)(AccountsContainer); )(AccountsContainer);

View File

@ -41,6 +41,9 @@ const entitiesDefaultState = {
}; };
const sorter = { const sorter = {
accounts: function(a, b) {
return a.index - b.index;
},
blocks: function(a, b) { blocks: function(a, b) {
return b.number - a.number; return b.number - a.number;
}, },

View File

@ -420,6 +420,7 @@ export function *initBlockHeader() {
channel.close(); channel.close();
return; return;
} }
yield put({type: actions.ACCOUNTS[actions.REQUEST]});
yield put({type: actions.BLOCKS[actions.REQUEST]}); yield put({type: actions.BLOCKS[actions.REQUEST]});
yield put({type: actions.BLOCKS_FULL[actions.REQUEST], txObjects: true, txReceipts: true}); yield put({type: actions.BLOCKS_FULL[actions.REQUEST], txObjects: true, txReceipts: true});
yield put({type: actions.TRANSACTIONS[actions.REQUEST]}); yield put({type: actions.TRANSACTIONS[actions.REQUEST]});