mirror of https://github.com/embarklabs/embark.git
Add Contract Events to UI
This commit is contained in:
parent
6ce78dfce3
commit
96c3575e75
|
@ -152,6 +152,13 @@ export const contractLogs = {
|
|||
failure: (error) => action(CONTRACT_LOGS[FAILURE], {error})
|
||||
};
|
||||
|
||||
export const CONTRACT_EVENTS = createRequestTypes('CONTRACT_EVENTS');
|
||||
export const contractEvents = {
|
||||
request: () => action(CONTRACT_EVENTS[REQUEST]),
|
||||
success: (contractEvents) => action(CONTRACT_EVENTS[SUCCESS], {contractEvents}),
|
||||
failure: (error) => action(CONTRACT_EVENTS[FAILURE], {error})
|
||||
};
|
||||
|
||||
export const CONTRACTS = createRequestTypes('CONTRACTS');
|
||||
export const contracts = {
|
||||
request: () => action(CONTRACTS[REQUEST]),
|
||||
|
@ -299,6 +306,7 @@ export const gasOracle = {
|
|||
export const WATCH_NEW_PROCESS_LOGS = 'WATCH_NEW_PROCESS_LOGS';
|
||||
export const STOP_NEW_PROCESS_LOGS = 'STOP_NEW_PROCESS_LOGS';
|
||||
export const WATCH_NEW_CONTRACT_LOGS = 'WATCH_NEW_CONTRACT_LOGS';
|
||||
export const WATCH_NEW_CONTRACT_EVENTS = 'WATCH_NEW_CONTRACT_EVENTS';
|
||||
export const INIT_BLOCK_HEADER = 'INIT_BLOCK_HEADER';
|
||||
export const STOP_BLOCK_HEADER = 'STOP_BLOCK_HEADER';
|
||||
export const WATCH_GAS_ORACLE = 'WATCH_GAS_ORACLE';
|
||||
|
@ -324,6 +332,12 @@ export function listenToContractLogs() {
|
|||
};
|
||||
}
|
||||
|
||||
export function listenToContractEvents() {
|
||||
return {
|
||||
type: WATCH_NEW_CONTRACT_EVENTS
|
||||
};
|
||||
}
|
||||
|
||||
export function initBlockHeader(){
|
||||
return {
|
||||
type: INIT_BLOCK_HEADER
|
||||
|
|
|
@ -7,7 +7,14 @@ import {
|
|||
Form
|
||||
} from "tabler-react";
|
||||
|
||||
const ANY_STATE = 'Any';
|
||||
const TX_STATES = ['Success', 'Fail', ANY_STATE];
|
||||
|
||||
class ContractLogger extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {method: '', event: '', status: ANY_STATE};
|
||||
}
|
||||
|
||||
getMethods() {
|
||||
if (!this.props.contract.abiDefinition) {
|
||||
|
@ -26,45 +33,60 @@ class ContractLogger extends React.Component {
|
|||
return this.props.contract.abiDefinition.filter(method => method.type === 'event');
|
||||
}
|
||||
|
||||
updateState(key, value) {
|
||||
this.setState({[key]: value});
|
||||
}
|
||||
|
||||
dataToDisplay() {
|
||||
return this.props.contractLogs.map(contractLog => {
|
||||
const events = this.props.contractEvents
|
||||
.filter(contractEvent => contractEvent.transactionHash === contractLog.transactionHash)
|
||||
.map(contractEvent => contractEvent.event);
|
||||
contractLog.events = events;
|
||||
return contractLog;
|
||||
}).filter(contractLog => {
|
||||
if (this.state.method || this.state.event || this.state.status !== ANY_STATE) {
|
||||
return contractLog.status === '0x1' && this.state.status === 'Success' &&
|
||||
(this.state.method === contractLog.functionName || contractLog.events.includes(this.state.event));
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Page.Content title={this.props.contractName + ' Logger'}>
|
||||
<Page.Content title={this.props.contract.className + ' Logger'}>
|
||||
<Form>
|
||||
<Grid.Row>
|
||||
<Grid.Col md={6}>
|
||||
<Form.Group label="Functions">
|
||||
<Form.Select>
|
||||
{this.getMethods().map((method, index) => <option key={index}>{method.name}</option>)}
|
||||
<Form.Select onChange={(event) => this.updateState('method', event.target.value)} value={this.state.method}>
|
||||
<option value=""></option>
|
||||
{this.getMethods().map((method, index) => <option value={method.name} key={index}>{method.name}</option>)}
|
||||
</Form.Select>
|
||||
</Form.Group>
|
||||
</Grid.Col>
|
||||
<Grid.Col md={6}>
|
||||
<Form.Group label="Events">
|
||||
<Form.Select>
|
||||
{this.getEvents().map((event, index) => <option key={index}>{event.name}</option>)}
|
||||
<Form.Select onChange={(event) => this.updateState('event', event.target.value)} value={this.state.event}>
|
||||
<option value=""></option>
|
||||
{this.getEvents().map((event, index) => <option value={event.name} key={index}>{event.name}</option>)}
|
||||
</Form.Select>
|
||||
</Form.Group>
|
||||
</Grid.Col>
|
||||
<Grid.Col>
|
||||
<Form.Group label="Tx Status">
|
||||
{TX_STATES.map(state => (
|
||||
<Form.Radio
|
||||
key={state}
|
||||
isInline
|
||||
label="Failed"
|
||||
name="example-inline-radios"
|
||||
value="option1"
|
||||
/>
|
||||
<Form.Radio
|
||||
isInline
|
||||
label="Success"
|
||||
name="example-inline-radios"
|
||||
value="option2"
|
||||
/>
|
||||
<Form.Radio
|
||||
isInline
|
||||
label="Any"
|
||||
name="example-inline-radios"
|
||||
value="option3"
|
||||
label={state}
|
||||
value={state}
|
||||
onChange={(event) => this.updateState('status', event.target.value)}
|
||||
checked={state === this.state.status}
|
||||
/>
|
||||
))}
|
||||
</Form.Group>
|
||||
</Grid.Col>
|
||||
</Grid.Row>
|
||||
|
@ -78,23 +100,25 @@ class ContractLogger extends React.Component {
|
|||
className="text-nowrap">
|
||||
<Table.Header>
|
||||
<Table.Row>
|
||||
<Table.ColHeader>call</Table.ColHeader>
|
||||
<Table.ColHeader>Transaction hash</Table.ColHeader>
|
||||
<Table.ColHeader>Call</Table.ColHeader>
|
||||
<Table.ColHeader>Events</Table.ColHeader>
|
||||
<Table.ColHeader>Gas Used</Table.ColHeader>
|
||||
<Table.ColHeader>Block number</Table.ColHeader>
|
||||
<Table.ColHeader>Status</Table.ColHeader>
|
||||
<Table.ColHeader>Transaction hash</Table.ColHeader>
|
||||
</Table.Row>
|
||||
</Table.Header>
|
||||
<Table.Body>
|
||||
{
|
||||
this.props.contractLogs.map((log, index) => {
|
||||
this.dataToDisplay().map((log, index) => {
|
||||
return (
|
||||
<Table.Row key={'log-' + index}>
|
||||
<Table.Col>{`${log.name}.${log.functionName}(${log.paramString})`}</Table.Col>
|
||||
<Table.Col>{log.transactionHash}</Table.Col>
|
||||
<Table.Col>{log.events.join(' ')}</Table.Col>
|
||||
<Table.Col>{log.gasUsed}</Table.Col>
|
||||
<Table.Col>{log.blockNumber}</Table.Col>
|
||||
<Table.Col>{log.status}</Table.Col>
|
||||
<Table.Col>{log.transactionHash}</Table.Col>
|
||||
</Table.Row>
|
||||
);
|
||||
})
|
||||
|
@ -109,8 +133,8 @@ class ContractLogger extends React.Component {
|
|||
}
|
||||
|
||||
ContractLogger.propTypes = {
|
||||
contractName: PropTypes.string.isRequired,
|
||||
contractLogs: PropTypes.array.isRequired.Header,
|
||||
contractLogs: PropTypes.array,
|
||||
contractEvents: PropTypes.array,
|
||||
contract: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
|
|
|
@ -1,24 +1,31 @@
|
|||
import React, {Component} from 'react';
|
||||
import {connect} from 'react-redux';
|
||||
import PropTypes from 'prop-types';
|
||||
import {contractLogs as contractLogsAction, listenToContractLogs} from '../actions';
|
||||
import {contractEvents as contractEventsAction, contractLogs as contractLogsAction, listenToContractLogs, listenToContractEvents} from '../actions';
|
||||
|
||||
import ContractLogger from '../components/ContractLogger';
|
||||
import DataWrapper from "../components/DataWrapper";
|
||||
import {getContractLogsByContract} from "../reducers/selectors";
|
||||
import {getContractLogsByContract, getContractEventsByContract} from "../reducers/selectors";
|
||||
|
||||
class ContractLoggerContainer extends Component {
|
||||
componentDidMount() {
|
||||
if (this.props.contractLogs.length === 0) {
|
||||
this.props.listenToContractLogs();
|
||||
this.props.fetchContractLogs(this.props.contract.className);
|
||||
this.props.fetchContractLogs();
|
||||
}
|
||||
|
||||
if (this.props.contractEvents.length === 0) {
|
||||
this.props.listenToContractEvents();
|
||||
this.props.fetchContractEvents();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<DataWrapper shouldRender={this.props.contractLogs !== undefined } {...this.props} render={() => (
|
||||
<ContractLogger contractLogs={this.props.contractLogs} contractName={this.props.contract.className}/>
|
||||
<ContractLogger contractLogs={this.props.contractLogs}
|
||||
contractEvents={this.props.contractEvents}
|
||||
contract={this.props.contract}/>
|
||||
)} />
|
||||
);
|
||||
}
|
||||
|
@ -26,15 +33,19 @@ class ContractLoggerContainer extends Component {
|
|||
|
||||
function mapStateToProps(state, props) {
|
||||
return {
|
||||
contractLogs: getContractLogsByContract(state, props.contract.className)
|
||||
contractLogs: getContractLogsByContract(state, props.contract.className),
|
||||
contractEvents: getContractEventsByContract(state, props.match.params.contractName)
|
||||
};
|
||||
}
|
||||
|
||||
ContractLoggerContainer.propTypes = {
|
||||
contract: PropTypes.object,
|
||||
contractLogs: PropTypes.array,
|
||||
contractEvents: PropTypes.array,
|
||||
fetchContractLogs: PropTypes.func,
|
||||
listenToContractLogs: PropTypes.func,
|
||||
fetchContractEvents: PropTypes.func,
|
||||
listenToContractEvents: PropTypes.func,
|
||||
match: PropTypes.object
|
||||
};
|
||||
|
||||
|
@ -42,6 +53,8 @@ export default connect(
|
|||
mapStateToProps,
|
||||
{
|
||||
fetchContractLogs: contractLogsAction.request,
|
||||
listenToContractLogs: listenToContractLogs
|
||||
listenToContractLogs: listenToContractLogs,
|
||||
fetchContractEvents: contractEventsAction.request,
|
||||
listenToContractEvents: listenToContractEvents
|
||||
}
|
||||
)(ContractLoggerContainer);
|
||||
|
|
|
@ -21,6 +21,11 @@ const entitiesDefaultState = {
|
|||
contractDeploys: [],
|
||||
contractCompiles: [],
|
||||
contractLogs: [],
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
contractEvents: [],
|
||||
commands: [],
|
||||
>>>>>>> Add Contract Events to UI
|
||||
messages: [],
|
||||
messageChannels: [],
|
||||
versions: [],
|
||||
|
|
|
@ -64,6 +64,10 @@ export function getContractLogsByContract(state, contractName) {
|
|||
return state.entities.contractLogs.filter((contractLog => contractLog.name === contractName));
|
||||
}
|
||||
|
||||
export function getContractEventsByContract(state, contractName) {
|
||||
return state.entities.contractEvents.filter((contractEvent => contractEvent.name === contractName));
|
||||
}
|
||||
|
||||
export function getContracts(state) {
|
||||
return state.entities.contracts.filter(contract => !contract.isFiddle);
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ export const fetchProcesses = doRequest.bind(null, actions.processes, api.fetchP
|
|||
export const postCommand = doRequest.bind(null, actions.commands, api.postCommand);
|
||||
export const postCommandSuggestions = doRequest.bind(null, actions.commandSuggestions, api.postCommandSuggestions);
|
||||
export const fetchProcessLogs = doRequest.bind(null, actions.processLogs, api.fetchProcessLogs);
|
||||
export const fetchContractEvents = doRequest.bind(null, actions.contractEvents, api.fetchContractEvents);
|
||||
export const fetchContractLogs = doRequest.bind(null, actions.contractLogs, api.fetchContractLogs);
|
||||
export const fetchContracts = doRequest.bind(null, actions.contracts, api.fetchContracts);
|
||||
export const fetchContract = doRequest.bind(null, actions.contract, api.fetchContract);
|
||||
|
@ -98,6 +99,10 @@ export function *watchFetchContractLogs() {
|
|||
yield takeEvery(actions.CONTRACT_LOGS[actions.REQUEST], fetchContractLogs);
|
||||
}
|
||||
|
||||
export function *watchFetchContractEvents() {
|
||||
yield takeEvery(actions.CONTRACT_EVENTS[actions.REQUEST], fetchContractEvents);
|
||||
}
|
||||
|
||||
export function *watchFetchContract() {
|
||||
yield takeEvery(actions.CONTRACT[actions.REQUEST], fetchContract);
|
||||
}
|
||||
|
@ -278,6 +283,20 @@ export function *watchListenToContractLogs() {
|
|||
yield takeEvery(actions.WATCH_NEW_CONTRACT_LOGS, listenToContractLogs);
|
||||
}
|
||||
|
||||
export function *listenToContractEvents() {
|
||||
const credentials = yield select(getCredentials);
|
||||
const socket = api.webSocketContractEvents(credentials);
|
||||
const channel = yield call(createChannel, socket);
|
||||
while (true) {
|
||||
const contractEvent = yield take(channel);
|
||||
yield put(actions.contractEvents.success([contractEvent]));
|
||||
}
|
||||
}
|
||||
|
||||
export function *watchListenToContractEvents() {
|
||||
yield takeEvery(actions.WATCH_NEW_CONTRACT_EVENTS, listenToContractEvents);
|
||||
}
|
||||
|
||||
export function *listenGasOracle() {
|
||||
const credentials = yield select(getCredentials);
|
||||
const socket = api.websocketGasOracle(credentials);
|
||||
|
@ -318,8 +337,10 @@ export default function *root() {
|
|||
fork(watchFetchProcesses),
|
||||
fork(watchFetchProcessLogs),
|
||||
fork(watchFetchContractLogs),
|
||||
fork(watchFetchContractEvents),
|
||||
fork(watchListenToProcessLogs),
|
||||
fork(watchListenToContractLogs),
|
||||
fork(watchListenToContractEvents),
|
||||
fork(watchFetchBlock),
|
||||
fork(watchFetchTransactions),
|
||||
fork(watchPostCommand),
|
||||
|
|
|
@ -94,6 +94,10 @@ export function fetchContractLogs() {
|
|||
return get(`/contracts/logs`, ...arguments);
|
||||
}
|
||||
|
||||
export function fetchContractEvents() {
|
||||
return get(`/blockchain/contracts/events`, ...arguments);
|
||||
}
|
||||
|
||||
export function fetchContracts() {
|
||||
return get('/contracts', ...arguments);
|
||||
}
|
||||
|
@ -179,6 +183,10 @@ export function webSocketContractLogs(credentials) {
|
|||
return new WebSocket(`ws://${credentials.host}/embark-api/contracts/logs`, [credentials.token]);
|
||||
}
|
||||
|
||||
export function webSocketContractEvents(credentials) {
|
||||
return new WebSocket(`ws://${credentials.host}/embark-api/blockchain/contracts/event`, [credentials.token]);
|
||||
}
|
||||
|
||||
export function webSocketBlockHeader(credentials) {
|
||||
return new WebSocket(`ws://${credentials.host}/embark-api/blockchain/blockHeader`, [credentials.token]);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue