openethereum/js/src/views/Account/Verification/verification.js

452 lines
11 KiB
JavaScript
Raw Normal View History

// Copyright 2015-2017 Parity Technologies (UK) Ltd.
2016-11-03 18:01:24 +01:00
// 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 { FormattedMessage } from 'react-intl';
import { connect } from 'react-redux';
2016-11-15 16:21:53 +01:00
import { observer } from 'mobx-react';
import { observable } from 'mobx';
2016-11-03 18:01:24 +01:00
import { Button, IdentityIcon, Portal } from '~/ui';
2016-12-06 17:16:54 +01:00
import RadioButtons from '~/ui/Form/RadioButtons';
import { CancelIcon, DoneIcon } from '~/ui/Icons';
import SMSVerificationStore from './sms-store';
import EmailVerificationStore from './email-store';
2016-12-06 17:16:54 +01:00
2016-12-09 12:37:36 +01:00
import styles from './verification.css';
const METHODS = {
2016-12-09 12:37:36 +01:00
sms: {
label: (
<FormattedMessage
id='verification.types.sms.label'
defaultMessage='SMS Verification'
/>
),
key: 0,
value: 'sms',
description: (
<p className={ styles.noSpacing }>
<FormattedMessage
id='verification.types.sms.description'
defaultMessage='It will be stored on the blockchain that you control a phone number (not <em>which</em>).'
/>
</p>
)
2016-12-09 12:37:36 +01:00
},
email: {
label: (
<FormattedMessage
id='verification.types.email.label'
defaultMessage='E-mail Verification'
/>
),
key: 1,
value: 'email',
description: (
<p className={ styles.noSpacing }>
<FormattedMessage
id='verification.types.email.description'
defaultMessage='The hash of the e-mail address you prove control over will be stored on the blockchain.'
/>
</p>
)
2016-12-09 12:37:36 +01:00
}
2016-12-06 17:16:54 +01:00
};
2016-11-03 18:01:24 +01:00
const STEPS = [
<FormattedMessage
id='verification.steps.method'
defaultMessage='Method'
/>,
<FormattedMessage
id='verification.steps.data'
defaultMessage='Enter Data'
/>,
<FormattedMessage
id='verification.steps.request'
defaultMessage='Request'
/>,
<FormattedMessage
id='verification.steps.code'
defaultMessage='Enter Code'
/>,
<FormattedMessage
id='verification.steps.confirm'
defaultMessage='Confirm'
/>,
<FormattedMessage
id='verification.steps.completed'
defaultMessage='Completed'
/>
];
import {
2016-11-17 14:21:44 +01:00
LOADING,
QUERY_DATA,
2016-11-15 16:21:53 +01:00
POSTING_REQUEST, POSTED_REQUEST,
2016-12-06 16:09:02 +01:00
REQUESTING_CODE, QUERY_CODE,
2016-11-15 16:21:53 +01:00
POSTING_CONFIRMATION, POSTED_CONFIRMATION,
DONE
} from './store';
2016-11-03 18:01:24 +01:00
2016-11-07 10:23:42 +01:00
import GatherData from './GatherData';
2016-11-07 15:13:22 +01:00
import SendRequest from './SendRequest';
2016-11-09 16:54:38 +01:00
import QueryCode from './QueryCode';
2016-11-09 17:34:21 +01:00
import SendConfirmation from './SendConfirmation';
import Done from './Done';
2016-11-03 18:01:24 +01:00
2016-11-15 16:21:53 +01:00
@observer
class Verification extends Component {
static contextTypes = {
api: PropTypes.object.isRequired
}
2016-11-03 18:01:24 +01:00
static propTypes = {
account: PropTypes.string.isRequired,
isTest: PropTypes.bool.isRequired,
2016-11-03 18:01:24 +01:00
onClose: PropTypes.func.isRequired
}
static phases = { // mapping (store steps -> steps)
[LOADING]: 1, [QUERY_DATA]: 1,
[POSTING_REQUEST]: 2, [POSTED_REQUEST]: 2, [REQUESTING_CODE]: 2,
[QUERY_CODE]: 3,
[POSTING_CONFIRMATION]: 4, [POSTED_CONFIRMATION]: 4,
[DONE]: 5
2016-11-03 18:01:24 +01:00
}
2016-12-06 17:16:54 +01:00
state = {
method: 'sms'
};
@observable store = null;
2016-11-03 18:01:24 +01:00
render () {
const { onClose } = this.props;
const store = this.store;
let phase = 0;
let error = false;
let isStepValid = true;
2016-12-06 17:16:54 +01:00
if (store) {
phase = Verification.phases[store.step];
error = store.error;
isStepValid = store.isStepValid;
}
2016-11-03 18:13:45 +01:00
2016-11-03 18:01:24 +01:00
return (
<Portal
activeStep={ phase }
busySteps={
error
? []
: [ 2, 4 ]
}
buttons={ this.renderDialogActions(phase, error, isStepValid) }
onClose={ onClose }
open
steps={ STEPS }
title={
<FormattedMessage
id='verification.title'
defaultMessage='verify your account'
/>
}
2016-11-03 18:01:24 +01:00
>
{ this.renderStep(phase, error) }
</Portal>
2016-11-03 18:01:24 +01:00
);
}
renderDialogActions (phase, error, isStepValid) {
const { account, onClose } = this.props;
const store = this.store;
2016-11-03 18:13:45 +01:00
const cancelButton = (
2016-11-03 18:13:45 +01:00
<Button
icon={ <CancelIcon /> }
key='cancel'
label={
<FormattedMessage
id='verification.button.cancel'
defaultMessage='Cancel'
/>
}
2016-11-03 18:13:45 +01:00
onClick={ onClose }
/>
);
if (error) {
return cancelButton;
}
2016-11-03 18:13:45 +01:00
if (phase === 5) {
return [
cancelButton,
<Button
disabled={ !isStepValid }
icon={ <DoneIcon /> }
key='done'
label={
<FormattedMessage
id='verification.button.done'
defaultMessage='Done'
/>
}
onClick={ onClose }
/>
];
2016-11-03 18:13:45 +01:00
}
2016-11-03 18:01:24 +01:00
2016-11-17 14:21:44 +01:00
let action = () => {};
switch (phase) {
2016-12-06 17:16:54 +01:00
case 0:
action = () => {
const { method } = this.state;
this.onSelectMethod(method);
2016-12-06 17:16:54 +01:00
};
break;
case 1:
2016-12-06 17:16:54 +01:00
action = store.sendRequest;
break;
case 2:
2016-12-06 17:16:54 +01:00
action = store.queryCode;
break;
case 3:
2016-12-06 17:16:54 +01:00
action = store.sendConfirmation;
break;
case 4:
action = store.done;
break;
2016-11-15 16:21:53 +01:00
}
return [
cancelButton,
<Button
disabled={ !isStepValid }
icon={
<IdentityIcon
address={ account }
button
/>
}
key='next'
label={
<FormattedMessage
id='verification.button.next'
defaultMessage='Next'
/>
}
onClick={ action }
/>
];
2016-11-03 18:01:24 +01:00
}
2016-11-03 18:13:45 +01:00
renderStep (phase, error) {
if (error) {
return (
<p>{ error }</p>
);
}
2016-11-15 16:21:53 +01:00
2016-12-07 19:21:29 +01:00
const { method } = this.state;
2016-12-06 17:16:54 +01:00
if (phase === 0) {
const values = Object.values(METHODS);
2016-12-06 17:16:54 +01:00
const value = values.findIndex((v) => v.value === method);
2016-12-06 17:16:54 +01:00
return (
<RadioButtons
onChange={ this.selectMethod }
2016-12-06 17:16:54 +01:00
value={ value < 0 ? 0 : value }
values={ values }
/>
);
}
2016-11-15 16:21:53 +01:00
const {
step,
isServerRunning, isAbleToRequest, fee, accountIsVerified, accountHasRequested,
2016-11-15 16:21:53 +01:00
requestTx, isCodeValid, confirmationTx,
setCode
} = this.store;
2016-11-07 15:13:22 +01:00
switch (phase) {
2016-12-06 17:16:54 +01:00
case 1:
if (step === LOADING) {
return (
<p>
<FormattedMessage
id='verification.loading'
defaultMessage='Loading verification data.'
/>
</p>
);
}
const { setConsentGiven } = this.store;
2016-12-08 11:55:51 +01:00
const fields = [];
2016-12-06 17:16:54 +01:00
if (method === 'sms') {
fields.push({
key: 'number',
label: (
<FormattedMessage
id='verification.gatherData.phoneNumber.label'
defaultMessage='phone number in international format'
/>
),
hint: (
<FormattedMessage
id='verification.gatherData.phoneNumber.hint'
defaultMessage='the SMS will be sent to this number'
/>
),
error: this.store.isNumberValid
? null
: (
<FormattedMessage
id='verification.gatherDate.phoneNumber.error'
defaultMessage='invalid number'
/>
),
onChange: this.store.setNumber
2016-12-06 17:16:54 +01:00
});
} else if (method === 'email') {
fields.push({
key: 'email',
label: (
<FormattedMessage
id='verification.gatherData.email.label'
defaultMessage='e-mail address'
/>
),
hint: (
<FormattedMessage
id='verification.gatherData.email.hint'
defaultMessage='the code will be sent to this address'
/>
),
error: this.store.isEmailValid
? null
: (
<FormattedMessage
id='verification.gatherDate.email.error'
defaultMessage='invalid e-mail'
/>
),
onChange: this.store.setEmail
2016-12-06 17:16:54 +01:00
});
}
return (
<GatherData
fee={ fee }
accountHasRequested={ accountHasRequested }
isServerRunning={ isServerRunning }
isAbleToRequest={ isAbleToRequest }
accountIsVerified={ accountIsVerified }
method={ method }
fields={ fields }
2016-12-07 11:53:48 +01:00
setConsentGiven={ setConsentGiven }
/>
);
case 2:
return (
<SendRequest
step={ step }
tx={ requestTx }
/>
);
case 3:
let receiver;
let hint;
2016-12-07 19:21:29 +01:00
if (method === 'sms') {
receiver = this.store.number;
hint = (
<FormattedMessage
id='verification.sms.enterCode'
defaultMessage='Enter the code you received via SMS.'
/>
);
2016-12-07 19:21:29 +01:00
} else if (method === 'email') {
receiver = this.store.email;
hint = (
<FormattedMessage
id='verification.email.enterCode'
defaultMessage='Enter the code you received via e-mail.'
/>
);
2016-12-07 19:21:29 +01:00
}
return (
<QueryCode
2016-12-07 19:21:29 +01:00
hint={ hint }
isCodeValid={ isCodeValid }
receiver={ receiver }
setCode={ setCode }
/>
);
case 4:
return (
<SendConfirmation
step={ step }
tx={ confirmationTx }
/>
);
case 5:
return (
<Done />
);
default:
return null;
}
}
2016-12-06 17:16:54 +01:00
onSelectMethod = (name) => {
const { api } = this.context;
const { account, isTest } = this.props;
if (name === 'sms') {
this.store = new SMSVerificationStore(api, account, isTest);
} else if (name === 'email') {
this.store = new EmailVerificationStore(api, account, isTest);
}
}
2016-12-06 17:16:54 +01:00
selectMethod = (choice, i) => {
this.setState({ method: choice.value });
}
2016-11-03 18:01:24 +01:00
}
const mapStateToProps = (state) => ({
isTest: state.nodeStatus.isTest
});
export default connect(
mapStateToProps,
null
)(Verification);