Extract device code into a separate view.

Convert more components from class components to functional.
Fix invalid finish state when client was auto approved
This commit is contained in:
ErickSkrauch
2024-12-14 13:16:29 +01:00
parent 3f0565e26b
commit af59cc033f
20 changed files with 241 additions and 315 deletions

View File

@@ -1,17 +1,16 @@
import React, { FC } from 'react';
import { Route, RouteProps } from 'react-router-dom';
import React, { FC, ComponentType } from 'react';
import { Route, RouteProps, RouteComponentProps } from 'react-router-dom';
import AuthFlowRouteContents from './AuthFlowRouteContents';
// Make "component" prop required
type Props = Omit<RouteProps, 'component'> & Required<Pick<RouteProps, 'component'>>;
type Props = Omit<RouteProps, 'component'> & {
component: ComponentType<RouteComponentProps>;
};
const AuthFlowRoute: FC<Props> = ({ component: Component, ...props }) => {
return (
<Route
{...props}
render={(routerProps) => <AuthFlowRouteContents routerProps={routerProps} component={Component} />}
/>
<Route {...props} render={(routerProps) => <AuthFlowRouteContents component={Component} {...routerProps} />} />
);
};

View File

@@ -43,7 +43,7 @@ describe('AuthFlowRouteContents', () => {
(authFlow.handleRequest as any).callsArg(2);
render(<AuthFlowRouteContents routerProps={routerProps} component={Component} />);
render(<AuthFlowRouteContents component={Component} {...routerProps} />);
const component = screen.getByTestId('test-component');

View File

@@ -1,89 +1,34 @@
import React from 'react';
import { Redirect, RouteComponentProps } from 'react-router-dom';
import React, { FC, ReactElement, ComponentType, useEffect, useState } from 'react';
import { RouteComponentProps } from 'react-router-dom';
import { useIsMounted } from 'app/hooks';
import authFlow from 'app/services/authFlow';
interface Props {
component: React.ComponentType<RouteComponentProps> | React.ComponentType<any>;
routerProps: RouteComponentProps;
interface Props extends RouteComponentProps {
component: ComponentType<RouteComponentProps>;
}
interface State {
access: null | 'rejected' | 'allowed';
component: React.ReactElement | null;
}
export default class AuthFlowRouteContents extends React.Component<Props, State> {
state: State = {
access: null,
component: null,
};
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() {
this.mounted = true;
this.handleProps(this.props);
}
componentDidUpdate() {
this.handleProps(this.props);
}
componentWillUnmount() {
this.mounted = false;
}
render() {
return this.state.component;
}
handleProps(props: Props) {
const { routerProps } = props;
const AuthFlowRouteContents: FC<Props> = ({ component: WantedComponent, location, match, history }) => {
const isMounted = useIsMounted();
const [component, setComponent] = useState<ReactElement | null>(null);
useEffect(() => {
authFlow.handleRequest(
{
path: routerProps.location.pathname,
params: routerProps.match.params,
query: new URLSearchParams(routerProps.location.search),
path: location.pathname,
params: match.params,
query: new URLSearchParams(location.search),
},
history.push,
() => {
if (isMounted()) {
setComponent(<WantedComponent history={history} location={location} match={match} />);
}
},
this.onRedirect.bind(this),
this.onRouteAllowed.bind(this, props),
);
}
}, [location.pathname, location.search]);
onRedirect(path: string) {
if (!this.mounted) {
return;
}
return component;
};
this.setState({
access: 'rejected',
component: <Redirect to={path} />,
});
}
onRouteAllowed(props: Props): void {
if (!this.mounted) {
return;
}
const { component: Component } = props;
this.setState({
access: 'allowed',
component: <Component {...props.routerProps} />,
});
}
}
export default AuthFlowRouteContents;