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:
Nicolas Gotchac 2017-01-09 09:38:27 +01:00 committed by Jaco Greeff
parent 71e6e24a1f
commit cf0a20f08b
13 changed files with 161 additions and 99 deletions

View File

@ -75,6 +75,10 @@ export default class Contract {
return this._functions; return this._functions;
} }
get receipt () {
return this._receipt;
}
get instance () { get instance () {
this._instance.address = this._address; this._instance.address = this._address;
return this._instance; return this._instance;
@ -139,6 +143,7 @@ export default class Contract {
} }
setState({ state: 'hasReceipt', receipt }); setState({ state: 'hasReceipt', receipt });
this._receipt = receipt;
this._address = receipt.contractAddress; this._address = receipt.contractAddress;
return this._address; return this._address;
}); });

View File

@ -455,10 +455,15 @@ class DeployContract extends Component {
this.setState({ step: 'DEPLOYMENT' }); this.setState({ step: 'DEPLOYMENT' });
api const contract = api.newContract(abiParsed);
.newContract(abiParsed)
contract
.deploy(options, params, this.onDeploymentState) .deploy(options, params, this.onDeploymentState)
.then((address) => { .then((address) => {
const blockNumber = contract._receipt
? contract.receipt.blockNumber.toNumber()
: null;
return Promise.all([ return Promise.all([
api.parity.setAccountName(address, name), api.parity.setAccountName(address, name),
api.parity.setAccountMeta(address, { api.parity.setAccountMeta(address, {
@ -466,8 +471,9 @@ class DeployContract extends Component {
contract: true, contract: true,
timestamp: Date.now(), timestamp: Date.now(),
deleted: false, deleted: false,
source, blockNumber,
description description,
source
}) })
]) ])
.then(() => { .then(() => {

View File

@ -23,7 +23,7 @@
.empty { .empty {
line-height: 24px; line-height: 24px;
margin: 0.75em 0.5em 0 0; margin: 0 0.5em 0 0;
opacity: 0.25; opacity: 0.25;
} }

View File

@ -14,7 +14,7 @@
/* You should have received a copy of the GNU General Public License /* You should have received a copy of the GNU General Public License
/* along with Parity. If not, see <http://www.gnu.org/licenses/>. /* along with Parity. If not, see <http://www.gnu.org/licenses/>.
*/ */
.byline { .byline, .description {
overflow: hidden; overflow: hidden;
position: relative; position: relative;
line-height: 1.2em; line-height: 1.2em;
@ -31,6 +31,11 @@
} }
} }
.description {
font-size: 0.75em;
margin: 0.5em 0 0;
}
.title { .title {
text-transform: uppercase; text-transform: uppercase;
margin: 0; margin: 0;

View File

@ -22,13 +22,14 @@ import styles from './title.css';
export default class Title extends Component { export default class Title extends Component {
static propTypes = { static propTypes = {
byline: nodeOrStringProptype(),
className: PropTypes.string, className: PropTypes.string,
title: nodeOrStringProptype(), description: nodeOrStringProptype(),
byline: nodeOrStringProptype() title: nodeOrStringProptype()
} }
render () { render () {
const { className, title, byline } = this.props; const { byline, className, title } = this.props;
const byLine = typeof byline === 'string' const byLine = typeof byline === 'string'
? ( ? (
@ -46,6 +47,29 @@ export default class Title extends Component {
<div className={ styles.byline }> <div className={ styles.byline }>
{ byLine } { byLine }
</div> </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> </div>
); );
} }

View File

@ -57,7 +57,7 @@ class List extends Component {
} }
renderAccounts () { renderAccounts () {
const { accounts, balances, empty, link, handleAddSearchToken } = this.props; const { accounts, balances, empty } = this.props;
if (empty) { if (empty) {
return ( return (
@ -80,20 +80,29 @@ class List extends Component {
return ( return (
<div <div
className={ styles.item } className={ styles.item }
key={ address }> key={ address }
<Summary >
link={ link } { this.renderSummary(account, balance, owners) }
account={ account }
balance={ balance }
owners={ owners }
handleAddSearchToken={ handleAddSearchToken }
showCertifications
/>
</div> </div>
); );
}); });
} }
renderSummary (account, balance, owners) {
const { handleAddSearchToken, link } = this.props;
return (
<Summary
account={ account }
balance={ balance }
handleAddSearchToken={ handleAddSearchToken }
link={ link }
owners={ owners }
showCertifications
/>
);
}
getAddresses () { getAddresses () {
const filteredAddresses = this.getFilteredAddresses(); const filteredAddresses = this.getFilteredAddresses();
return this.sortAddresses(filteredAddresses); 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') { if (key === 'name') {
return accountA.name.localeCompare(accountB.name); return accountA.name.localeCompare(accountB.name);
} }
@ -177,7 +194,9 @@ class List extends Component {
return tagsA.localeCompare(tagsB); return tagsA.localeCompare(tagsB);
} }
const reverse = key === 'timestamp' ? -1 : 1; const reverse = _reverse
? -1
: 1;
const metaA = accountA.meta[key]; const metaA = accountA.meta[key];
const metaB = accountB.meta[key]; const metaB = accountB.meta[key];

View File

@ -19,6 +19,7 @@ import React, { Component, PropTypes } from 'react';
import { Link } from 'react-router'; import { Link } from 'react-router';
import { isEqual } from 'lodash'; import { isEqual } from 'lodash';
import ReactTooltip from 'react-tooltip'; import ReactTooltip from 'react-tooltip';
import { FormattedMessage } from 'react-intl';
import { Balance, Container, ContainerTitle, IdentityIcon, IdentityName, Tags, Input } from '~/ui'; import { Balance, Container, ContainerTitle, IdentityIcon, IdentityName, Tags, Input } from '~/ui';
import Certifications from '~/ui/Certifications'; import Certifications from '~/ui/Certifications';
@ -107,14 +108,22 @@ export default class Summary extends Component {
/> />
); );
const description = this.getDescription(account.meta);
return ( return (
<Container> <Container>
<Tags tags={ tags } handleAddSearchToken={ handleAddSearchToken } /> <Tags tags={ tags } handleAddSearchToken={ handleAddSearchToken } />
<IdentityIcon <div className={ styles.heading }>
address={ address } /> <IdentityIcon
<ContainerTitle address={ address }
title={ this.renderLink() } />
byline={ addressComponent } /> <ContainerTitle
byline={ addressComponent }
className={ styles.main }
description={ description }
title={ this.renderLink() }
/>
</div>
{ this.renderOwners() } { this.renderOwners() }
{ this.renderBalance() } { 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 () { renderOwners () {
const { owners } = this.props; const { owners } = this.props;
const ownersValid = (owners || []).filter((owner) => owner.address && new BigNumber(owner.address).gt(0)); const ownersValid = (owners || []).filter((owner) => owner.address && new BigNumber(owner.address).gt(0));

View File

@ -56,3 +56,12 @@
} }
} }
} }
.heading {
display: flex;
flex-direction: row;
.main {
flex: 1;
}
}

View File

@ -45,6 +45,11 @@
} }
} }
.blockNumber {
color: rgba(255, 255, 255, 0.25);
margin-top: 1.5em;
}
.origin { .origin {
text-align: left; text-align: left;
padding-left: 32px; padding-left: 32px;

View File

@ -17,6 +17,9 @@
import React, { Component, PropTypes } from 'react'; import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { bindActionCreators } from '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 ActionDelete from 'material-ui/svg-icons/action/delete';
import AvPlayArrow from 'material-ui/svg-icons/av/play-arrow'; import AvPlayArrow from 'material-ui/svg-icons/av/play-arrow';
import ContentCreate from 'material-ui/svg-icons/content/create'; import ContentCreate from 'material-ui/svg-icons/content/create';
@ -136,7 +139,9 @@ class Contract extends Component {
account={ account } account={ account }
balance={ balance } balance={ balance }
isContract isContract
/> >
{ this.renderBlockNumber(account.meta) }
</Header>
<Queries <Queries
accountsInfo={ accountsInfo } 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) { renderDetails (contract) {
const { showDetailsDialog } = this.state; const { showDetailsDialog } = this.state;

View File

@ -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';

View File

@ -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>
);
}
}

View File

@ -45,7 +45,7 @@ class Contracts extends Component {
state = { state = {
addContract: false, addContract: false,
deployContract: false, deployContract: false,
sortOrder: 'timestamp', sortOrder: 'blockNumber',
searchValues: [], searchValues: [],
searchTokens: [] searchTokens: []
} }
@ -92,7 +92,8 @@ class Contracts extends Component {
empty={ !hasContracts } empty={ !hasContracts }
order={ sortOrder } order={ sortOrder }
orderFallback='name' orderFallback='name'
handleAddSearchToken={ this.onAddSearchToken } /> handleAddSearchToken={ this.onAddSearchToken }
/>
</Page> </Page>
</div> </div>
); );
@ -109,7 +110,8 @@ class Contracts extends Component {
id='sortContracts' id='sortContracts'
order={ this.state.sortOrder } order={ this.state.sortOrder }
metas={ [ metas={ [
{ key: 'timestamp', label: 'date' } { key: 'timestamp', label: 'date' },
{ key: 'blockNumber:-1', label: 'mined block' }
] } ] }
showDefault={ false } showDefault={ false }
onChange={ onChange } /> onChange={ onChange } />