mirror of
https://github.com/elyby/accounts-frontend.git
synced 2024-12-02 11:41:04 +05:30
Remove deprecated methods usage
This commit is contained in:
parent
2a2a473e39
commit
3b20475cc6
@ -140,7 +140,7 @@ module.exports = {
|
|||||||
'react/jsx-uses-vars': 'warn',
|
'react/jsx-uses-vars': 'warn',
|
||||||
'react/jsx-no-comment-textnodes': 'warn',
|
'react/jsx-no-comment-textnodes': 'warn',
|
||||||
'react/jsx-wrap-multilines': 'warn',
|
'react/jsx-wrap-multilines': 'warn',
|
||||||
'react/no-deprecated': 'warn',
|
'react/no-deprecated': 'error',
|
||||||
'react/no-did-mount-set-state': 'warn',
|
'react/no-did-mount-set-state': 'warn',
|
||||||
'react/no-did-update-set-state': 'warn',
|
'react/no-did-update-set-state': 'warn',
|
||||||
'react/no-direct-mutation-state': 'warn',
|
'react/no-direct-mutation-state': 'warn',
|
||||||
|
@ -23,6 +23,8 @@ import { omit, debounce } from 'app/functions';
|
|||||||
|
|
||||||
type ChildState = any;
|
type ChildState = any;
|
||||||
|
|
||||||
|
// TODO: this may be rewritten in more efficient way using resize/mutation observer
|
||||||
|
|
||||||
export default class MeasureHeight extends React.PureComponent<
|
export default class MeasureHeight extends React.PureComponent<
|
||||||
{
|
{
|
||||||
shouldMeasure: (prevState: ChildState, newState: ChildState) => boolean;
|
shouldMeasure: (prevState: ChildState, newState: ChildState) => boolean;
|
||||||
|
@ -31,6 +31,7 @@ export default class BaseAuthBody extends React.Component<
|
|||||||
|
|
||||||
autoFocusField: string | null = '';
|
autoFocusField: string | null = '';
|
||||||
|
|
||||||
|
// eslint-disable-next-line react/no-deprecated
|
||||||
componentWillReceiveProps(nextProps, nextContext) {
|
componentWillReceiveProps(nextProps, nextContext) {
|
||||||
if (nextContext.auth.error !== this.context.auth.error) {
|
if (nextContext.auth.error !== this.context.auth.error) {
|
||||||
this.form.setErrors(nextContext.auth.error || {});
|
this.form.setErrors(nextContext.auth.error || {});
|
||||||
|
@ -185,17 +185,18 @@ class PanelTransition extends React.Component<Props, State> {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps: Props) {
|
componentDidUpdate(prevProps: Props) {
|
||||||
const nextPanel: PanelId =
|
const nextPanel: PanelId =
|
||||||
nextProps.Body && (nextProps.Body.type as any).panelId;
|
|
||||||
const prevPanel: PanelId =
|
|
||||||
this.props.Body && (this.props.Body.type as any).panelId;
|
this.props.Body && (this.props.Body.type as any).panelId;
|
||||||
|
const prevPanel: PanelId =
|
||||||
|
prevProps.Body && (prevProps.Body.type as any).panelId;
|
||||||
|
|
||||||
if (nextPanel !== prevPanel) {
|
if (nextPanel !== prevPanel) {
|
||||||
const direction = this.getDirection(nextPanel, prevPanel);
|
const direction = this.getDirection(nextPanel, prevPanel);
|
||||||
const forceHeight = direction === 'Y' && nextPanel !== prevPanel ? 1 : 0;
|
const forceHeight = direction === 'Y' && nextPanel !== prevPanel ? 1 : 0;
|
||||||
|
|
||||||
this.props.clearErrors();
|
this.props.clearErrors();
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
direction,
|
direction,
|
||||||
panelId: nextPanel,
|
panelId: nextPanel,
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import React, { Component } from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { FormattedMessage as Message } from 'react-intl';
|
import { FormattedMessage as Message } from 'react-intl';
|
||||||
import Helmet from 'react-helmet';
|
import Helmet from 'react-helmet';
|
||||||
import { SlideMotion } from 'app/components/ui/motion';
|
import { SlideMotion } from 'app/components/ui/motion';
|
||||||
@ -21,35 +20,30 @@ import messages from './ChangeEmail.intl.json';
|
|||||||
|
|
||||||
const STEPS_TOTAL = 3;
|
const STEPS_TOTAL = 3;
|
||||||
|
|
||||||
export default class ChangeEmail extends Component {
|
export type ChangeEmailStep = 0 | 1 | 2;
|
||||||
static propTypes = {
|
type HeightProp = 'step0Height' | 'step1Height' | 'step2Height';
|
||||||
onChangeStep: PropTypes.func,
|
type HeightDict = {
|
||||||
lang: PropTypes.string.isRequired,
|
[K in HeightProp]?: number;
|
||||||
email: PropTypes.string.isRequired,
|
|
||||||
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.`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
),
|
|
||||||
onSubmit: PropTypes.func.isRequired,
|
|
||||||
step: PropTypes.oneOf([0, 1, 2]),
|
|
||||||
code: PropTypes.string,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static get defaultProps() {
|
interface Props {
|
||||||
|
onChangeStep: (step: ChangeEmailStep) => void;
|
||||||
|
lang: string;
|
||||||
|
email: string;
|
||||||
|
stepForms: FormModel[];
|
||||||
|
onSubmit: (step: ChangeEmailStep, form: FormModel) => Promise<void>;
|
||||||
|
step: ChangeEmailStep;
|
||||||
|
code?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface State extends HeightDict {
|
||||||
|
newEmail: string | null;
|
||||||
|
activeStep: ChangeEmailStep;
|
||||||
|
code: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class ChangeEmail extends React.Component<Props, State> {
|
||||||
|
static get defaultProps(): Partial<Props> {
|
||||||
return {
|
return {
|
||||||
stepForms: [new FormModel(), new FormModel(), new FormModel()],
|
stepForms: [new FormModel(), new FormModel(), new FormModel()],
|
||||||
onChangeStep() {},
|
onChangeStep() {},
|
||||||
@ -57,19 +51,18 @@ export default class ChangeEmail extends Component {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
state = {
|
state: State = {
|
||||||
|
newEmail: null,
|
||||||
activeStep: this.props.step,
|
activeStep: this.props.step,
|
||||||
code: this.props.code || '',
|
code: this.props.code || '',
|
||||||
};
|
};
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps) {
|
static getDerivedStateFromProps(props: Props, state: State) {
|
||||||
this.setState({
|
return {
|
||||||
activeStep:
|
activeStep:
|
||||||
typeof nextProps.step === 'number'
|
typeof props.step === 'number' ? props.step : state.activeStep,
|
||||||
? nextProps.step
|
code: props.code || '',
|
||||||
: this.state.activeStep,
|
};
|
||||||
code: nextProps.code || '',
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@ -272,8 +265,9 @@ export default class ChangeEmail extends Component {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
onStepMeasure(step) {
|
onStepMeasure(step: ChangeEmailStep) {
|
||||||
return height =>
|
return (height: number) =>
|
||||||
|
// @ts-ignore
|
||||||
this.setState({
|
this.setState({
|
||||||
[`step${step}Height`]: height,
|
[`step${step}Height`]: height,
|
||||||
});
|
});
|
||||||
@ -290,11 +284,11 @@ export default class ChangeEmail extends Component {
|
|||||||
|
|
||||||
if (nextStep < STEPS_TOTAL) {
|
if (nextStep < STEPS_TOTAL) {
|
||||||
this.setState({
|
this.setState({
|
||||||
activeStep: nextStep,
|
activeStep: nextStep as ChangeEmailStep,
|
||||||
newEmail,
|
newEmail,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.props.onChangeStep(nextStep);
|
this.props.onChangeStep(nextStep as ChangeEmailStep);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -32,7 +32,7 @@ interface State {
|
|||||||
qrCodeSrc: string;
|
qrCodeSrc: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class MfaEnable extends React.Component<Props, State> {
|
export default class MfaEnable extends React.PureComponent<Props, State> {
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
confirmationForm: new FormModel(),
|
confirmationForm: new FormModel(),
|
||||||
step: 0,
|
step: 0,
|
||||||
@ -51,12 +51,22 @@ export default class MfaEnable extends React.Component<Props, State> {
|
|||||||
|
|
||||||
confirmationFormEl: Form | null;
|
confirmationFormEl: Form | null;
|
||||||
|
|
||||||
componentWillMount() {
|
componentDidMount() {
|
||||||
this.syncState(this.props);
|
this.syncState(this.props);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps: Props) {
|
static getDerivedStateFromProps(props: Props, state: State) {
|
||||||
this.syncState(nextProps);
|
if (typeof props.step === 'number' && props.step !== state.activeStep) {
|
||||||
|
return {
|
||||||
|
activeStep: props.step,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate() {
|
||||||
|
this.syncState(this.props);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@ -108,23 +118,23 @@ export default class MfaEnable extends React.Component<Props, State> {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<SlideMotion activeStep={activeStep}>
|
<SlideMotion activeStep={activeStep}>
|
||||||
{[
|
<Instructions key="step1" />
|
||||||
<Instructions key="step1" />,
|
<KeyForm key="step2" secret={secret} qrCodeSrc={qrCodeSrc} />
|
||||||
<KeyForm key="step2" secret={secret} qrCodeSrc={qrCodeSrc} />,
|
|
||||||
<Confirmation
|
<Confirmation
|
||||||
key="step3"
|
key="step3"
|
||||||
form={this.props.confirmationForm}
|
form={this.props.confirmationForm}
|
||||||
formRef={(el: Form) => (this.confirmationFormEl = el)}
|
formRef={(el: Form) => (this.confirmationFormEl = el)}
|
||||||
onSubmit={this.onTotpSubmit}
|
onSubmit={this.onTotpSubmit}
|
||||||
onInvalid={() => this.forceUpdate()}
|
onInvalid={() => this.forceUpdate()}
|
||||||
/>,
|
/>
|
||||||
]}
|
|
||||||
</SlideMotion>
|
</SlideMotion>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
syncState(props: Props) {
|
syncState(props: Props) {
|
||||||
if (props.step === 1) {
|
const { isLoading, qrCodeSrc } = this.state;
|
||||||
|
|
||||||
|
if (props.step === 1 && !isLoading && !qrCodeSrc) {
|
||||||
this.setState({ isLoading: true });
|
this.setState({ isLoading: true });
|
||||||
|
|
||||||
getSecret(this.context.userId).then(resp => {
|
getSecret(this.context.userId).then(resp => {
|
||||||
@ -135,11 +145,6 @@ export default class MfaEnable extends React.Component<Props, State> {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({
|
|
||||||
activeStep:
|
|
||||||
typeof props.step === 'number' ? props.step : this.state.activeStep,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nextStep() {
|
nextStep() {
|
||||||
|
@ -4,37 +4,38 @@ import MeasureHeight from 'app/components/MeasureHeight';
|
|||||||
|
|
||||||
import styles from './collapse.scss';
|
import styles from './collapse.scss';
|
||||||
|
|
||||||
type Props = {
|
interface Props {
|
||||||
isOpened?: boolean;
|
isOpened?: boolean;
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
onRest: () => void;
|
onRest: () => void;
|
||||||
};
|
}
|
||||||
|
|
||||||
export default class Collapse extends Component<
|
interface State {
|
||||||
Props,
|
isOpened?: boolean; // just to track value for derived updates
|
||||||
{
|
|
||||||
height: number;
|
height: number;
|
||||||
wasInitialized: boolean;
|
wasInitialized: boolean;
|
||||||
}
|
}
|
||||||
> {
|
|
||||||
|
export default class Collapse extends Component<Props, State> {
|
||||||
state = {
|
state = {
|
||||||
|
isOpened: this.props.isOpened,
|
||||||
height: 0,
|
height: 0,
|
||||||
wasInitialized: false,
|
wasInitialized: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps: Partial<Props> = {
|
||||||
onRest: () => {},
|
onRest: () => {},
|
||||||
};
|
};
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps: Props) {
|
static getDerivedStateFromProps(props: Props, state: State) {
|
||||||
if (
|
if (props.isOpened !== state.isOpened && !state.wasInitialized) {
|
||||||
this.props.isOpened !== nextProps.isOpened &&
|
return {
|
||||||
!this.state.wasInitialized
|
isOpened: props.isOpened,
|
||||||
) {
|
|
||||||
this.setState({
|
|
||||||
wasInitialized: true,
|
wasInitialized: true,
|
||||||
});
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -14,6 +14,7 @@ interface Props {
|
|||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}
|
}
|
||||||
interface State {
|
interface State {
|
||||||
|
id: string; // just to track value for derived updates
|
||||||
isTouched: boolean;
|
isTouched: boolean;
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
}
|
}
|
||||||
@ -27,7 +28,8 @@ export default class Form extends React.Component<Props, State> {
|
|||||||
onInvalid() {},
|
onInvalid() {},
|
||||||
};
|
};
|
||||||
|
|
||||||
state = {
|
state: State = {
|
||||||
|
id: this.props.id,
|
||||||
isTouched: false,
|
isTouched: false,
|
||||||
isLoading: this.props.isLoading || false,
|
isLoading: this.props.isLoading || false,
|
||||||
};
|
};
|
||||||
@ -44,29 +46,38 @@ export default class Form extends React.Component<Props, State> {
|
|||||||
this.mounted = true;
|
this.mounted = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps: Props) {
|
static getDerivedStateFromProps(props: Props, state: State) {
|
||||||
if (nextProps.id !== this.props.id) {
|
const patch: Partial<State> = {};
|
||||||
this.setState({
|
|
||||||
isTouched: false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
typeof nextProps.isLoading !== 'undefined' &&
|
typeof props.isLoading !== 'undefined' &&
|
||||||
nextProps.isLoading !== this.state.isLoading
|
props.isLoading !== state.isLoading
|
||||||
) {
|
) {
|
||||||
this.setState({
|
patch.isLoading = props.isLoading;
|
||||||
isLoading: nextProps.isLoading,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const nextForm = nextProps.form;
|
if (props.id !== state.id) {
|
||||||
|
patch.id = props.id;
|
||||||
|
patch.isTouched = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (nextForm && this.props.form && nextForm !== this.props.form) {
|
return patch;
|
||||||
this.props.form.removeLoadingListener(this.onLoading);
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps: Props) {
|
||||||
|
const nextForm = this.props.form;
|
||||||
|
const prevForm = prevProps.form;
|
||||||
|
|
||||||
|
if (nextForm !== prevForm) {
|
||||||
|
if (prevForm) {
|
||||||
|
prevForm.removeLoadingListener(this.onLoading);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nextForm) {
|
||||||
nextForm.addLoadingListener(this.onLoading);
|
nextForm.addLoadingListener(this.onLoading);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
if (this.props.form) {
|
if (this.props.form) {
|
||||||
|
@ -15,11 +15,11 @@ export default class FormInputComponent<P, S = {}> extends FormComponent<
|
|||||||
error?: Error;
|
error?: Error;
|
||||||
}
|
}
|
||||||
> {
|
> {
|
||||||
componentWillReceiveProps() {
|
componentDidUpdate() {
|
||||||
if (this.state && this.state.error) {
|
if (this.state && this.state.error) {
|
||||||
Reflect.deleteProperty(this.state, 'error');
|
this.setState({
|
||||||
|
error: undefined,
|
||||||
this.setState(this.state);
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ export default class ImageLoader extends React.Component<
|
|||||||
isLoading: true,
|
isLoading: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
componentWillMount() {
|
componentDidMount() {
|
||||||
this.preloadImage();
|
this.preloadImage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,29 +4,37 @@ import MeasureHeight from 'app/components/MeasureHeight';
|
|||||||
|
|
||||||
import styles from './slide-motion.scss';
|
import styles from './slide-motion.scss';
|
||||||
|
|
||||||
interface State {
|
interface Props {
|
||||||
[stepHeight: string]: number;
|
|
||||||
version: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
class SlideMotion extends React.Component<
|
|
||||||
{
|
|
||||||
activeStep: number;
|
activeStep: number;
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
},
|
}
|
||||||
State
|
|
||||||
> {
|
interface State {
|
||||||
|
// [stepHeight: string]: number;
|
||||||
|
version: string;
|
||||||
|
prevChildren: React.ReactNode | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
class SlideMotion extends React.PureComponent<Props, State> {
|
||||||
state: State = {
|
state: State = {
|
||||||
version: 0,
|
prevChildren: undefined, // to track version updates
|
||||||
|
version: `${this.props.activeStep}.0`,
|
||||||
};
|
};
|
||||||
|
|
||||||
isHeightMeasured: boolean;
|
isHeightMeasured: boolean;
|
||||||
|
|
||||||
componentWillReceiveProps() {
|
static getDerivedStateFromProps(props: Props, state: State) {
|
||||||
|
let [, version] = state.version.split('.').map(Number);
|
||||||
|
|
||||||
|
if (props.children !== state.prevChildren) {
|
||||||
|
version++;
|
||||||
|
}
|
||||||
|
|
||||||
// mark this view as dirty to re-measure height
|
// mark this view as dirty to re-measure height
|
||||||
this.setState({
|
return {
|
||||||
version: this.state.version + 1,
|
prevChildren: props.children,
|
||||||
});
|
version: `${props.activeStep}.${version}`,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@ -90,6 +98,7 @@ class SlideMotion extends React.Component<
|
|||||||
|
|
||||||
onStepMeasure(step: number) {
|
onStepMeasure(step: number) {
|
||||||
return (height: number) =>
|
return (height: number) =>
|
||||||
|
// @ts-ignore
|
||||||
this.setState({
|
this.setState({
|
||||||
[`step${step}Height`]: height,
|
[`step${step}Height`]: height,
|
||||||
});
|
});
|
||||||
|
@ -14,7 +14,7 @@ export class PopupStack extends React.Component<{
|
|||||||
}> {
|
}> {
|
||||||
unlistenTransition: () => void;
|
unlistenTransition: () => void;
|
||||||
|
|
||||||
componentWillMount() {
|
componentDidMount() {
|
||||||
document.addEventListener('keyup', this.onKeyPress);
|
document.addEventListener('keyup', this.onKeyPress);
|
||||||
this.unlistenTransition = browserHistory.listen(this.onRouteLeave);
|
this.unlistenTransition = browserHistory.listen(this.onRouteLeave);
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,10 @@ import AuthFlowRouteContents from './AuthFlowRouteContents';
|
|||||||
export default function AuthFlowRoute(props: RouteProps) {
|
export default function AuthFlowRoute(props: RouteProps) {
|
||||||
const { component: Component, ...routeProps } = props;
|
const { component: Component, ...routeProps } = props;
|
||||||
|
|
||||||
|
if (!Component) {
|
||||||
|
throw new Error('props.component required');
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Route
|
<Route
|
||||||
{...routeProps}
|
{...routeProps}
|
||||||
|
@ -2,7 +2,6 @@ import React from 'react';
|
|||||||
import sinon from 'sinon';
|
import sinon from 'sinon';
|
||||||
import expect from 'app/test/unexpected';
|
import expect from 'app/test/unexpected';
|
||||||
import { mount } from 'enzyme';
|
import { mount } from 'enzyme';
|
||||||
|
|
||||||
import authFlow from 'app/services/authFlow';
|
import authFlow from 'app/services/authFlow';
|
||||||
|
|
||||||
import AuthFlowRouteContents from './AuthFlowRouteContents';
|
import AuthFlowRouteContents from './AuthFlowRouteContents';
|
||||||
@ -21,7 +20,7 @@ describe('AuthFlowRouteContents', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
it('should render component if route allowed', () => {
|
it('should render component if route allowed', () => {
|
||||||
const request = {
|
const authRequest = {
|
||||||
path: '/path',
|
path: '/path',
|
||||||
params: { foo: 1 },
|
params: { foo: 1 },
|
||||||
query: new URLSearchParams(),
|
query: new URLSearchParams(),
|
||||||
@ -29,13 +28,14 @@ describe('AuthFlowRouteContents', () => {
|
|||||||
|
|
||||||
const routerProps = {
|
const routerProps = {
|
||||||
location: {
|
location: {
|
||||||
pathname: request.path,
|
pathname: authRequest.path,
|
||||||
query: request.query,
|
search: '',
|
||||||
|
query: new URLSearchParams(),
|
||||||
},
|
},
|
||||||
match: {
|
match: {
|
||||||
params: request.params,
|
params: authRequest.params,
|
||||||
},
|
},
|
||||||
};
|
} as any;
|
||||||
|
|
||||||
(authFlow.handleRequest as any).callsArg(2);
|
(authFlow.handleRequest as any).callsArg(2);
|
||||||
|
|
||||||
@ -46,7 +46,10 @@ describe('AuthFlowRouteContents', () => {
|
|||||||
const component = wrapper.find(Component);
|
const component = wrapper.find(Component);
|
||||||
|
|
||||||
expect(authFlow.handleRequest, 'to have a call satisfying', [
|
expect(authFlow.handleRequest, 'to have a call satisfying', [
|
||||||
request,
|
{
|
||||||
|
...authRequest,
|
||||||
|
query: expect.it('to be a', URLSearchParams),
|
||||||
|
},
|
||||||
expect.it('to be a function'),
|
expect.it('to be a function'),
|
||||||
expect.it('to be a function'),
|
expect.it('to be a function'),
|
||||||
]);
|
]);
|
||||||
|
@ -1,52 +1,70 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Redirect } from 'react-router-dom';
|
import { Redirect, RouteComponentProps } from 'react-router-dom';
|
||||||
|
|
||||||
import authFlow from 'app/services/authFlow';
|
import authFlow from 'app/services/authFlow';
|
||||||
|
|
||||||
type ComponentProps = {
|
interface Props {
|
||||||
component: any;
|
component:
|
||||||
routerProps: { [key: string]: any };
|
| React.ComponentType<RouteComponentProps<any>>
|
||||||
};
|
| React.ComponentType<any>;
|
||||||
|
routerProps: RouteComponentProps;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface State {
|
||||||
|
access: null | 'rejected' | 'allowed';
|
||||||
|
component: React.ReactElement | null;
|
||||||
|
}
|
||||||
|
|
||||||
export default class AuthFlowRouteContents extends React.Component<
|
export default class AuthFlowRouteContents extends React.Component<
|
||||||
ComponentProps,
|
Props,
|
||||||
{
|
State
|
||||||
component: any;
|
|
||||||
}
|
|
||||||
> {
|
> {
|
||||||
state: {
|
state: State = {
|
||||||
component: any;
|
access: null,
|
||||||
} = {
|
|
||||||
component: null,
|
component: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
_isMounted = false;
|
mounted = false;
|
||||||
|
|
||||||
|
shouldComponentUpdate(
|
||||||
|
{ routerProps: nextRoute, component: nextComponent }: Props,
|
||||||
|
state: State,
|
||||||
|
) {
|
||||||
|
const { component: prevComponent, routerProps: prevRoute } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
prevRoute.location.pathname !== nextRoute.location.pathname ||
|
||||||
|
prevRoute.location.search !== nextRoute.location.search ||
|
||||||
|
prevComponent !== nextComponent ||
|
||||||
|
this.state.access !== state.access
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this._isMounted = true;
|
this.mounted = true;
|
||||||
this.handleProps(this.props);
|
this.handleProps(this.props);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps: ComponentProps) {
|
componentDidUpdate() {
|
||||||
this.handleProps(nextProps);
|
this.handleProps(this.props);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
this._isMounted = false;
|
this.mounted = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return this.state.component;
|
return this.state.component;
|
||||||
}
|
}
|
||||||
|
|
||||||
handleProps(props: ComponentProps) {
|
handleProps(props: Props) {
|
||||||
const { routerProps } = props;
|
const { routerProps } = props;
|
||||||
|
|
||||||
authFlow.handleRequest(
|
authFlow.handleRequest(
|
||||||
{
|
{
|
||||||
path: routerProps.location.pathname,
|
path: routerProps.location.pathname,
|
||||||
params: routerProps.match.params,
|
params: routerProps.match.params,
|
||||||
query: routerProps.location.query,
|
query: new URLSearchParams(routerProps.location.search),
|
||||||
},
|
},
|
||||||
this.onRedirect.bind(this),
|
this.onRedirect.bind(this),
|
||||||
this.onRouteAllowed.bind(this, props),
|
this.onRouteAllowed.bind(this, props),
|
||||||
@ -54,23 +72,25 @@ export default class AuthFlowRouteContents extends React.Component<
|
|||||||
}
|
}
|
||||||
|
|
||||||
onRedirect(path: string) {
|
onRedirect(path: string) {
|
||||||
if (!this._isMounted) {
|
if (!this.mounted) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
|
access: 'rejected',
|
||||||
component: <Redirect to={path} />,
|
component: <Redirect to={path} />,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onRouteAllowed(props: ComponentProps) {
|
onRouteAllowed(props: Props) {
|
||||||
const { component: Component } = props;
|
if (!this.mounted) {
|
||||||
|
|
||||||
if (!this._isMounted) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { component: Component } = props;
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
|
access: 'allowed',
|
||||||
component: <Component {...props.routerProps} />,
|
component: <Component {...props.routerProps} />,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { RouteComponentProps } from 'react-router-dom';
|
import { RouteComponentProps, Redirect } from 'react-router-dom';
|
||||||
import FormModel from 'app/components/ui/form/FormModel';
|
import FormModel from 'app/components/ui/form/FormModel';
|
||||||
import ChangeEmail from 'app/components/profile/changeEmail/ChangeEmail';
|
import ChangeEmail, {
|
||||||
|
ChangeEmailStep,
|
||||||
|
} from 'app/components/profile/changeEmail/ChangeEmail';
|
||||||
import {
|
import {
|
||||||
requestEmailChange,
|
requestEmailChange,
|
||||||
setNewEmail,
|
setNewEmail,
|
||||||
@ -28,24 +30,20 @@ class ChangeEmailPage extends React.Component<Props> {
|
|||||||
goToProfile: PropTypes.func.isRequired,
|
goToProfile: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
componentWillMount() {
|
render() {
|
||||||
const { step } = this.props.match.params;
|
const { step = 'step1', code } = this.props.match.params;
|
||||||
|
|
||||||
if (step && !/^step[123]$/.test(step)) {
|
if (step && !/^step[123]$/.test(step)) {
|
||||||
// wrong param value
|
// wrong param value
|
||||||
this.props.history.push('/404');
|
return <Redirect to="/404" />;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { step = 'step1', code } = this.props.match.params;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ChangeEmail
|
<ChangeEmail
|
||||||
onSubmit={this.onSubmit}
|
onSubmit={this.onSubmit}
|
||||||
email={this.props.email}
|
email={this.props.email}
|
||||||
lang={this.props.lang}
|
lang={this.props.lang}
|
||||||
step={Number(step.slice(-1)) - 1}
|
step={(Number(step.slice(-1)) - 1) as ChangeEmailStep}
|
||||||
onChangeStep={this.onChangeStep}
|
onChangeStep={this.onChangeStep}
|
||||||
code={code}
|
code={code}
|
||||||
/>
|
/>
|
||||||
@ -56,7 +54,7 @@ class ChangeEmailPage extends React.Component<Props> {
|
|||||||
this.props.history.push(`/profile/change-email/step${++step}`);
|
this.props.history.push(`/profile/change-email/step${++step}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
onSubmit = (step: number, form: FormModel) => {
|
onSubmit = (step: number, form: FormModel): Promise<void> => {
|
||||||
return this.context
|
return this.context
|
||||||
.onSubmit({
|
.onSubmit({
|
||||||
form,
|
form,
|
||||||
|
@ -7,8 +7,6 @@ import { changeUsername } from 'app/services/api/accounts';
|
|||||||
import { FormModel } from 'app/components/ui/form';
|
import { FormModel } from 'app/components/ui/form';
|
||||||
import ChangeUsername from 'app/components/profile/changeUsername/ChangeUsername';
|
import ChangeUsername from 'app/components/profile/changeUsername/ChangeUsername';
|
||||||
|
|
||||||
type OwnProps = {};
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
username: string;
|
username: string;
|
||||||
updateUsername: (username: string) => void;
|
updateUsername: (username: string) => void;
|
||||||
@ -23,11 +21,7 @@ class ChangeUsernamePage extends React.Component<Props> {
|
|||||||
|
|
||||||
form = new FormModel();
|
form = new FormModel();
|
||||||
|
|
||||||
actualUsername: string;
|
actualUsername: string = this.props.username;
|
||||||
|
|
||||||
componentWillMount() {
|
|
||||||
this.actualUsername = this.props.username;
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
this.props.updateUsername(this.actualUsername);
|
this.props.updateUsername(this.actualUsername);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { RouteComponentProps } from 'react-router-dom';
|
import { RouteComponentProps, Redirect } from 'react-router-dom';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import MultiFactorAuth, {
|
import MultiFactorAuth, {
|
||||||
@ -22,26 +22,24 @@ class MultiFactorAuthPage extends React.Component<Props> {
|
|||||||
goToProfile: PropTypes.func.isRequired,
|
goToProfile: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
componentWillMount() {
|
render() {
|
||||||
const { step } = this.props.match.params;
|
const {
|
||||||
const { user } = this.props;
|
user,
|
||||||
|
match: {
|
||||||
|
params: { step },
|
||||||
|
},
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
if (step) {
|
if (step) {
|
||||||
if (!/^[1-3]$/.test(step)) {
|
if (!/^[1-3]$/.test(step)) {
|
||||||
// wrong param value
|
// wrong param value
|
||||||
this.props.history.push('/404');
|
return <Redirect to="/404" />;
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user.isOtpEnabled) {
|
if (user.isOtpEnabled) {
|
||||||
this.props.history.push('/mfa');
|
return <Redirect to="/mfa" />;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { user } = this.props;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MultiFactorAuth
|
<MultiFactorAuth
|
||||||
@ -58,7 +56,7 @@ class MultiFactorAuthPage extends React.Component<Props> {
|
|||||||
const step = Number(this.props.match.params.step) - 1;
|
const step = Number(this.props.match.params.step) - 1;
|
||||||
|
|
||||||
if (step !== 0 && step !== 1 && step !== 2) {
|
if (step !== 0 && step !== 1 && step !== 2) {
|
||||||
return 1;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return step;
|
return step;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { Component } from 'react';
|
import React from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { resetAuth } from 'app/components/auth/actions';
|
import { resetAuth } from 'app/components/auth/actions';
|
||||||
import { withRouter } from 'react-router-dom';
|
import { withRouter } from 'react-router-dom';
|
||||||
@ -25,7 +25,7 @@ import { RootState } from 'app/reducers';
|
|||||||
import styles from './root.scss';
|
import styles from './root.scss';
|
||||||
import messages from './RootPage.intl.json';
|
import messages from './RootPage.intl.json';
|
||||||
|
|
||||||
class RootPage extends Component<{
|
class RootPage extends React.PureComponent<{
|
||||||
account: Account | null;
|
account: Account | null;
|
||||||
user: User;
|
user: User;
|
||||||
isPopupActive: boolean;
|
isPopupActive: boolean;
|
||||||
@ -88,8 +88,13 @@ class RootPage extends Component<{
|
|||||||
<Route path="/404" component={PageNotFound} />
|
<Route path="/404" component={PageNotFound} />
|
||||||
<Route path="/rules" component={RulesPage} />
|
<Route path="/rules" component={RulesPage} />
|
||||||
<Route path="/dev" component={DevPage} />
|
<Route path="/dev" component={DevPage} />
|
||||||
<AuthFlowRoute exact path="/" component={ProfilePage} />
|
|
||||||
|
{user.isGuest ? (
|
||||||
<AuthFlowRoute path="/" component={AuthPage} />
|
<AuthFlowRoute path="/" component={AuthPage} />
|
||||||
|
) : (
|
||||||
|
<AuthFlowRoute exact path="/" component={ProfilePage} />
|
||||||
|
)}
|
||||||
|
|
||||||
<Route component={PageNotFound} />
|
<Route component={PageNotFound} />
|
||||||
</Switch>
|
</Switch>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
Reference in New Issue
Block a user