Add show components

This commit is contained in:
Anthony Laibe 2018-08-03 16:51:15 +01:00 committed by Iuri Matias
parent 099b9b0665
commit 77c47e2d2f
12 changed files with 452 additions and 36 deletions

View File

@ -2,6 +2,9 @@
export const FETCH_ACCOUNTS = 'FETCH_ACCOUNTS'; export const FETCH_ACCOUNTS = 'FETCH_ACCOUNTS';
export const RECEIVE_ACCOUNTS = 'RECEIVE_ACCOUNTS'; export const RECEIVE_ACCOUNTS = 'RECEIVE_ACCOUNTS';
export const RECEIVE_ACCOUNTS_ERROR = 'RECEIVE_ACCOUNTS_ERROR'; 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 // Processes
export const FETCH_PROCESSES = 'FETCH_PROCESSES'; export const FETCH_PROCESSES = 'FETCH_PROCESSES';
export const RECEIVE_PROCESSES = 'RECEIVE_PROCESSES'; export const RECEIVE_PROCESSES = 'RECEIVE_PROCESSES';
@ -16,10 +19,16 @@ export const RECEIVE_PROCESS_LOGS_ERROR = 'RECEIVE_PROCESS_LOGS_ERROR';
export const FETCH_BLOCKS = 'FETCH_BLOCKS'; export const FETCH_BLOCKS = 'FETCH_BLOCKS';
export const RECEIVE_BLOCKS = 'RECEIVE_BLOCKS'; export const RECEIVE_BLOCKS = 'RECEIVE_BLOCKS';
export const RECEIVE_BLOCKS_ERROR = 'RECEIVE_BLOCKS_ERROR'; 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 // Transactions
export const FETCH_TRANSACTIONS = 'FETCH_TRANSACTIONS'; export const FETCH_TRANSACTIONS = 'FETCH_TRANSACTIONS';
export const RECEIVE_TRANSACTIONS = 'RECEIVE_TRANSACTIONS'; export const RECEIVE_TRANSACTIONS = 'RECEIVE_TRANSACTIONS';
export const RECEIVE_TRANSACTIONS_ERROR = 'RECEIVE_TRANSACTIONS_ERROR'; 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 // BlockHeader
export const INIT_BLOCK_HEADER = 'INIT_BLOCK_HEADER'; export const INIT_BLOCK_HEADER = 'INIT_BLOCK_HEADER';
@ -42,6 +51,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() { export function fetchProcesses() {
return { return {
type: FETCH_PROCESSES type: FETCH_PROCESSES
@ -111,6 +140,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) { export function fetchTransactions(blockFrom) {
return { return {
type: FETCH_TRANSACTIONS, type: FETCH_TRANSACTIONS,
@ -131,6 +180,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(){ export function initBlockHeader(){
return { return {
type: INIT_BLOCK_HEADER type: INIT_BLOCK_HEADER

View File

@ -5,14 +5,26 @@ export function fetchAccounts() {
return axios.get(`${constants.httpEndpoint}/blockchain/accounts`); return axios.get(`${constants.httpEndpoint}/blockchain/accounts`);
} }
export function fetchAccount(address) {
return axios.get(`${constants.httpEndpoint}/blockchain/accounts/${address}`);
}
export function fetchBlocks(from) { export function fetchBlocks(from) {
return axios.get(`${constants.httpEndpoint}/blockchain/blocks`, {params: {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) { export function fetchTransactions(blockFrom) {
return axios.get(`${constants.httpEndpoint}/blockchain/transactions`, {params: {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() { export function fetchProcesses() {
return axios.get(`${constants.httpEndpoint}/processes`); return axios.get(`${constants.httpEndpoint}/processes`);
} }

View File

@ -0,0 +1,18 @@
import React from 'react';
import {
Page
} from "tabler-react";
import PropTypes from 'prop-types';
const Account = ({account}) => (
<Page.Content title={`Account ${account.address}`}>
<p>Hello</p>
</Page.Content>
);
Account.propTypes = {
account: PropTypes.object
};
export default Account;

View File

@ -0,0 +1,18 @@
import React from 'react';
import {
Page
} from "tabler-react";
import PropTypes from 'prop-types';
const Block = ({block}) => (
<Page.Content title={`Block ${block.number}`}>
<p>Hello</p>
</Page.Content>
);
Block.propTypes = {
block: PropTypes.object
};
export default Block;

View File

@ -7,8 +7,11 @@ import {
} from "tabler-react"; } from "tabler-react";
import AccountsContainer from '../containers/AccountsContainer'; import AccountsContainer from '../containers/AccountsContainer';
import AccountContainer from '../containers/AccountContainer';
import BlocksContainer from '../containers/BlocksContainer'; import BlocksContainer from '../containers/BlocksContainer';
import BlockContainer from '../containers/BlockContainer';
import TransactionsContainer from '../containers/TransactionsContainer'; import TransactionsContainer from '../containers/TransactionsContainer';
import TransactionContainer from '../containers/TransactionContainer';
const ExplorerLayout = () => ( const ExplorerLayout = () => (
<Grid.Row> <Grid.Row>
@ -46,8 +49,11 @@ const ExplorerLayout = () => (
<Grid.Col md={9}> <Grid.Col md={9}>
<Switch> <Switch>
<Route exact path="/embark/explorer/accounts" component={AccountsContainer} /> <Route exact path="/embark/explorer/accounts" component={AccountsContainer} />
<Route exact path="/embark/explorer/accounts/:address" component={AccountContainer} />
<Route exact path="/embark/explorer/blocks" component={BlocksContainer} /> <Route exact path="/embark/explorer/blocks" component={BlocksContainer} />
<Route exact path="/embark/explorer/blocks/:blockNumber" component={BlockContainer} />
<Route exact path="/embark/explorer/transactions" component={TransactionsContainer} /> <Route exact path="/embark/explorer/transactions" component={TransactionsContainer} />
<Route exact path="/embark/explorer/transactions/:hash" component={TransactionContainer} />
</Switch> </Switch>
</Grid.Col> </Grid.Col>
</Grid.Row> </Grid.Row>

View File

@ -0,0 +1,18 @@
import React from 'react';
import {
Page
} from "tabler-react";
import PropTypes from 'prop-types';
const Transaction = ({transaction}) => (
<Page.Content title={`Transaction ${transaction.hash}`}>
<p>Hello</p>
</Page.Content>
);
Transaction.propTypes = {
transaction: PropTypes.object
};
export default Transaction;

View File

@ -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 <NoMatch />;
}
return (
<React.Fragment>
<Account account={account} />
<Transactions transactions={account.transactions || []} />
</React.Fragment>
);
}
}
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));

View File

@ -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 <Loading />;
}
if (block.error) {
return (
<h1>
<i>Error API...</i>
</h1>
);
}
return (
<React.Fragment>
<Block blocks={block.data} />
<Transactions transactions={block.data.transactions} />
</React.Fragment>
);
}
}
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);

View File

@ -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 <Loading />;
}
if (transaction.error) {
return (
<h1>
<i>Error API...</i>
</h1>
);
}
return (
<React.Fragment>
<Transaction transactions={transaction.data} />
</React.Fragment>
);
}
}
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);

View File

@ -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) { export default function accounts(state = {}, action) {
switch (action.type) { switch (action.type) {
@ -6,6 +6,10 @@ export default function accounts(state = {}, action) {
return Object.assign({}, state, {data: action.accounts.data}); return Object.assign({}, state, {data: action.accounts.data});
case RECEIVE_ACCOUNTS_ERROR: case RECEIVE_ACCOUNTS_ERROR:
return Object.assign({}, state, {error: true}); 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: default:
return state; return state;
} }

View File

@ -3,6 +3,19 @@ import * as api from '../api';
import {eventChannel} from 'redux-saga'; import {eventChannel} from 'redux-saga';
import {all, call, fork, put, takeEvery, take} from 'redux-saga/effects'; 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) { export function *fetchTransactions(payload) {
try { try {
const transactions = yield call(api.fetchTransactions, payload.blockFrom); const transactions = yield call(api.fetchTransactions, payload.blockFrom);
@ -16,6 +29,19 @@ export function *watchFetchTransactions() {
yield takeEvery(actions.FETCH_TRANSACTIONS, fetchTransactions); 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) { export function *fetchBlocks(payload) {
try { try {
const blocks = yield call(api.fetchBlocks, payload.from); const blocks = yield call(api.fetchBlocks, payload.from);
@ -29,6 +55,19 @@ export function *watchFetchBlocks() {
yield takeEvery(actions.FETCH_BLOCKS, fetchBlocks); 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() { export function *fetchAccounts() {
try { try {
const accounts = yield call(api.fetchAccounts); const accounts = yield call(api.fetchAccounts);
@ -110,10 +149,13 @@ export default function *root() {
yield all([ yield all([
fork(watchInitBlockHeader), fork(watchInitBlockHeader),
fork(watchFetchAccounts), fork(watchFetchAccounts),
fork(watchFetchAccount),
fork(watchFetchProcesses), fork(watchFetchProcesses),
fork(watchFetchProcessLogs), fork(watchFetchProcessLogs),
fork(watchListenToProcessLogs), fork(watchListenToProcessLogs),
fork(watchFetchBlocks), fork(watchFetchBlocks),
fork(watchFetchTransactions) fork(watchFetchBlock),
fork(watchFetchTransactions),
fork(watchFetchTransaction)
]); ]);
} }

View File

@ -177,39 +177,15 @@ class BlockchainConnector {
'get', 'get',
'/embark-api/blockchain/accounts', '/embark-api/blockchain/accounts',
(req, res) => { (req, res) => {
let results = []; self.getAccountsWithTransactionCount(res.send.bind(res));
self.getAccounts((err, addresses) => { }
async.eachOf(addresses, (address, index, eachCb) => { );
let result = {address, index};
results.push(result); plugin.registerAPICall(
async.waterfall([ 'get',
function(callback) { '/embark-api/blockchain/accounts/:address',
self.getTransactionCount(address, (err, count) => { (req, res) => {
if (err) { self.getAccount(req.params.address, res.send.bind(res));
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);
});
});
} }
); );
@ -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( plugin.registerAPICall(
'get', 'get',
'/embark-api/blockchain/transactions', '/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( plugin.registerAPICall(
'ws', '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) { getTransactions(blockFrom, blockLimit, callback) {
this.getBlocks(blockFrom, blockLimit, true, (blocks) => { this.getBlocks(blockFrom, blockLimit, true, (blocks) => {
let transactions = blocks.reduce((acc, block) => acc.concat(block.transactions), []); let transactions = blocks.reduce((acc, block) => acc.concat(block.transactions), []);
@ -328,7 +398,11 @@ class BlockchainConnector {
} }
getBlock(blockNumber, cb) { 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) { getGasPrice(cb) {