diff --git a/packages/embark-ui/src/actions/index.js b/packages/embark-ui/src/actions/index.js index 5f1f65a8a..9315ba5a6 100644 --- a/packages/embark-ui/src/actions/index.js +++ b/packages/embark-ui/src/actions/index.js @@ -82,6 +82,15 @@ export const blocks = { failure: (error) => action(BLOCKS[FAILURE], {error}) }; +export const BLOCKS_FULL = createRequestTypes('BLOCKS_FULL'); +export const blocksFull = { + request: (from, limit) => { + return action(BLOCKS_FULL[REQUEST], {from, limit, txObjects: true, txReceipts: true}); + }, + success: (blocksFull) => action(BLOCKS_FULL[SUCCESS], {blocksFull}), + failure: (error) => action(BLOCKS_FULL[FAILURE], {error}) +}; + export const BLOCK = createRequestTypes('BLOCK'); export const block = { request: (blockNumber) => action(BLOCK[REQUEST], {blockNumber}), diff --git a/packages/embark-ui/src/components/Blocks.js b/packages/embark-ui/src/components/Blocks.js index 87bf29dfd..d50c0ed9f 100644 --- a/packages/embark-ui/src/components/Blocks.js +++ b/packages/embark-ui/src/components/Blocks.js @@ -38,7 +38,7 @@ const Blocks = ({blocks, changePage, currentPage, numberOfPages}) => ( ))} - + {numberOfPages > 0 && } diff --git a/packages/embark-ui/src/components/ExplorerDashboardLayout.js b/packages/embark-ui/src/components/ExplorerDashboardLayout.js index cd5e3de76..98ccc981f 100644 --- a/packages/embark-ui/src/components/ExplorerDashboardLayout.js +++ b/packages/embark-ui/src/components/ExplorerDashboardLayout.js @@ -25,7 +25,7 @@ const ExplorerDashboardLayout = () => ( - + diff --git a/packages/embark-ui/src/components/Transactions.js b/packages/embark-ui/src/components/Transactions.js index 1642a48f4..1ac1a3304 100644 --- a/packages/embark-ui/src/components/Transactions.js +++ b/packages/embark-ui/src/components/Transactions.js @@ -15,6 +15,7 @@ const Transactions = ({transactions, contracts, changePage, currentPage, numberO

Transactions

+ {!transactions.length && "No transactions to display"} {transactions.map(transaction => (
Transaction  @@ -47,7 +48,7 @@ const Transactions = ({transactions, contracts, changePage, currentPage, numberO
))} - {numberOfPages && } + {numberOfPages > 0 && }
diff --git a/packages/embark-ui/src/containers/TransactionsContainer.js b/packages/embark-ui/src/containers/TransactionsContainer.js index 4c79984f9..d3c3ef125 100644 --- a/packages/embark-ui/src/containers/TransactionsContainer.js +++ b/packages/embark-ui/src/containers/TransactionsContainer.js @@ -1,12 +1,14 @@ import React, {Component} from 'react'; import {connect} from 'react-redux'; import PropTypes from 'prop-types'; - -import {transactions as transactionsAction, initBlockHeader, stopBlockHeader, contracts as contractsAction} from '../actions'; +import {blocksFull as blocksAction, + contracts as contractsAction, + initBlockHeader, + stopBlockHeader} from '../actions'; import Transactions from '../components/Transactions'; import DataWrapper from "../components/DataWrapper"; import PageHead from "../components/PageHead"; -import {getTransactions, getContracts} from "../reducers/selectors"; +import {getBlocksFull, getContracts} from "../reducers/selectors"; const MAX_TXS = 10; // TODO use same constant as API @@ -14,13 +16,13 @@ class TransactionsContainer extends Component { constructor(props) { super(props); - this.state = {currentPage: 0}; - this.numberOfTxs = 0; - this.currentTxs = []; + this.numTxsToDisplay = this.props.numTxsToDisplay || MAX_TXS; + this.numBlocksToFetch = this.numTxsToDisplay; + this.state = {currentPage: 1}; } componentDidMount() { - this.props.fetchTransactions(); + this.props.fetchBlocksFull(null, this.numBlocksToFetch); this.props.fetchContracts(); this.props.initBlockHeader(); } @@ -29,45 +31,79 @@ class TransactionsContainer extends Component { this.props.stopBlockHeader(); } + get numberOfBlocks() { + const blocks = this.props.blocks; + return !blocks.length ? 0 : blocks[0].number + 1; + } + + get estNumberOfTxs() { + const blocks = this.props.blocks; + const numBlocksInProps = blocks.length; + const numTxsInPropsBlocks = blocks.reduce((txCount, block) => ( + txCount + block.transactions.length + ), 0); + const missingNumBlocks = this.numberOfBlocks - numBlocksInProps; + return missingNumBlocks + numTxsInPropsBlocks; + } + getNumberOfPages() { - 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); + return Math.ceil(this.estNumberOfTxs / this.numTxsToDisplay); } changePage(newPage) { this.setState({currentPage: newPage}); - - this.props.fetchTransactions((newPage * MAX_TXS) + MAX_TXS); + this.props.fetchBlocksFull( + this.numberOfBlocks - 1 - (this.numBlocksToFetch * (newPage - 1)), + this.numBlocksToFetch + ); } 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); + if (!this.props.blocks.length) return []; + let relativeBlock = this.numberOfBlocks - 1; + let offset = 0; + let txs = this.props.blocks.reduce((txs, block) => { + offset = relativeBlock - block.number; + if (offset <= 1) { + offset = 0; + } + relativeBlock = block.number; + const txsLength = txs.length; + block.transactions.forEach((tx, idx) => { + txs[txsLength + idx + offset] = tx; + }); + return txs; + }, []); + const estNumberOfTxs = this.estNumberOfTxs; + return txs.filter((tx, idx) => { + const txNumber = estNumberOfTxs - idx; + const index = ( + (estNumberOfTxs - + (this.numTxsToDisplay * (this.state.currentPage - 1))) - + txNumber + 1 + ); + return index <= this.numTxsToDisplay && index > 0; + }); } render() { const newTxs = this.getCurrentTransactions(); - if (newTxs.length) { - this.currentTxs = newTxs; - } return ( - - 0} {...this.props} render={() => ( - this.changePage(newPage)} - currentPage={this.state.currentPage || this.getNumberOfPages()} /> - )} /> + + ( + this.changePage(newPage)} + currentPage={this.state.currentPage} /> + )} /> ); } @@ -75,7 +111,7 @@ class TransactionsContainer extends Component { function mapStateToProps(state) { return { - transactions: getTransactions(state), + blocks: getBlocksFull(state), contracts: getContracts(state), error: state.errorMessage, loading: state.loading @@ -83,9 +119,9 @@ function mapStateToProps(state) { } TransactionsContainer.propTypes = { - transactions: PropTypes.arrayOf(PropTypes.object), + blocks: PropTypes.arrayOf(PropTypes.object), contracts: PropTypes.arrayOf(PropTypes.object), - fetchTransactions: PropTypes.func, + fetchBlocksFull: PropTypes.func, fetchContracts: PropTypes.func, initBlockHeader: PropTypes.func, stopBlockHeader: PropTypes.func, @@ -97,7 +133,7 @@ TransactionsContainer.propTypes = { export default connect( mapStateToProps, { - fetchTransactions: transactionsAction.request, + fetchBlocksFull: blocksAction.request, fetchContracts: contractsAction.request, initBlockHeader, stopBlockHeader diff --git a/packages/embark-ui/src/reducers/index.js b/packages/embark-ui/src/reducers/index.js index 46cdcd952..6a10210ae 100644 --- a/packages/embark-ui/src/reducers/index.js +++ b/packages/embark-ui/src/reducers/index.js @@ -18,6 +18,7 @@ const PROCESS_LOGS_LIMIT = ELEMENTS_LIMIT * 2; const entitiesDefaultState = { accounts: [], blocks: [], + blocksFull: [], transactions: [], processes: [], services: [], @@ -43,6 +44,9 @@ const sorter = { blocks: function(a, b) { return b.number - a.number; }, + blocksFull: function(a, b) { + return b.number - a.number; + }, transactions: function(a, b) { return ((BN_FACTOR * b.blockNumber) + b.transactionIndex) - ((BN_FACTOR * a.blockNumber) + a.transactionIndex); }, @@ -120,6 +124,13 @@ const filters = { return index === self.findIndex((t) => t.number === block.number); }, + blocksFull: function(block, index, self) { + if (index > ELEMENTS_LIMIT) { + return false; + } + + return index === self.findIndex((t) => t.number === block.number); + }, transactions: function(tx, index, self) { if (index > ELEMENTS_LIMIT) { return false; diff --git a/packages/embark-ui/src/reducers/selectors.js b/packages/embark-ui/src/reducers/selectors.js index ffcaabd1b..0bcf3f654 100644 --- a/packages/embark-ui/src/reducers/selectors.js +++ b/packages/embark-ui/src/reducers/selectors.js @@ -44,6 +44,10 @@ export function getBlocks(state) { return state.entities.blocks; } +export function getBlocksFull(state) { + return state.entities.blocksFull; +} + export function getLastBlock(state) { return state.entities.blocks[0]; } diff --git a/packages/embark-ui/src/sagas/index.js b/packages/embark-ui/src/sagas/index.js index ed1dcf02b..c36530ec9 100644 --- a/packages/embark-ui/src/sagas/index.js +++ b/packages/embark-ui/src/sagas/index.js @@ -47,6 +47,7 @@ export const fetchBlock = doRequest.bind(null, actions.block, api.fetchBlock); export const fetchTransaction = doRequest.bind(null, actions.transaction, api.fetchTransaction); export const fetchAccounts = doRequest.bind(null, actions.accounts, api.fetchAccounts); export const fetchBlocks = doRequest.bind(null, actions.blocks, api.fetchBlocks); +export const fetchBlocksFull = doRequest.bind(null, actions.blocksFull, api.fetchBlocks); export const fetchTransactions = doRequest.bind(null, actions.transactions, api.fetchTransactions); export const fetchProcesses = doRequest.bind(null, actions.processes, api.fetchProcesses); export const fetchServices = doRequest.bind(null, actions.services, api.fetchServices); @@ -120,6 +121,10 @@ export function *watchFetchBlocks() { yield takeEvery(actions.BLOCKS[actions.REQUEST], fetchBlocks); } +export function *watchFetchBlocksFull() { + yield takeEvery(actions.BLOCKS_FULL[actions.REQUEST], fetchBlocksFull); +} + export function *watchFetchAccount() { yield takeEvery(actions.ACCOUNT[actions.REQUEST], fetchAccount); } @@ -389,6 +394,7 @@ export function *initBlockHeader() { return; } yield put({type: actions.BLOCKS[actions.REQUEST]}); + yield put({type: actions.BLOCKS_FULL[actions.REQUEST], txObjects: true, txReceipts: true}); yield put({type: actions.TRANSACTIONS[actions.REQUEST]}); } } @@ -568,6 +574,7 @@ export default function *root() { fork(watchFetchVersions), fork(watchFetchPlugins), fork(watchFetchBlocks), + fork(watchFetchBlocksFull), fork(watchFetchContracts), fork(watchFetchContractProfile), fork(watchPostContractFunction), diff --git a/packages/embark/src/lib/modules/blockchain_connector/index.js b/packages/embark/src/lib/modules/blockchain_connector/index.js index b6b4f6e02..cb2ea3117 100644 --- a/packages/embark/src/lib/modules/blockchain_connector/index.js +++ b/packages/embark/src/lib/modules/blockchain_connector/index.js @@ -387,9 +387,9 @@ class BlockchainConnector { 'get', '/embark-api/blockchain/blocks', (req, res) => { - let from = parseInt(req.query.from, 10); - let limit = req.query.limit || 10; - self.getBlocks(from, limit, false, res.send.bind(res)); + const from = parseInt(req.query.from, 10); + const limit = req.query.limit || 10; + self.getBlocks(from, limit, !!req.query.txObjects, !!req.query.txReceipts, res.send.bind(res)); } ); @@ -565,7 +565,7 @@ class BlockchainConnector { } getTransactions(blockFrom, blockLimit, callback) { - this.getBlocks(blockFrom, blockLimit, true, (blocks) => { + this.getBlocks(blockFrom, blockLimit, true, true, (blocks) => { let transactions = blocks.reduce((acc, block) => { if (!block || !block.transactions) { return acc; @@ -576,7 +576,7 @@ class BlockchainConnector { }); } - getBlocks(from, limit, returnTransactionObjects, callback) { + getBlocks(from, limit, returnTransactionObjects, includeTransactionReceipts, callback) { let self = this; let blocks = []; async.waterfall([ @@ -597,12 +597,32 @@ class BlockchainConnector { function(next) { async.times(limit, function(n, eachCb) { self.web3.eth.getBlock(from - n, returnTransactionObjects, function(err, block) { - if (err && err.message) { + if (err) { // FIXME Returns an error because we are too low return eachCb(); } - blocks.push(block); - eachCb(); + if (!block) { + return eachCb(); + } + if (!(returnTransactionObjects && includeTransactionReceipts) || + !(block.transactions && block.transactions.length)) { + blocks.push(block); + return eachCb(); + } + return Promise.all(block.transactions.map(tx => ( + self.web3.eth.getTransactionReceipt(tx.hash) + ))) + .then(receipts => { + block.transactions.forEach((tx, index) => { + tx['receipt'] = receipts[index]; + }); + blocks.push(block); + eachCb(); + }) + .catch((err) => { + self.logger.error(err.message || err); + eachCb(); + }); }); }, next); }