Display contract block creation (#4069)
* Add contract block creation to metadata * Display mined block for contract on Contracts view * Better use of Summary for Accounts * Add sorted by mined block for contracts * Proper Block Number sort // display in contract page * PR Grumble * Linting issues
This commit is contained in:
		
							parent
							
								
									71e6e24a1f
								
							
						
					
					
						commit
						cf0a20f08b
					
				| @ -75,6 +75,10 @@ export default class Contract { | ||||
|     return this._functions; | ||||
|   } | ||||
| 
 | ||||
|   get receipt () { | ||||
|     return this._receipt; | ||||
|   } | ||||
| 
 | ||||
|   get instance () { | ||||
|     this._instance.address = this._address; | ||||
|     return this._instance; | ||||
| @ -139,6 +143,7 @@ export default class Contract { | ||||
|             } | ||||
| 
 | ||||
|             setState({ state: 'hasReceipt', receipt }); | ||||
|             this._receipt = receipt; | ||||
|             this._address = receipt.contractAddress; | ||||
|             return this._address; | ||||
|           }); | ||||
|  | ||||
| @ -455,10 +455,15 @@ class DeployContract extends Component { | ||||
| 
 | ||||
|     this.setState({ step: 'DEPLOYMENT' }); | ||||
| 
 | ||||
|     api | ||||
|       .newContract(abiParsed) | ||||
|     const contract = api.newContract(abiParsed); | ||||
| 
 | ||||
|     contract | ||||
|       .deploy(options, params, this.onDeploymentState) | ||||
|       .then((address) => { | ||||
|         const blockNumber = contract._receipt | ||||
|           ? contract.receipt.blockNumber.toNumber() | ||||
|           : null; | ||||
| 
 | ||||
|         return Promise.all([ | ||||
|           api.parity.setAccountName(address, name), | ||||
|           api.parity.setAccountMeta(address, { | ||||
| @ -466,8 +471,9 @@ class DeployContract extends Component { | ||||
|             contract: true, | ||||
|             timestamp: Date.now(), | ||||
|             deleted: false, | ||||
|             source, | ||||
|             description | ||||
|             blockNumber, | ||||
|             description, | ||||
|             source | ||||
|           }) | ||||
|         ]) | ||||
|         .then(() => { | ||||
|  | ||||
| @ -23,7 +23,7 @@ | ||||
| 
 | ||||
| .empty { | ||||
|   line-height: 24px; | ||||
|   margin: 0.75em 0.5em 0 0; | ||||
|   margin: 0 0.5em 0 0; | ||||
|   opacity: 0.25; | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -14,7 +14,7 @@ | ||||
| /* You should have received a copy of the GNU General Public License | ||||
| /* along with Parity.  If not, see <http://www.gnu.org/licenses/>. | ||||
| */ | ||||
| .byline { | ||||
| .byline, .description { | ||||
|   overflow: hidden; | ||||
|   position: relative; | ||||
|   line-height: 1.2em; | ||||
| @ -31,6 +31,11 @@ | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| .description { | ||||
|   font-size: 0.75em; | ||||
|   margin: 0.5em 0 0; | ||||
| } | ||||
| 
 | ||||
| .title { | ||||
|   text-transform: uppercase; | ||||
|   margin: 0; | ||||
|  | ||||
| @ -22,13 +22,14 @@ import styles from './title.css'; | ||||
| 
 | ||||
| export default class Title extends Component { | ||||
|   static propTypes = { | ||||
|     byline: nodeOrStringProptype(), | ||||
|     className: PropTypes.string, | ||||
|     title: nodeOrStringProptype(), | ||||
|     byline: nodeOrStringProptype() | ||||
|     description: nodeOrStringProptype(), | ||||
|     title: nodeOrStringProptype() | ||||
|   } | ||||
| 
 | ||||
|   render () { | ||||
|     const { className, title, byline } = this.props; | ||||
|     const { byline, className, title } = this.props; | ||||
| 
 | ||||
|     const byLine = typeof byline === 'string' | ||||
|       ? ( | ||||
| @ -46,6 +47,29 @@ export default class Title extends Component { | ||||
|         <div className={ styles.byline }> | ||||
|           { byLine } | ||||
|         </div> | ||||
|         { this.renderDescription() } | ||||
|       </div> | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   renderDescription () { | ||||
|     const { description } = this.props; | ||||
| 
 | ||||
|     if (!description) { | ||||
|       return null; | ||||
|     } | ||||
| 
 | ||||
|     const desc = typeof description === 'string' | ||||
|       ? ( | ||||
|         <span title={ description }> | ||||
|           { description } | ||||
|         </span> | ||||
|       ) | ||||
|       : description; | ||||
| 
 | ||||
|     return ( | ||||
|       <div className={ styles.description }> | ||||
|         { desc } | ||||
|       </div> | ||||
|     ); | ||||
|   } | ||||
|  | ||||
| @ -57,7 +57,7 @@ class List extends Component { | ||||
|   } | ||||
| 
 | ||||
|   renderAccounts () { | ||||
|     const { accounts, balances, empty, link, handleAddSearchToken } = this.props; | ||||
|     const { accounts, balances, empty } = this.props; | ||||
| 
 | ||||
|     if (empty) { | ||||
|       return ( | ||||
| @ -80,20 +80,29 @@ class List extends Component { | ||||
|       return ( | ||||
|         <div | ||||
|           className={ styles.item } | ||||
|           key={ address }> | ||||
|           <Summary | ||||
|             link={ link } | ||||
|             account={ account } | ||||
|             balance={ balance } | ||||
|             owners={ owners } | ||||
|             handleAddSearchToken={ handleAddSearchToken } | ||||
|             showCertifications | ||||
|           /> | ||||
|           key={ address } | ||||
|         > | ||||
|           { this.renderSummary(account, balance, owners) } | ||||
|         </div> | ||||
|       ); | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   renderSummary (account, balance, owners) { | ||||
|     const { handleAddSearchToken, link } = this.props; | ||||
| 
 | ||||
|     return ( | ||||
|       <Summary | ||||
|         account={ account } | ||||
|         balance={ balance } | ||||
|         handleAddSearchToken={ handleAddSearchToken } | ||||
|         link={ link } | ||||
|         owners={ owners } | ||||
|         showCertifications | ||||
|       /> | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   getAddresses () { | ||||
|     const filteredAddresses = this.getFilteredAddresses(); | ||||
|     return this.sortAddresses(filteredAddresses); | ||||
| @ -122,7 +131,15 @@ class List extends Component { | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   compareAccounts (accountA, accountB, key) { | ||||
|   compareAccounts (accountA, accountB, key, _reverse = null) { | ||||
|     if (key && key.split(':')[1] === '-1') { | ||||
|       return this.compareAccounts(accountA, accountB, key.split(':')[0], true); | ||||
|     } | ||||
| 
 | ||||
|     if (key === 'timestamp' && _reverse === null) { | ||||
|       return this.compareAccounts(accountA, accountB, key, true); | ||||
|     } | ||||
| 
 | ||||
|     if (key === 'name') { | ||||
|       return accountA.name.localeCompare(accountB.name); | ||||
|     } | ||||
| @ -177,7 +194,9 @@ class List extends Component { | ||||
|       return tagsA.localeCompare(tagsB); | ||||
|     } | ||||
| 
 | ||||
|     const reverse = key === 'timestamp' ? -1 : 1; | ||||
|     const reverse = _reverse | ||||
|       ? -1 | ||||
|       : 1; | ||||
| 
 | ||||
|     const metaA = accountA.meta[key]; | ||||
|     const metaB = accountB.meta[key]; | ||||
|  | ||||
| @ -19,6 +19,7 @@ import React, { Component, PropTypes } from 'react'; | ||||
| import { Link } from 'react-router'; | ||||
| import { isEqual } from 'lodash'; | ||||
| import ReactTooltip from 'react-tooltip'; | ||||
| import { FormattedMessage } from 'react-intl'; | ||||
| 
 | ||||
| import { Balance, Container, ContainerTitle, IdentityIcon, IdentityName, Tags, Input } from '~/ui'; | ||||
| import Certifications from '~/ui/Certifications'; | ||||
| @ -107,14 +108,22 @@ export default class Summary extends Component { | ||||
|       /> | ||||
|     ); | ||||
| 
 | ||||
|     const description = this.getDescription(account.meta); | ||||
| 
 | ||||
|     return ( | ||||
|       <Container> | ||||
|         <Tags tags={ tags } handleAddSearchToken={ handleAddSearchToken } /> | ||||
|         <div className={ styles.heading }> | ||||
|           <IdentityIcon | ||||
|           address={ address } /> | ||||
|             address={ address } | ||||
|           /> | ||||
|           <ContainerTitle | ||||
|             byline={ addressComponent } | ||||
|             className={ styles.main } | ||||
|             description={ description } | ||||
|             title={ this.renderLink() } | ||||
|           byline={ addressComponent } /> | ||||
|           /> | ||||
|         </div> | ||||
| 
 | ||||
|         { this.renderOwners() } | ||||
|         { this.renderBalance() } | ||||
| @ -123,6 +132,26 @@ export default class Summary extends Component { | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   getDescription (meta = {}) { | ||||
|     const { blockNumber } = meta; | ||||
| 
 | ||||
|     if (!blockNumber) { | ||||
|       return null; | ||||
|     } | ||||
| 
 | ||||
|     const formattedBlockNumber = (new BigNumber(blockNumber)).toFormat(); | ||||
| 
 | ||||
|     return ( | ||||
|       <FormattedMessage | ||||
|         id='accounts.summary.minedBlock' | ||||
|         defaultMessage='Mined at block #{blockNumber}' | ||||
|         values={ { | ||||
|           blockNumber: formattedBlockNumber | ||||
|         } } | ||||
|       /> | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   renderOwners () { | ||||
|     const { owners } = this.props; | ||||
|     const ownersValid = (owners || []).filter((owner) => owner.address && new BigNumber(owner.address).gt(0)); | ||||
|  | ||||
| @ -56,3 +56,12 @@ | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| .heading { | ||||
|   display: flex; | ||||
|   flex-direction: row; | ||||
| 
 | ||||
|   .main { | ||||
|     flex: 1; | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -45,6 +45,11 @@ | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| .blockNumber { | ||||
|   color: rgba(255, 255, 255, 0.25); | ||||
|   margin-top: 1.5em; | ||||
| } | ||||
| 
 | ||||
| .origin { | ||||
|   text-align: left; | ||||
|   padding-left: 32px; | ||||
|  | ||||
| @ -17,6 +17,9 @@ | ||||
| import React, { Component, PropTypes } from 'react'; | ||||
| import { connect } from 'react-redux'; | ||||
| import { bindActionCreators } from 'redux'; | ||||
| import { FormattedMessage } from 'react-intl'; | ||||
| import BigNumber from 'bignumber.js'; | ||||
| 
 | ||||
| import ActionDelete from 'material-ui/svg-icons/action/delete'; | ||||
| import AvPlayArrow from 'material-ui/svg-icons/av/play-arrow'; | ||||
| import ContentCreate from 'material-ui/svg-icons/content/create'; | ||||
| @ -136,7 +139,9 @@ class Contract extends Component { | ||||
|             account={ account } | ||||
|             balance={ balance } | ||||
|             isContract | ||||
|           /> | ||||
|           > | ||||
|             { this.renderBlockNumber(account.meta) } | ||||
|           </Header> | ||||
| 
 | ||||
|           <Queries | ||||
|             accountsInfo={ accountsInfo } | ||||
| @ -156,6 +161,28 @@ class Contract extends Component { | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   renderBlockNumber (meta = {}) { | ||||
|     const { blockNumber } = meta; | ||||
| 
 | ||||
|     if (!blockNumber) { | ||||
|       return null; | ||||
|     } | ||||
| 
 | ||||
|     const formattedBlockNumber = (new BigNumber(blockNumber)).toFormat(); | ||||
| 
 | ||||
|     return ( | ||||
|       <div className={ styles.blockNumber }> | ||||
|         <FormattedMessage | ||||
|           id='contract.minedBlock' | ||||
|           defaultMessage='Mined at block #{blockNumber}' | ||||
|           values={ { | ||||
|             blockNumber: formattedBlockNumber | ||||
|           } } | ||||
|         /> | ||||
|       </div> | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   renderDetails (contract) { | ||||
|     const { showDetailsDialog } = this.state; | ||||
| 
 | ||||
|  | ||||
| @ -1,17 +0,0 @@ | ||||
| // Copyright 2015, 2016 Parity Technologies (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 <http://www.gnu.org/licenses/>.
 | ||||
| 
 | ||||
| export default from './summary'; | ||||
| @ -1,52 +0,0 @@ | ||||
| // Copyright 2015, 2016 Parity Technologies (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 <http://www.gnu.org/licenses/>.
 | ||||
| 
 | ||||
| import React, { Component, PropTypes } from 'react'; | ||||
| import { Link } from 'react-router'; | ||||
| 
 | ||||
| import { Container, ContainerTitle, IdentityIcon, IdentityName } from '~/ui'; | ||||
| 
 | ||||
| export default class Summary extends Component { | ||||
|   static contextTypes = { | ||||
|     api: React.PropTypes.object.isRequired | ||||
|   } | ||||
| 
 | ||||
|   static propTypes = { | ||||
|     contract: PropTypes.object.isRequired, | ||||
|     children: PropTypes.node | ||||
|   } | ||||
| 
 | ||||
|   render () { | ||||
|     const contract = this.props.contract; | ||||
| 
 | ||||
|     if (!contract) { | ||||
|       return null; | ||||
|     } | ||||
| 
 | ||||
|     const viewLink = `/app/${contract.address}`; | ||||
| 
 | ||||
|     return ( | ||||
|       <Container> | ||||
|         <IdentityIcon | ||||
|           address={ contract.address } /> | ||||
|         <ContainerTitle | ||||
|           title={ <Link to={ viewLink }>{ <IdentityName address={ contract.address } unknown /> }</Link> } | ||||
|           byline={ contract.address } /> | ||||
|         { this.props.children } | ||||
|       </Container> | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| @ -45,7 +45,7 @@ class Contracts extends Component { | ||||
|   state = { | ||||
|     addContract: false, | ||||
|     deployContract: false, | ||||
|     sortOrder: 'timestamp', | ||||
|     sortOrder: 'blockNumber', | ||||
|     searchValues: [], | ||||
|     searchTokens: [] | ||||
|   } | ||||
| @ -92,7 +92,8 @@ class Contracts extends Component { | ||||
|             empty={ !hasContracts } | ||||
|             order={ sortOrder } | ||||
|             orderFallback='name' | ||||
|             handleAddSearchToken={ this.onAddSearchToken } /> | ||||
|             handleAddSearchToken={ this.onAddSearchToken } | ||||
|           /> | ||||
|         </Page> | ||||
|       </div> | ||||
|     ); | ||||
| @ -109,7 +110,8 @@ class Contracts extends Component { | ||||
|         id='sortContracts' | ||||
|         order={ this.state.sortOrder } | ||||
|         metas={ [ | ||||
|           { key: 'timestamp', label: 'date' } | ||||
|           { key: 'timestamp', label: 'date' }, | ||||
|           { key: 'blockNumber:-1', label: 'mined block' } | ||||
|         ] } | ||||
|         showDefault={ false } | ||||
|         onChange={ onChange } /> | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user