Merge branch 'master' into ng-webpack-update
This commit is contained in:
commit
d78de18385
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -1263,7 +1263,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "parity-ui-precompiled"
|
name = "parity-ui-precompiled"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
source = "git+https://github.com/ethcore/js-precompiled.git#7cb42b0c636f76eb478c9270a1e507ac3c3ba434"
|
source = "git+https://github.com/ethcore/js-precompiled.git#0ad3b5d2b885f9b3d582bedebec2f8db0238b4b9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "parity.js",
|
"name": "parity.js",
|
||||||
"version": "0.2.74",
|
"version": "0.2.76",
|
||||||
"main": "release/index.js",
|
"main": "release/index.js",
|
||||||
"jsnext:main": "src/index.js",
|
"jsnext:main": "src/index.js",
|
||||||
"author": "Parity Team <admin@parity.io>",
|
"author": "Parity Team <admin@parity.io>",
|
||||||
@ -131,6 +131,7 @@
|
|||||||
"es6-error": "~4.0.0",
|
"es6-error": "~4.0.0",
|
||||||
"es6-promise": "~3.2.1",
|
"es6-promise": "~3.2.1",
|
||||||
"ethereumjs-tx": "~1.1.2",
|
"ethereumjs-tx": "~1.1.2",
|
||||||
|
"eventemitter3": "~2.0.2",
|
||||||
"file-saver": "~1.3.3",
|
"file-saver": "~1.3.3",
|
||||||
"format-json": "~1.0.3",
|
"format-json": "~1.0.3",
|
||||||
"format-number": "~2.0.1",
|
"format-number": "~2.0.1",
|
||||||
|
5
js/src/3rdparty/sms-verification/index.js
vendored
5
js/src/3rdparty/sms-verification/index.js
vendored
@ -27,9 +27,10 @@ export const termsOfService = (
|
|||||||
</ul>
|
</ul>
|
||||||
);
|
);
|
||||||
|
|
||||||
export const postToServer = (query) => {
|
export const postToServer = (query, isTestnet = false) => {
|
||||||
|
const port = isTestnet ? 8443 : 443;
|
||||||
query = stringify(query);
|
query = stringify(query);
|
||||||
return fetch('https://sms-verification.parity.io/?' + query, {
|
return fetch(`https://sms-verification.parity.io:${port}/?` + query, {
|
||||||
method: 'POST', mode: 'cors', cache: 'no-store'
|
method: 'POST', mode: 'cors', cache: 'no-store'
|
||||||
})
|
})
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
|
@ -1 +1 @@
|
|||||||
[{"constant":false,"inputs":[{"name":"_new","type":"address"}],"name":"setOwner","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_who","type":"address"}],"name":"certify","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"request","outputs":[],"payable":true,"type":"function"},{"constant":false,"inputs":[{"name":"_who","type":"address"},{"name":"_puzzle","type":"bytes32"}],"name":"puzzle","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_who","type":"address"},{"name":"_field","type":"string"}],"name":"getAddress","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_new","type":"uint256"}],"name":"setFee","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_who","type":"address"}],"name":"revoke","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_code","type":"bytes32"}],"name":"confirm","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"drain","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"delegate","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_who","type":"address"},{"name":"_field","type":"string"}],"name":"getUint","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_new","type":"address"}],"name":"setDelegate","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_who","type":"address"}],"name":"certified","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"fee","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_who","type":"address"},{"name":"_field","type":"string"}],"name":"get","outputs":[{"name":"","type":"bytes32"}],"payable":false,"type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"who","type":"address"}],"name":"Requested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"who","type":"address"},{"indexed":false,"name":"puzzle","type":"bytes32"}],"name":"Puzzled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"who","type":"address"}],"name":"Confirmed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"who","type":"address"}],"name":"Revoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"old","type":"address"},{"indexed":true,"name":"current","type":"address"}],"name":"NewOwner","type":"event"}]
|
[{"constant":false,"inputs":[{"name":"_new","type":"address"}],"name":"setOwner","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_who","type":"address"}],"name":"certify","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"request","outputs":[],"payable":true,"type":"function"},{"constant":false,"inputs":[{"name":"_who","type":"address"},{"name":"_puzzle","type":"bytes32"}],"name":"puzzle","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_who","type":"address"},{"name":"_field","type":"string"}],"name":"getAddress","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_new","type":"uint256"}],"name":"setFee","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_who","type":"address"}],"name":"revoke","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_code","type":"bytes32"}],"name":"confirm","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"drain","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"delegate","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_who","type":"address"},{"name":"_field","type":"string"}],"name":"getUint","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_new","type":"address"}],"name":"setDelegate","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_who","type":"address"}],"name":"certified","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"fee","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_who","type":"address"},{"name":"_field","type":"string"}],"name":"get","outputs":[{"name":"","type":"bytes32"}],"payable":false,"type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"who","type":"address"}],"name":"Requested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"who","type":"address"},{"indexed":false,"name":"puzzle","type":"bytes32"}],"name":"Puzzled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"who","type":"address"}],"name":"Confirmed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"who","type":"address"}],"name":"Revoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"old","type":"address"},{"indexed":true,"name":"current","type":"address"}],"name":"NewOwner","type":"event"}]
|
||||||
|
@ -14,6 +14,8 @@
|
|||||||
// 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/>.
|
||||||
|
|
||||||
|
import subscribeToEvent from '../util/subscribe-to-event';
|
||||||
|
|
||||||
export const checkIfVerified = (contract, account) => {
|
export const checkIfVerified = (contract, account) => {
|
||||||
return contract.instance.certified.call({}, [account]);
|
return contract.instance.certified.call({}, [account]);
|
||||||
};
|
};
|
||||||
@ -50,3 +52,36 @@ export const checkIfRequested = (contract, account) => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const blockNumber = (api) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
api.subscribe('eth_blockNumber', (err, block) => {
|
||||||
|
if (err) {
|
||||||
|
return reject(err);
|
||||||
|
}
|
||||||
|
resolve(block);
|
||||||
|
})
|
||||||
|
.then((subscription) => {
|
||||||
|
api.unsubscribe(subscription);
|
||||||
|
})
|
||||||
|
.catch(reject);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const awaitPuzzle = (api, contract, account) => {
|
||||||
|
return blockNumber(api)
|
||||||
|
.then((block) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const subscription = subscribeToEvent(contract, 'Puzzled', {
|
||||||
|
from: block.toNumber(),
|
||||||
|
filter: (log) => log.params.who.value === account
|
||||||
|
});
|
||||||
|
subscription.once('error', reject);
|
||||||
|
subscription.once('log', subscription.unsubscribe);
|
||||||
|
subscription.once('log', resolve);
|
||||||
|
subscription.once('timeout', () => {
|
||||||
|
reject(new Error('Timed out waiting for the puzzle.'));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
@ -53,7 +53,7 @@ export default class GatherData extends Component {
|
|||||||
{ this.renderCertified() }
|
{ this.renderCertified() }
|
||||||
{ this.renderRequested() }
|
{ this.renderRequested() }
|
||||||
<Input
|
<Input
|
||||||
label={ 'phone number' }
|
label={ 'phone number in international format' }
|
||||||
hint={ 'the SMS will be sent to this number' }
|
hint={ 'the SMS will be sent to this number' }
|
||||||
error={ isNumberValid ? null : 'invalid number' }
|
error={ isNumberValid ? null : 'invalid number' }
|
||||||
disabled={ isVerified }
|
disabled={ isVerified }
|
||||||
|
@ -25,7 +25,7 @@ import {
|
|||||||
LOADING,
|
LOADING,
|
||||||
QUERY_DATA,
|
QUERY_DATA,
|
||||||
POSTING_REQUEST, POSTED_REQUEST,
|
POSTING_REQUEST, POSTED_REQUEST,
|
||||||
REQUESTING_SMS, REQUESTED_SMS,
|
REQUESTING_SMS, QUERY_CODE,
|
||||||
POSTING_CONFIRMATION, POSTED_CONFIRMATION,
|
POSTING_CONFIRMATION, POSTED_CONFIRMATION,
|
||||||
DONE
|
DONE
|
||||||
} from './store';
|
} from './store';
|
||||||
@ -48,7 +48,7 @@ export default class SMSVerification extends Component {
|
|||||||
[LOADING]: 0,
|
[LOADING]: 0,
|
||||||
[QUERY_DATA]: 1,
|
[QUERY_DATA]: 1,
|
||||||
[POSTING_REQUEST]: 2, [POSTED_REQUEST]: 2, [REQUESTING_SMS]: 2,
|
[POSTING_REQUEST]: 2, [POSTED_REQUEST]: 2, [REQUESTING_SMS]: 2,
|
||||||
[REQUESTED_SMS]: 3,
|
[QUERY_CODE]: 3,
|
||||||
[POSTING_CONFIRMATION]: 4, [POSTED_CONFIRMATION]: 4,
|
[POSTING_CONFIRMATION]: 4, [POSTED_CONFIRMATION]: 4,
|
||||||
[DONE]: 5
|
[DONE]: 5
|
||||||
}
|
}
|
||||||
|
@ -47,7 +47,7 @@ export default class SendRequest extends Component {
|
|||||||
|
|
||||||
case REQUESTING_SMS:
|
case REQUESTING_SMS:
|
||||||
return (
|
return (
|
||||||
<p>Requesting an SMS from the Parity server.</p>
|
<p>Requesting an SMS from the Parity server and waiting for the puzzle to be put into the contract.</p>
|
||||||
);
|
);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -20,19 +20,16 @@ import { sha3 } from '../../api/util/sha3';
|
|||||||
|
|
||||||
import Contracts from '../../contracts';
|
import Contracts from '../../contracts';
|
||||||
|
|
||||||
import { checkIfVerified, checkIfRequested } from '../../contracts/sms-verification';
|
import { checkIfVerified, checkIfRequested, awaitPuzzle } from '../../contracts/sms-verification';
|
||||||
import { postToServer } from '../../3rdparty/sms-verification';
|
import { postToServer } from '../../3rdparty/sms-verification';
|
||||||
import checkIfTxFailed from '../../util/check-if-tx-failed';
|
import checkIfTxFailed from '../../util/check-if-tx-failed';
|
||||||
import waitForConfirmations from '../../util/wait-for-block-confirmations';
|
import waitForConfirmations from '../../util/wait-for-block-confirmations';
|
||||||
|
|
||||||
const validCode = /^[A-Z\s]+$/i;
|
|
||||||
|
|
||||||
export const LOADING = 'fetching-contract';
|
export const LOADING = 'fetching-contract';
|
||||||
export const QUERY_DATA = 'query-data';
|
export const QUERY_DATA = 'query-data';
|
||||||
export const POSTING_REQUEST = 'posting-request';
|
export const POSTING_REQUEST = 'posting-request';
|
||||||
export const POSTED_REQUEST = 'posted-request';
|
export const POSTED_REQUEST = 'posted-request';
|
||||||
export const REQUESTING_SMS = 'requesting-sms';
|
export const REQUESTING_SMS = 'requesting-sms';
|
||||||
export const REQUESTED_SMS = 'requested-sms';
|
|
||||||
export const QUERY_CODE = 'query-code';
|
export const QUERY_CODE = 'query-code';
|
||||||
export const POSTING_CONFIRMATION = 'posting-confirmation';
|
export const POSTING_CONFIRMATION = 'posting-confirmation';
|
||||||
export const POSTED_CONFIRMATION = 'posted-confirmation';
|
export const POSTED_CONFIRMATION = 'posted-confirmation';
|
||||||
@ -50,11 +47,9 @@ export default class VerificationStore {
|
|||||||
@observable number = '';
|
@observable number = '';
|
||||||
@observable requestTx = null;
|
@observable requestTx = null;
|
||||||
@observable code = '';
|
@observable code = '';
|
||||||
|
@observable isCodeValid = null;
|
||||||
@observable confirmationTx = null;
|
@observable confirmationTx = null;
|
||||||
|
|
||||||
@computed get isCodeValid () {
|
|
||||||
return validCode.test(this.code);
|
|
||||||
}
|
|
||||||
@computed get isNumberValid () {
|
@computed get isNumberValid () {
|
||||||
return phone.isValidNumber(this.number);
|
return phone.isValidNumber(this.number);
|
||||||
}
|
}
|
||||||
@ -72,20 +67,19 @@ export default class VerificationStore {
|
|||||||
return this.contract && this.fee && this.isVerified !== null && this.hasRequested !== null;
|
return this.contract && this.fee && this.isVerified !== null && this.hasRequested !== null;
|
||||||
case QUERY_DATA:
|
case QUERY_DATA:
|
||||||
return this.isNumberValid && this.consentGiven;
|
return this.isNumberValid && this.consentGiven;
|
||||||
case REQUESTED_SMS:
|
|
||||||
return this.requestTx;
|
|
||||||
case QUERY_CODE:
|
case QUERY_CODE:
|
||||||
return this.isCodeValid;
|
return this.requestTx && this.isCodeValid === true;
|
||||||
case POSTED_CONFIRMATION:
|
case POSTED_CONFIRMATION:
|
||||||
return this.confirmationTx;
|
return !!this.confirmationTx;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor (api, account) {
|
constructor (api, account, isTestnet) {
|
||||||
this.api = api;
|
this.api = api;
|
||||||
this.account = account;
|
this.account = account;
|
||||||
|
this.isTestnet = isTestnet;
|
||||||
|
|
||||||
this.step = LOADING;
|
this.step = LOADING;
|
||||||
Contracts.create(api).registry.getContract('smsverification')
|
Contracts.create(api).registry.getContract('smsverification')
|
||||||
@ -151,7 +145,26 @@ export default class VerificationStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@action setCode = (code) => {
|
@action setCode = (code) => {
|
||||||
|
const { contract, account } = this;
|
||||||
|
if (!contract || !account || code.length === 0) return;
|
||||||
|
|
||||||
|
const confirm = contract.functions.find((fn) => fn.name === 'confirm');
|
||||||
|
const options = { from: account };
|
||||||
|
const values = [ sha3(code) ];
|
||||||
|
|
||||||
this.code = code;
|
this.code = code;
|
||||||
|
this.isCodeValid = null;
|
||||||
|
confirm.estimateGas(options, values)
|
||||||
|
.then((gas) => {
|
||||||
|
options.gas = gas.mul(1.2).toFixed(0);
|
||||||
|
return confirm.call(options, values);
|
||||||
|
})
|
||||||
|
.then((result) => {
|
||||||
|
this.isCodeValid = result === true;
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
this.error = 'Failed to check if the code is valid: ' + err.message;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@action sendRequest = () => {
|
@action sendRequest = () => {
|
||||||
@ -188,11 +201,15 @@ export default class VerificationStore {
|
|||||||
|
|
||||||
chain
|
chain
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.step = REQUESTING_SMS;
|
return api.parity.netChain();
|
||||||
return postToServer({ number, address: account });
|
|
||||||
})
|
})
|
||||||
|
.then((chain) => {
|
||||||
|
this.step = REQUESTING_SMS;
|
||||||
|
return postToServer({ number, address: account }, this.isTestnet);
|
||||||
|
})
|
||||||
|
.then(() => awaitPuzzle(api, contract, account))
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.step = REQUESTED_SMS;
|
this.step = QUERY_CODE;
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
this.error = 'Failed to request a confirmation SMS: ' + err.message;
|
this.error = 'Failed to request a confirmation SMS: ' + err.message;
|
||||||
|
@ -18,6 +18,8 @@ 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 ShortenedHash from '../ShortenedHash';
|
||||||
|
|
||||||
const defaultName = 'UNNAMED';
|
const defaultName = 'UNNAMED';
|
||||||
|
|
||||||
class IdentityName extends Component {
|
class IdentityName extends Component {
|
||||||
@ -41,7 +43,7 @@ class IdentityName extends Component {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const addressFallback = shorten ? this.formatHash(address) : address;
|
const addressFallback = shorten ? (<ShortenedHash data={ address } />) : address;
|
||||||
const fallback = unknown ? defaultName : addressFallback;
|
const fallback = unknown ? defaultName : addressFallback;
|
||||||
const isUuid = hasAccount && account.name === account.uuid;
|
const isUuid = hasAccount && account.name === account.uuid;
|
||||||
const displayName = (name && name.toUpperCase().trim()) ||
|
const displayName = (name && name.toUpperCase().trim()) ||
|
||||||
@ -55,14 +57,6 @@ class IdentityName extends Component {
|
|||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
formatHash (hash) {
|
|
||||||
if (!hash || hash.length <= 16) {
|
|
||||||
return hash;
|
|
||||||
}
|
|
||||||
|
|
||||||
return `${hash.substr(2, 6)}...${hash.slice(-6)}`;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function mapStateToProps (state) {
|
function mapStateToProps (state) {
|
||||||
|
17
js/src/ui/ShortenedHash/index.js
Normal file
17
js/src/ui/ShortenedHash/index.js
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
export default from './shortenedHash';
|
21
js/src/ui/ShortenedHash/shortenedHash.css
Normal file
21
js/src/ui/ShortenedHash/shortenedHash.css
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.hash {
|
||||||
|
display: inline-block;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
41
js/src/ui/ShortenedHash/shortenedHash.js
Normal file
41
js/src/ui/ShortenedHash/shortenedHash.js
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
// 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import React, { Component, PropTypes } from 'react';
|
||||||
|
|
||||||
|
import styles from './shortenedHash.css';
|
||||||
|
|
||||||
|
export default class ShortenedHash extends Component {
|
||||||
|
static propTypes = {
|
||||||
|
data: PropTypes.string.isRequired
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const { data } = this.props;
|
||||||
|
|
||||||
|
let shortened = data.toLowerCase();
|
||||||
|
if (shortened.slice(0, 2) === '0x') {
|
||||||
|
shortened = shortened.slice(2);
|
||||||
|
}
|
||||||
|
if (shortened.length > (6 + 6)) {
|
||||||
|
shortened = shortened.slice(0, 6) + '…' + shortened.slice(-6);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<abbr className={ styles.hash } title={ shortened }>{ shortened }</abbr>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -15,19 +15,12 @@
|
|||||||
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.details {
|
|
||||||
}
|
|
||||||
|
|
||||||
.header {
|
|
||||||
}
|
|
||||||
|
|
||||||
.hash {
|
.hash {
|
||||||
padding-top: 1em;
|
padding-top: 1em;
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
}
|
}
|
||||||
|
|
||||||
.confirm {
|
.confirm {
|
||||||
padding-top: 1em;
|
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,7 +19,9 @@ 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 { LinearProgress } from 'material-ui';
|
import { LinearProgress } from 'material-ui';
|
||||||
|
|
||||||
import { txLink } from '../../3rdparty/etherscan/links';
|
import { txLink } from '../../3rdparty/etherscan/links';
|
||||||
|
import ShortenedHash from '../ShortenedHash';
|
||||||
|
|
||||||
import styles from './txHash.css';
|
import styles from './txHash.css';
|
||||||
|
|
||||||
@ -62,22 +64,23 @@ class TxHash extends Component {
|
|||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { hash, isTest, summary } = this.props;
|
const { hash, isTest, summary } = this.props;
|
||||||
let header = null;
|
|
||||||
|
|
||||||
if (!summary) {
|
const link = (
|
||||||
header = (
|
<a href={ txLink(hash, isTest) } target='_blank'>
|
||||||
<div className={ styles.header }>
|
<ShortenedHash data={ hash } />
|
||||||
The transaction has been posted to the network with a transaction hash of
|
</a>
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let header = (
|
||||||
|
<p>The transaction has been posted to the network, with a hash of { link }.</p>
|
||||||
|
);
|
||||||
|
if (summary) {
|
||||||
|
header = (<p>{ link }</p>);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={ styles.details }>
|
<div>
|
||||||
{ header }
|
{ header }
|
||||||
<div className={ styles.hash }>
|
|
||||||
<a href={ txLink(hash, isTest) } target='_blank'>{ hash }</a>
|
|
||||||
</div>
|
|
||||||
{ this.renderConfirmations() }
|
{ this.renderConfirmations() }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@ -87,17 +90,29 @@ class TxHash extends Component {
|
|||||||
const { maxConfirmations } = this.props;
|
const { maxConfirmations } = this.props;
|
||||||
const { blockNumber, transaction } = this.state;
|
const { blockNumber, transaction } = this.state;
|
||||||
|
|
||||||
let txBlock = 'Pending';
|
if (!(transaction && transaction.blockNumber && transaction.blockNumber.gt(0))) {
|
||||||
let confirmations = 'No';
|
return (
|
||||||
let value = 0;
|
<div className={ styles.confirm }>
|
||||||
|
<LinearProgress
|
||||||
if (transaction && transaction.blockNumber && transaction.blockNumber.gt(0)) {
|
className={ styles.progressbar }
|
||||||
const num = blockNumber.minus(transaction.blockNumber).plus(1);
|
color='white'
|
||||||
txBlock = `#${transaction.blockNumber.toFormat(0)}`;
|
mode='indeterminate'
|
||||||
confirmations = num.toFormat(0);
|
/>
|
||||||
value = num.gt(maxConfirmations) ? maxConfirmations : num.toNumber();
|
<div className={ styles.progressinfo }>waiting for confirmations</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const confirmations = blockNumber.minus(transaction.blockNumber).plus(1);
|
||||||
|
const value = Math.min(confirmations.toNumber(), maxConfirmations);
|
||||||
|
let count;
|
||||||
|
if (confirmations.gt(maxConfirmations)) {
|
||||||
|
count = confirmations.toFormat(0);
|
||||||
|
} else {
|
||||||
|
count = confirmations.toFormat(0) + `/${maxConfirmations}`;
|
||||||
|
}
|
||||||
|
const unit = value === 1 ? 'confirmation' : 'confirmations';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={ styles.confirm }>
|
<div className={ styles.confirm }>
|
||||||
<LinearProgress
|
<LinearProgress
|
||||||
@ -106,9 +121,10 @@ class TxHash extends Component {
|
|||||||
max={ maxConfirmations }
|
max={ maxConfirmations }
|
||||||
value={ value }
|
value={ value }
|
||||||
color='white'
|
color='white'
|
||||||
mode='determinate' />
|
mode='determinate'
|
||||||
|
/>
|
||||||
<div className={ styles.progressinfo }>
|
<div className={ styles.progressinfo }>
|
||||||
{ txBlock } / { confirmations } confirmations
|
<abbr title={ `block #${blockNumber.toFormat(0)}` }>{ count } { unit }</abbr>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -37,6 +37,7 @@ import Modal, { Busy as BusyStep, Completed as CompletedStep } from './Modal';
|
|||||||
import muiTheme from './Theme';
|
import muiTheme from './Theme';
|
||||||
import Page from './Page';
|
import Page from './Page';
|
||||||
import ParityBackground from './ParityBackground';
|
import ParityBackground from './ParityBackground';
|
||||||
|
import ShortenedHash from './ShortenedHash';
|
||||||
import SignerIcon from './SignerIcon';
|
import SignerIcon from './SignerIcon';
|
||||||
import Tags from './Tags';
|
import Tags from './Tags';
|
||||||
import Tooltips, { Tooltip } from './Tooltips';
|
import Tooltips, { Tooltip } from './Tooltips';
|
||||||
@ -79,6 +80,7 @@ export {
|
|||||||
Page,
|
Page,
|
||||||
ParityBackground,
|
ParityBackground,
|
||||||
RadioButtons,
|
RadioButtons,
|
||||||
|
ShortenedHash,
|
||||||
SignerIcon,
|
SignerIcon,
|
||||||
Tags,
|
Tags,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
|
19
js/src/util/is-testnet.js
Normal file
19
js/src/util/is-testnet.js
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
// 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
export default (chain) => {
|
||||||
|
return chain === 'morden' || chain === 'ropsten' || chain === 'testnet';
|
||||||
|
};
|
77
js/src/util/subscribe-to-event.js
Normal file
77
js/src/util/subscribe-to-event.js
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
// 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import EventEmitter from 'eventemitter3';
|
||||||
|
|
||||||
|
const defaults = {
|
||||||
|
from: 0, // TODO
|
||||||
|
to: 'latest',
|
||||||
|
timeout: null,
|
||||||
|
filter: () => true
|
||||||
|
};
|
||||||
|
|
||||||
|
const subscribeToEvent = (contract, name, opt = {}) => {
|
||||||
|
opt = Object.assign({}, defaults, opt);
|
||||||
|
|
||||||
|
let subscription = null;
|
||||||
|
let timeout = null;
|
||||||
|
|
||||||
|
const unsubscribe = () => {
|
||||||
|
if (subscription) {
|
||||||
|
contract.unsubscribe(subscription);
|
||||||
|
subscription = null;
|
||||||
|
}
|
||||||
|
if (timeout) {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
timeout = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const emitter = new EventEmitter();
|
||||||
|
emitter.unsubscribe = unsubscribe;
|
||||||
|
|
||||||
|
if (typeof opt.timeout === 'number') {
|
||||||
|
timeout = setTimeout(() => {
|
||||||
|
unsubscribe();
|
||||||
|
emitter.emit('timeout');
|
||||||
|
}, opt.timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
const callback = (err, logs) => {
|
||||||
|
if (err) {
|
||||||
|
return emitter.emit('error', err);
|
||||||
|
}
|
||||||
|
for (let log of logs) {
|
||||||
|
if (opt.filter(log)) {
|
||||||
|
emitter.emit('log', log);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
contract.subscribe(name, {
|
||||||
|
fromBlock: opt.from, toBlock: opt.to
|
||||||
|
}, callback)
|
||||||
|
.then((_subscription) => {
|
||||||
|
subscription = _subscription;
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
emitter.emit('error', err);
|
||||||
|
});
|
||||||
|
|
||||||
|
return emitter;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default subscribeToEvent;
|
@ -19,6 +19,7 @@ import React, { Component, PropTypes } from 'react';
|
|||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
|
||||||
import { IdentityIcon, IdentityName, MethodDecoding } from '../../../../ui';
|
import { IdentityIcon, IdentityName, MethodDecoding } from '../../../../ui';
|
||||||
|
import ShortenedHash from '../../../../ui/ShortenedHash';
|
||||||
import { txLink, addressLink } from '../../../../3rdparty/etherscan/links';
|
import { txLink, addressLink } from '../../../../3rdparty/etherscan/links';
|
||||||
|
|
||||||
import styles from '../transactions.css';
|
import styles from '../transactions.css';
|
||||||
@ -95,7 +96,7 @@ export default class Transaction extends Component {
|
|||||||
href={ txLink(transaction.hash, isTest) }
|
href={ txLink(transaction.hash, isTest) }
|
||||||
target='_blank'
|
target='_blank'
|
||||||
>
|
>
|
||||||
{ this.formatHash(transaction.hash) }
|
<ShortenedHash data={ transaction.hash } />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
@ -150,14 +151,6 @@ export default class Transaction extends Component {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
formatHash (hash) {
|
|
||||||
if (!hash || hash.length <= 16) {
|
|
||||||
return hash;
|
|
||||||
}
|
|
||||||
|
|
||||||
return `${hash.substr(2, 6)}...${hash.slice(-6)}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
formatNumber (number) {
|
formatNumber (number) {
|
||||||
return new BigNumber(number).toFormat();
|
return new BigNumber(number).toFormat();
|
||||||
}
|
}
|
||||||
|
@ -47,6 +47,7 @@ class Account extends Component {
|
|||||||
|
|
||||||
params: PropTypes.object,
|
params: PropTypes.object,
|
||||||
accounts: PropTypes.object,
|
accounts: PropTypes.object,
|
||||||
|
isTestnet: PropTypes.bool,
|
||||||
balances: PropTypes.object
|
balances: PropTypes.object
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,8 +66,9 @@ class Account extends Component {
|
|||||||
componentDidMount () {
|
componentDidMount () {
|
||||||
const { api } = this.context;
|
const { api } = this.context;
|
||||||
const { address } = this.props.params;
|
const { address } = this.props.params;
|
||||||
|
const { isTestnet } = this.props;
|
||||||
|
|
||||||
const verificationStore = new VerificationStore(api, address);
|
const verificationStore = new VerificationStore(api, address, isTestnet);
|
||||||
this.setState({ verificationStore });
|
this.setState({ verificationStore });
|
||||||
this.setVisibleAccounts();
|
this.setVisibleAccounts();
|
||||||
}
|
}
|
||||||
@ -326,11 +328,13 @@ class Account extends Component {
|
|||||||
|
|
||||||
function mapStateToProps (state) {
|
function mapStateToProps (state) {
|
||||||
const { accounts } = state.personal;
|
const { accounts } = state.personal;
|
||||||
|
const { isTest } = state.nodeStatus;
|
||||||
const { balances } = state.balances;
|
const { balances } = state.balances;
|
||||||
const { images } = state;
|
const { images } = state;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
accounts,
|
accounts,
|
||||||
|
isTestnet: isTest,
|
||||||
balances,
|
balances,
|
||||||
images
|
images
|
||||||
};
|
};
|
||||||
|
@ -19,6 +19,7 @@ import { connect } from 'react-redux';
|
|||||||
import { bindActionCreators } from 'redux';
|
import { bindActionCreators } from 'redux';
|
||||||
import { Toolbar, ToolbarGroup } from 'material-ui/Toolbar';
|
import { Toolbar, ToolbarGroup } from 'material-ui/Toolbar';
|
||||||
import { Tab as MUITab } from 'material-ui/Tabs';
|
import { Tab as MUITab } from 'material-ui/Tabs';
|
||||||
|
import { isEqual } from 'lodash';
|
||||||
|
|
||||||
import { Badge, Tooltip } from '../../../ui';
|
import { Badge, Tooltip } from '../../../ui';
|
||||||
|
|
||||||
@ -162,9 +163,13 @@ class TabBar extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
shouldComponentUpdate (nextProps, nextState) {
|
shouldComponentUpdate (nextProps, nextState) {
|
||||||
|
const prevViews = this.props.views.map((v) => v.id).sort();
|
||||||
|
const nextViews = nextProps.views.map((v) => v.id).sort();
|
||||||
|
|
||||||
return (nextProps.hash !== this.props.hash) ||
|
return (nextProps.hash !== this.props.hash) ||
|
||||||
(nextProps.pending.length !== this.props.pending.length) ||
|
(nextProps.pending.length !== this.props.pending.length) ||
|
||||||
(nextState.activeViewId !== this.state.activeViewId);
|
(nextState.activeViewId !== this.state.activeViewId) ||
|
||||||
|
(!isEqual(prevViews, nextViews));
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
@ -217,7 +222,7 @@ class TabBar extends Component {
|
|||||||
active={ active }
|
active={ active }
|
||||||
view={ view }
|
view={ view }
|
||||||
onChange={ this.onChange }
|
onChange={ this.onChange }
|
||||||
key={ index }
|
key={ view.id }
|
||||||
pendings={ pending.length }
|
pendings={ pending.length }
|
||||||
>
|
>
|
||||||
{ body }
|
{ body }
|
||||||
|
@ -19,6 +19,7 @@ import moment from 'moment';
|
|||||||
import React, { Component, PropTypes } from 'react';
|
import React, { Component, PropTypes } from 'react';
|
||||||
|
|
||||||
import { IdentityIcon, IdentityName, Input, InputAddress } from '../../../../ui';
|
import { IdentityIcon, IdentityName, Input, InputAddress } from '../../../../ui';
|
||||||
|
import ShortenedHash from '../../../../ui/ShortenedHash';
|
||||||
import { txLink } from '../../../../3rdparty/etherscan/links';
|
import { txLink } from '../../../../3rdparty/etherscan/links';
|
||||||
|
|
||||||
import styles from '../../contract.css';
|
import styles from '../../contract.css';
|
||||||
@ -71,7 +72,7 @@ export default class Event extends Component {
|
|||||||
<div className={ styles.eventType }>
|
<div className={ styles.eventType }>
|
||||||
{ event.type }({ keys })
|
{ event.type }({ keys })
|
||||||
</div>
|
</div>
|
||||||
<a href={ url } target='_blank'>{ this.formatHash(event.transactionHash) }</a>
|
<a href={ url } target='_blank'><ShortenedHash data={ event.transactionHash } /></a>
|
||||||
</td>
|
</td>
|
||||||
<td className={ styles.eventDetails }>
|
<td className={ styles.eventDetails }>
|
||||||
<div className={ styles.eventParams }>
|
<div className={ styles.eventParams }>
|
||||||
@ -82,14 +83,6 @@ export default class Event extends Component {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
formatHash (hash) {
|
|
||||||
if (!hash || hash.length <= 16) {
|
|
||||||
return hash;
|
|
||||||
}
|
|
||||||
|
|
||||||
return `${hash.substr(2, 6)}...${hash.slice(-6)}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
renderAddressName (address, withName = true) {
|
renderAddressName (address, withName = true) {
|
||||||
return (
|
return (
|
||||||
<span className={ styles.eventAddress }>
|
<span className={ styles.eventAddress }>
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
.transaction {
|
.transaction {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.transaction > * {
|
.transaction > * {
|
||||||
|
Loading…
Reference in New Issue
Block a user