2016-05-02 23:02:03 +05:30
|
|
|
import React, { Component, PropTypes } from 'react';
|
|
|
|
|
|
|
|
import { FormattedMessage as Message } from 'react-intl';
|
|
|
|
import { Link } from 'react-router';
|
|
|
|
import classNames from 'classnames';
|
|
|
|
import Helmet from 'react-helmet';
|
|
|
|
import { Motion, spring } from 'react-motion';
|
|
|
|
|
|
|
|
import { Input, Button, Form, FormModel } from 'components/ui/form';
|
|
|
|
import styles from 'components/profile/profileForm.scss';
|
|
|
|
import helpLinks from 'components/auth/helpLinks.scss';
|
|
|
|
import MeasureHeight from 'components/MeasureHeight';
|
|
|
|
|
|
|
|
import changeEmail from './changeEmail.scss';
|
2016-05-22 13:23:40 +05:30
|
|
|
import messages from './ChangeEmail.intl.json';
|
2016-05-02 23:02:03 +05:30
|
|
|
|
|
|
|
const STEPS_TOTAL = 3;
|
|
|
|
|
|
|
|
export default class ChangeEmail extends Component {
|
|
|
|
static displayName = 'ChangeEmail';
|
|
|
|
|
|
|
|
static propTypes = {
|
2016-05-20 10:44:14 +05:30
|
|
|
onChangeStep: PropTypes.func,
|
2016-05-02 23:02:03 +05:30
|
|
|
email: PropTypes.string.isRequired,
|
2016-05-22 13:23:40 +05:30
|
|
|
stepForms: PropTypes.arrayOf((propValue, key, componentName, location, propFullName) => {
|
|
|
|
if (propValue.length !== 3) {
|
|
|
|
return new Error(`\`${propFullName}\` must be an array of 3 FormModel instances. Validation failed.`);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(propValue[key] instanceof FormModel)) {
|
|
|
|
return new Error(
|
|
|
|
`Invalid prop \`${propFullName}\` supplied to \
|
|
|
|
\`${componentName}\`. Validation failed.`
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}),
|
2016-05-20 10:44:14 +05:30
|
|
|
onSubmit: PropTypes.func.isRequired,
|
|
|
|
step: PropTypes.oneOf([0, 1, 2]),
|
|
|
|
code: PropTypes.string
|
2016-05-02 23:02:03 +05:30
|
|
|
};
|
|
|
|
|
|
|
|
static get defaultProps() {
|
|
|
|
return {
|
2016-05-22 13:23:40 +05:30
|
|
|
stepForms: [
|
|
|
|
new FormModel(),
|
|
|
|
new FormModel(),
|
|
|
|
new FormModel()
|
|
|
|
],
|
2016-05-20 10:44:14 +05:30
|
|
|
onChangeStep() {},
|
|
|
|
step: 0
|
2016-05-02 23:02:03 +05:30
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
state = {
|
2016-05-20 10:44:14 +05:30
|
|
|
activeStep: this.props.step,
|
|
|
|
code: this.props.code || ''
|
2016-05-02 23:02:03 +05:30
|
|
|
};
|
|
|
|
|
2016-05-20 10:44:14 +05:30
|
|
|
componentWillReceiveProps(nextProps) {
|
|
|
|
this.setState({
|
2016-05-22 13:23:40 +05:30
|
|
|
activeStep: typeof nextProps.step === 'number' ? nextProps.step : this.state.activeStep,
|
2016-05-20 10:44:14 +05:30
|
|
|
code: nextProps.code || ''
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2016-05-02 23:02:03 +05:30
|
|
|
render() {
|
|
|
|
const {activeStep} = this.state;
|
2016-05-22 13:23:40 +05:30
|
|
|
const form = this.props.stepForms[activeStep];
|
2016-05-02 23:02:03 +05:30
|
|
|
|
|
|
|
return (
|
2016-05-22 13:23:40 +05:30
|
|
|
<Form form={form}
|
|
|
|
onSubmit={this.onFormSubmit}
|
|
|
|
onInvalid={() => this.forceUpdate()}
|
2016-05-02 23:02:03 +05:30
|
|
|
>
|
|
|
|
<div className={styles.contentWithBackButton}>
|
|
|
|
<Link className={styles.backButton} to="/" />
|
|
|
|
|
|
|
|
<div className={styles.form}>
|
|
|
|
<div className={styles.formBody}>
|
|
|
|
<Message {...messages.changeEmailTitle}>
|
|
|
|
{(pageTitle) => (
|
|
|
|
<h3 className={styles.violetTitle}>
|
|
|
|
<Helmet title={pageTitle} />
|
|
|
|
{pageTitle}
|
|
|
|
</h3>
|
|
|
|
)}
|
|
|
|
</Message>
|
|
|
|
|
|
|
|
<div className={styles.formRow}>
|
|
|
|
<p className={styles.description}>
|
|
|
|
<Message {...messages.changeEmailDescription} />
|
|
|
|
</p>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div className={changeEmail.steps}>
|
|
|
|
{(new Array(STEPS_TOTAL)).fill(0).map((_, step) => (
|
|
|
|
<div className={classNames(changeEmail.step, {
|
|
|
|
[changeEmail.activeStep]: step <= activeStep
|
|
|
|
})} key={step} />
|
|
|
|
))}
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div className={styles.form}>
|
|
|
|
{this.renderStepForms()}
|
|
|
|
|
|
|
|
<Button
|
|
|
|
color="violet"
|
|
|
|
block
|
|
|
|
label={this.isLastStep() ? messages.changeEmailButton : messages.sendEmailButton}
|
2016-05-22 13:23:40 +05:30
|
|
|
onClick={this.onSubmit}
|
2016-05-02 23:02:03 +05:30
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div className={helpLinks.helpLinks}>
|
|
|
|
{this.isLastStep() ? null : (
|
|
|
|
<a href="#" onClick={this.onSwitchStep}>
|
|
|
|
<Message {...messages.alreadyReceivedCode} />
|
|
|
|
</a>
|
|
|
|
)}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</Form>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
renderStepForms() {
|
2016-05-22 13:23:40 +05:30
|
|
|
const {email} = this.props;
|
2016-05-20 10:44:14 +05:30
|
|
|
const {activeStep, code} = this.state;
|
2016-05-22 13:23:40 +05:30
|
|
|
const isCodeSpecified = !!this.props.code;
|
2016-05-02 23:02:03 +05:30
|
|
|
|
|
|
|
const activeStepHeight = this.state[`step${activeStep}Height`] || 0;
|
|
|
|
|
|
|
|
// a hack to disable height animation on first render
|
|
|
|
const isHeightMeasured = this.isHeightMeasured;
|
2016-05-02 23:22:37 +05:30
|
|
|
this.isHeightMeasured = isHeightMeasured || activeStepHeight > 0;
|
2016-05-02 23:02:03 +05:30
|
|
|
|
|
|
|
return (
|
|
|
|
<Motion
|
|
|
|
style={{
|
|
|
|
transform: spring(activeStep * 100, {stiffness: 500, damping: 50, precision: 0.5}),
|
|
|
|
height: isHeightMeasured ? spring(activeStepHeight, {stiffness: 500, damping: 20, precision: 0.5}) : activeStepHeight
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
{(interpolatingStyle) => (
|
|
|
|
<div style={{
|
|
|
|
overflow: 'hidden',
|
|
|
|
height: `${interpolatingStyle.height}px`
|
|
|
|
}}>
|
|
|
|
<div className={changeEmail.stepForms} style={{
|
|
|
|
WebkitTransform: `translateX(-${interpolatingStyle.transform}%)`,
|
|
|
|
transform: `translateX(-${interpolatingStyle.transform}%)`
|
|
|
|
}}>
|
2016-05-22 13:23:40 +05:30
|
|
|
{(new Array(STEPS_TOTAL)).fill(0).map((_, step) => {
|
|
|
|
const form = this.props.stepForms[step];
|
|
|
|
|
|
|
|
return (
|
|
|
|
<MeasureHeight
|
|
|
|
className={changeEmail.stepForm}
|
|
|
|
onMeasure={this.onStepMeasure(step)}
|
|
|
|
state={`${step}.${form.hasErrors()}`}
|
|
|
|
key={step}
|
|
|
|
>
|
|
|
|
{this[`renderStep${step}`]({
|
|
|
|
email,
|
|
|
|
code,
|
|
|
|
form,
|
|
|
|
isActiveStep: step === activeStep
|
|
|
|
})}
|
|
|
|
</MeasureHeight>
|
|
|
|
);
|
|
|
|
})}
|
2016-05-02 23:02:03 +05:30
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
)}
|
|
|
|
</Motion>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2016-05-22 13:23:40 +05:30
|
|
|
renderStep0({email}) {
|
|
|
|
return (
|
|
|
|
<div className={styles.formBody}>
|
|
|
|
<div className={styles.formRow}>
|
|
|
|
<p className={styles.description}>
|
|
|
|
<Message {...messages.currentAccountEmail} />
|
|
|
|
</p>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div className={styles.formRow}>
|
|
|
|
<h2 className={changeEmail.currentAccountEmail}>
|
|
|
|
{email}
|
|
|
|
</h2>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div className={styles.formRow}>
|
|
|
|
<p className={styles.description}>
|
|
|
|
<Message {...messages.pressButtonToStart} />
|
|
|
|
</p>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
renderStep1({email, form, code, isCodeSpecified, isActiveStep}) {
|
|
|
|
return (
|
|
|
|
<div className={styles.formBody}>
|
|
|
|
<div className={styles.formRow}>
|
|
|
|
<p className={styles.description}>
|
|
|
|
<Message {...messages.enterInitializationCode} values={{
|
|
|
|
email: (<b>{email}</b>)
|
|
|
|
}} />
|
|
|
|
</p>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div className={styles.formRow}>
|
|
|
|
<Input {...form.bindField('key')}
|
|
|
|
required={isActiveStep}
|
|
|
|
disabled={isCodeSpecified}
|
|
|
|
value={code}
|
|
|
|
onChange={this.onCodeInput}
|
|
|
|
autoComplete="off"
|
|
|
|
skin="light"
|
|
|
|
color="violet"
|
|
|
|
placeholder={messages.codePlaceholder}
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div className={styles.formRow}>
|
|
|
|
<p className={styles.description}>
|
|
|
|
<Message {...messages.enterNewEmail} />
|
|
|
|
</p>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div className={styles.formRow}>
|
|
|
|
<Input {...form.bindField('email')}
|
|
|
|
required={isActiveStep}
|
|
|
|
skin="light"
|
|
|
|
color="violet"
|
|
|
|
placeholder={messages.newEmailPlaceholder}
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
renderStep2({form, code, isCodeSpecified, isActiveStep}) {
|
|
|
|
const newEmail = this.props.stepForms[1].value('email');
|
|
|
|
|
|
|
|
return (
|
|
|
|
<div className={styles.formBody}>
|
|
|
|
<div className={styles.formRow}>
|
|
|
|
<p className={styles.description}>
|
|
|
|
{newEmail ? (
|
|
|
|
<span>
|
|
|
|
<Message {...messages.finalizationCodeWasSentToEmail} values={{
|
|
|
|
email: (<b>{newEmail}</b>)
|
|
|
|
}} />
|
|
|
|
{' '}
|
|
|
|
</span>
|
|
|
|
) : null}
|
|
|
|
<Message {...messages.enterFinalizationCode} />
|
|
|
|
</p>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div className={styles.formRow}>
|
|
|
|
<Input {...form.bindField('key')}
|
|
|
|
required={isActiveStep}
|
|
|
|
disabled={isCodeSpecified}
|
|
|
|
value={code}
|
|
|
|
onChange={this.onCodeInput}
|
|
|
|
autoComplete="off"
|
|
|
|
skin="light"
|
|
|
|
color="violet"
|
|
|
|
placeholder={messages.codePlaceholder}
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2016-05-02 23:02:03 +05:30
|
|
|
onStepMeasure(step) {
|
|
|
|
return (height) => this.setState({
|
|
|
|
[`step${step}Height`]: height
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2016-05-22 13:23:40 +05:30
|
|
|
nextStep() {
|
2016-05-02 23:02:03 +05:30
|
|
|
const {activeStep} = this.state;
|
|
|
|
const nextStep = activeStep + 1;
|
|
|
|
|
|
|
|
if (nextStep < STEPS_TOTAL) {
|
|
|
|
this.setState({
|
|
|
|
activeStep: nextStep
|
|
|
|
});
|
2016-05-20 10:44:14 +05:30
|
|
|
|
|
|
|
this.props.onChangeStep(nextStep);
|
2016-05-02 23:02:03 +05:30
|
|
|
}
|
2016-05-22 13:23:40 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
isLastStep() {
|
|
|
|
return this.state.activeStep + 1 === STEPS_TOTAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
onSwitchStep = (event) => {
|
|
|
|
event.preventDefault();
|
|
|
|
|
|
|
|
this.nextStep();
|
2016-05-02 23:02:03 +05:30
|
|
|
};
|
|
|
|
|
2016-05-20 10:44:14 +05:30
|
|
|
onCodeInput = (event) => {
|
|
|
|
const {value} = event.target;
|
|
|
|
|
|
|
|
this.setState({
|
|
|
|
code: this.props.code || value
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2016-05-02 23:02:03 +05:30
|
|
|
onFormSubmit = () => {
|
2016-05-22 13:23:40 +05:30
|
|
|
const {activeStep} = this.state;
|
|
|
|
const promise = this.props.onSubmit(activeStep, this.props.stepForms[activeStep]);
|
|
|
|
|
|
|
|
if (!promise || !promise.then) {
|
|
|
|
throw new Error('Expecting promise from onSubmit');
|
|
|
|
}
|
|
|
|
|
|
|
|
promise.then(() => this.nextStep(), () => this.forceUpdate());
|
2016-05-02 23:02:03 +05:30
|
|
|
};
|
|
|
|
}
|