accounts-frontend/packages/app/components/profile/changeEmail/ChangeEmail.tsx

347 lines
12 KiB
TypeScript
Raw Normal View History

import React, { ReactNode } from 'react';
import { defineMessages, FormattedMessage as Message } from 'react-intl';
import { Helmet } from 'react-helmet-async';
import { SlideMotion } from 'app/components/ui/motion';
import { ScrollIntoView } from 'app/components/ui/scroll';
2020-05-24 04:38:24 +05:30
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';
import changeEmail from './changeEmail.scss';
const STEPS_TOTAL = 3;
2019-12-10 13:17:32 +05:30
export type ChangeEmailStep = 0 | 1 | 2;
interface Props {
2020-05-24 04:38:24 +05:30
onChangeStep: (step: ChangeEmailStep) => void;
email: string;
stepForms: Array<FormModel>;
onSubmit: (step: ChangeEmailStep, form: FormModel) => Promise<void>;
step: ChangeEmailStep;
code?: string;
2019-12-10 13:17:32 +05:30
}
2019-12-07 16:58:52 +05:30
interface State {
2020-05-24 04:38:24 +05:30
newEmail: string | null;
activeStep: ChangeEmailStep;
code: string;
2019-12-10 13:17:32 +05:30
}
interface FormStepParams {
2020-05-24 04:38:24 +05:30
form: FormModel;
isActiveStep: boolean;
isCodeSpecified: boolean;
email: string;
code?: string;
}
const labels = defineMessages({
changeEmailButton: 'Change Email',
sendEmailButton: 'Send Email',
codePlaceholder: 'Paste the code here',
newEmailPlaceholder: 'Enter new Email',
});
2019-12-10 13:17:32 +05:30
export default class ChangeEmail extends React.Component<Props, State> {
2020-05-24 04:38:24 +05:30
static get defaultProps(): Partial<Props> {
return {
stepForms: [new FormModel(), new FormModel(), new FormModel()],
onChangeStep() {},
step: 0,
};
}
2020-05-24 04:38:24 +05:30
state: State = {
newEmail: null,
activeStep: this.props.step,
code: this.props.code || '',
};
2020-05-24 04:38:24 +05:30
static getDerivedStateFromProps(props: Props, state: State) {
return {
activeStep: typeof props.step === 'number' ? props.step : state.activeStep,
code: props.code || state.code,
};
}
2020-05-24 04:38:24 +05:30
render() {
const { activeStep } = this.state;
const form = this.props.stepForms[activeStep];
return (
<Form form={form} onSubmit={this.onFormSubmit} onInvalid={() => this.forceUpdate()}>
<div className={styles.contentWithBackButton} data-testid="change-email">
<BackButton />
<div className={styles.form}>
<div className={styles.formBody}>
<Message key="changeEmailTitle" defaultMessage="Change Email">
2020-05-24 04:38:24 +05:30
{(pageTitle) => (
<h3 className={styles.violetTitle}>
<Helmet title={pageTitle as string} />
{pageTitle}
</h3>
)}
</Message>
<div className={styles.formRow}>
<p className={styles.description}>
<Message
key="changeEmailDescription"
defaultMessage="To change current account Email you must first verify that you own the current address and then confirm the new one."
/>
2020-05-24 04:38:24 +05:30
</p>
</div>
</div>
</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() ? labels.changeEmailButton : labels.sendEmailButton}
2020-05-24 04:38:24 +05:30
/>
</div>
<div className={helpLinks.helpLinks}>
{this.isLastStep() ? null : (
<a href="#" onClick={this.onSwitchStep}>
<Message key="alreadyReceivedCode" defaultMessage="Already received code" />
2020-05-24 04:38:24 +05:30
</a>
)}
</div>
</div>
</Form>
);
}
2020-05-24 04:38:24 +05:30
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 formParams: FormStepParams = {
form: this.props.stepForms[step],
isActiveStep: step === activeStep,
isCodeSpecified,
email,
code,
};
switch (step) {
case 0:
return this.renderStep0(formParams);
case 1:
return this.renderStep1(formParams);
case 2:
return this.renderStep2(formParams);
}
})}
</SlideMotion>
);
}
2020-05-24 04:38:24 +05:30
renderStep0({ email, form }: FormStepParams): ReactNode {
return (
<div key="step0" data-testid="step1" className={styles.formBody}>
<div className={styles.formRow}>
<p className={styles.description}>
<Message key="currentAccountEmail" defaultMessage="Current account Email address:" />
2020-05-24 04:38:24 +05:30
</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
key="pressButtonToStart"
defaultMessage="Press the button below to send a message with the code for Email change initialization."
/>
2020-05-24 04:38:24 +05:30
</p>
</div>
</div>
);
}
2020-05-24 04:38:24 +05:30
renderStep1({ email, form, code, isCodeSpecified, isActiveStep }: FormStepParams): ReactNode {
return (
<div key="step1" data-testid="step2" className={styles.formBody}>
<div className={styles.formRow}>
<p className={styles.description}>
<Message
key="enterInitializationCode"
defaultMessage="The Email with an initialization code for Email change procedure was sent to {email}. Please enter the code into the field below:"
2020-05-24 04:38:24 +05:30
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={labels.codePlaceholder}
2020-05-24 04:38:24 +05:30
/>
</div>
<div className={styles.formRow}>
<p className={styles.description}>
<Message
key="enterNewEmail"
defaultMessage="Then provide your new Email address, that you want to use with this account. You will be mailed with confirmation code."
/>
2020-05-24 04:38:24 +05:30
</p>
</div>
<div className={styles.formRow}>
<Input
{...form.bindField('email')}
required={isActiveStep}
skin="light"
color="violet"
placeholder={labels.newEmailPlaceholder}
2020-05-24 04:38:24 +05:30
/>
</div>
</div>
);
}
2020-05-24 04:38:24 +05:30
renderStep2({ form, code, isCodeSpecified, isActiveStep }: FormStepParams): ReactNode {
const { newEmail } = this.state;
return (
<div key="step2" data-testid="step3" className={styles.formBody}>
<div className={styles.formRow}>
<p className={styles.description}>
{newEmail ? (
<span>
<Message
key="finalizationCodeWasSentToEmail"
defaultMessage="The Email change confirmation code was sent to {email}."
2020-05-24 04:38:24 +05:30
values={{
email: <b>{newEmail}</b>,
}}
/>{' '}
</span>
) : null}
<Message
key="enterFinalizationCode"
defaultMessage="In order to confirm your new Email, please enter the code received into the field below:"
/>
2020-05-24 04:38:24 +05:30
</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={labels.codePlaceholder}
2020-05-24 04:38:24 +05:30
/>
</div>
</div>
);
}
2020-05-24 04:38:24 +05:30
nextStep() {
const { activeStep } = this.state;
const nextStep = activeStep + 1;
let newEmail = null;
2020-05-24 04:38:24 +05:30
if (activeStep === 1) {
newEmail = this.props.stepForms[1].value('email');
}
if (nextStep < STEPS_TOTAL) {
this.setState({
activeStep: nextStep as ChangeEmailStep,
newEmail,
});
2020-05-24 04:38:24 +05:30
this.props.onChangeStep(nextStep as ChangeEmailStep);
}
}
2020-05-24 04:38:24 +05:30
isLastStep() {
return this.state.activeStep + 1 === STEPS_TOTAL;
}
onSwitchStep = (event: React.MouseEvent<HTMLAnchorElement>) => {
event.preventDefault();
this.nextStep();
2020-05-24 04:38:24 +05:30
};
onCodeInput = (event: React.ChangeEvent<HTMLInputElement>) => {
const { value } = event.target;
if (this.props.code) {
return;
}
this.setState({
2020-05-24 04:38:24 +05:30
code: value,
});
2020-05-24 04:38:24 +05:30
};
onFormSubmit = () => {
const { activeStep } = this.state;
const form = this.props.stepForms[activeStep];
const promise = this.props.onSubmit(activeStep, form);
if (!promise || !promise.then) {
throw new Error('Expecting promise from onSubmit');
}
2020-05-24 04:38:24 +05:30
promise.then(
() => {
this.nextStep();
this.setState({
code: '',
});
},
(resp) => {
if (resp.errors) {
form.setErrors(resp.errors);
this.forceUpdate();
} else {
return Promise.reject(resp);
}
},
);
};
}