2019-12-10 13:17:32 +05:30
|
|
|
import React from 'react';
|
2016-05-02 23:02:03 +05:30
|
|
|
import { FormattedMessage as Message } from 'react-intl';
|
|
|
|
import Helmet from 'react-helmet';
|
2019-12-08 00:32:00 +05:30
|
|
|
import { SlideMotion } from 'app/components/ui/motion';
|
|
|
|
import { ScrollIntoView } from 'app/components/ui/scroll';
|
|
|
|
import {
|
|
|
|
Input,
|
|
|
|
Button,
|
|
|
|
Form,
|
|
|
|
FormModel,
|
|
|
|
FormError,
|
|
|
|
} from 'app/components/ui/form';
|
|
|
|
import { BackButton } from 'app/components/profile/ProfileForm';
|
|
|
|
import styles from 'app/components/profile/profileForm.scss';
|
|
|
|
import helpLinks from 'app/components/auth/helpLinks.scss';
|
|
|
|
import Stepper from 'app/components/ui/stepper';
|
2016-05-02 23:02:03 +05:30
|
|
|
|
|
|
|
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;
|
|
|
|
|
2019-12-10 13:17:32 +05:30
|
|
|
export type ChangeEmailStep = 0 | 1 | 2;
|
|
|
|
type HeightProp = 'step0Height' | 'step1Height' | 'step2Height';
|
|
|
|
type HeightDict = {
|
|
|
|
[K in HeightProp]?: number;
|
|
|
|
};
|
|
|
|
|
|
|
|
interface Props {
|
|
|
|
onChangeStep: (step: ChangeEmailStep) => void;
|
|
|
|
lang: string;
|
|
|
|
email: string;
|
|
|
|
stepForms: FormModel[];
|
|
|
|
onSubmit: (step: ChangeEmailStep, form: FormModel) => Promise<void>;
|
|
|
|
step: ChangeEmailStep;
|
|
|
|
code?: string;
|
|
|
|
}
|
2019-12-07 16:58:52 +05:30
|
|
|
|
2019-12-10 13:17:32 +05:30
|
|
|
interface State extends HeightDict {
|
|
|
|
newEmail: string | null;
|
|
|
|
activeStep: ChangeEmailStep;
|
|
|
|
code: string;
|
|
|
|
}
|
2019-11-27 14:33:32 +05:30
|
|
|
|
2019-12-10 13:17:32 +05:30
|
|
|
export default class ChangeEmail extends React.Component<Props, State> {
|
|
|
|
static get defaultProps(): Partial<Props> {
|
2019-11-27 14:33:32 +05:30
|
|
|
return {
|
|
|
|
stepForms: [new FormModel(), new FormModel(), new FormModel()],
|
|
|
|
onChangeStep() {},
|
|
|
|
step: 0,
|
2016-05-02 23:02:03 +05:30
|
|
|
};
|
2019-11-27 14:33:32 +05:30
|
|
|
}
|
|
|
|
|
2019-12-10 13:17:32 +05:30
|
|
|
state: State = {
|
|
|
|
newEmail: null,
|
2019-11-27 14:33:32 +05:30
|
|
|
activeStep: this.props.step,
|
|
|
|
code: this.props.code || '',
|
|
|
|
};
|
|
|
|
|
2019-12-10 13:17:32 +05:30
|
|
|
static getDerivedStateFromProps(props: Props, state: State) {
|
|
|
|
return {
|
2019-11-27 14:33:32 +05:30
|
|
|
activeStep:
|
2019-12-10 13:17:32 +05:30
|
|
|
typeof props.step === 'number' ? props.step : state.activeStep,
|
2019-12-29 18:56:07 +05:30
|
|
|
code: props.code || state.code,
|
2019-12-10 13:17:32 +05:30
|
|
|
};
|
2019-11-27 14:33:32 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
render() {
|
|
|
|
const { activeStep } = this.state;
|
|
|
|
const form = this.props.stepForms[activeStep];
|
|
|
|
|
|
|
|
return (
|
|
|
|
<Form
|
|
|
|
form={form}
|
|
|
|
onSubmit={this.onFormSubmit}
|
|
|
|
onInvalid={() => this.forceUpdate()}
|
|
|
|
>
|
2019-12-29 18:56:07 +05:30
|
|
|
<div
|
|
|
|
className={styles.contentWithBackButton}
|
|
|
|
data-testid="change-email"
|
|
|
|
>
|
2019-11-27 14:33:32 +05:30
|
|
|
<BackButton />
|
|
|
|
|
|
|
|
<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>
|
2016-05-22 13:23:40 +05:30
|
|
|
</div>
|
2019-11-27 14:33:32 +05:30
|
|
|
</div>
|
|
|
|
|
|
|
|
<div className={styles.stepper}>
|
|
|
|
<Stepper
|
|
|
|
color="violet"
|
|
|
|
totalSteps={STEPS_TOTAL}
|
|
|
|
activeStep={activeStep}
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div className={styles.form}>
|
|
|
|
{activeStep > 0 ? <ScrollIntoView /> : null}
|
|
|
|
|
|
|
|
{this.renderStepForms()}
|
|
|
|
|
|
|
|
<Button
|
|
|
|
color="violet"
|
|
|
|
type="submit"
|
|
|
|
block
|
|
|
|
label={
|
|
|
|
this.isLastStep()
|
|
|
|
? messages.changeEmailButton
|
|
|
|
: messages.sendEmailButton
|
|
|
|
}
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div className={helpLinks.helpLinks}>
|
|
|
|
{this.isLastStep() ? null : (
|
|
|
|
<a href="#" onClick={this.onSwitchStep}>
|
|
|
|
<Message {...messages.alreadyReceivedCode} />
|
|
|
|
</a>
|
|
|
|
)}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</Form>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
renderStepForms() {
|
|
|
|
const { email } = this.props;
|
|
|
|
const { activeStep, code } = this.state;
|
|
|
|
const isCodeSpecified = !!this.props.code;
|
|
|
|
|
|
|
|
return (
|
|
|
|
<SlideMotion activeStep={activeStep}>
|
|
|
|
{new Array(STEPS_TOTAL).fill(0).map((_, step) => {
|
|
|
|
const form = this.props.stepForms[step];
|
|
|
|
|
|
|
|
return this[`renderStep${step}`]({
|
|
|
|
email,
|
|
|
|
code,
|
|
|
|
isCodeSpecified,
|
|
|
|
form,
|
|
|
|
isActiveStep: step === activeStep,
|
|
|
|
});
|
|
|
|
})}
|
|
|
|
</SlideMotion>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
renderStep0({ email, form }) {
|
|
|
|
return (
|
2019-12-29 18:56:07 +05:30
|
|
|
<div key="step0" data-testid="step1" className={styles.formBody}>
|
2019-11-27 14:33:32 +05:30
|
|
|
<div className={styles.formRow}>
|
|
|
|
<p className={styles.description}>
|
|
|
|
<Message {...messages.currentAccountEmail} />
|
|
|
|
</p>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div className={styles.formRow}>
|
|
|
|
<h2 className={changeEmail.currentAccountEmail}>{email}</h2>
|
|
|
|
|
|
|
|
<FormError error={form.getError('email')} />
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div className={styles.formRow}>
|
|
|
|
<p className={styles.description}>
|
|
|
|
<Message {...messages.pressButtonToStart} />
|
|
|
|
</p>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
renderStep1({ email, form, code, isCodeSpecified, isActiveStep }) {
|
|
|
|
return (
|
2019-12-29 18:56:07 +05:30
|
|
|
<div key="step1" data-testid="step2" className={styles.formBody}>
|
2019-11-27 14:33:32 +05:30
|
|
|
<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.state;
|
|
|
|
|
|
|
|
return (
|
2019-12-29 18:56:07 +05:30
|
|
|
<div key="step2" data-testid="step3" className={styles.formBody}>
|
2019-11-27 14:33:32 +05:30
|
|
|
<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>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2019-12-10 13:17:32 +05:30
|
|
|
onStepMeasure(step: ChangeEmailStep) {
|
|
|
|
return (height: number) =>
|
|
|
|
// @ts-ignore
|
2019-11-27 14:33:32 +05:30
|
|
|
this.setState({
|
|
|
|
[`step${step}Height`]: height,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
nextStep() {
|
|
|
|
const { activeStep } = this.state;
|
|
|
|
const nextStep = activeStep + 1;
|
|
|
|
let newEmail = null;
|
|
|
|
|
|
|
|
if (activeStep === 1) {
|
|
|
|
newEmail = this.props.stepForms[1].value('email');
|
2016-05-22 13:23:40 +05:30
|
|
|
}
|
|
|
|
|
2019-11-27 14:33:32 +05:30
|
|
|
if (nextStep < STEPS_TOTAL) {
|
|
|
|
this.setState({
|
2019-12-10 13:17:32 +05:30
|
|
|
activeStep: nextStep as ChangeEmailStep,
|
2019-11-27 14:33:32 +05:30
|
|
|
newEmail,
|
|
|
|
});
|
2016-05-22 13:23:40 +05:30
|
|
|
|
2019-12-10 13:17:32 +05:30
|
|
|
this.props.onChangeStep(nextStep as ChangeEmailStep);
|
2016-05-22 13:23:40 +05:30
|
|
|
}
|
2019-11-27 14:33:32 +05:30
|
|
|
}
|
2016-05-22 13:23:40 +05:30
|
|
|
|
2019-11-27 14:33:32 +05:30
|
|
|
isLastStep() {
|
|
|
|
return this.state.activeStep + 1 === STEPS_TOTAL;
|
|
|
|
}
|
2016-05-22 13:23:40 +05:30
|
|
|
|
2019-12-29 18:56:07 +05:30
|
|
|
onSwitchStep = (event: React.MouseEvent) => {
|
2019-11-27 14:33:32 +05:30
|
|
|
event.preventDefault();
|
2016-05-22 13:23:40 +05:30
|
|
|
|
2019-11-27 14:33:32 +05:30
|
|
|
this.nextStep();
|
|
|
|
};
|
2016-05-22 13:23:40 +05:30
|
|
|
|
2019-12-29 18:56:07 +05:30
|
|
|
onCodeInput = (event: React.ChangeEvent<HTMLInputElement>) => {
|
2019-11-27 14:33:32 +05:30
|
|
|
const { value } = event.target;
|
2016-05-02 23:02:03 +05:30
|
|
|
|
2019-12-29 18:56:07 +05:30
|
|
|
if (this.props.code) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-11-27 14:33:32 +05:30
|
|
|
this.setState({
|
2019-12-29 18:56:07 +05:30
|
|
|
code: value,
|
2019-11-27 14:33:32 +05:30
|
|
|
});
|
|
|
|
};
|
2016-05-20 10:44:14 +05:30
|
|
|
|
2019-11-27 14:33:32 +05:30
|
|
|
onFormSubmit = () => {
|
|
|
|
const { activeStep } = this.state;
|
|
|
|
const form = this.props.stepForms[activeStep];
|
|
|
|
const promise = this.props.onSubmit(activeStep, form);
|
2016-05-20 10:44:14 +05:30
|
|
|
|
2019-11-27 14:33:32 +05:30
|
|
|
if (!promise || !promise.then) {
|
|
|
|
throw new Error('Expecting promise from onSubmit');
|
|
|
|
}
|
2016-05-22 13:23:40 +05:30
|
|
|
|
2019-11-27 14:33:32 +05:30
|
|
|
promise.then(
|
2019-12-29 18:56:07 +05:30
|
|
|
() => {
|
|
|
|
this.nextStep();
|
|
|
|
this.setState({
|
|
|
|
code: '',
|
|
|
|
});
|
|
|
|
},
|
2019-11-27 14:33:32 +05:30
|
|
|
resp => {
|
|
|
|
if (resp.errors) {
|
|
|
|
form.setErrors(resp.errors);
|
|
|
|
this.forceUpdate();
|
|
|
|
} else {
|
|
|
|
return Promise.reject(resp);
|
2016-05-22 13:23:40 +05:30
|
|
|
}
|
2019-11-27 14:33:32 +05:30
|
|
|
},
|
|
|
|
);
|
|
|
|
};
|
2016-05-02 23:02:03 +05:30
|
|
|
}
|