diff --git a/embark-ui/src/actions/index.js b/embark-ui/src/actions/index.js index 9b84c7613..e6f621e60 100644 --- a/embark-ui/src/actions/index.js +++ b/embark-ui/src/actions/index.js @@ -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 diff --git a/embark-ui/src/components/ContractLogger.js b/embark-ui/src/components/ContractLogger.js index fb0677bb5..91de4ff67 100644 --- a/embark-ui/src/components/ContractLogger.js +++ b/embark-ui/src/components/ContractLogger.js @@ -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 ( - +
- - {this.getMethods().map((method, index) => )} + this.updateState('method', event.target.value)} value={this.state.method}> + + {this.getMethods().map((method, index) => )} - - {this.getEvents().map((event, index) => )} + this.updateState('event', event.target.value)} value={this.state.event}> + + {this.getEvents().map((event, index) => )} - - - + {TX_STATES.map(state => ( + this.updateState('status', event.target.value)} + checked={state === this.state.status} + /> + ))} @@ -78,23 +100,25 @@ class ContractLogger extends React.Component { className="text-nowrap"> - call - Transaction hash + Call + Events Gas Used Block number Status + Transaction hash { - this.props.contractLogs.map((log, index) => { + this.dataToDisplay().map((log, index) => { return ( {`${log.name}.${log.functionName}(${log.paramString})`} - {log.transactionHash} + {log.events.join(' ')} {log.gasUsed} {log.blockNumber} {log.status} + {log.transactionHash} ); }) @@ -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 }; diff --git a/embark-ui/src/containers/ContractLoggerContainer.js b/embark-ui/src/containers/ContractLoggerContainer.js index d62666444..4b269bd8d 100644 --- a/embark-ui/src/containers/ContractLoggerContainer.js +++ b/embark-ui/src/containers/ContractLoggerContainer.js @@ -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 ( ( - + )} /> ); } @@ -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); diff --git a/embark-ui/src/reducers/index.js b/embark-ui/src/reducers/index.js index 592eb9bb0..4a51efd74 100644 --- a/embark-ui/src/reducers/index.js +++ b/embark-ui/src/reducers/index.js @@ -21,6 +21,11 @@ const entitiesDefaultState = { contractDeploys: [], contractCompiles: [], contractLogs: [], +<<<<<<< HEAD +======= + contractEvents: [], + commands: [], +>>>>>>> Add Contract Events to UI messages: [], messageChannels: [], versions: [], diff --git a/embark-ui/src/reducers/selectors.js b/embark-ui/src/reducers/selectors.js index a1bb4d017..b59174e4f 100644 --- a/embark-ui/src/reducers/selectors.js +++ b/embark-ui/src/reducers/selectors.js @@ -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); } diff --git a/embark-ui/src/sagas/index.js b/embark-ui/src/sagas/index.js index a645d30f0..2412953dc 100644 --- a/embark-ui/src/sagas/index.js +++ b/embark-ui/src/sagas/index.js @@ -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), diff --git a/embark-ui/src/services/api.js b/embark-ui/src/services/api.js index 616f651d9..e126af08e 100644 --- a/embark-ui/src/services/api.js +++ b/embark-ui/src/services/api.js @@ -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]); }