Merge pull request #156 from status-im/features/explorer-pagination

Add pagination to explorer
This commit is contained in:
Iuri Matias 2018-10-24 17:13:16 -04:00 committed by GitHub
commit 90c033187d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 159 additions and 54 deletions

View File

@ -2,11 +2,11 @@ import React from 'react';
import {Link} from "react-router-dom"; import {Link} from "react-router-dom";
import {Row, Col, Card, CardHeader, CardBody} from 'reactstrap'; import {Row, Col, Card, CardHeader, CardBody} from 'reactstrap';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import Pagination from './Pagination';
import CardTitleIdenticon from './CardTitleIdenticon'; import CardTitleIdenticon from './CardTitleIdenticon';
import LoadMore from "./LoadMore";
const Blocks = ({blocks, showLoadMore, loadMore}) => ( const Blocks = ({blocks, changePage, currentPage, numberOfPages}) => (
<Row> <Row>
<Col> <Col>
<Card> <Card>
@ -37,7 +37,7 @@ const Blocks = ({blocks, showLoadMore, loadMore}) => (
</Row> </Row>
</div> </div>
))} ))}
{showLoadMore && <LoadMore loadMore={() => loadMore()}/>} <Pagination changePage={changePage} currentPage={currentPage} numberOfPages={numberOfPages}/>
</CardBody> </CardBody>
</Card> </Card>
</Col> </Col>
@ -46,8 +46,9 @@ const Blocks = ({blocks, showLoadMore, loadMore}) => (
Blocks.propTypes = { Blocks.propTypes = {
blocks: PropTypes.arrayOf(PropTypes.object), blocks: PropTypes.arrayOf(PropTypes.object),
showLoadMore: PropTypes.bool, changePage: PropTypes.func,
loadMore: PropTypes.func currentPage: PropTypes.number,
numberOfPages: PropTypes.number
}; };
export default Blocks; export default Blocks;

View File

@ -2,6 +2,10 @@
border-top-width: 0 !important; /*Bootstrap uses important, so we need to override it*/ border-top-width: 0 !important; /*Bootstrap uses important, so we need to override it*/
} }
.explorer-overview .card-body {
overflow: hidden;
}
.explorer-row + .explorer-row { .explorer-row + .explorer-row {
margin-top: 5px; margin-top: 5px;
padding-top: 20px; padding-top: 20px;
@ -12,3 +16,8 @@
width: 90%; width: 90%;
display: inline-block; display: inline-block;
} }
.explorer-row .text-truncate {
width: 90%;
display: inline-block;
}

View File

@ -11,21 +11,21 @@ import TransactionsContainer from '../containers/TransactionsContainer';
import './Explorer.css'; import './Explorer.css';
const ExplorerDashboardLayout = () => ( const ExplorerDashboardLayout = () => (
<React.Fragment> <div className="explorer-overview">
<Row className="mt-4"> <Row className="mt-4">
<Col> <Col>
<AccountsContainer /> <AccountsContainer />
</Col> </Col>
</Row> </Row>
<Row> <Row>
<Col md={6}> <Col xl={6}>
<BlocksContainer /> <BlocksContainer />
</Col> </Col>
<Col md={6}> <Col xl={6}>
<TransactionsContainer /> <TransactionsContainer />
</Col> </Col>
</Row> </Row>
</React.Fragment> </div>
); );
export default ExplorerDashboardLayout; export default ExplorerDashboardLayout;

View File

@ -129,7 +129,7 @@ class Layout extends React.Component {
</Nav> </Nav>
<AppHeaderDropdown className="list-unstyled d-xl-none" direction="down"> <AppHeaderDropdown className="list-unstyled d-xl-none" direction="down">
<DropdownToggle nav> <DropdownToggle nav>
<FontAwesome name='bars'/> <FontAwesome name="bars"/>
</DropdownToggle> </DropdownToggle>
<DropdownMenu> <DropdownMenu>
{HEADER_NAV_ITEMS.map((item) => ( {HEADER_NAV_ITEMS.map((item) => (

View File

@ -1,17 +0,0 @@
import React from 'react';
import {Row, Col, Button} from 'reactstrap';
import PropTypes from 'prop-types';
const LoadMore = ({loadMore}) => (
<Row className="my-3">
<Col className="text-center">
<Button onClick={loadMore} icon="plus" outline color="primary">Load More</Button>
</Col>
</Row>
);
LoadMore.propTypes = {
loadMore: PropTypes.func
};
export default LoadMore;

View File

@ -0,0 +1,56 @@
import React from 'react';
import {Pagination as RPagination, PaginationItem, PaginationLink} from 'reactstrap';
import PropTypes from 'prop-types';
const NB_PAGES_MAX = 8;
const Pagination = ({currentPage, numberOfPages, changePage}) => {
let max = currentPage + NB_PAGES_MAX / 2;
if (max > numberOfPages) {
max = numberOfPages;
}
let i = max - NB_PAGES_MAX;
if (i < 1) {
i = 1;
}
if (max - i < NB_PAGES_MAX) {
max += NB_PAGES_MAX - max + 1;
}
const pageNumbers = [];
for (i; i <= max; i++) {
pageNumbers.push(i);
}
return (
<RPagination aria-label="Explorer navigation" className="mt-4 mb-0 float-right">
<PaginationItem disabled={currentPage <= 1}>
<PaginationLink previous href="#" onClick={(e) => {
e.preventDefault();
changePage(currentPage - 1);
}}/>
</PaginationItem>
{pageNumbers.map(number => (<PaginationItem active={currentPage === number} key={'page-' + number}>
<PaginationLink href="#" onClick={(e) => {
e.preventDefault();
changePage(number);
}}>
{number}
</PaginationLink>
</PaginationItem>))}
<PaginationItem disabled={currentPage >= numberOfPages}>
<PaginationLink next href="#" onClick={(e) => {
e.preventDefault();
changePage(currentPage + 1);
}}/>
</PaginationItem>
</RPagination>
);
};
Pagination.propTypes = {
numberOfPages: PropTypes.number,
currentPage: PropTypes.number,
changePage: PropTypes.func
};
export default Pagination;

View File

@ -4,9 +4,9 @@ import {Row, Col, Card, CardHeader, CardBody} from 'reactstrap';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import CardTitleIdenticon from './CardTitleIdenticon'; import CardTitleIdenticon from './CardTitleIdenticon';
import LoadMore from "./LoadMore"; import Pagination from "./Pagination";
const Transactions = ({transactions, showLoadMore, loadMore}) => ( const Transactions = ({transactions, changePage, currentPage, numberOfPages}) => (
<Row> <Row>
<Col> <Col>
<Card> <Card>
@ -41,7 +41,7 @@ const Transactions = ({transactions, showLoadMore, loadMore}) => (
</Row> </Row>
</div> </div>
))} ))}
{showLoadMore && <LoadMore loadMore={() => loadMore()}/>} <Pagination changePage={changePage} currentPage={currentPage} numberOfPages={numberOfPages}/>
</CardBody> </CardBody>
</Card> </Card>
</Col> </Col>
@ -50,8 +50,9 @@ const Transactions = ({transactions, showLoadMore, loadMore}) => (
Transactions.propTypes = { Transactions.propTypes = {
transactions: PropTypes.arrayOf(PropTypes.object), transactions: PropTypes.arrayOf(PropTypes.object),
showLoadMore: PropTypes.bool, changePage: PropTypes.func,
loadMore: PropTypes.func currentPage: PropTypes.number,
numberOfPages: PropTypes.number
}; };
export default Transactions; export default Transactions;

View File

@ -7,7 +7,17 @@ import Blocks from '../components/Blocks';
import DataWrapper from "../components/DataWrapper"; import DataWrapper from "../components/DataWrapper";
import {getBlocks} from "../reducers/selectors"; import {getBlocks} from "../reducers/selectors";
const MAX_BLOCKS = 10; // TODO use same constant as API
class BlocksContainer extends Component { class BlocksContainer extends Component {
constructor(props) {
super(props);
this.state = {currentPage: 0};
this.numberOfBlocks = 0;
this.currentBlocks = [];
}
componentDidMount() { componentDidMount() {
this.props.fetchBlocks(); this.props.fetchBlocks();
this.props.initBlockHeader(); this.props.initBlockHeader();
@ -17,23 +27,41 @@ class BlocksContainer extends Component {
this.props.stopBlockHeader(); this.props.stopBlockHeader();
} }
loadMore() { getNumberOfPages() {
this.props.fetchBlocks(this.loadMoreFrom()); if (!this.numberOfBlocks) {
let blocks = this.props.blocks;
if (blocks.length === 0) {
this.numberOfBlocks = 0;
} else {
this.numberOfBlocks = blocks[blocks.length - 1].number - 1;
}
}
return Math.ceil(this.numberOfBlocks / MAX_BLOCKS);
} }
loadMoreFrom() { changePage(newPage) {
let blocks = this.props.blocks; this.setState({currentPage: newPage});
if (blocks.length === 0) {
return 0; this.props.fetchBlocks((newPage * MAX_BLOCKS) + MAX_BLOCKS);
} }
return blocks[blocks.length - 1].number - 1;
getCurrentBlocks() {
const currentPage = this.state.currentPage || this.getNumberOfPages();
return this.props.blocks.filter(block => block.number <= (currentPage * MAX_BLOCKS) + MAX_BLOCKS &&
block.number > currentPage * MAX_BLOCKS);
} }
render() { render() {
const newBlocks = this.getCurrentBlocks();
if (newBlocks.length) {
this.currentBlocks = newBlocks;
}
return ( return (
<React.Fragment> <React.Fragment>
<DataWrapper shouldRender={this.props.blocks.length > 0} {...this.props} render={({blocks}) => ( <DataWrapper shouldRender={this.currentBlocks.length > 0} {...this.props} render={() => (
<Blocks blocks={blocks} showLoadMore={(this.loadMoreFrom() >= 0)} loadMore={() => this.loadMore()} /> <Blocks blocks={this.currentBlocks} numberOfPages={this.getNumberOfPages()}
changePage={(newPage) => this.changePage(newPage)}
currentPage={this.state.currentPage || this.getNumberOfPages()} />
)} /> )} />
</React.Fragment> </React.Fragment>
); );

View File

@ -7,7 +7,17 @@ import Transactions from '../components/Transactions';
import DataWrapper from "../components/DataWrapper"; import DataWrapper from "../components/DataWrapper";
import {getTransactions} from "../reducers/selectors"; import {getTransactions} from "../reducers/selectors";
const MAX_TXS = 10; // TODO use same constant as API
class TransactionsContainer extends Component { class TransactionsContainer extends Component {
constructor(props) {
super(props);
this.state = {currentPage: 0};
this.numberOfTxs = 0;
this.currentTxs = [];
}
componentDidMount() { componentDidMount() {
this.props.fetchTransactions(); this.props.fetchTransactions();
this.props.initBlockHeader(); this.props.initBlockHeader();
@ -17,24 +27,41 @@ class TransactionsContainer extends Component {
this.props.stopBlockHeader(); this.props.stopBlockHeader();
} }
loadMore() { getNumberOfPages() {
this.props.fetchTransactions(this.loadMoreFrom()); if (!this.numberOfTxs) {
let transactions = this.props.transactions;
if (transactions.length === 0) {
this.numberOfTxs = 0;
} else {
this.numberOfTxs = transactions[transactions.length - 1].blockNumber - 1;
}
}
return Math.ceil(this.numberOfTxs / MAX_TXS);
} }
loadMoreFrom() { changePage(newPage) {
let transactions = this.props.transactions; this.setState({currentPage: newPage});
if (transactions.length === 0) {
return 0; this.props.fetchTransactions((newPage * MAX_TXS) + MAX_TXS);
} }
return transactions[transactions.length - 1].blockNumber - 1;
getCurrentTransactions() {
const currentPage = this.state.currentPage || this.getNumberOfPages();
return this.props.transactions.filter(tx => tx.blockNumber <= (currentPage * MAX_TXS) + MAX_TXS &&
tx.blockNumber > currentPage * MAX_TXS);
} }
render() { render() {
const newTxs = this.getCurrentTransactions();
if (newTxs.length) {
this.currentTxs = newTxs;
}
return ( return (
<React.Fragment> <React.Fragment>
<DataWrapper shouldRender={this.props.transactions.length > 0} {...this.props} render={({transactions}) => ( <DataWrapper shouldRender={this.currentTxs.length > 0} {...this.props} render={() => (
<Transactions transactions={transactions} <Transactions transactions={this.currentTxs} numberOfPages={this.getNumberOfPages()}
showLoadMore={(this.loadMoreFrom() >= 0)} loadMore={() => this.loadMore()} /> changePage={(newPage) => this.changePage(newPage)}
currentPage={this.state.currentPage || this.getNumberOfPages()} />
)} /> )} />
</React.Fragment> </React.Fragment>
); );