Merge pull request #3336 from ethcore/sms-verification-modal
sms verification modal
This commit is contained in:
commit
25a7ff1433
@ -139,6 +139,7 @@
|
||||
"mobx-react": "^3.5.8",
|
||||
"mobx-react-devtools": "^4.2.9",
|
||||
"moment": "^2.14.1",
|
||||
"phoneformat.js": "^1.0.3",
|
||||
"qs": "^6.3.0",
|
||||
"react": "^15.2.1",
|
||||
"react-ace": "^4.0.0",
|
||||
|
@ -23,6 +23,7 @@ import githubhint from './githubhint.json';
|
||||
import owned from './owned.json';
|
||||
import registry from './registry.json';
|
||||
import signaturereg from './signaturereg.json';
|
||||
import smsverification from './sms-verification.json';
|
||||
import tokenreg from './tokenreg.json';
|
||||
import wallet from './wallet.json';
|
||||
|
||||
@ -36,6 +37,7 @@ export {
|
||||
owned,
|
||||
registry,
|
||||
signaturereg,
|
||||
smsverification,
|
||||
tokenreg,
|
||||
wallet
|
||||
};
|
||||
|
1
js/src/contracts/abi/sms-verification.json
Normal file
1
js/src/contracts/abi/sms-verification.json
Normal file
@ -0,0 +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"}]
|
@ -19,6 +19,7 @@ import Registry from './registry';
|
||||
import SignatureReg from './signaturereg';
|
||||
import TokenReg from './tokenreg';
|
||||
import GithubHint from './githubhint';
|
||||
import smsVerification from './sms-verification';
|
||||
|
||||
let instance = null;
|
||||
|
||||
@ -54,6 +55,10 @@ export default class Contracts {
|
||||
return this._githubhint;
|
||||
}
|
||||
|
||||
get smsVerification () {
|
||||
return smsVerification;
|
||||
}
|
||||
|
||||
static create (api) {
|
||||
return new Contracts(api);
|
||||
}
|
||||
|
52
js/src/contracts/sms-verification.js
Normal file
52
js/src/contracts/sms-verification.js
Normal file
@ -0,0 +1,52 @@
|
||||
// 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 { stringify } from 'querystring';
|
||||
|
||||
export const checkIfVerified = (contract, account) => {
|
||||
return contract.instance.certified.call({}, [account]);
|
||||
};
|
||||
|
||||
export const checkIfRequested = (contract, account) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
contract.subscribe('Requested', {
|
||||
fromBlock: 0, toBlock: 'pending'
|
||||
}, (err, logs) => {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
const e = logs.find((l) => {
|
||||
return l.type === 'mined' && l.params.who && l.params.who.value === account;
|
||||
});
|
||||
resolve(e ? e.transactionHash : false);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export const postToServer = (query) => {
|
||||
query = stringify(query);
|
||||
return fetch('https://sms-verification.parity.io/?' + query, {
|
||||
method: 'POST', mode: 'cors', cache: 'no-store'
|
||||
})
|
||||
.then((res) => {
|
||||
return res.json().then((data) => {
|
||||
if (res.ok) {
|
||||
return data.message;
|
||||
}
|
||||
throw new Error(data.message || 'unknown error');
|
||||
});
|
||||
});
|
||||
};
|
31
js/src/modals/SMSVerification/Done/done.css
Normal file
31
js/src/modals/SMSVerification/Done/done.css
Normal file
@ -0,0 +1,31 @@
|
||||
/* 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/>.
|
||||
*/
|
||||
.spacing {
|
||||
margin-top: 1.5em;
|
||||
}
|
||||
|
||||
.container {
|
||||
margin-top: .5em;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.message {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
margin-left: .5em;
|
||||
}
|
31
js/src/modals/SMSVerification/Done/done.js
Normal file
31
js/src/modals/SMSVerification/Done/done.js
Normal file
@ -0,0 +1,31 @@
|
||||
// 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 } from 'react';
|
||||
import SuccessIcon from 'material-ui/svg-icons/navigation/check';
|
||||
|
||||
import styles from './done.css';
|
||||
|
||||
export default class Done extends Component {
|
||||
render () {
|
||||
return (
|
||||
<div className={ styles.container }>
|
||||
<SuccessIcon />
|
||||
<p className={ styles.message }>Congratulations, your account is verified!</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
17
js/src/modals/SMSVerification/Done/index.js
Normal file
17
js/src/modals/SMSVerification/Done/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 './done';
|
49
js/src/modals/SMSVerification/GatherData/gatherData.css
Normal file
49
js/src/modals/SMSVerification/GatherData/gatherData.css
Normal file
@ -0,0 +1,49 @@
|
||||
/* 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/>.
|
||||
*/
|
||||
|
||||
.list li {
|
||||
padding: .1em 0;
|
||||
}
|
||||
|
||||
.spacing {
|
||||
margin-top: 1.5em;
|
||||
}
|
||||
|
||||
.container {
|
||||
margin-top: .5em;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.message {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
margin-left: .5em;
|
||||
}
|
||||
|
||||
.terms {
|
||||
line-height: 1.3;
|
||||
opacity: .7;
|
||||
|
||||
ul {
|
||||
padding-left: 1.5em;
|
||||
}
|
||||
|
||||
li {
|
||||
margin-top: .2em;
|
||||
margin-bottom: .2em;
|
||||
}
|
||||
}
|
151
js/src/modals/SMSVerification/GatherData/gatherData.js
Normal file
151
js/src/modals/SMSVerification/GatherData/gatherData.js
Normal file
@ -0,0 +1,151 @@
|
||||
// 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 nullable from '../../../util/nullable-proptype';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import { Checkbox } from 'material-ui';
|
||||
import InfoIcon from 'material-ui/svg-icons/action/info-outline';
|
||||
import SuccessIcon from 'material-ui/svg-icons/navigation/check';
|
||||
import ErrorIcon from 'material-ui/svg-icons/navigation/close';
|
||||
|
||||
import { fromWei } from '../../../api/util/wei';
|
||||
import { Form, Input } from '../../../ui';
|
||||
|
||||
import terms from '../terms-of-service';
|
||||
import styles from './gatherData.css';
|
||||
|
||||
export default class GatherData extends Component {
|
||||
static propTypes = {
|
||||
fee: React.PropTypes.instanceOf(BigNumber),
|
||||
isNumberValid: PropTypes.bool.isRequired,
|
||||
isVerified: nullable(PropTypes.bool.isRequired),
|
||||
hasRequested: nullable(PropTypes.bool.isRequired),
|
||||
setNumber: PropTypes.func.isRequired,
|
||||
setConsentGiven: PropTypes.func.isRequired
|
||||
}
|
||||
|
||||
render () {
|
||||
const { isNumberValid, isVerified } = this.props;
|
||||
|
||||
return (
|
||||
<Form>
|
||||
<p>The following steps will let you prove that you control both an account and a phone number.</p>
|
||||
<ol className={ styles.list }>
|
||||
<li>You send a verification request to a specific contract.</li>
|
||||
<li>Our server puts a puzzle into this contract.</li>
|
||||
<li>The code you receive via SMS is the solution to this puzzle.</li>
|
||||
</ol>
|
||||
{ this.renderFee() }
|
||||
{ this.renderCertified() }
|
||||
{ this.renderRequested() }
|
||||
<Input
|
||||
label={ 'phone number' }
|
||||
hint={ 'the SMS will be sent to this number' }
|
||||
error={ isNumberValid ? null : 'invalid number' }
|
||||
disabled={ isVerified }
|
||||
onChange={ this.numberOnChange }
|
||||
onSubmit={ this.numberOnSubmit }
|
||||
/>
|
||||
<Checkbox
|
||||
className={ styles.spacing }
|
||||
label={ 'I agree to the terms and conditions below.' }
|
||||
disabled={ isVerified }
|
||||
onCheck={ this.consentOnChange }
|
||||
/>
|
||||
<div className={ styles.terms }>{ terms }</div>
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
|
||||
renderFee () {
|
||||
const { fee } = this.props;
|
||||
|
||||
if (!fee) {
|
||||
return (<p>Fetching the fee…</p>);
|
||||
}
|
||||
return (
|
||||
<div className={ styles.container }>
|
||||
<InfoIcon />
|
||||
<p className={ styles.message }>The fee is { fromWei(fee).toFixed(3) } ETH.</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderCertified () {
|
||||
const { isVerified } = this.props;
|
||||
|
||||
if (isVerified) {
|
||||
return (
|
||||
<div className={ styles.container }>
|
||||
<ErrorIcon />
|
||||
<p className={ styles.message }>Your account is already verified.</p>
|
||||
</div>
|
||||
);
|
||||
} else if (isVerified === false) {
|
||||
return (
|
||||
<div className={ styles.container }>
|
||||
<SuccessIcon />
|
||||
<p className={ styles.message }>Your account is not verified yet.</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<p className={ styles.message }>Checking if your account is verified…</p>
|
||||
);
|
||||
}
|
||||
|
||||
renderRequested () {
|
||||
const { isVerified, hasRequested } = this.props;
|
||||
|
||||
// If the account is verified, don't show that it has requested verification.
|
||||
if (isVerified) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (hasRequested) {
|
||||
return (
|
||||
<div className={ styles.container }>
|
||||
<InfoIcon />
|
||||
<p className={ styles.message }>You already requested verification.</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (hasRequested === false) {
|
||||
return (
|
||||
<div className={ styles.container }>
|
||||
<SuccessIcon />
|
||||
<p className={ styles.message }>You did not request verification yet.</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<p className={ styles.message }>Checking if you requested verification…</p>
|
||||
);
|
||||
}
|
||||
|
||||
numberOnSubmit = (value) => {
|
||||
this.props.setNumber(value);
|
||||
}
|
||||
|
||||
numberOnChange = (_, value) => {
|
||||
this.props.setNumber(value);
|
||||
}
|
||||
|
||||
consentOnChange = (_, consentGiven) => {
|
||||
this.props.setConsentGiven(consentGiven);
|
||||
}
|
||||
}
|
17
js/src/modals/SMSVerification/GatherData/index.js
Normal file
17
js/src/modals/SMSVerification/GatherData/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 './gatherData';
|
17
js/src/modals/SMSVerification/QueryCode/index.js
Normal file
17
js/src/modals/SMSVerification/QueryCode/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 './queryCode';
|
52
js/src/modals/SMSVerification/QueryCode/queryCode.js
Normal file
52
js/src/modals/SMSVerification/QueryCode/queryCode.js
Normal file
@ -0,0 +1,52 @@
|
||||
// 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 { Form, Input } from '../../../ui';
|
||||
|
||||
export default class QueryCode extends Component {
|
||||
static propTypes = {
|
||||
number: PropTypes.string.isRequired,
|
||||
isCodeValid: PropTypes.bool.isRequired,
|
||||
setCode: PropTypes.func.isRequired
|
||||
}
|
||||
|
||||
render () {
|
||||
const { number, isCodeValid } = this.props;
|
||||
|
||||
return (
|
||||
<Form>
|
||||
<p>The verification code has been sent to { number }.</p>
|
||||
<Input
|
||||
label={ 'verification code' }
|
||||
hint={ 'Enter the code you received via SMS.' }
|
||||
error={ isCodeValid ? null : 'invalid code' }
|
||||
onChange={ this.onChange }
|
||||
onSubmit={ this.onSubmit }
|
||||
/>
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
|
||||
onChange = (_, code) => {
|
||||
this.props.setCode(code.trim());
|
||||
}
|
||||
|
||||
onSubmit = (code) => {
|
||||
this.props.setCode(code.trim());
|
||||
}
|
||||
}
|
176
js/src/modals/SMSVerification/SMSVerification.js
Normal file
176
js/src/modals/SMSVerification/SMSVerification.js
Normal file
@ -0,0 +1,176 @@
|
||||
// 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 { observer } from 'mobx-react';
|
||||
import ActionDoneAll from 'material-ui/svg-icons/action/done-all';
|
||||
import ContentClear from 'material-ui/svg-icons/content/clear';
|
||||
|
||||
import { Button, IdentityIcon, Modal } from '../../ui';
|
||||
|
||||
import {
|
||||
LOADING,
|
||||
QUERY_DATA,
|
||||
POSTING_REQUEST, POSTED_REQUEST,
|
||||
REQUESTING_SMS, REQUESTED_SMS,
|
||||
POSTING_CONFIRMATION, POSTED_CONFIRMATION,
|
||||
DONE
|
||||
} from './store';
|
||||
|
||||
import GatherData from './GatherData';
|
||||
import SendRequest from './SendRequest';
|
||||
import QueryCode from './QueryCode';
|
||||
import SendConfirmation from './SendConfirmation';
|
||||
import Done from './Done';
|
||||
|
||||
@observer
|
||||
export default class SMSVerification extends Component {
|
||||
static propTypes = {
|
||||
store: PropTypes.any.isRequired,
|
||||
account: PropTypes.string.isRequired,
|
||||
onClose: PropTypes.func.isRequired
|
||||
}
|
||||
|
||||
static phases = { // mapping (store steps -> steps)
|
||||
[LOADING]: 0,
|
||||
[QUERY_DATA]: 1,
|
||||
[POSTING_REQUEST]: 2, [POSTED_REQUEST]: 2, [REQUESTING_SMS]: 2,
|
||||
[REQUESTED_SMS]: 3,
|
||||
[POSTING_CONFIRMATION]: 4, [POSTED_CONFIRMATION]: 4,
|
||||
[DONE]: 5
|
||||
}
|
||||
|
||||
render () {
|
||||
const phase = SMSVerification.phases[this.props.store.step];
|
||||
const { error, isStepValid } = this.props.store;
|
||||
|
||||
return (
|
||||
<Modal
|
||||
actions={ this.renderDialogActions(phase, error, isStepValid) }
|
||||
title='verify your account via SMS'
|
||||
visible scroll
|
||||
current={ phase }
|
||||
steps={ ['Prepare', 'Enter Data', 'Request', 'Enter Code', 'Confirm', 'Done!'] }
|
||||
waiting={ error ? [] : [ 0, 2, 4 ] }
|
||||
>
|
||||
{ this.renderStep(phase, error) }
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
renderDialogActions (phase, error, isStepValid) {
|
||||
const { store, account, onClose } = this.props;
|
||||
|
||||
const cancel = (
|
||||
<Button
|
||||
key='cancel' label='Cancel'
|
||||
icon={ <ContentClear /> }
|
||||
onClick={ onClose }
|
||||
/>
|
||||
);
|
||||
if (error) {
|
||||
return (<div>{ cancel }</div>);
|
||||
}
|
||||
|
||||
if (phase === 5) {
|
||||
return (
|
||||
<div>
|
||||
{ cancel }
|
||||
<Button
|
||||
key='done' label='Done'
|
||||
disabled={ !isStepValid }
|
||||
icon={ <ActionDoneAll /> }
|
||||
onClick={ onClose }
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
let action = () => {};
|
||||
switch (phase) {
|
||||
case 1:
|
||||
action = store.sendRequest;
|
||||
break;
|
||||
case 2:
|
||||
action = store.queryCode;
|
||||
break;
|
||||
case 3:
|
||||
action = store.sendConfirmation;
|
||||
break;
|
||||
case 4:
|
||||
action = store.done;
|
||||
break;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{ cancel }
|
||||
<Button
|
||||
key='next' label='Next'
|
||||
disabled={ !isStepValid }
|
||||
icon={ <IdentityIcon address={ account } button /> }
|
||||
onClick={ action }
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderStep (phase, error) {
|
||||
if (error) {
|
||||
return (<p>{ error }</p>);
|
||||
}
|
||||
|
||||
const {
|
||||
step,
|
||||
fee, number, isNumberValid, isVerified, hasRequested,
|
||||
requestTx, isCodeValid, confirmationTx,
|
||||
setNumber, setConsentGiven, setCode
|
||||
} = this.props.store;
|
||||
|
||||
if (phase === 5) {
|
||||
return (<Done />);
|
||||
}
|
||||
if (phase === 4) {
|
||||
return (<SendConfirmation step={ step } tx={ confirmationTx } />);
|
||||
}
|
||||
if (phase === 3) {
|
||||
return (
|
||||
<QueryCode
|
||||
number={ number } fee={ fee } isCodeValid={ isCodeValid }
|
||||
setCode={ setCode }
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (phase === 2) {
|
||||
return (<SendRequest step={ step } tx={ requestTx } />);
|
||||
}
|
||||
if (phase === 1) {
|
||||
const { setNumber, setConsentGiven } = this.props.store;
|
||||
return (
|
||||
<GatherData
|
||||
fee={ fee } isNumberValid={ isNumberValid }
|
||||
isVerified={ isVerified } hasRequested={ hasRequested }
|
||||
setNumber={ setNumber } setConsentGiven={ setConsentGiven }
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (phase === 0) {
|
||||
return (<p>Preparing awesomeness!</p>);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
17
js/src/modals/SMSVerification/SendConfirmation/index.js
Normal file
17
js/src/modals/SMSVerification/SendConfirmation/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 './sendConfirmation';
|
@ -0,0 +1,20 @@
|
||||
/* 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/>.
|
||||
*/
|
||||
|
||||
.centered {
|
||||
text-align: center;
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
// 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 nullable from '../../../util/nullable-proptype';
|
||||
|
||||
import TxHash from '../../../ui/TxHash';
|
||||
import {
|
||||
POSTING_CONFIRMATION, POSTED_CONFIRMATION
|
||||
} from '../store';
|
||||
|
||||
import styles from './sendConfirmation.css';
|
||||
|
||||
export default class SendConfirmation extends Component {
|
||||
static propTypes = {
|
||||
step: PropTypes.any.isRequired,
|
||||
tx: nullable(PropTypes.any.isRequired)
|
||||
}
|
||||
|
||||
render () {
|
||||
const { step, tx } = this.props;
|
||||
|
||||
if (step === POSTING_CONFIRMATION) {
|
||||
return (<p>The verification code will be sent to the contract. Please authorize this using the Parity Signer.</p>);
|
||||
}
|
||||
|
||||
if (step === POSTED_CONFIRMATION) {
|
||||
return (
|
||||
<div className={ styles.centered }>
|
||||
<TxHash hash={ tx } maxConfirmations={ 2 } />
|
||||
<p>Please keep this window open.</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
17
js/src/modals/SMSVerification/SendRequest/index.js
Normal file
17
js/src/modals/SMSVerification/SendRequest/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 './sendRequest';
|
20
js/src/modals/SMSVerification/SendRequest/sendRequest.css
Normal file
20
js/src/modals/SMSVerification/SendRequest/sendRequest.css
Normal file
@ -0,0 +1,20 @@
|
||||
/* 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/>.
|
||||
*/
|
||||
|
||||
.centered {
|
||||
text-align: center;
|
||||
}
|
57
js/src/modals/SMSVerification/SendRequest/sendRequest.js
Normal file
57
js/src/modals/SMSVerification/SendRequest/sendRequest.js
Normal file
@ -0,0 +1,57 @@
|
||||
// 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 nullable from '../../../util/nullable-proptype';
|
||||
|
||||
import TxHash from '../../../ui/TxHash';
|
||||
import {
|
||||
POSTING_REQUEST, POSTED_REQUEST, REQUESTING_SMS
|
||||
} from '../store';
|
||||
|
||||
import styles from './sendRequest.css';
|
||||
|
||||
export default class SendRequest extends Component {
|
||||
static propTypes = {
|
||||
step: PropTypes.any.isRequired,
|
||||
tx: nullable(PropTypes.any.isRequired)
|
||||
}
|
||||
|
||||
render () {
|
||||
const { step, tx } = this.props;
|
||||
|
||||
switch (step) {
|
||||
case POSTING_REQUEST:
|
||||
return (<p>A verification request will be sent to the contract. Please authorize this using the Parity Signer.</p>);
|
||||
|
||||
case POSTED_REQUEST:
|
||||
return (
|
||||
<div className={ styles.centered }>
|
||||
<TxHash hash={ tx } maxConfirmations={ 1 } />
|
||||
<p>Please keep this window open.</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
case REQUESTING_SMS:
|
||||
return (
|
||||
<p>Requesting an SMS from the Parity server.</p>
|
||||
);
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
17
js/src/modals/SMSVerification/index.js
Normal file
17
js/src/modals/SMSVerification/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 './SMSVerification';
|
246
js/src/modals/SMSVerification/store.js
Normal file
246
js/src/modals/SMSVerification/store.js
Normal file
@ -0,0 +1,246 @@
|
||||
// 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 { observable, computed, autorun, action } from 'mobx';
|
||||
import phone from 'phoneformat.js';
|
||||
import { sha3 } from '../../api/util/sha3';
|
||||
|
||||
import Contracts from '../../contracts';
|
||||
|
||||
import { checkIfVerified, checkIfRequested, postToServer } from '../../contracts/sms-verification';
|
||||
import checkIfTxFailed from '../../util/check-if-tx-failed';
|
||||
import waitForConfirmations from '../../util/wait-for-block-confirmations';
|
||||
|
||||
const validCode = /^[A-Z\s]+$/i;
|
||||
|
||||
export const LOADING = 'fetching-contract';
|
||||
export const QUERY_DATA = 'query-data';
|
||||
export const POSTING_REQUEST = 'posting-request';
|
||||
export const POSTED_REQUEST = 'posted-request';
|
||||
export const REQUESTING_SMS = 'requesting-sms';
|
||||
export const REQUESTED_SMS = 'requested-sms';
|
||||
export const QUERY_CODE = 'query-code';
|
||||
export const POSTING_CONFIRMATION = 'posting-confirmation';
|
||||
export const POSTED_CONFIRMATION = 'posted-confirmation';
|
||||
export const DONE = 'done';
|
||||
|
||||
export default class VerificationStore {
|
||||
@observable step = null;
|
||||
@observable error = null;
|
||||
|
||||
@observable contract = null;
|
||||
@observable fee = null;
|
||||
@observable isVerified = null;
|
||||
@observable hasRequested = null;
|
||||
@observable consentGiven = false;
|
||||
@observable number = '';
|
||||
@observable requestTx = null;
|
||||
@observable code = '';
|
||||
@observable confirmationTx = null;
|
||||
|
||||
@computed get isCodeValid () {
|
||||
return validCode.test(this.code);
|
||||
}
|
||||
@computed get isNumberValid () {
|
||||
return phone.isValidNumber(this.number);
|
||||
}
|
||||
|
||||
@computed get isStepValid () {
|
||||
if (this.step === DONE) {
|
||||
return true;
|
||||
}
|
||||
if (this.error) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (this.step) {
|
||||
case LOADING:
|
||||
return this.contract && this.fee && this.isVerified !== null && this.hasRequested !== null;
|
||||
case QUERY_DATA:
|
||||
return this.isNumberValid && this.consentGiven;
|
||||
case REQUESTED_SMS:
|
||||
return this.requestTx;
|
||||
case QUERY_CODE:
|
||||
return this.isCodeValid;
|
||||
case POSTED_CONFIRMATION:
|
||||
return this.confirmationTx;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
constructor (api, account) {
|
||||
this.api = api;
|
||||
this.account = account;
|
||||
|
||||
this.step = LOADING;
|
||||
Contracts.create(api).registry.getContract('smsVerification')
|
||||
.then((contract) => {
|
||||
this.contract = contract;
|
||||
this.load();
|
||||
})
|
||||
.catch((err) => {
|
||||
this.error = 'Failed to fetch the contract: ' + err.message;
|
||||
});
|
||||
|
||||
autorun(() => {
|
||||
if (this.error) {
|
||||
console.error('sms verification: ' + this.error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@action load = () => {
|
||||
const { contract, account } = this;
|
||||
this.step = LOADING;
|
||||
|
||||
const fee = contract.instance.fee.call()
|
||||
.then((fee) => {
|
||||
this.fee = fee;
|
||||
})
|
||||
.catch((err) => {
|
||||
this.error = 'Failed to fetch the fee: ' + err.message;
|
||||
});
|
||||
|
||||
const isVerified = checkIfVerified(contract, account)
|
||||
.then((isVerified) => {
|
||||
this.isVerified = isVerified;
|
||||
})
|
||||
.catch((err) => {
|
||||
this.error = 'Failed to check if verified: ' + err.message;
|
||||
});
|
||||
|
||||
const hasRequested = checkIfRequested(contract, account)
|
||||
.then((txHash) => {
|
||||
this.hasRequested = !!txHash;
|
||||
if (txHash) {
|
||||
this.requestTx = txHash;
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
this.error = 'Failed to check if requested: ' + err.message;
|
||||
});
|
||||
|
||||
Promise
|
||||
.all([ fee, isVerified, hasRequested ])
|
||||
.then(() => {
|
||||
this.step = QUERY_DATA;
|
||||
});
|
||||
}
|
||||
|
||||
@action setNumber = (number) => {
|
||||
this.number = number;
|
||||
}
|
||||
|
||||
@action setConsentGiven = (consentGiven) => {
|
||||
this.consentGiven = consentGiven;
|
||||
}
|
||||
|
||||
@action setCode = (code) => {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
@action sendRequest = () => {
|
||||
const { api, account, contract, fee, number, hasRequested } = this;
|
||||
|
||||
const request = contract.functions.find((fn) => fn.name === 'request');
|
||||
const options = { from: account, value: fee.toString() };
|
||||
|
||||
let chain = Promise.resolve();
|
||||
if (!hasRequested) {
|
||||
this.step = POSTING_REQUEST;
|
||||
chain = request.estimateGas(options, [])
|
||||
.then((gas) => {
|
||||
options.gas = gas.mul(1.2).toFixed(0);
|
||||
return request.postTransaction(options, []);
|
||||
})
|
||||
.then((handle) => {
|
||||
// TODO: The "request rejected" error doesn't have any property to
|
||||
// distinguish it from other errors, so we can't give a meaningful error here.
|
||||
return api.pollMethod('parity_checkRequest', handle);
|
||||
})
|
||||
.then((txHash) => {
|
||||
this.requestTx = txHash;
|
||||
return checkIfTxFailed(api, txHash, options.gas)
|
||||
.then((hasFailed) => {
|
||||
if (hasFailed) {
|
||||
throw new Error('Transaction failed, all gas used up.');
|
||||
}
|
||||
this.step = POSTED_REQUEST;
|
||||
return waitForConfirmations(api, txHash, 1);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
chain
|
||||
.then(() => {
|
||||
this.step = REQUESTING_SMS;
|
||||
return postToServer({ number, address: account });
|
||||
})
|
||||
.then(() => {
|
||||
this.step = REQUESTED_SMS;
|
||||
})
|
||||
.catch((err) => {
|
||||
this.error = 'Failed to request a confirmation SMS: ' + err.message;
|
||||
});
|
||||
}
|
||||
|
||||
@action queryCode = () => {
|
||||
this.step = QUERY_CODE;
|
||||
}
|
||||
|
||||
@action sendConfirmation = () => {
|
||||
const { api, account, contract, code } = this;
|
||||
const token = sha3(code);
|
||||
|
||||
const confirm = contract.functions.find((fn) => fn.name === 'confirm');
|
||||
const options = { from: account };
|
||||
const values = [ token ];
|
||||
|
||||
this.step = POSTING_CONFIRMATION;
|
||||
confirm.estimateGas(options, values)
|
||||
.then((gas) => {
|
||||
options.gas = gas.mul(1.2).toFixed(0);
|
||||
return confirm.postTransaction(options, values);
|
||||
})
|
||||
.then((handle) => {
|
||||
// TODO: The "request rejected" error doesn't have any property to
|
||||
// distinguish it from other errors, so we can't give a meaningful error here.
|
||||
return api.pollMethod('parity_checkRequest', handle);
|
||||
})
|
||||
.then((txHash) => {
|
||||
this.confirmationTx = txHash;
|
||||
return checkIfTxFailed(api, txHash, options.gas)
|
||||
.then((hasFailed) => {
|
||||
if (hasFailed) {
|
||||
throw new Error('Transaction failed, all gas used up.');
|
||||
}
|
||||
this.step = POSTED_CONFIRMATION;
|
||||
return waitForConfirmations(api, txHash, 1);
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
this.step = DONE;
|
||||
})
|
||||
.catch((err) => {
|
||||
this.error = 'Failed to send the verification code: ' + err.message;
|
||||
});
|
||||
}
|
||||
|
||||
@action done = () => {
|
||||
this.step = DONE;
|
||||
}
|
||||
}
|
27
js/src/modals/SMSVerification/terms-of-service.js
Normal file
27
js/src/modals/SMSVerification/terms-of-service.js
Normal file
@ -0,0 +1,27 @@
|
||||
// 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 from 'react';
|
||||
|
||||
export default (
|
||||
<ul>
|
||||
<li>This privacy notice relates to your use of the Parity SMS verification service. We take your privacy seriously and deal in an honest, direct and transparent way when it comes to your data.</li>
|
||||
<li>We collect your phone number when you use this service. This is temporarily kept in memory, and then encrypted and stored in our EU servers. We only retain the cryptographic hash of the number to prevent duplicated accounts. You consent to this use.</li>
|
||||
<li>You pay a fee for the cost of this service using the account you want to verify.</li>
|
||||
<li>Your phone number is transmitted to a third party US SMS verification service Twilio for the sole purpose of the SMS verification. You consent to this use. Twilio’s privacy policy is here: <a href={ 'https://www.twilio.com/legal/privacy/developer' }>https://www.twilio.com/legal/privacy/developer</a>.</li>
|
||||
<li><i>Parity Technology Limited</i> is registered in England and Wales under company number <code>09760015</code> and complies with the Data Protection Act 1998 (UK). You may contact us via email at <a href={ 'mailto:admin@parity.io' }>admin@parity.io</a>. Our general privacy policy can be found here: <a href={ 'https://ethcore.io/legal.html' }>https://ethcore.io/legal.html</a>.</li>
|
||||
</ul>
|
||||
);
|
@ -22,6 +22,7 @@ import EditMeta from './EditMeta';
|
||||
import ExecuteContract from './ExecuteContract';
|
||||
import FirstRun from './FirstRun';
|
||||
import Shapeshift from './Shapeshift';
|
||||
import SMSVerification from './SMSVerification';
|
||||
import Transfer from './Transfer';
|
||||
import PasswordManager from './PasswordManager';
|
||||
import SaveContract from './SaveContract';
|
||||
@ -36,6 +37,7 @@ export {
|
||||
ExecuteContract,
|
||||
FirstRun,
|
||||
Shapeshift,
|
||||
SMSVerification,
|
||||
Transfer,
|
||||
PasswordManager,
|
||||
LoadContract,
|
||||
|
@ -31,9 +31,14 @@ class TxHash extends Component {
|
||||
static propTypes = {
|
||||
hash: PropTypes.string.isRequired,
|
||||
isTest: PropTypes.bool,
|
||||
summary: PropTypes.bool
|
||||
summary: PropTypes.bool,
|
||||
maxConfirmations: PropTypes.number
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
maxConfirmations: 10
|
||||
};
|
||||
|
||||
state = {
|
||||
blockNumber: new BigNumber(0),
|
||||
transaction: null,
|
||||
@ -79,6 +84,7 @@ class TxHash extends Component {
|
||||
}
|
||||
|
||||
renderConfirmations () {
|
||||
const { maxConfirmations } = this.props;
|
||||
const { blockNumber, transaction } = this.state;
|
||||
|
||||
let txBlock = 'Pending';
|
||||
@ -89,14 +95,16 @@ class TxHash extends Component {
|
||||
const num = blockNumber.minus(transaction.blockNumber).plus(1);
|
||||
txBlock = `#${transaction.blockNumber.toFormat(0)}`;
|
||||
confirmations = num.toFormat(0);
|
||||
value = num.gt(10) ? 10 : num.toNumber();
|
||||
value = num.gt(maxConfirmations) ? maxConfirmations : num.toNumber();
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={ styles.confirm }>
|
||||
<LinearProgress
|
||||
className={ styles.progressbar }
|
||||
min={ 0 } max={ 10 } value={ value }
|
||||
min={ 0 }
|
||||
max={ maxConfirmations }
|
||||
value={ value }
|
||||
color='white'
|
||||
mode='determinate' />
|
||||
<div className={ styles.progressinfo }>
|
||||
|
28
js/src/util/check-if-tx-failed.js
Normal file
28
js/src/util/check-if-tx-failed.js
Normal file
@ -0,0 +1,28 @@
|
||||
// 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/>.
|
||||
|
||||
const checkIfTxFailed = (api, tx, gasSent) => {
|
||||
return api.pollMethod('eth_getTransactionReceipt', tx)
|
||||
.then((receipt) => {
|
||||
// TODO: Right now, there's no way to tell wether the EVM code crashed.
|
||||
// Because you usually send a bit more gas than estimated (to make sure
|
||||
// it gets mined quickly), we transaction probably failed if all the gas
|
||||
// has been used up.
|
||||
return receipt.gasUsed.eq(gasSent);
|
||||
});
|
||||
};
|
||||
|
||||
export default checkIfTxFailed;
|
21
js/src/util/nullable-proptype.js
Normal file
21
js/src/util/nullable-proptype.js
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/>.
|
||||
|
||||
import { PropTypes } from 'react';
|
||||
|
||||
export default function (type) {
|
||||
return PropTypes.oneOfType([ PropTypes.oneOf([ null ]), type ]);
|
||||
}
|
44
js/src/util/wait-for-block-confirmations.js
Normal file
44
js/src/util/wait-for-block-confirmations.js
Normal file
@ -0,0 +1,44 @@
|
||||
// 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/>.
|
||||
|
||||
const isValidReceipt = (receipt) => {
|
||||
return receipt && receipt.blockNumber && receipt.blockNumber.gt(0);
|
||||
};
|
||||
|
||||
const waitForConfirmations = (api, tx, confirmations) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
api.pollMethod('eth_getTransactionReceipt', tx, isValidReceipt)
|
||||
.then((receipt) => {
|
||||
let subscription;
|
||||
api.subscribe('eth_blockNumber', (err, block) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else if (block.minus(confirmations - 1).gte(receipt.blockNumber)) {
|
||||
if (subscription) {
|
||||
api.unsubscribe(subscription);
|
||||
}
|
||||
resolve();
|
||||
}
|
||||
})
|
||||
.then((_subscription) => {
|
||||
subscription = _subscription;
|
||||
})
|
||||
.catch(reject);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export default waitForConfirmations;
|
@ -20,8 +20,9 @@ import { bindActionCreators } from 'redux';
|
||||
import ContentCreate from 'material-ui/svg-icons/content/create';
|
||||
import ContentSend from 'material-ui/svg-icons/content/send';
|
||||
import LockIcon from 'material-ui/svg-icons/action/lock';
|
||||
import VerifyIcon from 'material-ui/svg-icons/action/verified-user';
|
||||
|
||||
import { EditMeta, Shapeshift, Transfer, PasswordManager } from '../../modals';
|
||||
import { EditMeta, Shapeshift, SMSVerification, Transfer, PasswordManager } from '../../modals';
|
||||
import { Actionbar, Button, Page } from '../../ui';
|
||||
|
||||
import shapeshiftBtn from '../../../assets/images/shapeshift-btn.png';
|
||||
@ -29,9 +30,15 @@ import shapeshiftBtn from '../../../assets/images/shapeshift-btn.png';
|
||||
import Header from './Header';
|
||||
import Transactions from './Transactions';
|
||||
|
||||
import VerificationStore from '../../modals/SMSVerification/store';
|
||||
|
||||
import styles from './account.css';
|
||||
|
||||
class Account extends Component {
|
||||
static contextTypes = {
|
||||
api: PropTypes.object.isRequired
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
params: PropTypes.object,
|
||||
accounts: PropTypes.object,
|
||||
@ -45,10 +52,20 @@ class Account extends Component {
|
||||
state = {
|
||||
showEditDialog: false,
|
||||
showFundDialog: false,
|
||||
showVerificationDialog: false,
|
||||
verificationStore: null,
|
||||
showTransferDialog: false,
|
||||
showPasswordDialog: false
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
const { api } = this.context;
|
||||
const { address } = this.props.params;
|
||||
|
||||
const store = new VerificationStore(api, address);
|
||||
this.setState({ verificationStore: store });
|
||||
}
|
||||
|
||||
render () {
|
||||
const { accounts, balances, isTest } = this.props;
|
||||
const { address } = this.props.params;
|
||||
@ -64,6 +81,7 @@ class Account extends Component {
|
||||
<div className={ styles.account }>
|
||||
{ this.renderEditDialog(account) }
|
||||
{ this.renderFundDialog() }
|
||||
{ this.renderVerificationDialog() }
|
||||
{ this.renderTransferDialog() }
|
||||
{ this.renderPasswordDialog() }
|
||||
{ this.renderActionbar() }
|
||||
@ -99,6 +117,11 @@ class Account extends Component {
|
||||
icon={ <img src={ shapeshiftBtn } className={ styles.btnicon } /> }
|
||||
label='shapeshift'
|
||||
onClick={ this.onShapeshiftAccountClick } />,
|
||||
<Button
|
||||
key='sms-verification'
|
||||
icon={ <VerifyIcon /> }
|
||||
label='Verify'
|
||||
onClick={ this.openVerification } />,
|
||||
<Button
|
||||
key='editmeta'
|
||||
icon={ <ContentCreate /> }
|
||||
@ -149,6 +172,22 @@ class Account extends Component {
|
||||
);
|
||||
}
|
||||
|
||||
renderVerificationDialog () {
|
||||
if (!this.state.showVerificationDialog) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const store = this.state.verificationStore;
|
||||
const { address } = this.props.params;
|
||||
|
||||
return (
|
||||
<SMSVerification
|
||||
store={ store } account={ address }
|
||||
onClose={ this.onVerificationClose }
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
renderTransferDialog () {
|
||||
const { showTransferDialog } = this.state;
|
||||
|
||||
@ -205,6 +244,14 @@ class Account extends Component {
|
||||
this.onShapeshiftAccountClick();
|
||||
}
|
||||
|
||||
openVerification = () => {
|
||||
this.setState({ showVerificationDialog: true });
|
||||
}
|
||||
|
||||
onVerificationClose = () => {
|
||||
this.setState({ showVerificationDialog: false });
|
||||
}
|
||||
|
||||
onTransferClick = () => {
|
||||
this.setState({
|
||||
showTransferDialog: !this.state.showTransferDialog
|
||||
|
@ -15,6 +15,7 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import nullable from '../../../../util/nullable-proptype';
|
||||
|
||||
import Account from '../Account';
|
||||
import TransactionPendingForm from '../TransactionPendingForm';
|
||||
@ -22,8 +23,6 @@ import TxHashLink from '../TxHashLink';
|
||||
|
||||
import styles from './SignRequest.css';
|
||||
|
||||
const nullable = (type) => React.PropTypes.oneOfType([ React.PropTypes.oneOf([ null ]), type ]);
|
||||
|
||||
export default class SignRequest extends Component {
|
||||
static contextTypes = {
|
||||
api: PropTypes.object
|
||||
|
@ -15,6 +15,7 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import nullable from '../../../../util/nullable-proptype';
|
||||
|
||||
import CircularProgress from 'material-ui/CircularProgress';
|
||||
|
||||
@ -29,8 +30,6 @@ import styles from './TransactionFinished.css';
|
||||
import * as tUtil from '../util/transaction';
|
||||
import { capitalize } from '../util/util';
|
||||
|
||||
const nullable = (type) => React.PropTypes.oneOfType([ React.PropTypes.oneOf([ null ]), type ]);
|
||||
|
||||
export default class TransactionFinished extends Component {
|
||||
static contextTypes = {
|
||||
api: PropTypes.object.isRequired
|
||||
|
Loading…
Reference in New Issue
Block a user