Merge pull request #1041 from embark-framework/feature/debug-button

feat: add debug button to transaction and contract log
This commit is contained in:
Iuri Matias 2018-11-09 04:34:03 -05:00 committed by GitHub
commit 13f7a3ff41
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 201 additions and 95 deletions

View File

@ -7,14 +7,23 @@ import {
Button Button
} from "reactstrap"; } from "reactstrap";
import ReactJson from 'react-json-view'; import ReactJson from 'react-json-view';
import DebugButton from './DebugButton';
class ContractDebugger extends Component { class ContractDebugger extends Component {
handleChange(e) { constructor(props) {
this.setState({txHash: e.target.value}); super(props);
this.state = {txHash: ''};
} }
debug(_e) { componentDidMount() {
this.props.startDebug(this.state.txHash); if (this.props.debuggerTransactionHash) {
this.setState({txHash: this.props.debuggerTransactionHash});
this.props.startDebug(this.props.debuggerTransactionHash);
}
}
handleChange(e) {
this.setState({txHash: e.target.value});
} }
debugJumpBack(_e) { debugJumpBack(_e) {
@ -46,8 +55,8 @@ class ContractDebugger extends Component {
<div> <div>
<Row> <Row>
<Col> <Col>
<Input name="txHash" id="txHash" onChange={(e) => this.handleChange(e)}/> <Input name="txHash" id="txHash" value={this.state.txHash} onChange={(e) => this.handleChange(e)}/>
<Button color="primary" onClick={(e) => this.debug(e)}>Debug Tx</Button> <DebugButton forceDebuggable transaction={{hash: this.state.txHash}} />
</Col> </Col>
</Row> </Row>
<Row> <Row>
@ -74,7 +83,7 @@ class ContractDebugger extends Component {
} }
ContractDebugger.propTypes = { ContractDebugger.propTypes = {
contract: PropTypes.object.isRequired, debuggerTransactionHash: PropTypes.string,
startDebug: PropTypes.func, startDebug: PropTypes.func,
debugJumpBack: PropTypes.func, debugJumpBack: PropTypes.func,
debugJumpForward: PropTypes.func, debugJumpForward: PropTypes.func,

View File

@ -7,7 +7,6 @@ import classnames from 'classnames';
import ContractDetail from '../components/ContractDetail'; import ContractDetail from '../components/ContractDetail';
import ContractTransactionsContainer from '../containers/ContractTransactionsContainer'; import ContractTransactionsContainer from '../containers/ContractTransactionsContainer';
import ContractOverviewContainer from '../containers/ContractOverviewContainer'; import ContractOverviewContainer from '../containers/ContractOverviewContainer';
import ContractDebuggerContainer from '../containers/ContractDebuggerContainer';
class ContractLayout extends React.Component { class ContractLayout extends React.Component {
constructor(props) { constructor(props) {
@ -57,14 +56,6 @@ class ContractLayout extends React.Component {
<FontAwesomeIcon className="mr-2" name="list-alt" />Transactions <FontAwesomeIcon className="mr-2" name="list-alt" />Transactions
</NavLink> </NavLink>
</NavItem> </NavItem>
<NavItem>
<NavLink
className={classnames({ active: this.state.activeTab === '4' })}
onClick={() => { this.toggle('4'); }}
>
<FontAwesomeIcon className="mr-2" name="bug" />Debugger
</NavLink>
</NavItem>
</Nav> </Nav>
<TabContent activeTab={this.state.activeTab}> <TabContent activeTab={this.state.activeTab}>
<TabPane tabId="1"> <TabPane tabId="1">
@ -76,9 +67,6 @@ class ContractLayout extends React.Component {
<TabPane tabId="3"> <TabPane tabId="3">
<ContractTransactionsContainer contract={this.props.contract} /> <ContractTransactionsContainer contract={this.props.contract} />
</TabPane> </TabPane>
<TabPane tabId="4">
<ContractDebuggerContainer contract={this.props.contract} />
</TabPane>
</TabContent> </TabContent>
</CardBody> </CardBody>
</Card> </Card>

View File

@ -2,6 +2,8 @@ import PropTypes from "prop-types";
import React from 'react'; import React from 'react';
import {Row, Col, Table, FormGroup, Label, Input, Form} from 'reactstrap'; import {Row, Col, Table, FormGroup, Label, Input, Form} from 'reactstrap';
import DebugButton from './DebugButton'
const TX_STATES = {Success: '0x1', Fail: '0x0', Any: ''}; const TX_STATES = {Success: '0x1', Fail: '0x0', Any: ''};
const EVENT = 'event'; const EVENT = 'event';
const FUNCTION = 'function'; const FUNCTION = 'function';
@ -107,6 +109,7 @@ class ContractTransactions extends React.Component {
<Table> <Table>
<thead> <thead>
<tr> <tr>
<th></th>
<th>Call</th> <th>Call</th>
<th>Events</th> <th>Events</th>
<th>Gas Used</th> <th>Gas Used</th>
@ -120,6 +123,7 @@ class ContractTransactions extends React.Component {
this.dataToDisplay().map((log, index) => { this.dataToDisplay().map((log, index) => {
return ( return (
<tr key={'log-' + index}> <tr key={'log-' + index}>
<td><DebugButton forceDebuggable transaction={{hash: log.transactionHash}}/></td>
<td>{`${log.name}.${log.functionName}(${log.paramString})`}</td> <td>{`${log.name}.${log.functionName}(${log.paramString})`}</td>
<td>{log.events.join(', ')}</td> <td>{log.events.join(', ')}</td>
<td>{log.gasUsed}</td> <td>{log.gasUsed}</td>

View File

@ -0,0 +1,42 @@
import React from 'react';
import PropTypes from 'prop-types';
import {Button} from "reactstrap";
import FontAwesome from 'react-fontawesome';
import {withRouter} from "react-router-dom";
class DebugButton extends React.Component {
onClick() {
this.props.history.push(`/embark/editor?debuggerTransactionHash=${this.props.transaction.hash}`);
}
isDebuggable() {
return this.props.forceDebuggable ||
(this.props.contracts && this.props.contracts.find(contract => contract.address === this.props.transaction.to));
}
render() {
if (!this.isDebuggable()) {
return <React.Fragment />
}
return (
<Button color="primary" onClick={() => this.onClick()}>
<FontAwesome className="mr-2" name="bug"/>
Debug
</Button>
);
}
}
DebugButton.defaultProps = {
forceDebuggable: false
}
DebugButton.propTypes = {
forceDebuggable: PropTypes.bool,
history: PropTypes.object,
transaction: PropTypes.object,
contracts: PropTypes.arrayOf(PropTypes.object)
};
export default withRouter(DebugButton);

View File

@ -123,13 +123,14 @@ class TextEditor extends React.Component {
} }
componentDidUpdate(prevProps) { componentDidUpdate(prevProps) {
if (this.props.currentFile.content !== prevProps.currentFile.content) { const isNewContent = this.props.currentFile.content !== prevProps.currentFile.content;
if (isNewContent) {
editor.setValue(this.props.currentFile.content || ''); editor.setValue(this.props.currentFile.content || '');
} }
this.updateMarkers(); this.updateMarkers();
const expectedDecorationsLength = this.props.debuggerLine ? this.props.breakpoints.length + 1 : this.props.breakpoints.length; const expectedDecorationsLength = this.props.debuggerLine ? this.props.breakpoints.length + 1 : this.props.breakpoints.length;
if (expectedDecorationsLength !== this.state.decorations.length || this.props.debuggerLine !== prevProps.debuggerLine) { if (expectedDecorationsLength !== this.state.decorations.length || this.props.debuggerLine !== prevProps.debuggerLine || isNewContent) {
this.updateDecorations(); this.updateDecorations();
} }

View File

@ -22,6 +22,10 @@ class TextEditorToolbar extends Component {
return tab === TextEditorToolbarTabs.Browser; return tab === TextEditorToolbarTabs.Browser;
} }
isDebuggerTab(tab) {
return tab === TextEditorToolbarTabs.Debugger;
}
renderTab(tab) { renderTab(tab) {
return ( return (
<NavLink key={tab.label} className={classnames('btn', { active: this.isActiveTab(tab)})} onClick={() => this.props.openAsideTab(tab)}> <NavLink key={tab.label} className={classnames('btn', { active: this.isActiveTab(tab)})} onClick={() => this.props.openAsideTab(tab)}>
@ -45,7 +49,8 @@ class TextEditorToolbar extends Component {
</li> </li>
<li className="breadcrumb-menu"> <li className="breadcrumb-menu">
<Nav className="btn-group"> <Nav className="btn-group">
{this.props.isContract && Object.values(TextEditorToolbarTabs).map(tab => !this.isBrowserTab(tab) && this.renderTab(tab))} {this.props.isContract && Object.values(TextEditorToolbarTabs).map(tab => !this.isBrowserTab(tab) && !this.isDebuggerTab(tab) && this.renderTab(tab))}
{this.renderTab(TextEditorToolbarTabs.Debugger)}
{this.renderTab(TextEditorToolbarTabs.Browser)} {this.renderTab(TextEditorToolbarTabs.Browser)}
</Nav> </Nav>
</li> </li>

View File

@ -3,17 +3,23 @@ import {Link} from 'react-router-dom';
import {Row, Col, Card, CardHeader, CardBody} from 'reactstrap'; import {Row, Col, Card, CardHeader, CardBody} from 'reactstrap';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import DebugButton from './DebugButton';
import Description from './Description'; import Description from './Description';
import CardTitleIdenticon from './CardTitleIdenticon'; import CardTitleIdenticon from './CardTitleIdenticon';
import {utils} from 'web3'; import {utils} from 'web3';
const Transaction = ({transaction}) => ( const Transaction = ({transaction, contracts}) => (
<Row> <Row>
<Col> <Col>
<Card> <Card>
<CardHeader> <CardHeader>
<CardTitleIdenticon id={transaction.hash}>Transaction {transaction.hash}</CardTitleIdenticon> <CardTitleIdenticon id={transaction.hash}>
Transaction {transaction.hash}
<div className="float-right">
<DebugButton contracts={contracts} transaction={transaction} />
</div>
</CardTitleIdenticon>
</CardHeader> </CardHeader>
<CardBody> <CardBody>
<dl className="row"> <dl className="row">
@ -33,6 +39,7 @@ const Transaction = ({transaction}) => (
); );
Transaction.propTypes = { Transaction.propTypes = {
contracts: PropTypes.arrayOf(PropTypes.object),
transaction: PropTypes.object transaction: PropTypes.object
}; };

View File

@ -3,10 +3,11 @@ import {Link} from "react-router-dom";
import {Row, Col, Card, CardHeader, CardBody} from 'reactstrap'; import {Row, Col, Card, CardHeader, CardBody} from 'reactstrap';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import DebugButton from './DebugButton';
import CardTitleIdenticon from './CardTitleIdenticon'; import CardTitleIdenticon from './CardTitleIdenticon';
import Pagination from "./Pagination"; import Pagination from "./Pagination";
const Transactions = ({transactions, changePage, currentPage, numberOfPages}) => ( const Transactions = ({transactions, contracts, changePage, currentPage, numberOfPages}) => (
<Row> <Row>
<Col> <Col>
<Card> <Card>
@ -21,6 +22,11 @@ const Transactions = ({transactions, changePage, currentPage, numberOfPages}) =>
{transaction.hash} {transaction.hash}
</Link> </Link>
</CardTitleIdenticon> </CardTitleIdenticon>
<Row>
<Col>
<DebugButton transaction={transaction} contracts={contracts} />
</Col>
</Row>
<Row> <Row>
<Col md={6}> <Col md={6}>
<strong>Block number</strong> <strong>Block number</strong>
@ -50,6 +56,7 @@ const Transactions = ({transactions, changePage, currentPage, numberOfPages}) =>
Transactions.propTypes = { Transactions.propTypes = {
transactions: PropTypes.arrayOf(PropTypes.object), transactions: PropTypes.arrayOf(PropTypes.object),
contracts: PropTypes.arrayOf(PropTypes.object),
changePage: PropTypes.func, changePage: PropTypes.func,
currentPage: PropTypes.number, currentPage: PropTypes.number,
numberOfPages: PropTypes.number numberOfPages: PropTypes.number

View File

@ -1,42 +1,43 @@
import React, {Component} from 'react'; import React, {Component} from 'react';
import {connect} from 'react-redux'; import {connect} from 'react-redux';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import {startDebug, debugJumpBack, debugJumpForward, debugStepOverForward, debugStepOverBackward, debugStepIntoForward, debugStepIntoBackward} from '../actions'; import {
startDebug,
debugJumpBack,
debugJumpForward,
debugStepOverForward,
debugStepOverBackward,
debugStepIntoForward,
debugStepIntoBackward
} from '../actions';
import ContractDebugger from '../components/ContractDebugger'; import ContractDebugger from '../components/ContractDebugger';
import DataWrapper from "../components/DataWrapper"; import {getDebuggerInfo} from "../reducers/selectors";
import {getContractLogsByContract, debuggerInfo} from "../reducers/selectors";
class ContractDebuggerContainer extends Component { class ContractDebuggerContainer extends Component {
render() { render() {
return ( return (
<DataWrapper shouldRender={this.props.contractLogs !== undefined } {...this.props} render={() => ( <ContractDebugger debuggerTransactionHash={this.props.debuggerTransactionHash}
<ContractDebugger contract={this.props.contract} startDebug={this.props.startDebug} startDebug={this.props.startDebug}
debugJumpBack={this.props.debugJumpBack} debugJumpForward={this.props.debugJumpForward} debugJumpBack={this.props.debugJumpBack}
debugStepOverForward={this.props.debugStepOverForward} debugJumpForward={this.props.debugJumpForward}
debugStepOverBackward={this.props.debugStepOverBackward} debugStepOverForward={this.props.debugStepOverForward}
debugStepIntoForward={this.props.debugStepIntoForward} debugStepOverBackward={this.props.debugStepOverBackward}
debugStepIntoBackward={this.props.debugStepIntoBackward} debugStepIntoForward={this.props.debugStepIntoForward}
debuggerInfo={this.props.debuggerInfo} debugStepIntoBackward={this.props.debugStepIntoBackward}
/> debuggerInfo={this.props.debuggerInfo}/>
)} />
); );
} }
} }
function mapStateToProps(state, props) { function mapStateToProps(state, props) {
return { return {
contractLogs: getContractLogsByContract(state, props.contract.className), debuggerInfo: getDebuggerInfo(state)
debuggerInfo: debuggerInfo(state)
}; };
} }
ContractDebuggerContainer.propTypes = { ContractDebuggerContainer.propTypes = {
contractLogs: PropTypes.array, debuggerTransactionHash: PropTypes.string,
fetchContractLogs: PropTypes.func,
listenToContractLogs: PropTypes.func,
match: PropTypes.object,
contract: PropTypes.object,
startDebug: PropTypes.func, startDebug: PropTypes.func,
debugJumpBack: PropTypes.func, debugJumpBack: PropTypes.func,
debugJumpForward: PropTypes.func, debugJumpForward: PropTypes.func,

View File

@ -1,13 +1,20 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import {connect} from 'react-redux'; import {connect} from 'react-redux';
import {withRouter} from "react-router-dom";
import {Row, Col} from 'reactstrap'; import {Row, Col} from 'reactstrap';
import TextEditorAsideContainer from './TextEditorAsideContainer'; import TextEditorAsideContainer from './TextEditorAsideContainer';
import TextEditorContainer from './TextEditorContainer'; import TextEditorContainer from './TextEditorContainer';
import FileExplorerContainer from './FileExplorerContainer'; import FileExplorerContainer from './FileExplorerContainer';
import TextEditorToolbarContainer from './TextEditorToolbarContainer'; import TextEditorToolbarContainer from './TextEditorToolbarContainer';
import {fetchEditorTabs as fetchEditorTabsAction} from '../actions'; import {
import {getCurrentFile} from '../reducers/selectors'; fetchEditorTabs as fetchEditorTabsAction,
contracts as contractsAction,
file as fileAction,
transaction as transactionAction
} from '../actions';
import {getCurrentFile, getContracts, getTransaction} from '../reducers/selectors';
import {getDebuggerTransactionHash} from '../utils/utils';
import classnames from 'classnames'; import classnames from 'classnames';
import Resizable from 're-resizable'; import Resizable from 're-resizable';
import {OPERATIONS} from '../constants'; import {OPERATIONS} from '../constants';
@ -24,16 +31,21 @@ class EditorContainer extends React.Component {
this.windowWidth = window.innerWidth; this.windowWidth = window.innerWidth;
this.state = { this.state = {
currentAsideTab: {}, showHiddenFiles: false, currentFile: this.props.currentFile, currentAsideTab: {},
showHiddenFiles: false,
currentFile: this.props.currentFile,
editorHeight: this.DEFAULT_HEIGHT, editorHeight: this.DEFAULT_HEIGHT,
editorWidth: ((this.windowWidth < this.SMALL_SIZE) ? this.DEFAULT_EDITOR_WIDTH_SMALL : this.DEFAULT_EDITOR_WIDTH) + '%', editorWidth: ((this.windowWidth < this.SMALL_SIZE) ? this.DEFAULT_EDITOR_WIDTH_SMALL : this.DEFAULT_EDITOR_WIDTH) + '%',
asideHeight: '100%', asideWidth: '25%', asideHeight: '100%',
asideWidth: '25%',
isSmallSize: (this.windowWidth < this.SMALL_SIZE) isSmallSize: (this.windowWidth < this.SMALL_SIZE)
}; };
} }
componentDidMount() { componentDidMount() {
this.props.fetchEditorTabs(); this.props.fetchEditorTabs();
this.props.fetchContracts();
this.props.fetchTransaction(this.props.debuggerTransactionHash);
window.addEventListener("resize", this.updateDimensions.bind(this)); window.addEventListener("resize", this.updateDimensions.bind(this));
} }
@ -56,6 +68,14 @@ class EditorContainer extends React.Component {
if (this.props.currentFile.path !== prevProps.currentFile.path) { if (this.props.currentFile.path !== prevProps.currentFile.path) {
this.setState({currentFile: this.props.currentFile}); this.setState({currentFile: this.props.currentFile});
} }
if(this.props.contracts && this.props.transaction !== prevProps.transaction) {
const debuggingContract = this.props.contracts.find(contract => contract.address === this.props.transaction.to)
if (debuggingContract) {
this.setState({currentAsideTab: 'debugger'})
this.props.fetchFile({path: debuggingContract.path});
}
}
} }
isContract() { isContract() {
@ -129,6 +149,7 @@ class EditorContainer extends React.Component {
const aside = ( const aside = (
<div className="editor-aside"> <div className="editor-aside">
<TextEditorAsideContainer currentAsideTab={this.state.currentAsideTab} <TextEditorAsideContainer currentAsideTab={this.state.currentAsideTab}
debuggerTransactionHash={this.props.debuggerTransactionHash}
currentFile={this.props.currentFile}/> currentFile={this.props.currentFile}/>
</div> </div>
); );
@ -188,21 +209,36 @@ class EditorContainer extends React.Component {
} }
} }
function mapStateToProps(state, _props) { function mapStateToProps(state, props) {
const currentFile = getCurrentFile(state); const currentFile = getCurrentFile(state);
const debuggerTransactionHash = getDebuggerTransactionHash(props.location);
return { return {
currentFile currentFile,
debuggerTransactionHash,
transaction: getTransaction(state, debuggerTransactionHash),
contracts: getContracts(state)
}; };
} }
EditorContainer.propTypes = { EditorContainer.propTypes = {
debuggerTransactionHash: PropTypes.string,
contracts: PropTypes.array,
transaction: PropTypes.object,
fetchContracts: PropTypes.func,
fetchFile: PropTypes.func,
fetchTransaction: PropTypes.func,
currentFile: PropTypes.object, currentFile: PropTypes.object,
fetchEditorTabs: PropTypes.func fetchEditorTabs: PropTypes.func
}; };
export default connect( export default withRouter(connect(
mapStateToProps, mapStateToProps,
{fetchEditorTabs: fetchEditorTabsAction.request} {
)(EditorContainer); fetchEditorTabs: fetchEditorTabsAction.request,
fetchTransaction: transactionAction.request,
fetchFile: fileAction.request,
fetchContracts: contractsAction.request
},
)(EditorContainer));

View File

@ -4,7 +4,6 @@ import PropTypes from 'prop-types';
import {Card, CardBody} from 'reactstrap'; import {Card, CardBody} from 'reactstrap';
import Preview from '../components/Preview'; import Preview from '../components/Preview';
import {contracts as contractsAction} from '../actions';
import {getContractsByPath} from "../reducers/selectors"; import {getContractsByPath} from "../reducers/selectors";
import ContractDetail from '../components/ContractDetail'; import ContractDetail from '../components/ContractDetail';
import ContractTransactionsContainer from './ContractTransactionsContainer'; import ContractTransactionsContainer from './ContractTransactionsContainer';
@ -13,19 +12,8 @@ import ContractDebuggerContainer from '../containers/ContractDebuggerContainer';
import { TextEditorToolbarTabs } from '../components/TextEditorToolbar'; import { TextEditorToolbarTabs } from '../components/TextEditorToolbar';
class TextEditorAsideContainer extends Component { class TextEditorAsideContainer extends Component {
componentDidMount() {
this.props.fetchContracts();
}
renderContent(contract, index) { renderContent(contract, index) {
switch (this.props.currentAsideTab.label) { switch (this.props.currentAsideTab) {
case TextEditorToolbarTabs.Debugger.label:
return (
<React.Fragment>
<h2>{contract.className} - Debugger</h2>
<ContractDebuggerContainer key={index} contract={contract}/>
</React.Fragment>
);
case TextEditorToolbarTabs.Details.label: case TextEditorToolbarTabs.Details.label:
return ( return (
<React.Fragment> <React.Fragment>
@ -56,15 +44,21 @@ class TextEditorAsideContainer extends Component {
if (this.props.currentAsideTab.label === TextEditorToolbarTabs.Browser.label) { if (this.props.currentAsideTab.label === TextEditorToolbarTabs.Browser.label) {
return <Preview/>; return <Preview/>;
} }
return this.props.contracts.map((contract, index) => { if (this.props.currentAsideTab.label === TextEditorToolbarTabs.Debugger.label) {
return ( return (
<Card key={'contract-' + index} className="editor-aside-card rounded-0 border-top-0"> <React.Fragment>
<CardBody> <h2>Debugger</h2>
{this.renderContent(contract, index)} <ContractDebuggerContainer debuggerTransactionHash={this.props.debuggerTransactionHash}/>
</CardBody> </React.Fragment>
</Card>
); );
}); }
return this.props.contracts.map((contract, index) => (
<Card key={'contract-' + index} className="editor-aside-card rounded-0 border-top-0">
<CardBody>
{this.renderContent(contract, index)}
</CardBody>
</Card>
));
} }
} }
@ -76,15 +70,12 @@ function mapStateToProps(state, props) {
TextEditorAsideContainer.propTypes = { TextEditorAsideContainer.propTypes = {
currentFile: PropTypes.object, currentFile: PropTypes.object,
currentAsideTab: PropTypes.object, debuggerTransactionHash: PropTypes.string,
contract: PropTypes.array, currentAsideTab: PropTypes.string,
fetchContracts: PropTypes.func,
contracts: PropTypes.array contracts: PropTypes.array
}; };
export default connect( export default connect(
mapStateToProps, mapStateToProps,
{ {}
fetchContracts: contractsAction.request
}
)(TextEditorAsideContainer); )(TextEditorAsideContainer);

View File

@ -3,20 +3,21 @@ import {connect} from 'react-redux';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import {withRouter} from 'react-router-dom'; import {withRouter} from 'react-router-dom';
import {transaction as transactionAction} from '../actions'; import {transaction as transactionAction, contracts as contractsAction} from '../actions';
import Transaction from '../components/Transaction'; import Transaction from '../components/Transaction';
import DataWrapper from "../components/DataWrapper"; import DataWrapper from "../components/DataWrapper";
import {getTransaction} from "../reducers/selectors"; import {getTransaction, getContracts} from "../reducers/selectors";
class TransactionContainer extends Component { class TransactionContainer extends Component {
componentDidMount() { componentDidMount() {
this.props.fetchContracts();
this.props.fetchTransaction(this.props.match.params.hash); this.props.fetchTransaction(this.props.match.params.hash);
} }
render() { render() {
return ( return (
<DataWrapper shouldRender={this.props.transaction !== undefined } {...this.props} render={({transaction}) => ( <DataWrapper shouldRender={this.props.transaction !== undefined } {...this.props} render={({transaction}) => (
<Transaction transaction={transaction} /> <Transaction contracts={this.props.contracts} transaction={transaction} />
)} /> )} />
); );
} }
@ -25,6 +26,7 @@ class TransactionContainer extends Component {
function mapStateToProps(state, props) { function mapStateToProps(state, props) {
return { return {
transaction: getTransaction(state, props.match.params.hash), transaction: getTransaction(state, props.match.params.hash),
contracts: getContracts(state),
error: state.errorMessage, error: state.errorMessage,
loading: state.loading loading: state.loading
}; };
@ -33,7 +35,8 @@ function mapStateToProps(state, props) {
TransactionContainer.propTypes = { TransactionContainer.propTypes = {
match: PropTypes.object, match: PropTypes.object,
transaction: PropTypes.object, transaction: PropTypes.object,
transactions: PropTypes.arrayOf(PropTypes.object), contracts: PropTypes.arrayOf(PropTypes.object),
fetchContracts: PropTypes.func,
fetchTransaction: PropTypes.func, fetchTransaction: PropTypes.func,
error: PropTypes.string error: PropTypes.string
}; };
@ -41,6 +44,7 @@ TransactionContainer.propTypes = {
export default withRouter(connect( export default withRouter(connect(
mapStateToProps, mapStateToProps,
{ {
fetchContracts: contractsAction.request,
fetchTransaction: transactionAction.request fetchTransaction: transactionAction.request
} }
)(TransactionContainer)); )(TransactionContainer));

View File

@ -2,10 +2,10 @@ import React, {Component} from 'react';
import {connect} from 'react-redux'; import {connect} from 'react-redux';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import {transactions as transactionsAction, initBlockHeader, stopBlockHeader} from '../actions'; import {transactions as transactionsAction, initBlockHeader, stopBlockHeader, contracts as contractsAction} from '../actions';
import Transactions from '../components/Transactions'; import Transactions from '../components/Transactions';
import DataWrapper from "../components/DataWrapper"; import DataWrapper from "../components/DataWrapper";
import {getTransactions} from "../reducers/selectors"; import {getTransactions, getContracts} from "../reducers/selectors";
const MAX_TXS = 10; // TODO use same constant as API const MAX_TXS = 10; // TODO use same constant as API
@ -20,6 +20,7 @@ class TransactionsContainer extends Component {
componentDidMount() { componentDidMount() {
this.props.fetchTransactions(); this.props.fetchTransactions();
this.props.fetchContracts();
this.props.initBlockHeader(); this.props.initBlockHeader();
} }
@ -59,7 +60,9 @@ class TransactionsContainer extends Component {
return ( return (
<React.Fragment> <React.Fragment>
<DataWrapper shouldRender={this.currentTxs.length > 0} {...this.props} render={() => ( <DataWrapper shouldRender={this.currentTxs.length > 0} {...this.props} render={() => (
<Transactions transactions={this.currentTxs} numberOfPages={this.getNumberOfPages()} <Transactions transactions={this.currentTxs}
contracts={this.props.contracts}
numberOfPages={this.getNumberOfPages()}
changePage={(newPage) => this.changePage(newPage)} changePage={(newPage) => this.changePage(newPage)}
currentPage={this.state.currentPage || this.getNumberOfPages()} /> currentPage={this.state.currentPage || this.getNumberOfPages()} />
)} /> )} />
@ -69,12 +72,19 @@ class TransactionsContainer extends Component {
} }
function mapStateToProps(state) { function mapStateToProps(state) {
return {transactions: getTransactions(state), error: state.errorMessage, loading: state.loading}; return {
transactions: getTransactions(state),
contracts: getContracts(state),
error: state.errorMessage,
loading: state.loading
};
} }
TransactionsContainer.propTypes = { TransactionsContainer.propTypes = {
transactions: PropTypes.arrayOf(PropTypes.object), transactions: PropTypes.arrayOf(PropTypes.object),
contracts: PropTypes.arrayOf(PropTypes.object),
fetchTransactions: PropTypes.func, fetchTransactions: PropTypes.func,
fetchContracts: PropTypes.func,
initBlockHeader: PropTypes.func, initBlockHeader: PropTypes.func,
stopBlockHeader: PropTypes.func, stopBlockHeader: PropTypes.func,
error: PropTypes.string, error: PropTypes.string,
@ -85,6 +95,7 @@ export default connect(
mapStateToProps, mapStateToProps,
{ {
fetchTransactions: transactionsAction.request, fetchTransactions: transactionsAction.request,
fetchContracts: contractsAction.request,
initBlockHeader, initBlockHeader,
stopBlockHeader stopBlockHeader
}, },

View File

@ -225,7 +225,7 @@ export function getWeb3Deployments(state) {
return state.web3.deployments; return state.web3.deployments;
} }
export function debuggerInfo(state) { export function getDebuggerInfo(state) {
return state.debuggerInfo; return state.debuggerInfo;
} }

View File

@ -25,6 +25,10 @@ export function getQueryToken(location) {
return qs.parse(location.search, {ignoreQueryPrefix: true}).token; return qs.parse(location.search, {ignoreQueryPrefix: true}).token;
} }
export function getDebuggerTransactionHash(location) {
return qs.parse(location.search, {ignoreQueryPrefix: true}).debuggerTransactionHash;
}
export function stripQueryToken(location) { export function stripQueryToken(location) {
const _location = Object.assign({}, location); const _location = Object.assign({}, location);
_location.search = _location.search.replace( _location.search = _location.search.replace(

View File

@ -607,12 +607,8 @@ class BlockchainConnector {
return this.web3.eth.net.getId(); return this.web3.eth.net.getId();
} }
//TODO: fix me, why is this gasPrice??
getTransaction(hash, cb) { getTransaction(hash, cb) {
const self = this; return this.web3.eth.getTransaction(hash, cb);
this.onReady(() => {
self.web3.eth.getGasPrice(cb);
});
} }
ContractObject(params) { ContractObject(params) {