From 143f0228e1d609da701a3c2153a31bf3afc4db7e Mon Sep 17 00:00:00 2001 From: Anthony Laibe Date: Fri, 3 Aug 2018 16:51:15 +0100 Subject: [PATCH 1/7] Add show components --- embark-ui/src/actions/index.js | 69 +++++++++ embark-ui/src/api/index.js | 12 ++ embark-ui/src/components/Account.js | 18 +++ embark-ui/src/components/Block.js | 18 +++ embark-ui/src/components/ExplorerLayout.js | 6 + embark-ui/src/components/Transaction.js | 18 +++ embark-ui/src/containers/AccountContainer.js | 49 ++++++ embark-ui/src/containers/BlockContainer.js | 54 +++++++ .../src/containers/TransactionContainer.js | 52 +++++++ embark-ui/src/reducers/accountsReducer.js | 6 +- embark-ui/src/sagas/index.js | 44 +++++- lib/modules/blockchain_connector/index.js | 142 +++++++++++++----- 12 files changed, 452 insertions(+), 36 deletions(-) create mode 100644 embark-ui/src/components/Account.js create mode 100644 embark-ui/src/components/Block.js create mode 100644 embark-ui/src/components/Transaction.js create mode 100644 embark-ui/src/containers/AccountContainer.js create mode 100644 embark-ui/src/containers/BlockContainer.js create mode 100644 embark-ui/src/containers/TransactionContainer.js diff --git a/embark-ui/src/actions/index.js b/embark-ui/src/actions/index.js index b540a998..e981b02c 100644 --- a/embark-ui/src/actions/index.js +++ b/embark-ui/src/actions/index.js @@ -2,6 +2,9 @@ export const FETCH_ACCOUNTS = 'FETCH_ACCOUNTS'; export const RECEIVE_ACCOUNTS = 'RECEIVE_ACCOUNTS'; export const RECEIVE_ACCOUNTS_ERROR = 'RECEIVE_ACCOUNTS_ERROR'; +export const FETCH_ACCOUNT = 'FETCH_ACCOUNT'; +export const RECEIVE_ACCOUNT = 'RECEIVE_ACCOUNT'; +export const RECEIVE_ACCOUNT_ERROR = 'RECEIVE_ACCOUNT_ERROR'; // Processes export const FETCH_PROCESSES = 'FETCH_PROCESSES'; export const RECEIVE_PROCESSES = 'RECEIVE_PROCESSES'; @@ -14,10 +17,16 @@ export const RECEIVE_PROCESS_LOGS_ERROR = 'RECEIVE_PROCESS_LOGS_ERROR'; export const FETCH_BLOCKS = 'FETCH_BLOCKS'; export const RECEIVE_BLOCKS = 'RECEIVE_BLOCKS'; export const RECEIVE_BLOCKS_ERROR = 'RECEIVE_BLOCKS_ERROR'; +export const FETCH_BLOCK = 'FETCH_BLOCK'; +export const RECEIVE_BLOCK = 'RECEIVE_BLOCK'; +export const RECEIVE_BLOCK_ERROR = 'RECEIVE_BLOCK_ERROR'; // Transactions export const FETCH_TRANSACTIONS = 'FETCH_TRANSACTIONS'; export const RECEIVE_TRANSACTIONS = 'RECEIVE_TRANSACTIONS'; export const RECEIVE_TRANSACTIONS_ERROR = 'RECEIVE_TRANSACTIONS_ERROR'; +export const FETCH_TRANSACTION = 'FETCH_TRANSACTION'; +export const RECEIVE_TRANSACTION = 'RECEIVE_TRANSACTION'; +export const RECEIVE_TRANSACTION_ERROR = 'RECEIVE_TRANSACTION_ERROR'; // BlockHeader export const INIT_BLOCK_HEADER = 'INIT_BLOCK_HEADER'; @@ -40,6 +49,26 @@ export function receiveAccountsError() { }; } +export function fetchAccount(address) { + return { + type: FETCH_ACCOUNT, + address + }; +} + +export function receiveAccount(account) { + return { + type: RECEIVE_ACCOUNT, + account + }; +} + +export function receiveAccountError() { + return { + type: RECEIVE_ACCOUNT_ERROR + }; +} + export function fetchProcesses() { return { type: FETCH_PROCESSES @@ -102,6 +131,26 @@ export function receiveBlocksError() { }; } +export function fetchBlock(blockNumber) { + return { + type: FETCH_BLOCK, + blockNumber + }; +} + +export function receiveBlock(block) { + return { + type: RECEIVE_BLOCK, + block + }; +} + +export function receiveBlockError() { + return { + type: RECEIVE_BLOCK_ERROR + }; +} + export function fetchTransactions(blockFrom) { return { type: FETCH_TRANSACTIONS, @@ -122,6 +171,26 @@ export function receiveTransactionsError() { }; } +export function fetchTransaction(hash) { + return { + type: FETCH_TRANSACTION, + hash + }; +} + +export function receiveTransaction(transaction) { + return { + type: RECEIVE_TRANSACTION, + transaction + }; +} + +export function receiveTransactionError() { + return { + type: RECEIVE_TRANSACTION_ERROR + }; +} + export function initBlockHeader(){ return { type: INIT_BLOCK_HEADER diff --git a/embark-ui/src/api/index.js b/embark-ui/src/api/index.js index ab46e38f..1e489256 100644 --- a/embark-ui/src/api/index.js +++ b/embark-ui/src/api/index.js @@ -5,14 +5,26 @@ export function fetchAccounts() { return axios.get(`${constants.httpEndpoint}/blockchain/accounts`); } +export function fetchAccount(address) { + return axios.get(`${constants.httpEndpoint}/blockchain/accounts/${address}`); +} + export function fetchBlocks(from) { return axios.get(`${constants.httpEndpoint}/blockchain/blocks`, {params: {from}}); } +export function fetchBlock(blockNumber) { + return axios.get(`${constants.httpEndpoint}/blockchain/blocks/${blockNumber}`); +} + export function fetchTransactions(blockFrom) { return axios.get(`${constants.httpEndpoint}/blockchain/transactions`, {params: {blockFrom}}); } +export function fetchTransaction(hash) { + return axios.get(`${constants.httpEndpoint}/blockchain/transactions${hash}`); +} + export function fetchProcesses() { return axios.get(`${constants.httpEndpoint}/processes`); } diff --git a/embark-ui/src/components/Account.js b/embark-ui/src/components/Account.js new file mode 100644 index 00000000..9193aeea --- /dev/null +++ b/embark-ui/src/components/Account.js @@ -0,0 +1,18 @@ +import React from 'react'; +import { + Page +} from "tabler-react"; +import PropTypes from 'prop-types'; + + +const Account = ({account}) => ( + +

Hello

