From 85f3c38160e6a591252e940deb7796d5e15b7b80 Mon Sep 17 00:00:00 2001 From: jacogr Date: Sat, 22 Oct 2016 12:09:54 +0200 Subject: [PATCH 1/3] Event into own component, align with transactions --- js/src/views/Contract/Events/Event/event.js | 141 ++++++++++++++++++++ js/src/views/Contract/Events/Event/index.js | 17 +++ js/src/views/Contract/Events/events.js | 91 +------------ js/src/views/Contract/contract.css | 7 + js/src/views/Contract/contract.js | 1 - 5 files changed, 169 insertions(+), 88 deletions(-) create mode 100644 js/src/views/Contract/Events/Event/event.js create mode 100644 js/src/views/Contract/Events/Event/index.js diff --git a/js/src/views/Contract/Events/Event/event.js b/js/src/views/Contract/Events/Event/event.js new file mode 100644 index 00000000000..cee243bac8e --- /dev/null +++ b/js/src/views/Contract/Events/Event/event.js @@ -0,0 +1,141 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +import BigNumber from 'bignumber.js'; +import moment from 'moment'; +import React, { Component, PropTypes } from 'react'; +import { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; + +import { fetchBlock, fetchTransaction } from '../../../../redux/providers/blockchainActions'; + +import styles from '../../contract.css'; + +class Event extends Component { + static contextTypes = { + api: PropTypes.object.isRequired + } + + static propTypes = { + event: PropTypes.object.isRequired, + blocks: PropTypes.object, + transactions: PropTypes.object, + isTest: PropTypes.bool, + fetchBlock: PropTypes.func.isRequired, + fetchTransaction: PropTypes.func.isRequired + } + + componentDidMount () { + this.retrieveTransaction(); + } + + render () { + const { event, blocks, transactions, isTest } = this.props; + + const block = blocks[event.blockNumber.toString()]; + const transaction = transactions[event.transactionHash] || {}; + const classes = `${styles.event} ${styles[event.state]}`; + const url = `https://${isTest ? 'testnet.' : ''}etherscan.io/tx/${event.transactionHash}`; + const keys = Object.keys(event.params).map((key, index) => { + return
{ key }
; + }); + const values = Object.values(event.params).map((value, index) => { + return ( +
+ { this.renderValue(value) } +
+ ); + }); + + return ( + + +
{ event.state === 'pending' ? 'pending' : this.formatBlockTimestamp(block) }
+
{ this.formatNumber(transaction.blockNumber) }
+ + +
{ transaction.from }
+ { event.transactionHash } + + +
{ event.type } =>
+ { keys } + + +
 
+ { values } + + + ); + } + + renderValue (value) { + const { api } = this.context; + + if (api.util.isInstanceOf(value, BigNumber)) { + return value.toFormat(0); + } else if (api.util.isArray(value)) { + return api.util.bytesToHex(value); + } + + return value.toString(); + } + + formatBlockTimestamp (block) { + if (!block) { + return null; + } + + return moment(block.timestamp).fromNow(); + } + + formatNumber (number) { + if (!number) { + return null; + } + + return new BigNumber(number).toFormat(); + } + + retrieveTransaction () { + const { event, fetchBlock, fetchTransaction } = this.props; + + fetchBlock(event.blockNumber); + fetchTransaction(event.transactionHash); + } +} + +function mapStateToProps (state) { + const { isTest } = state.nodeStatus; + const { blocks, transactions } = state.blockchain; + + return { + isTest, + blocks, + transactions + }; +} + +function mapDispatchToProps (dispatch) { + return bindActionCreators({ + fetchBlock, fetchTransaction + }, dispatch); +} + +export default connect( + mapStateToProps, + mapDispatchToProps +)(Event); diff --git a/js/src/views/Contract/Events/Event/index.js b/js/src/views/Contract/Events/Event/index.js new file mode 100644 index 00000000000..0925882d3e3 --- /dev/null +++ b/js/src/views/Contract/Events/Event/index.js @@ -0,0 +1,17 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +export default from './event'; diff --git a/js/src/views/Contract/Events/events.js b/js/src/views/Contract/Events/events.js index 7ff0341b3d0..0428a0ee3ca 100644 --- a/js/src/views/Contract/Events/events.js +++ b/js/src/views/Contract/Events/events.js @@ -14,11 +14,11 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -import BigNumber from 'bignumber.js'; import React, { Component, PropTypes } from 'react'; import { Container, ContainerTitle } from '../../../ui'; +import Event from './Event'; import styles from '../contract.css'; export default class Events extends Component { @@ -27,106 +27,23 @@ export default class Events extends Component { } static propTypes = { - events: PropTypes.array, - isTest: PropTypes.bool - } - - state = { - transactions: {} - } - - componentDidMount () { - this.componentWillReceiveProps(this.props); - } - - componentWillReceiveProps (newProps) { - this.retrieveTransactions(newProps.events); + events: PropTypes.array } render () { - const { events, isTest } = this.props; - const { transactions } = this.state; + const { events } = this.props; if (!events || !events.length) { return null; } - const rows = events.map((event) => { - const transaction = transactions[event.transactionHash] || {}; - const classes = `${styles.event} ${styles[event.state]}`; - const url = `https://${isTest ? 'testnet.' : ''}etherscan.io/tx/${event.transactionHash}`; - const keys = Object.keys(event.params).map((key, index) => { - return
{ key }
; - }); - const values = Object.values(event.params).map((value, index) => { - return ( -
- { this.renderValue(value) } -
- ); - }); - - return ( - - { event.state === 'pending' ? 'pending' : event.blockNumber.toFormat(0) } - -
{ transaction.from }
- { event.transactionHash } - - -
{ event.type } =>
- { keys } - - -
 
- { values } - - - ); - }); - return ( - { rows } + { events.map((event) => ) }
); } - - renderValue (value) { - const { api } = this.context; - - if (api.util.isInstanceOf(value, BigNumber)) { - return value.toFormat(0); - } else if (api.util.isArray(value)) { - return api.util.bytesToHex(value); - } - - return value.toString(); - } - - retrieveTransactions (events) { - const { api } = this.context; - const { transactions } = this.state; - const hashes = {}; - - events.forEach((event) => { - if (!hashes[event.transactionHash] && !transactions[event.transactionHash]) { - hashes[event.transactionHash] = true; - } - }); - - Promise - .all(Object.keys(hashes).map((hash) => api.eth.getTransactionByHash(hash))) - .then((newTransactions) => { - this.setState({ - transactions: newTransactions.reduce((store, transaction) => { - transactions[transaction.hash] = transaction; - return transactions; - }, transactions) - }); - }); - } } diff --git a/js/src/views/Contract/contract.css b/js/src/views/Contract/contract.css index 6956b8c5010..7611af8bdb4 100644 --- a/js/src/views/Contract/contract.css +++ b/js/src/views/Contract/contract.css @@ -56,3 +56,10 @@ .pending { opacity: 0.5; } + +.timestamp { + padding-top: 1.5em; + text-align: right; + line-height: 1.5em; + opacity: 0.5; +} diff --git a/js/src/views/Contract/contract.js b/js/src/views/Contract/contract.js index 7a1889c1994..021005d1ded 100644 --- a/js/src/views/Contract/contract.js +++ b/js/src/views/Contract/contract.js @@ -116,7 +116,6 @@ class Contract extends Component { contract={ contract } values={ queryValues } /> From 6532f696c07ab79422724f28781185a589df9513 Mon Sep 17 00:00:00 2001 From: jacogr Date: Sat, 22 Oct 2016 14:36:08 +0200 Subject: [PATCH 2/3] Pass value & type through from event log params --- js/src/api/contract/contract.js | 4 +++- js/src/dapps/basiccoin/Deploy/Events/events.js | 4 ++++ js/src/dapps/basiccoin/Transfer/Events/events.js | 5 ++++- js/src/dapps/gavcoin/Events/events.js | 5 ++++- js/src/dapps/registry/Events/events.js | 8 ++++---- js/src/dapps/signaturereg/services.js | 5 ++++- js/src/dapps/tokenreg/Status/actions.js | 16 ++++++++-------- js/src/views/Contract/Events/Event/event.js | 16 ++++++++-------- 8 files changed, 39 insertions(+), 24 deletions(-) diff --git a/js/src/api/contract/contract.js b/js/src/api/contract/contract.js index 40caa764334..cef75eda734 100644 --- a/js/src/api/contract/contract.js +++ b/js/src/api/contract/contract.js @@ -150,7 +150,9 @@ export default class Contract { log.event = event.name; decoded.params.forEach((param) => { - log.params[param.name] = param.token.value; + const { type, value } = param.token; + + log.params[param.name] = { type, value }; }); return log; diff --git a/js/src/dapps/basiccoin/Deploy/Events/events.js b/js/src/dapps/basiccoin/Deploy/Events/events.js index a21672a8efc..4b51afb59b6 100644 --- a/js/src/dapps/basiccoin/Deploy/Events/events.js +++ b/js/src/dapps/basiccoin/Deploy/Events/events.js @@ -110,6 +110,10 @@ export default class Events extends Component { logToEvent = (log) => { log.key = api.util.sha3(JSON.stringify(log)); + log.params = Object.keys(log.params).reduce((params, name) => { + params[name] = log.params[name].value; + return params; + }, {}); return log; } diff --git a/js/src/dapps/basiccoin/Transfer/Events/events.js b/js/src/dapps/basiccoin/Transfer/Events/events.js index dcead03bb78..101c77f73a3 100644 --- a/js/src/dapps/basiccoin/Transfer/Events/events.js +++ b/js/src/dapps/basiccoin/Transfer/Events/events.js @@ -114,6 +114,10 @@ export default class Events extends Component { logToEvent = (log) => { log.key = api.util.sha3(JSON.stringify(log)); + log.params = Object.keys(log.params).reduce((params, name) => { + params[name] = log.params[name].value; + return params; + }, {}); return log; } @@ -139,7 +143,6 @@ export default class Events extends Component { .concat(pendingEvents) .filter((log) => !minedNew.find((event) => event.transactionHash === log.transactionHash)); const events = [].concat(pendingNew).concat(minedNew); - console.log('*** events', events.map((event) => event.address)); this.setState({ loading: false, events, minedEvents: minedNew, pendingEvents: pendingNew }); } } diff --git a/js/src/dapps/gavcoin/Events/events.js b/js/src/dapps/gavcoin/Events/events.js index cb287b3a7f3..ba71d65413c 100644 --- a/js/src/dapps/gavcoin/Events/events.js +++ b/js/src/dapps/gavcoin/Events/events.js @@ -106,7 +106,10 @@ export default class Events extends Component { logIndex, transactionHash, transactionIndex, - params, + params: Object.keys(params).reduce((data, name) => { + data[name] = params[name].value; + return data; + }, {}), key }; }; diff --git a/js/src/dapps/registry/Events/events.js b/js/src/dapps/registry/Events/events.js index ffb1fc9190a..10280ae5232 100644 --- a/js/src/dapps/registry/Events/events.js +++ b/js/src/dapps/registry/Events/events.js @@ -48,9 +48,9 @@ const renderEvent = (classNames, verb) => (e, accounts, contacts) => { return ( - { renderAddress(e.parameters.owner, accounts, contacts) } + { renderAddress(e.parameters.owner.value, accounts, contacts) } { verb } - { renderHash(bytesToHex(e.parameters.name)) } + { renderHash(bytesToHex(e.parameters.name.value)) } { renderStatus(e.timestamp, e.state === 'pending') } ); @@ -64,10 +64,10 @@ const renderDataChanged = (e, accounts, contacts) => { return ( - { renderAddress(e.parameters.owner, accounts, contacts) } + { renderAddress(e.parameters.owner.value, accounts, contacts) } updated - key { new Buffer(e.parameters.plainKey).toString('utf8') } of { renderHash(bytesToHex(e.parameters.name)) } + key { new Buffer(e.parameters.plainKey.value).toString('utf8') } of { renderHash(bytesToHex(e.parameters.name.value)) } { renderStatus(e.timestamp, e.state === 'pending') } diff --git a/js/src/dapps/signaturereg/services.js b/js/src/dapps/signaturereg/services.js index 3963d394c7f..0b124589791 100644 --- a/js/src/dapps/signaturereg/services.js +++ b/js/src/dapps/signaturereg/services.js @@ -30,7 +30,10 @@ const logToEvent = (log) => { logIndex, transactionHash, transactionIndex, - params, + params: Object.keys(params).reduce((data, name) => { + data[name] = params[name].value; + return data; + }, {}), key }; }; diff --git a/js/src/dapps/tokenreg/Status/actions.js b/js/src/dapps/tokenreg/Status/actions.js index a479179b223..d02c8e77576 100644 --- a/js/src/dapps/tokenreg/Status/actions.js +++ b/js/src/dapps/tokenreg/Status/actions.js @@ -148,27 +148,27 @@ export const subscribeEvents = () => (dispatch, getState) => { return dispatch(setTokenData(params.id.toNumber(), { tla: '...', base: -1, - address: params.addr, - name: params.name, + address: params.addr.value, + name: params.name.value, isPending: true })); } if (event === 'Registered' && type === 'mined') { - return dispatch(loadToken(params.id.toNumber())); + return dispatch(loadToken(params.id.value.toNumber())); } if (event === 'Unregistered' && type === 'pending') { - return dispatch(setTokenPending(params.id.toNumber(), true)); + return dispatch(setTokenPending(params.id.value.toNumber(), true)); } if (event === 'Unregistered' && type === 'mined') { - return dispatch(deleteToken(params.id.toNumber())); + return dispatch(deleteToken(params.id.value.toNumber())); } if (event === 'MetaChanged' && type === 'pending') { return dispatch(setTokenData( - params.id.toNumber(), + params.id.value.toNumber(), { metaPending: true, metaMined: false } )); } @@ -176,13 +176,13 @@ export const subscribeEvents = () => (dispatch, getState) => { if (event === 'MetaChanged' && type === 'mined') { setTimeout(() => { dispatch(setTokenData( - params.id.toNumber(), + params.id.value.toNumber(), { metaPending: false, metaMined: false } )); }, 5000); return dispatch(setTokenData( - params.id.toNumber(), + params.id.value.toNumber(), { metaPending: false, metaMined: true } )); } diff --git a/js/src/views/Contract/Events/Event/event.js b/js/src/views/Contract/Events/Event/event.js index cee243bac8e..17d06cb37f0 100644 --- a/js/src/views/Contract/Events/Event/event.js +++ b/js/src/views/Contract/Events/Event/event.js @@ -52,10 +52,10 @@ class Event extends Component { const keys = Object.keys(event.params).map((key, index) => { return
{ key }
; }); - const values = Object.values(event.params).map((value, index) => { + const values = Object.values(event.params).map((param, index) => { return (
- { this.renderValue(value) } + { this.renderParam(param) }
); }); @@ -82,16 +82,16 @@ class Event extends Component { ); } - renderValue (value) { + renderParam (param) { const { api } = this.context; - if (api.util.isInstanceOf(value, BigNumber)) { - return value.toFormat(0); - } else if (api.util.isArray(value)) { - return api.util.bytesToHex(value); + if (api.util.isInstanceOf(param.value, BigNumber)) { + return param.value.toFormat(0); + } else if (api.util.isArray(param.value)) { + return api.util.bytesToHex(param.value); } - return value.toString(); + return param.value.toString(); } formatBlockTimestamp (block) { From f6443481e6bf87508b5b81e104fbf567a9f53b46 Mon Sep 17 00:00:00 2001 From: jacogr Date: Sat, 22 Oct 2016 15:40:07 +0200 Subject: [PATCH 3/3] Reformat display columns --- js/src/views/Contract/Events/Event/event.js | 84 +++++++++++++++------ js/src/views/Contract/contract.css | 35 +++++++-- 2 files changed, 93 insertions(+), 26 deletions(-) diff --git a/js/src/views/Contract/Events/Event/event.js b/js/src/views/Contract/Events/Event/event.js index 17d06cb37f0..cfe5be25158 100644 --- a/js/src/views/Contract/Events/Event/event.js +++ b/js/src/views/Contract/Events/Event/event.js @@ -21,6 +21,7 @@ import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import { fetchBlock, fetchTransaction } from '../../../../redux/providers/blockchainActions'; +import { IdentityIcon, IdentityName, Input, InputAddress } from '../../../../ui'; import styles from '../../contract.css'; @@ -49,13 +50,13 @@ class Event extends Component { const transaction = transactions[event.transactionHash] || {}; const classes = `${styles.event} ${styles[event.state]}`; const url = `https://${isTest ? 'testnet.' : ''}etherscan.io/tx/${event.transactionHash}`; - const keys = Object.keys(event.params).map((key, index) => { - return
{ key }
; - }); - const values = Object.values(event.params).map((param, index) => { + const keys = Object.keys(event.params).join(', '); + const values = Object.keys(event.params).map((name, index) => { + const param = event.params[name]; + return ( -
- { this.renderParam(param) } +
+ { this.renderParam(name, param) }
); }); @@ -67,31 +68,72 @@ class Event extends Component {
{ this.formatNumber(transaction.blockNumber) }
-
{ transaction.from }
- { event.transactionHash } + { this.renderAddressName(transaction.from) } - -
{ event.type } =>
- { keys } + +
+ { event.type }({ keys }) +
+ { this.formatHash(event.transactionHash) } - -
 
- { values } + +
+ { values } +
); } - renderParam (param) { + formatHash (hash) { + if (!hash || hash.length <= 16) { + return hash; + } + + return `${hash.substr(2, 6)}...${hash.slice(-6)}`; + } + + renderAddressName (address, withName = true) { + return ( + + + { withName ? : address } + + ); + } + + renderParam (name, param) { const { api } = this.context; - if (api.util.isInstanceOf(param.value, BigNumber)) { - return param.value.toFormat(0); - } else if (api.util.isArray(param.value)) { - return api.util.bytesToHex(param.value); + switch (param.type) { + case 'address': + return ( + + ); + + default: + let value; + if (api.util.isInstanceOf(param.value, BigNumber)) { + value = param.value.toFormat(0); + } else if (api.util.isArray(param.value)) { + value = api.util.bytesToHex(param.value); + } else { + value = param.value.toString(); + } + + return ( + + ); } - - return param.value.toString(); } formatBlockTimestamp (block) { diff --git a/js/src/views/Contract/contract.css b/js/src/views/Contract/contract.css index 7611af8bdb4..4c6e93a3a99 100644 --- a/js/src/views/Contract/contract.css +++ b/js/src/views/Contract/contract.css @@ -24,9 +24,12 @@ border-spacing: 0; } -.event { +.events tr { + line-height: 32px; vertical-align: top; - line-height: 26px; +} + +.event { } .event td { @@ -43,9 +46,6 @@ color: #aaa; } -.value { -} - .event td div { white-space: nowrap; } @@ -62,4 +62,29 @@ text-align: right; line-height: 1.5em; opacity: 0.5; + white-space: nowrap; +} + +.eventDetails { +} + +.eventType { +} + +.eventParams { + padding-left: 2em; +} + +.eventValue { + margin-top: -16px; +} + +.eventAddress { + display: inline-block; + position: relative; +} + +.eventIdentityicon { + margin-bottom: -10px; + margin-right: 0.5em; }