+
+); + +Account.propTypes = { + account: PropTypes.object +}; + +export default Account; diff --git a/embark-ui/src/components/Block.js b/embark-ui/src/components/Block.js new file mode 100644 index 00000000..a64896c8 --- /dev/null +++ b/embark-ui/src/components/Block.js @@ -0,0 +1,18 @@ +import React from 'react'; +import { + Page +} from "tabler-react"; +import PropTypes from 'prop-types'; + + +const Block = ({block}) => ( + +

Hello

+
+); + +Block.propTypes = { + block: PropTypes.object +}; + +export default Block; diff --git a/embark-ui/src/components/ExplorerLayout.js b/embark-ui/src/components/ExplorerLayout.js index 11c64fde..bb7299c7 100644 --- a/embark-ui/src/components/ExplorerLayout.js +++ b/embark-ui/src/components/ExplorerLayout.js @@ -7,8 +7,11 @@ import { } from "tabler-react"; import AccountsContainer from '../containers/AccountsContainer'; +import AccountContainer from '../containers/AccountContainer'; import BlocksContainer from '../containers/BlocksContainer'; +import BlockContainer from '../containers/BlockContainer'; import TransactionsContainer from '../containers/TransactionsContainer'; +import TransactionContainer from '../containers/TransactionContainer'; const ExplorerLayout = () => ( @@ -46,8 +49,11 @@ const ExplorerLayout = () => ( + + + diff --git a/embark-ui/src/components/Transaction.js b/embark-ui/src/components/Transaction.js new file mode 100644 index 00000000..5f7288a9 --- /dev/null +++ b/embark-ui/src/components/Transaction.js @@ -0,0 +1,18 @@ +import React from 'react'; +import { + Page +} from "tabler-react"; +import PropTypes from 'prop-types'; + + +const Transaction = ({transaction}) => ( + +

Hello

+
+); + +Transaction.propTypes = { + transaction: PropTypes.object +}; + +export default Transaction; diff --git a/embark-ui/src/containers/AccountContainer.js b/embark-ui/src/containers/AccountContainer.js new file mode 100644 index 00000000..e187a8e6 --- /dev/null +++ b/embark-ui/src/containers/AccountContainer.js @@ -0,0 +1,49 @@ +import React, {Component} from 'react'; +import {connect} from 'react-redux'; +import PropTypes from 'prop-types'; +import {withRouter} from 'react-router-dom'; + +import {fetchAccount} from '../actions'; +import Account from '../components/Account'; +import NoMatch from "../components/NoMatch"; +import Transactions from '../components/Transactions'; + +class AccountContainer extends Component { + componentDidMount() { + this.props.fetchAccount(this.props.match.params.address); + } + + render() { + const {account} = this.props; + if (!account) { + return ; + } + + return ( + + + + + ); + } +} + +function mapStateToProps(state, props) { + if(state.accounts.data) { + return {account: state.accounts.data.find(account => account.address === props.match.params.address)}; + } + return null; +} + +AccountContainer.propTypes = { + router: PropTypes.object, + account: PropTypes.object, + fetchAccount: PropTypes.func +}; + +export default withRouter(connect( + mapStateToProps, + { + fetchAccount + } +)(AccountContainer)); diff --git a/embark-ui/src/containers/BlockContainer.js b/embark-ui/src/containers/BlockContainer.js new file mode 100644 index 00000000..d0c19c45 --- /dev/null +++ b/embark-ui/src/containers/BlockContainer.js @@ -0,0 +1,54 @@ +import React, {Component} from 'react'; +import {connect} from 'react-redux'; +import PropTypes from 'prop-types'; +import {withRouter} from 'react-router-dom'; + +import {fetchBlock} from '../actions'; +import Block from '../components/Block'; +import Transactions from '../components/Transactions'; +import Loading from '../components/Loading'; + +class BlockContainer extends Component { + componentDidMount() { + this.props.fetchBlock(this.props.router.match.params.blockNumber); + } + + render() { + const {block} = this.props; + if (!block.data) { + return ; + } + + if (block.error) { + return ( +

+ Error API... +

+ ); + } + + return ( + + + + + ); + } +} + +function mapStateToProps(state) { + return {block: state.block}; +} + +BlockContainer.propTypes = { + router: PropTypes.object, + block: PropTypes.object, + fetchBlock: PropTypes.func +}; + +export default withRouter(connect( + mapStateToProps, + { + fetchBlock + } +))(BlockContainer); diff --git a/embark-ui/src/containers/TransactionContainer.js b/embark-ui/src/containers/TransactionContainer.js new file mode 100644 index 00000000..dc56074e --- /dev/null +++ b/embark-ui/src/containers/TransactionContainer.js @@ -0,0 +1,52 @@ +import React, {Component} from 'react'; +import {connect} from 'react-redux'; +import PropTypes from 'prop-types'; +import {withRouter} from 'react-router-dom'; + +import {fetchTransaction} from '../actions'; +import Transaction from '../components/Transaction'; +import Loading from '../components/Loading'; + +class TransactionContainer extends Component { + componentDidMount() { + this.props.fetchTransaction(this.props.router.match.params.hash); + } + + render() { + const {transaction} = this.props; + if (!transaction.data) { + return ; + } + + if (transaction.error) { + return ( +

+ Error API... +

+ ); + } + + return ( + + + + ); + } +} + +function mapStateToProps(state) { + return {transaction: state.transaction}; +} + +TransactionContainer.propTypes = { + router: PropTypes.object, + transaction: PropTypes.object, + fetchTransaction: PropTypes.func +}; + +export default withRouter(connect( + mapStateToProps, + { + fetchTransaction + } +))(TransactionContainer); diff --git a/embark-ui/src/reducers/accountsReducer.js b/embark-ui/src/reducers/accountsReducer.js index a89cde48..0fb9e678 100644 --- a/embark-ui/src/reducers/accountsReducer.js +++ b/embark-ui/src/reducers/accountsReducer.js @@ -1,4 +1,4 @@ -import {RECEIVE_ACCOUNTS, RECEIVE_ACCOUNTS_ERROR} from "../actions"; +import {RECEIVE_ACCOUNTS, RECEIVE_ACCOUNTS_ERROR, RECEIVE_ACCOUNT, RECEIVE_ACCOUNT_ERROR} from "../actions"; export default function accounts(state = {}, action) { switch (action.type) { @@ -6,6 +6,10 @@ export default function accounts(state = {}, action) { return Object.assign({}, state, {data: action.accounts.data}); case RECEIVE_ACCOUNTS_ERROR: return Object.assign({}, state, {error: true}); + case RECEIVE_ACCOUNT: + return Object.assign({}, state, {data: action.account.data}); + case RECEIVE_ACCOUNT_ERROR: + return Object.assign({}, state, {error: true}); default: return state; } diff --git a/embark-ui/src/sagas/index.js b/embark-ui/src/sagas/index.js index b1868d72..29322674 100644 --- a/embark-ui/src/sagas/index.js +++ b/embark-ui/src/sagas/index.js @@ -3,6 +3,19 @@ import * as api from '../api'; import {eventChannel} from 'redux-saga'; import {all, call, fork, put, takeEvery, take} from 'redux-saga/effects'; +export function *fetchTransaction(payload) { + try { + const transaction = yield call(api.fetchTransaction, payload.hash); + yield put(actions.receiveTransaction(transaction)); + } catch (e) { + yield put(actions.receiveTransactionError()); + } +} + +export function *watchFetchTransaction() { + yield takeEvery(actions.FETCH_TRANSACTION, fetchTransaction); +} + export function *fetchTransactions(payload) { try { const transactions = yield call(api.fetchTransactions, payload.blockFrom); @@ -16,6 +29,19 @@ export function *watchFetchTransactions() { yield takeEvery(actions.FETCH_TRANSACTIONS, fetchTransactions); } +export function *fetchBlock(payload) { + try { + const block = yield call(api.fetchBlock, payload.blockNumber); + yield put(actions.receiveBlock(block)); + } catch (e) { + yield put(actions.receiveBlockError()); + } +} + +export function *watchFetchBlock() { + yield takeEvery(actions.FETCH_BLOCK, fetchBlock); +} + export function *fetchBlocks(payload) { try { const blocks = yield call(api.fetchBlocks, payload.from); @@ -29,6 +55,19 @@ export function *watchFetchBlocks() { yield takeEvery(actions.FETCH_BLOCKS, fetchBlocks); } +export function *fetchAccount(payload) { + try { + const account = yield call(api.fetchAccounts, payload.address); + yield put(actions.receiveAccount(account)); + } catch (e) { + yield put(actions.receiveAccountError()); + } +} + +export function *watchFetchAccount() { + yield takeEvery(actions.FETCH_ACCOUNT, fetchAccount); +} + export function *fetchAccounts() { try { const accounts = yield call(api.fetchAccounts); @@ -97,9 +136,12 @@ export default function *root() { yield all([ fork(watchInitBlockHeader), fork(watchFetchAccounts), + fork(watchFetchAccount), fork(watchFetchProcesses), fork(watchFetchProcessLogs), fork(watchFetchBlocks), - fork(watchFetchTransactions) + fork(watchFetchBlock), + fork(watchFetchTransactions), + fork(watchFetchTransaction) ]); } diff --git a/lib/modules/blockchain_connector/index.js b/lib/modules/blockchain_connector/index.js index 0d6eff59..35ad4a08 100644 --- a/lib/modules/blockchain_connector/index.js +++ b/lib/modules/blockchain_connector/index.js @@ -177,39 +177,15 @@ class BlockchainConnector { 'get', '/embark-api/blockchain/accounts', (req, res) => { - let results = []; - self.getAccounts((err, addresses) => { - async.eachOf(addresses, (address, index, eachCb) => { - let result = {address, index}; - results.push(result); - async.waterfall([ - function(callback) { - self.getTransactionCount(address, (err, count) => { - if (err) { - self.logger.error(err); - result.transactionCount = 0; - } else { - result.transactionCount = count; - } - callback(); - }); - }, - function(callback) { - self.getBalance(address, (err, balance) => { - if (err) { - self.logger.error(err); - result.balance = 0; - } else { - result.balance = self.web3.utils.fromWei(balance); - } - callback(); - }); - } - ], eachCb); - }, function () { - res.send(results); - }); - }); + self.getAccountsWithTransactionCount(res.send.bind(res)); + } + ); + + plugin.registerAPICall( + 'get', + '/embark-api/blockchain/accounts/:address', + (req, res) => { + self.getAccount(req.params.address, res.send.bind(res)); } ); @@ -223,6 +199,16 @@ class BlockchainConnector { } ); + plugin.registerAPICall( + 'get', + '/embark-api/blockchain/blocks/:blockNumber', + (req, res) => { + self.getBlock(req.params.blockNumber, (_err, block) => { + res.send(block); + }); + } + ); + plugin.registerAPICall( 'get', '/embark-api/blockchain/transactions', @@ -233,6 +219,15 @@ class BlockchainConnector { } ); + plugin.registerAPICall( + 'get', + '/embark-api/blockchain/transactions/:hash', + (req, res) => { + self.getTransaction(req.params.hash, (_err, transaction) => { + res.send(transaction); + }); + } + ); plugin.registerAPICall( 'ws', @@ -245,6 +240,81 @@ class BlockchainConnector { ); } + getAccountsWithTransactionCount(callback) { + let self = this; + self.getAccounts((err, addresses) => { + let accounts = []; + async.eachOf(addresses, (address, index, eachCb) => { + let account = {address, index}; + async.waterfall([ + function(callback) { + self.getTransactionCount(address, (err, count) => { + if (err) { + self.logger.error(err); + account.transactionCount = 0; + } else { + account.transactionCount = count; + } + callback(null, account); + }); + }, + function(account, callback) { + self.getBalance(address, (err, balance) => { + if (err) { + self.logger.error(err); + account.balance = 0; + } else { + account.balance = self.web3.utils.fromWei(balance); + } + callback(null, account); + }); + } + ], function(_err, account){ + accounts.push(account); + eachCb(); + }); + }, function () { + callback(accounts); + }); + }); + } + + getAccount(address, callback) { + let self = this; + async.waterfall([ + function(next) { + self.getAccountsWithTransactionCount((accounts) =>{ + let account = accounts.find((a) => a.address === address); + if(!account) { + next("No account found with this address"); + } + next(null, account); + }); + }, + function(account, next) { + self.getBlockNumber((err, blockNumber) => { + if (err) { + self.logger.error(err); + next(err); + } else { + next(null, blockNumber, account); + } + }); + }, + function(blockNumber, account, next) { + self.getTransactions(blockNumber, blockNumber, (transactions) => { + account.transactions = transactions.filter((transaction) => transaction.from === address); + next(null, account); + }); + } + ], function (err, result) { + if (err) { + callback(); + } + callback(result); + }); + } + getTransactions(blockFrom, blockLimit, callback) { this.getBlocks(blockFrom, blockLimit, true, (blocks) => { let transactions = blocks.reduce((acc, block) => acc.concat(block.transactions), []); @@ -328,7 +398,11 @@ class BlockchainConnector { } getBlock(blockNumber, cb) { - this.web3.eth.getBlock(blockNumber, cb); + this.web3.eth.getBlock(blockNumber, true, cb); + } + + getTransaction(hash, cb) { + this.web3.eth.getTransaction(hash, cb); } getGasPrice(cb) { From 64bbf4644440834e2f31e018dedc9c164b0f6bba Mon Sep 17 00:00:00 2001 From: Anthony Laibe Date: Mon, 6 Aug 2018 09:40:17 +0100 Subject: [PATCH 2/7] Adding link --- embark-ui/src/components/Accounts.js | 4 ++-- embark-ui/src/components/Blocks.js | 3 ++- embark-ui/src/components/Transactions.js | 7 ++++--- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/embark-ui/src/components/Accounts.js b/embark-ui/src/components/Accounts.js index 5917cfc2..348d776b 100644 --- a/embark-ui/src/components/Accounts.js +++ b/embark-ui/src/components/Accounts.js @@ -5,9 +5,9 @@ import { Card, Table } from "tabler-react"; +import { Link } from 'react-router-dom'; import PropTypes from 'prop-types'; - const Accounts = ({accounts}) => ( @@ -25,7 +25,7 @@ const Accounts = ({accounts}) => ( bodyItems={ accounts.map((account) => { return ([ - {content: account.address}, + {content: {account.address}}, {content: account.balance}, {content: account.transactionCount}, {content: account.index} diff --git a/embark-ui/src/components/Blocks.js b/embark-ui/src/components/Blocks.js index ef986d6a..67329281 100644 --- a/embark-ui/src/components/Blocks.js +++ b/embark-ui/src/components/Blocks.js @@ -1,4 +1,5 @@ import React from 'react'; +import {Link} from "react-router-dom"; import { Page, Grid, @@ -20,7 +21,7 @@ const Blocks = ({blocks}) => ( bodyItems={ blocks.map((block) => { return ([ - {content: block.number}, + {content: {block.number}}, {content: new Date(block.timestamp * 1000).toLocaleString()}, {content: block.gasUsed}, {content: block.transactions.length} diff --git a/embark-ui/src/components/Transactions.js b/embark-ui/src/components/Transactions.js index ff5e9d31..3d16815a 100644 --- a/embark-ui/src/components/Transactions.js +++ b/embark-ui/src/components/Transactions.js @@ -1,4 +1,5 @@ import React from 'react'; +import {Link} from "react-router-dom"; import { Page, Grid, @@ -16,20 +17,20 @@ const Transactions = ({transactions}) => ( responsive className="card-table table-vcenter text-nowrap" headerItems={[ + {content: "Hash"}, {content: "Block Number"}, {content: "From"}, {content: "To"}, - {content: "Type"}, - {content: "Hash"} + {content: "Type"} ]} bodyItems={ transactions.map((transaction) => { return ([ + {content: {transaction.hash}}, {content: transaction.blockNumber}, {content: transaction.from}, {content: transaction.to}, {content: transaction.to ? "Contract Call" : "Contract Creation"}, - {content: transaction.hash} ]); }) } From 227255a3b6790038a6a6a2937007e97a83bb0063 Mon Sep 17 00:00:00 2001 From: Anthony Laibe Date: Mon, 6 Aug 2018 10:03:52 +0100 Subject: [PATCH 3/7] Lint --- embark-ui/src/components/Account.js | 4 +-- embark-ui/src/components/Accounts.js | 2 +- embark-ui/src/components/Blocks.js | 1 - embark-ui/src/components/Transactions.js | 2 +- embark-ui/src/containers/AccountContainer.js | 2 +- embark-ui/src/containers/BlockContainer.js | 31 ++++++++----------- .../src/containers/TransactionContainer.js | 29 +++++++---------- lib/modules/blockchain_connector/index.js | 2 +- 8 files changed, 31 insertions(+), 42 deletions(-) diff --git a/embark-ui/src/components/Account.js b/embark-ui/src/components/Account.js index 9193aeea..548df2c3 100644 --- a/embark-ui/src/components/Account.js +++ b/embark-ui/src/components/Account.js @@ -4,10 +4,10 @@ import { } from "tabler-react"; import PropTypes from 'prop-types'; - const Account = ({account}) => ( -

Hello

+

Balance: {account.balance}

+

Tx count: {account.transactionCount}

); diff --git a/embark-ui/src/components/Accounts.js b/embark-ui/src/components/Accounts.js index 348d776b..34f64e37 100644 --- a/embark-ui/src/components/Accounts.js +++ b/embark-ui/src/components/Accounts.js @@ -5,7 +5,7 @@ import { Card, Table } from "tabler-react"; -import { Link } from 'react-router-dom'; +import {Link} from 'react-router-dom'; import PropTypes from 'prop-types'; const Accounts = ({accounts}) => ( diff --git a/embark-ui/src/components/Blocks.js b/embark-ui/src/components/Blocks.js index 67329281..92981ac8 100644 --- a/embark-ui/src/components/Blocks.js +++ b/embark-ui/src/components/Blocks.js @@ -8,7 +8,6 @@ import { } from "tabler-react"; import PropTypes from 'prop-types'; - const Blocks = ({blocks}) => ( diff --git a/embark-ui/src/components/Transactions.js b/embark-ui/src/components/Transactions.js index 3d16815a..9a295d80 100644 --- a/embark-ui/src/components/Transactions.js +++ b/embark-ui/src/components/Transactions.js @@ -30,7 +30,7 @@ const Transactions = ({transactions}) => ( {content: transaction.blockNumber}, {content: transaction.from}, {content: transaction.to}, - {content: transaction.to ? "Contract Call" : "Contract Creation"}, + {content: transaction.to ? "Contract Call" : "Contract Creation"} ]); }) } diff --git a/embark-ui/src/containers/AccountContainer.js b/embark-ui/src/containers/AccountContainer.js index e187a8e6..dab3ade9 100644 --- a/embark-ui/src/containers/AccountContainer.js +++ b/embark-ui/src/containers/AccountContainer.js @@ -36,7 +36,7 @@ function mapStateToProps(state, props) { } AccountContainer.propTypes = { - router: PropTypes.object, + match: PropTypes.object, account: PropTypes.object, fetchAccount: PropTypes.func }; diff --git a/embark-ui/src/containers/BlockContainer.js b/embark-ui/src/containers/BlockContainer.js index d0c19c45..d95477f6 100644 --- a/embark-ui/src/containers/BlockContainer.js +++ b/embark-ui/src/containers/BlockContainer.js @@ -5,43 +5,38 @@ import {withRouter} from 'react-router-dom'; import {fetchBlock} from '../actions'; import Block from '../components/Block'; +import NoMatch from "../components/NoMatch"; import Transactions from '../components/Transactions'; -import Loading from '../components/Loading'; class BlockContainer extends Component { componentDidMount() { - this.props.fetchBlock(this.props.router.match.params.blockNumber); + this.props.fetchBlock(this.props.match.params.blockNumber); } render() { const {block} = this.props; - if (!block.data) { - return ; - } - - if (block.error) { - return ( -

- Error API... -

- ); + if (!block) { + return ; } return ( - - + + ); } } -function mapStateToProps(state) { - return {block: state.block}; +function mapStateToProps(state, props) { + if(state.blocks.data) { + return {block: state.blocks.data.find(block => block.number === props.match.params.blockNumber)}; + } + return null; } BlockContainer.propTypes = { - router: PropTypes.object, + match: PropTypes.object, block: PropTypes.object, fetchBlock: PropTypes.func }; @@ -51,4 +46,4 @@ export default withRouter(connect( { fetchBlock } -))(BlockContainer); +)(BlockContainer)); diff --git a/embark-ui/src/containers/TransactionContainer.js b/embark-ui/src/containers/TransactionContainer.js index dc56074e..22707b14 100644 --- a/embark-ui/src/containers/TransactionContainer.js +++ b/embark-ui/src/containers/TransactionContainer.js @@ -4,42 +4,37 @@ import PropTypes from 'prop-types'; import {withRouter} from 'react-router-dom'; import {fetchTransaction} from '../actions'; +import NoMatch from "../components/NoMatch"; import Transaction from '../components/Transaction'; -import Loading from '../components/Loading'; class TransactionContainer extends Component { componentDidMount() { - this.props.fetchTransaction(this.props.router.match.params.hash); + this.props.fetchTransaction(this.props.match.params.hash); } render() { const {transaction} = this.props; - if (!transaction.data) { - return ; - } - - if (transaction.error) { - return ( -

- Error API... -

- ); + if (!transaction) { + return ; } return ( - + ); } } -function mapStateToProps(state) { - return {transaction: state.transaction}; +function mapStateToProps(state, props) { + if(state.transactions.data) { + return {transaction: state.transactions.data.find(transaction => transaction.hash === props.match.params.hash)}; + } + return null; } TransactionContainer.propTypes = { - router: PropTypes.object, + match: PropTypes.object, transaction: PropTypes.object, fetchTransaction: PropTypes.func }; @@ -49,4 +44,4 @@ export default withRouter(connect( { fetchTransaction } -))(TransactionContainer); +)(TransactionContainer)); diff --git a/lib/modules/blockchain_connector/index.js b/lib/modules/blockchain_connector/index.js index 35ad4a08..32b0a525 100644 --- a/lib/modules/blockchain_connector/index.js +++ b/lib/modules/blockchain_connector/index.js @@ -283,7 +283,7 @@ class BlockchainConnector { let self = this; async.waterfall([ function(next) { - self.getAccountsWithTransactionCount((accounts) =>{ + self.getAccountsWithTransactionCount((accounts) => { let account = accounts.find((a) => a.address === address); if(!account) { next("No account found with this address"); From 72ac9803c5e41e4c2b1618a5e8b11fc5018bc886 Mon Sep 17 00:00:00 2001 From: Anthony Laibe Date: Mon, 6 Aug 2018 10:31:06 +0100 Subject: [PATCH 4/7] Update reducers --- embark-ui/src/api/index.js | 2 +- embark-ui/src/containers/AccountContainer.js | 2 +- embark-ui/src/containers/BlockContainer.js | 6 ++-- .../src/containers/TransactionContainer.js | 4 +-- embark-ui/src/reducers/accountsReducer.js | 14 +++++++-- embark-ui/src/reducers/blocksReducer.js | 24 ++++++++++++--- embark-ui/src/reducers/transactionsReducer.js | 30 ++++++++++++++----- embark-ui/src/sagas/index.js | 2 +- 8 files changed, 62 insertions(+), 22 deletions(-) diff --git a/embark-ui/src/api/index.js b/embark-ui/src/api/index.js index 1e489256..27137008 100644 --- a/embark-ui/src/api/index.js +++ b/embark-ui/src/api/index.js @@ -22,7 +22,7 @@ export function fetchTransactions(blockFrom) { } export function fetchTransaction(hash) { - return axios.get(`${constants.httpEndpoint}/blockchain/transactions${hash}`); + return axios.get(`${constants.httpEndpoint}/blockchain/transactions/${hash}`); } export function fetchProcesses() { diff --git a/embark-ui/src/containers/AccountContainer.js b/embark-ui/src/containers/AccountContainer.js index dab3ade9..0bfa23e2 100644 --- a/embark-ui/src/containers/AccountContainer.js +++ b/embark-ui/src/containers/AccountContainer.js @@ -32,7 +32,7 @@ function mapStateToProps(state, props) { if(state.accounts.data) { return {account: state.accounts.data.find(account => account.address === props.match.params.address)}; } - return null; + return {}; } AccountContainer.propTypes = { diff --git a/embark-ui/src/containers/BlockContainer.js b/embark-ui/src/containers/BlockContainer.js index d95477f6..aef7e1d5 100644 --- a/embark-ui/src/containers/BlockContainer.js +++ b/embark-ui/src/containers/BlockContainer.js @@ -21,7 +21,7 @@ class BlockContainer extends Component { return ( - + ); @@ -30,9 +30,9 @@ class BlockContainer extends Component { function mapStateToProps(state, props) { if(state.blocks.data) { - return {block: state.blocks.data.find(block => block.number === props.match.params.blockNumber)}; + return {block: state.blocks.data.find(block => block.number.toString() === props.match.params.blockNumber)}; } - return null; + return {}; } BlockContainer.propTypes = { diff --git a/embark-ui/src/containers/TransactionContainer.js b/embark-ui/src/containers/TransactionContainer.js index 22707b14..2d98b3b1 100644 --- a/embark-ui/src/containers/TransactionContainer.js +++ b/embark-ui/src/containers/TransactionContainer.js @@ -20,7 +20,7 @@ class TransactionContainer extends Component { return ( - + ); } @@ -30,7 +30,7 @@ function mapStateToProps(state, props) { if(state.transactions.data) { return {transaction: state.transactions.data.find(transaction => transaction.hash === props.match.params.hash)}; } - return null; + return {}; } TransactionContainer.propTypes = { diff --git a/embark-ui/src/reducers/accountsReducer.js b/embark-ui/src/reducers/accountsReducer.js index 0fb9e678..90e11b83 100644 --- a/embark-ui/src/reducers/accountsReducer.js +++ b/embark-ui/src/reducers/accountsReducer.js @@ -1,13 +1,23 @@ import {RECEIVE_ACCOUNTS, RECEIVE_ACCOUNTS_ERROR, RECEIVE_ACCOUNT, RECEIVE_ACCOUNT_ERROR} from "../actions"; +function filterAccount(account, index, self) { + return index === self.findIndex((a) => a.address === account.address); +} + export default function accounts(state = {}, action) { switch (action.type) { case RECEIVE_ACCOUNTS: - return Object.assign({}, state, {data: action.accounts.data}); + return { + ...state, data: [...action.accounts.data, ...state.data || []] + .filter(filterAccount) + }; case RECEIVE_ACCOUNTS_ERROR: return Object.assign({}, state, {error: true}); case RECEIVE_ACCOUNT: - return Object.assign({}, state, {data: action.account.data}); + return { + ...state, data: [action.account.data, ...state.data || []] + .filter(filterAccount) + }; case RECEIVE_ACCOUNT_ERROR: return Object.assign({}, state, {error: true}); default: diff --git a/embark-ui/src/reducers/blocksReducer.js b/embark-ui/src/reducers/blocksReducer.js index 9b4c8de2..124f96a7 100644 --- a/embark-ui/src/reducers/blocksReducer.js +++ b/embark-ui/src/reducers/blocksReducer.js @@ -1,15 +1,31 @@ -import {RECEIVE_BLOCKS, RECEIVE_BLOCKS_ERROR} from "../actions"; +import {RECEIVE_BLOCK, RECEIVE_BLOCK_ERROR, RECEIVE_BLOCKS, RECEIVE_BLOCKS_ERROR} from "../actions"; + +function sortBlock(a, b) { + return b.number - a.number; +} + +function filterBlock(block, index, self) { + return index === self.findIndex((t) => t.number === block.number); +} export default function blocks(state = {}, action) { switch (action.type) { case RECEIVE_BLOCKS: return { - ...state, data: [...state.data || [], ...action.blocks.data] - .filter((block, index, self) => index === self.findIndex((t) => t.number === block.number)) - .sort((a, b) => b.number - a.number) + ...state, data: [...action.blocks.data, ...state.data || []] + .filter(filterBlock) + .sort(sortBlock) }; case RECEIVE_BLOCKS_ERROR: return Object.assign({}, state, {error: true}); + case RECEIVE_BLOCK: + return { + ...state, data: [action.block.data, ...state.data || []] + .filter(filterBlock) + .sort(sortBlock) + }; + case RECEIVE_BLOCK_ERROR: + return Object.assign({}, state, {error: true}); default: return state; } diff --git a/embark-ui/src/reducers/transactionsReducer.js b/embark-ui/src/reducers/transactionsReducer.js index e2f3b290..a2cc6b5e 100644 --- a/embark-ui/src/reducers/transactionsReducer.js +++ b/embark-ui/src/reducers/transactionsReducer.js @@ -1,21 +1,35 @@ -import {RECEIVE_TRANSACTIONS, RECEIVE_TRANSACTIONS_ERROR} from "../actions"; +import {RECEIVE_TRANSACTION, RECEIVE_TRANSACTION_ERROR, RECEIVE_TRANSACTIONS, RECEIVE_TRANSACTIONS_ERROR} from "../actions"; const BN_FACTOR = 10000; +function sortTransaction(a, b) { + return ((BN_FACTOR * b.blockNumber) + b.transactionIndex) - ((BN_FACTOR * a.blockNumber) + a.transactionIndex); +} + +function filterTransaction(tx, index, self) { + return index === self.findIndex((t) => ( + t.blockNumber === tx.blockNumber && t.transactionIndex === tx.transactionIndex + )); +} + export default function transactions(state = {}, action) { switch (action.type) { case RECEIVE_TRANSACTIONS: return { - ...state, data: [...state.data || [], ...action.transactions.data] - .filter((tx, index, self) => index === self.findIndex((t) => ( - t.blockNumber === tx.blockNumber && t.transactionIndex === tx.transactionIndex - ))) - .sort((a, b) => ( - ((BN_FACTOR * b.blockNumber) + b.transactionIndex) - ((BN_FACTOR * a.blockNumber) + a.transactionIndex)) - ) + ...state, data: [...action.transactions.data, ...state.data || []] + .filter(filterTransaction) + .sort(sortTransaction) }; case RECEIVE_TRANSACTIONS_ERROR: return Object.assign({}, state, {error: true}); + case RECEIVE_TRANSACTION: + return { + ...state, data: [action.transaction.data, ...state.data || []] + .filter(filterTransaction) + .sort(sortTransaction) + }; + case RECEIVE_TRANSACTION_ERROR: + return Object.assign({}, state, {error: true}); default: return state; } diff --git a/embark-ui/src/sagas/index.js b/embark-ui/src/sagas/index.js index 29322674..9b8fc3db 100644 --- a/embark-ui/src/sagas/index.js +++ b/embark-ui/src/sagas/index.js @@ -57,7 +57,7 @@ export function *watchFetchBlocks() { export function *fetchAccount(payload) { try { - const account = yield call(api.fetchAccounts, payload.address); + const account = yield call(api.fetchAccount, payload.address); yield put(actions.receiveAccount(account)); } catch (e) { yield put(actions.receiveAccountError()); From 9557914ed1b5a63f02410ba3cf33c5f8a772d5ee Mon Sep 17 00:00:00 2001 From: Anthony Laibe Date: Mon, 6 Aug 2018 10:41:17 +0100 Subject: [PATCH 5/7] Show info --- embark-ui/src/components/Block.js | 4 ++-- embark-ui/src/components/Transaction.js | 10 ++++++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/embark-ui/src/components/Block.js b/embark-ui/src/components/Block.js index a64896c8..2c69a352 100644 --- a/embark-ui/src/components/Block.js +++ b/embark-ui/src/components/Block.js @@ -4,10 +4,10 @@ import { } from "tabler-react"; import PropTypes from 'prop-types'; - const Block = ({block}) => ( -

Hello

+

Timestamp: {block.timestamp}

+

Gas used: {block.gasUsed}

); diff --git a/embark-ui/src/components/Transaction.js b/embark-ui/src/components/Transaction.js index 5f7288a9..ffa7e180 100644 --- a/embark-ui/src/components/Transaction.js +++ b/embark-ui/src/components/Transaction.js @@ -1,13 +1,19 @@ import React from 'react'; +import {Link} from 'react-router-dom'; import { Page } from "tabler-react"; import PropTypes from 'prop-types'; - const Transaction = ({transaction}) => ( -

Hello

+

Block: {transaction.blockNumber}

+

From: {transaction.from}

+

To: {transaction.to}

+

Input: {transaction.input}

+

Gas: {transaction.gas}

+

Gas Price: {transaction.gasPrice}

+

Nonce: {transaction.nonce}

); From 9a9afa05cae4d531b189e3f7bdfbc70b71487526 Mon Sep 17 00:00:00 2001 From: Anthony Laibe Date: Mon, 6 Aug 2018 12:54:11 +0100 Subject: [PATCH 6/7] Improve saga/actions/api interations --- embark-ui/src/actions/index.js | 74 ++++++++----------- embark-ui/src/api/index.js | 18 ++++- embark-ui/src/containers/AccountContainer.js | 4 +- embark-ui/src/containers/AccountsContainer.js | 4 +- embark-ui/src/reducers/accountsReducer.js | 10 +-- embark-ui/src/sagas/index.js | 36 ++++----- lib/modules/blockchain_connector/index.js | 2 +- 7 files changed, 69 insertions(+), 79 deletions(-) diff --git a/embark-ui/src/actions/index.js b/embark-ui/src/actions/index.js index e981b02c..209a1a7f 100644 --- a/embark-ui/src/actions/index.js +++ b/embark-ui/src/actions/index.js @@ -1,10 +1,33 @@ // Accounts -export const FETCH_ACCOUNTS = 'FETCH_ACCOUNTS'; -export const RECEIVE_ACCOUNTS = 'RECEIVE_ACCOUNTS'; -export const RECEIVE_ACCOUNTS_ERROR = 'RECEIVE_ACCOUNTS_ERROR'; -export const FETCH_ACCOUNT = 'FETCH_ACCOUNT'; -export const RECEIVE_ACCOUNT = 'RECEIVE_ACCOUNT'; -export const RECEIVE_ACCOUNT_ERROR = 'RECEIVE_ACCOUNT_ERROR'; +export const REQUEST = 'REQUEST'; +export const SUCCESS = 'SUCCESS'; +export const FAILURE = 'FAILURE'; + +function createRequestTypes(base) { + return [REQUEST, SUCCESS, FAILURE].reduce((acc, type) => { + acc[type] = `${base}_${type}`; + return acc; + }, {}); +} + +function action(type, payload = {}) { + return {type, ...payload}; +} + +export const ACCOUNTS = createRequestTypes('ACCOUNTS'); +export const accounts = { + request: () => action(ACCOUNTS[REQUEST]), + success: (accounts) => action(ACCOUNTS[SUCCESS], {accounts}), + failure: (error) => action(ACCOUNTS[FAILURE], {error}) +}; + +export const ACCOUNT = createRequestTypes('ACCOUNT'); +export const account = { + request: (address) => action(ACCOUNT[REQUEST], {address}), + success: (account) => action(ACCOUNT[SUCCESS], {account}), + failure: (error) => action(ACCOUNT[FAILURE], {error}) +}; + // Processes export const FETCH_PROCESSES = 'FETCH_PROCESSES'; export const RECEIVE_PROCESSES = 'RECEIVE_PROCESSES'; @@ -30,45 +53,6 @@ export const RECEIVE_TRANSACTION_ERROR = 'RECEIVE_TRANSACTION_ERROR'; // BlockHeader export const INIT_BLOCK_HEADER = 'INIT_BLOCK_HEADER'; -export function fetchAccounts() { - return { - type: FETCH_ACCOUNTS - }; -} - -export function receiveAccounts(accounts) { - return { - type: RECEIVE_ACCOUNTS, - accounts - }; -} - -export function receiveAccountsError() { - return { - type: RECEIVE_ACCOUNTS_ERROR - }; -} - -export function fetchAccount(address) { - return { - type: FETCH_ACCOUNT, - address - }; -} - -export function receiveAccount(account) { - return { - type: RECEIVE_ACCOUNT, - account - }; -} - -export function receiveAccountError() { - return { - type: RECEIVE_ACCOUNT_ERROR - }; -} - export function fetchProcesses() { return { type: FETCH_PROCESSES diff --git a/embark-ui/src/api/index.js b/embark-ui/src/api/index.js index 27137008..5ee45883 100644 --- a/embark-ui/src/api/index.js +++ b/embark-ui/src/api/index.js @@ -1,12 +1,22 @@ import axios from "axios"; import constants from '../constants'; -export function fetchAccounts() { - return axios.get(`${constants.httpEndpoint}/blockchain/accounts`); + +function get(path, params) { + return axios.get(constants.httpEndpoint + path, params) + .then((response) => { + return {response}; + }).catch((error) => { + return {response: null, error: error.message || 'Something bad happened'}; + }); } -export function fetchAccount(address) { - return axios.get(`${constants.httpEndpoint}/blockchain/accounts/${address}`); +export function fetchAccounts() { + return get('/blockchain/accounts'); +} + +export function fetchAccount(payload) { + return get(`/blockchain/accounts/${payload.address}`); } export function fetchBlocks(from) { diff --git a/embark-ui/src/containers/AccountContainer.js b/embark-ui/src/containers/AccountContainer.js index 0bfa23e2..562380d2 100644 --- a/embark-ui/src/containers/AccountContainer.js +++ b/embark-ui/src/containers/AccountContainer.js @@ -3,7 +3,7 @@ import {connect} from 'react-redux'; import PropTypes from 'prop-types'; import {withRouter} from 'react-router-dom'; -import {fetchAccount} from '../actions'; +import {account as accountAction} from '../actions'; import Account from '../components/Account'; import NoMatch from "../components/NoMatch"; import Transactions from '../components/Transactions'; @@ -44,6 +44,6 @@ AccountContainer.propTypes = { export default withRouter(connect( mapStateToProps, { - fetchAccount + fetchAccount: accountAction.request } )(AccountContainer)); diff --git a/embark-ui/src/containers/AccountsContainer.js b/embark-ui/src/containers/AccountsContainer.js index 95a817d7..3bfdb59a 100644 --- a/embark-ui/src/containers/AccountsContainer.js +++ b/embark-ui/src/containers/AccountsContainer.js @@ -2,7 +2,7 @@ import React, {Component} from 'react'; import {connect} from 'react-redux'; import PropTypes from 'prop-types'; -import {fetchAccounts} from '../actions'; +import {accounts as accountsAction} from '../actions'; import Accounts from '../components/Accounts'; import Loading from '../components/Loading'; @@ -43,6 +43,6 @@ AccountsContainer.propTypes = { export default connect( mapStateToProps, { - fetchAccounts + fetchAccounts: accountsAction.request }, )(AccountsContainer); diff --git a/embark-ui/src/reducers/accountsReducer.js b/embark-ui/src/reducers/accountsReducer.js index 90e11b83..e02fb3c2 100644 --- a/embark-ui/src/reducers/accountsReducer.js +++ b/embark-ui/src/reducers/accountsReducer.js @@ -1,4 +1,4 @@ -import {RECEIVE_ACCOUNTS, RECEIVE_ACCOUNTS_ERROR, RECEIVE_ACCOUNT, RECEIVE_ACCOUNT_ERROR} from "../actions"; +import * as actions from "../actions"; function filterAccount(account, index, self) { return index === self.findIndex((a) => a.address === account.address); @@ -6,19 +6,19 @@ function filterAccount(account, index, self) { export default function accounts(state = {}, action) { switch (action.type) { - case RECEIVE_ACCOUNTS: + case actions.ACCOUNTS[actions.SUCCESS]: return { ...state, data: [...action.accounts.data, ...state.data || []] .filter(filterAccount) }; - case RECEIVE_ACCOUNTS_ERROR: + case actions.ACCOUNTS[actions.FAILURE]: return Object.assign({}, state, {error: true}); - case RECEIVE_ACCOUNT: + case actions.ACCOUNT[actions.SUCCESS]: return { ...state, data: [action.account.data, ...state.data || []] .filter(filterAccount) }; - case RECEIVE_ACCOUNT_ERROR: + case actions.ACCOUNT[actions.FAILURE]: return Object.assign({}, state, {error: true}); default: return state; diff --git a/embark-ui/src/sagas/index.js b/embark-ui/src/sagas/index.js index 9b8fc3db..15abbc32 100644 --- a/embark-ui/src/sagas/index.js +++ b/embark-ui/src/sagas/index.js @@ -3,6 +3,20 @@ import * as api from '../api'; import {eventChannel} from 'redux-saga'; import {all, call, fork, put, takeEvery, take} from 'redux-saga/effects'; +const {account, accounts} = actions; + +function *fetchEntity(entity, apiFn, id) { + const {response, error} = yield call(apiFn, id); + if(response) { + yield put(entity.success(response)); + } else { + yield put(entity.failure(error)); + } +} + +export const fetchAccount = fetchEntity.bind(null, account, api.fetchAccount); +export const fetchAccounts = fetchEntity.bind(null, accounts, api.fetchAccounts); + export function *fetchTransaction(payload) { try { const transaction = yield call(api.fetchTransaction, payload.hash); @@ -55,30 +69,12 @@ export function *watchFetchBlocks() { yield takeEvery(actions.FETCH_BLOCKS, fetchBlocks); } -export function *fetchAccount(payload) { - try { - const account = yield call(api.fetchAccount, payload.address); - yield put(actions.receiveAccount(account)); - } catch (e) { - yield put(actions.receiveAccountError()); - } -} - export function *watchFetchAccount() { - yield takeEvery(actions.FETCH_ACCOUNT, fetchAccount); -} - -export function *fetchAccounts() { - try { - const accounts = yield call(api.fetchAccounts); - yield put(actions.receiveAccounts(accounts)); - } catch (e) { - yield put(actions.receiveAccountsError()); - } + yield takeEvery(actions.ACCOUNT[actions.REQUEST], fetchAccount); } export function *watchFetchAccounts() { - yield takeEvery(actions.FETCH_ACCOUNTS, fetchAccounts); + yield takeEvery(actions.ACCOUNTS[actions.REQUEST], fetchAccounts); } export function *fetchProcesses() { diff --git a/lib/modules/blockchain_connector/index.js b/lib/modules/blockchain_connector/index.js index 32b0a525..d7f12b8f 100644 --- a/lib/modules/blockchain_connector/index.js +++ b/lib/modules/blockchain_connector/index.js @@ -286,7 +286,7 @@ class BlockchainConnector { self.getAccountsWithTransactionCount((accounts) => { let account = accounts.find((a) => a.address === address); if(!account) { - next("No account found with this address"); + return next("No account found with this address"); } next(null, account); }); From 9ab0659646ea546f2c17d5e0025b619130ca1ceb Mon Sep 17 00:00:00 2001 From: Anthony Laibe Date: Mon, 6 Aug 2018 13:39:00 +0100 Subject: [PATCH 7/7] Apply changes for block and transaction + error --- embark-ui/src/actions/index.js | 122 ++++-------------- embark-ui/src/api/index.js | 16 +-- embark-ui/src/components/Error.js | 19 +++ embark-ui/src/containers/AccountContainer.js | 13 +- embark-ui/src/containers/AccountsContainer.js | 13 +- embark-ui/src/containers/BlockContainer.js | 16 ++- embark-ui/src/containers/BlocksContainer.js | 17 +-- .../src/containers/TransactionContainer.js | 16 ++- .../src/containers/TransactionsContainer.js | 17 +-- embark-ui/src/reducers/accountsReducer.js | 8 +- embark-ui/src/reducers/blocksReducer.js | 18 +-- embark-ui/src/reducers/transactionsReducer.js | 18 +-- embark-ui/src/sagas/index.js | 54 ++------ lib/modules/blockchain_connector/index.js | 10 +- 14 files changed, 150 insertions(+), 207 deletions(-) create mode 100644 embark-ui/src/components/Error.js diff --git a/embark-ui/src/actions/index.js b/embark-ui/src/actions/index.js index 209a1a7f..c5543c64 100644 --- a/embark-ui/src/actions/index.js +++ b/embark-ui/src/actions/index.js @@ -28,6 +28,34 @@ export const account = { failure: (error) => action(ACCOUNT[FAILURE], {error}) }; +export const BLOCKS = createRequestTypes('BLOCKS'); +export const blocks = { + request: (from) => action(BLOCKS[REQUEST], {from}), + success: (blocks) => action(BLOCKS[SUCCESS], {blocks}), + failure: (error) => action(BLOCKS[FAILURE], {error}) +}; + +export const BLOCK = createRequestTypes('BLOCK'); +export const block = { + request: (blockNumber) => action(BLOCK[REQUEST], {blockNumber}), + success: (block) => action(BLOCK[SUCCESS], {block}), + failure: (error) => action(BLOCK[FAILURE], {error}) +}; + +export const TRANSACTIONS = createRequestTypes('TRANSACTIONS'); +export const transactions = { + request: (blockFrom) => action(TRANSACTIONS[REQUEST], {blockFrom}), + success: (transactions) => action(TRANSACTIONS[SUCCESS], {transactions}), + failure: (error) => action(TRANSACTIONS[FAILURE], {error}) +}; + +export const TRANSACTION = createRequestTypes('TRANSACTION'); +export const transaction = { + request: (hash) => action(TRANSACTION[REQUEST], {hash}), + success: (transaction) => action(TRANSACTION[SUCCESS], {transaction}), + failure: (error) => action(TRANSACTION[FAILURE], {error}) +}; + // Processes export const FETCH_PROCESSES = 'FETCH_PROCESSES'; export const RECEIVE_PROCESSES = 'RECEIVE_PROCESSES'; @@ -36,20 +64,6 @@ export const RECEIVE_PROCESSES_ERROR = 'RECEIVE_PROCESSES_ERROR'; export const FETCH_PROCESS_LOGS = 'FETCH_PROCESS_LOGS'; export const RECEIVE_PROCESS_LOGS = 'RECEIVE_PROCESS_LOGS'; export const RECEIVE_PROCESS_LOGS_ERROR = 'RECEIVE_PROCESS_LOGS_ERROR'; -// Blocks -export const FETCH_BLOCKS = 'FETCH_BLOCKS'; -export const RECEIVE_BLOCKS = 'RECEIVE_BLOCKS'; -export const RECEIVE_BLOCKS_ERROR = 'RECEIVE_BLOCKS_ERROR'; -export const FETCH_BLOCK = 'FETCH_BLOCK'; -export const RECEIVE_BLOCK = 'RECEIVE_BLOCK'; -export const RECEIVE_BLOCK_ERROR = 'RECEIVE_BLOCK_ERROR'; -// Transactions -export const FETCH_TRANSACTIONS = 'FETCH_TRANSACTIONS'; -export const RECEIVE_TRANSACTIONS = 'RECEIVE_TRANSACTIONS'; -export const RECEIVE_TRANSACTIONS_ERROR = 'RECEIVE_TRANSACTIONS_ERROR'; -export const FETCH_TRANSACTION = 'FETCH_TRANSACTION'; -export const RECEIVE_TRANSACTION = 'RECEIVE_TRANSACTION'; -export const RECEIVE_TRANSACTION_ERROR = 'RECEIVE_TRANSACTION_ERROR'; // BlockHeader export const INIT_BLOCK_HEADER = 'INIT_BLOCK_HEADER'; @@ -95,86 +109,6 @@ export function receiveProcessLogsError(error) { }; } -export function fetchBlocks(from) { - return { - type: FETCH_BLOCKS, - from - }; -} - -export function receiveBlocks(blocks) { - return { - type: RECEIVE_BLOCKS, - blocks - }; -} - -export function receiveBlocksError() { - return { - type: RECEIVE_BLOCKS_ERROR - }; -} - -export function fetchBlock(blockNumber) { - return { - type: FETCH_BLOCK, - blockNumber - }; -} - -export function receiveBlock(block) { - return { - type: RECEIVE_BLOCK, - block - }; -} - -export function receiveBlockError() { - return { - type: RECEIVE_BLOCK_ERROR - }; -} - -export function fetchTransactions(blockFrom) { - return { - type: FETCH_TRANSACTIONS, - blockFrom - }; -} - -export function receiveTransactions(transactions) { - return { - type: RECEIVE_TRANSACTIONS, - transactions - }; -} - -export function receiveTransactionsError() { - return { - type: RECEIVE_TRANSACTIONS_ERROR - }; -} - -export function fetchTransaction(hash) { - return { - type: FETCH_TRANSACTION, - hash - }; -} - -export function receiveTransaction(transaction) { - return { - type: RECEIVE_TRANSACTION, - transaction - }; -} - -export function receiveTransactionError() { - return { - type: RECEIVE_TRANSACTION_ERROR - }; -} - export function initBlockHeader(){ return { type: INIT_BLOCK_HEADER diff --git a/embark-ui/src/api/index.js b/embark-ui/src/api/index.js index 5ee45883..65e67739 100644 --- a/embark-ui/src/api/index.js +++ b/embark-ui/src/api/index.js @@ -19,20 +19,20 @@ export function fetchAccount(payload) { return get(`/blockchain/accounts/${payload.address}`); } -export function fetchBlocks(from) { - return axios.get(`${constants.httpEndpoint}/blockchain/blocks`, {params: {from}}); +export function fetchBlocks(payload) { + return get('/blockchain/blocks', {params: payload}); } -export function fetchBlock(blockNumber) { - return axios.get(`${constants.httpEndpoint}/blockchain/blocks/${blockNumber}`); +export function fetchBlock(payload) { + return get(`/blockchain/blocks/${payload.blockNumber}`); } -export function fetchTransactions(blockFrom) { - return axios.get(`${constants.httpEndpoint}/blockchain/transactions`, {params: {blockFrom}}); +export function fetchTransactions(payload) { + return get('/blockchain/transactions', {params: payload}); } -export function fetchTransaction(hash) { - return axios.get(`${constants.httpEndpoint}/blockchain/transactions/${hash}`); +export function fetchTransaction(payload) { + return get(`/blockchain/transactions/${payload.hash}`); } export function fetchProcesses() { diff --git a/embark-ui/src/components/Error.js b/embark-ui/src/components/Error.js new file mode 100644 index 00000000..4875504a --- /dev/null +++ b/embark-ui/src/components/Error.js @@ -0,0 +1,19 @@ +import PropTypes from "prop-types"; +import React from 'react'; +import {Grid} from 'tabler-react'; + +const Error = ({error}) => ( + + +

+ {error} +

+
+
+); + +Error.propTypes = { + error: PropTypes.string +}; + +export default Error; diff --git a/embark-ui/src/containers/AccountContainer.js b/embark-ui/src/containers/AccountContainer.js index 562380d2..0b291195 100644 --- a/embark-ui/src/containers/AccountContainer.js +++ b/embark-ui/src/containers/AccountContainer.js @@ -7,6 +7,7 @@ import {account as accountAction} from '../actions'; import Account from '../components/Account'; import NoMatch from "../components/NoMatch"; import Transactions from '../components/Transactions'; +import Error from '../components/Error'; class AccountContainer extends Component { componentDidMount() { @@ -14,7 +15,11 @@ class AccountContainer extends Component { } render() { - const {account} = this.props; + const {account, error} = this.props; + if (error) { + return ; + } + if (!account) { return ; } @@ -29,6 +34,9 @@ class AccountContainer extends Component { } function mapStateToProps(state, props) { + if(state.accounts.error) { + return {error: state.accounts.error}; + } if(state.accounts.data) { return {account: state.accounts.data.find(account => account.address === props.match.params.address)}; } @@ -38,7 +46,8 @@ function mapStateToProps(state, props) { AccountContainer.propTypes = { match: PropTypes.object, account: PropTypes.object, - fetchAccount: PropTypes.func + fetchAccount: PropTypes.func, + error: PropTypes.string }; export default withRouter(connect( diff --git a/embark-ui/src/containers/AccountsContainer.js b/embark-ui/src/containers/AccountsContainer.js index 3bfdb59a..0aa9fe24 100644 --- a/embark-ui/src/containers/AccountsContainer.js +++ b/embark-ui/src/containers/AccountsContainer.js @@ -5,6 +5,7 @@ import PropTypes from 'prop-types'; import {accounts as accountsAction} from '../actions'; import Accounts from '../components/Accounts'; import Loading from '../components/Loading'; +import Error from '../components/Error'; class AccountsContainer extends Component { componentDidMount() { @@ -13,16 +14,12 @@ class AccountsContainer extends Component { render() { const {accounts} = this.props; - if (!accounts.data) { - return ; + if (accounts.error) { + return ; } - if (accounts.error) { - return ( -

- Error API... -

- ); + if (!accounts.data) { + return ; } return ( diff --git a/embark-ui/src/containers/BlockContainer.js b/embark-ui/src/containers/BlockContainer.js index aef7e1d5..9d8de292 100644 --- a/embark-ui/src/containers/BlockContainer.js +++ b/embark-ui/src/containers/BlockContainer.js @@ -3,8 +3,9 @@ import {connect} from 'react-redux'; import PropTypes from 'prop-types'; import {withRouter} from 'react-router-dom'; -import {fetchBlock} from '../actions'; +import {block as blockAction} from '../actions'; import Block from '../components/Block'; +import Error from "../components/Error"; import NoMatch from "../components/NoMatch"; import Transactions from '../components/Transactions'; @@ -14,7 +15,10 @@ class BlockContainer extends Component { } render() { - const {block} = this.props; + const {block, error} = this.props; + if (error) { + return ; + } if (!block) { return ; } @@ -29,6 +33,9 @@ class BlockContainer extends Component { } function mapStateToProps(state, props) { + if(state.blocks.error) { + return {error: state.blocks.error}; + } if(state.blocks.data) { return {block: state.blocks.data.find(block => block.number.toString() === props.match.params.blockNumber)}; } @@ -38,12 +45,13 @@ function mapStateToProps(state, props) { BlockContainer.propTypes = { match: PropTypes.object, block: PropTypes.object, - fetchBlock: PropTypes.func + fetchBlock: PropTypes.func, + error: PropTypes.string }; export default withRouter(connect( mapStateToProps, { - fetchBlock + fetchBlock: blockAction.request } )(BlockContainer)); diff --git a/embark-ui/src/containers/BlocksContainer.js b/embark-ui/src/containers/BlocksContainer.js index 985990a8..c2c4d264 100644 --- a/embark-ui/src/containers/BlocksContainer.js +++ b/embark-ui/src/containers/BlocksContainer.js @@ -2,10 +2,11 @@ import React, {Component} from 'react'; import {connect} from 'react-redux'; import PropTypes from 'prop-types'; -import {fetchBlocks} from '../actions'; +import {blocks as blocksAction} from '../actions'; import Blocks from '../components/Blocks'; import Loading from '../components/Loading'; import LoadMore from '../components/LoadMore'; +import Error from '../components/Error'; class BlocksContainer extends Component { componentDidMount() { @@ -23,16 +24,12 @@ class BlocksContainer extends Component { render() { const {blocks} = this.props; - if (!blocks.data) { - return ; + if (blocks.error) { + return ; } - if (blocks.error) { - return ( -

- Error API... -

- ); + if (!blocks.data) { + return ; } return ( @@ -56,6 +53,6 @@ BlocksContainer.propTypes = { export default connect( mapStateToProps, { - fetchBlocks + fetchBlocks: blocksAction.request }, )(BlocksContainer); diff --git a/embark-ui/src/containers/TransactionContainer.js b/embark-ui/src/containers/TransactionContainer.js index 2d98b3b1..32344549 100644 --- a/embark-ui/src/containers/TransactionContainer.js +++ b/embark-ui/src/containers/TransactionContainer.js @@ -3,7 +3,8 @@ import {connect} from 'react-redux'; import PropTypes from 'prop-types'; import {withRouter} from 'react-router-dom'; -import {fetchTransaction} from '../actions'; +import {transaction as transactionAction} from '../actions'; +import Error from "../components/Error"; import NoMatch from "../components/NoMatch"; import Transaction from '../components/Transaction'; @@ -13,7 +14,10 @@ class TransactionContainer extends Component { } render() { - const {transaction} = this.props; + const {transaction, error} = this.props; + if (error) { + return ; + } if (!transaction) { return ; } @@ -27,6 +31,9 @@ class TransactionContainer extends Component { } function mapStateToProps(state, props) { + if(state.transactions.error) { + return {error: state.transactions.error}; + } if(state.transactions.data) { return {transaction: state.transactions.data.find(transaction => transaction.hash === props.match.params.hash)}; } @@ -36,12 +43,13 @@ function mapStateToProps(state, props) { TransactionContainer.propTypes = { match: PropTypes.object, transaction: PropTypes.object, - fetchTransaction: PropTypes.func + fetchTransaction: PropTypes.func, + error: PropTypes.string }; export default withRouter(connect( mapStateToProps, { - fetchTransaction + fetchTransaction: transactionAction.request } )(TransactionContainer)); diff --git a/embark-ui/src/containers/TransactionsContainer.js b/embark-ui/src/containers/TransactionsContainer.js index 7e7edc81..01c6eebd 100644 --- a/embark-ui/src/containers/TransactionsContainer.js +++ b/embark-ui/src/containers/TransactionsContainer.js @@ -2,10 +2,11 @@ import React, {Component} from 'react'; import {connect} from 'react-redux'; import PropTypes from 'prop-types'; -import {fetchTransactions} from '../actions'; +import {transactions as transactionsAction} from '../actions'; import Transactions from '../components/Transactions'; import Loading from '../components/Loading'; import LoadMore from '../components/LoadMore'; +import Error from '../components/Error'; class TransactionsContainer extends Component { componentDidMount() { @@ -23,16 +24,12 @@ class TransactionsContainer extends Component { render() { const {transactions} = this.props; - if (!transactions.data) { - return ; + if (transactions.error) { + return ; } - if (transactions.error) { - return ( -

- Error API... -

- ); + if (!transactions.data) { + return ; } return ( @@ -56,6 +53,6 @@ TransactionsContainer.propTypes = { export default connect( mapStateToProps, { - fetchTransactions + fetchTransactions: transactionsAction.request }, )(TransactionsContainer); diff --git a/embark-ui/src/reducers/accountsReducer.js b/embark-ui/src/reducers/accountsReducer.js index e02fb3c2..49f29753 100644 --- a/embark-ui/src/reducers/accountsReducer.js +++ b/embark-ui/src/reducers/accountsReducer.js @@ -8,18 +8,18 @@ export default function accounts(state = {}, action) { switch (action.type) { case actions.ACCOUNTS[actions.SUCCESS]: return { - ...state, data: [...action.accounts.data, ...state.data || []] + ...state, error: null, data: [...action.accounts.data, ...state.data || []] .filter(filterAccount) }; case actions.ACCOUNTS[actions.FAILURE]: - return Object.assign({}, state, {error: true}); + return Object.assign({}, state, {error: action.error}); case actions.ACCOUNT[actions.SUCCESS]: return { - ...state, data: [action.account.data, ...state.data || []] + ...state, error: null, data: [action.account.data, ...state.data || []] .filter(filterAccount) }; case actions.ACCOUNT[actions.FAILURE]: - return Object.assign({}, state, {error: true}); + return Object.assign({}, state, {error: action.error}); default: return state; } diff --git a/embark-ui/src/reducers/blocksReducer.js b/embark-ui/src/reducers/blocksReducer.js index 124f96a7..6e9102cc 100644 --- a/embark-ui/src/reducers/blocksReducer.js +++ b/embark-ui/src/reducers/blocksReducer.js @@ -1,4 +1,4 @@ -import {RECEIVE_BLOCK, RECEIVE_BLOCK_ERROR, RECEIVE_BLOCKS, RECEIVE_BLOCKS_ERROR} from "../actions"; +import * as actions from "../actions"; function sortBlock(a, b) { return b.number - a.number; @@ -10,22 +10,22 @@ function filterBlock(block, index, self) { export default function blocks(state = {}, action) { switch (action.type) { - case RECEIVE_BLOCKS: + case actions.BLOCKS[actions.SUCCESS]: return { - ...state, data: [...action.blocks.data, ...state.data || []] + ...state, error: null, data: [...action.blocks.data, ...state.data || []] .filter(filterBlock) .sort(sortBlock) }; - case RECEIVE_BLOCKS_ERROR: - return Object.assign({}, state, {error: true}); - case RECEIVE_BLOCK: + case actions.BLOCKS[actions.FAILURE]: + return Object.assign({}, state, {error: action.error}); + case actions.BLOCK[actions.SUCCESS]: return { - ...state, data: [action.block.data, ...state.data || []] + ...state, error: null, data: [action.block.data, ...state.data || []] .filter(filterBlock) .sort(sortBlock) }; - case RECEIVE_BLOCK_ERROR: - return Object.assign({}, state, {error: true}); + case actions.BLOCK[actions.FAILURE]: + return Object.assign({}, state, {error: action.error}); default: return state; } diff --git a/embark-ui/src/reducers/transactionsReducer.js b/embark-ui/src/reducers/transactionsReducer.js index a2cc6b5e..23b353ff 100644 --- a/embark-ui/src/reducers/transactionsReducer.js +++ b/embark-ui/src/reducers/transactionsReducer.js @@ -1,4 +1,4 @@ -import {RECEIVE_TRANSACTION, RECEIVE_TRANSACTION_ERROR, RECEIVE_TRANSACTIONS, RECEIVE_TRANSACTIONS_ERROR} from "../actions"; +import * as actions from "../actions"; const BN_FACTOR = 10000; @@ -14,22 +14,22 @@ function filterTransaction(tx, index, self) { export default function transactions(state = {}, action) { switch (action.type) { - case RECEIVE_TRANSACTIONS: + case actions.TRANSACTIONS[actions.SUCCESS]: return { - ...state, data: [...action.transactions.data, ...state.data || []] + ...state, error: null, data: [...action.transactions.data, ...state.data || []] .filter(filterTransaction) .sort(sortTransaction) }; - case RECEIVE_TRANSACTIONS_ERROR: - return Object.assign({}, state, {error: true}); - case RECEIVE_TRANSACTION: + case actions.TRANSACTIONS[actions.FAILURE]: + return Object.assign({}, state, {error: action.error}); + case actions.TRANSACTION[actions.SUCCESS]: return { - ...state, data: [action.transaction.data, ...state.data || []] + ...state, error: null, data: [action.transaction.data, ...state.data || []] .filter(filterTransaction) .sort(sortTransaction) }; - case RECEIVE_TRANSACTION_ERROR: - return Object.assign({}, state, {error: true}); + case actions.TRANSACTION[actions.FAILURE]: + return Object.assign({}, state, {error: action.error}); default: return state; } diff --git a/embark-ui/src/sagas/index.js b/embark-ui/src/sagas/index.js index 15abbc32..f3c8c254 100644 --- a/embark-ui/src/sagas/index.js +++ b/embark-ui/src/sagas/index.js @@ -3,7 +3,7 @@ import * as api from '../api'; import {eventChannel} from 'redux-saga'; import {all, call, fork, put, takeEvery, take} from 'redux-saga/effects'; -const {account, accounts} = actions; +const {account, accounts, block, blocks, transaction, transactions} = actions; function *fetchEntity(entity, apiFn, id) { const {response, error} = yield call(apiFn, id); @@ -15,58 +15,26 @@ function *fetchEntity(entity, apiFn, id) { } export const fetchAccount = fetchEntity.bind(null, account, api.fetchAccount); +export const fetchBlock = fetchEntity.bind(null, block, api.fetchBlock); +export const fetchTransaction = fetchEntity.bind(null, transaction, api.fetchTransaction); export const fetchAccounts = fetchEntity.bind(null, accounts, api.fetchAccounts); - -export function *fetchTransaction(payload) { - try { - const transaction = yield call(api.fetchTransaction, payload.hash); - yield put(actions.receiveTransaction(transaction)); - } catch (e) { - yield put(actions.receiveTransactionError()); - } -} +export const fetchBlocks = fetchEntity.bind(null, blocks, api.fetchBlocks); +export const fetchTransactions = fetchEntity.bind(null, transactions, api.fetchTransactions); export function *watchFetchTransaction() { - yield takeEvery(actions.FETCH_TRANSACTION, fetchTransaction); -} - -export function *fetchTransactions(payload) { - try { - const transactions = yield call(api.fetchTransactions, payload.blockFrom); - yield put(actions.receiveTransactions(transactions)); - } catch (e) { - yield put(actions.receiveTransactionsError()); - } + yield takeEvery(actions.TRANSACTION[actions.REQUEST], fetchTransaction); } export function *watchFetchTransactions() { - yield takeEvery(actions.FETCH_TRANSACTIONS, fetchTransactions); -} - -export function *fetchBlock(payload) { - try { - const block = yield call(api.fetchBlock, payload.blockNumber); - yield put(actions.receiveBlock(block)); - } catch (e) { - yield put(actions.receiveBlockError()); - } + yield takeEvery(actions.TRANSACTIONS[actions.REQUEST], fetchTransactions); } export function *watchFetchBlock() { - yield takeEvery(actions.FETCH_BLOCK, fetchBlock); -} - -export function *fetchBlocks(payload) { - try { - const blocks = yield call(api.fetchBlocks, payload.from); - yield put(actions.receiveBlocks(blocks)); - } catch (e) { - yield put(actions.receiveBlocksError()); - } + yield takeEvery(actions.BLOCK[actions.REQUEST], fetchBlock); } export function *watchFetchBlocks() { - yield takeEvery(actions.FETCH_BLOCKS, fetchBlocks); + yield takeEvery(actions.BLOCKS[actions.REQUEST], fetchBlocks); } export function *watchFetchAccount() { @@ -119,8 +87,8 @@ export function *initBlockHeader() { const channel = yield call(createChannel, socket); while (true) { yield take(channel); - yield put({type: actions.FETCH_BLOCKS}); - yield put({type: actions.FETCH_TRANSACTIONS}); + yield put({type: actions.BLOCKS[actions.REQUEST]}); + yield put({type: actions.TRANSACTIONS[actions.REQUEST]}); } } diff --git a/lib/modules/blockchain_connector/index.js b/lib/modules/blockchain_connector/index.js index d7f12b8f..020ac4fb 100644 --- a/lib/modules/blockchain_connector/index.js +++ b/lib/modules/blockchain_connector/index.js @@ -203,7 +203,10 @@ class BlockchainConnector { 'get', '/embark-api/blockchain/blocks/:blockNumber', (req, res) => { - self.getBlock(req.params.blockNumber, (_err, block) => { + self.getBlock(req.params.blockNumber, (err, block) => { + if(err){ + self.logger.error(err); + } res.send(block); }); } @@ -223,7 +226,10 @@ class BlockchainConnector { 'get', '/embark-api/blockchain/transactions/:hash', (req, res) => { - self.getTransaction(req.params.hash, (_err, transaction) => { + self.getTransaction(req.params.hash, (err, transaction) => { + if(err){ + self.logger.error(err); + } res.send(transaction); }); }