import React, { ComponentType, FC, useContext } from 'react'; import { BlurCircular } from '@material-ui/icons'; import { NonAuthRoutes } from 'api/routes'; import { Redirect } from 'react-router-dom'; import { Unauthorized } from 'components/Unauthorized/Unauthorized'; import { AuthContext } from 'components/Auth/AuthContext'; import { useRole } from 'hooks/useRole'; import { useAuth } from 'hooks/useAuth'; const HandleIsAuth: FC<{ isAuth: boolean }> = ({ isAuth }) => isAuth ? ( <Unauthorized /> ) : ( <Redirect to={{ pathname: `${NonAuthRoutes.auth}${NonAuthRoutes.signIn}` }} /> ); interface WithAuthProps { allowedRoles: string[]; } /* eslint-disable react/jsx-props-no-spreading */ /** * * @param WrappedComponent component to be wrapped by the authentication control. * This creates a "personal area" in the working implementation. * @returns {FC<T>} wrapped component. */ export const withAuthorization = (allowedRoles: string[]) => < T extends WithAuthProps = WithAuthProps >( WrappedComponent: React.ComponentType<T>, ): FC<T> => { // Creating the inner component. The calculated Props type here is the where the magic happens. const ComponentWithAuthorization: FC<T> = ( props: Omit<T, keyof WithAuthProps>, ) => { const [role, setRole, isLoading] = useRole(); const [isAuth] = useAuth(); console.log(`ROLE ${role} AUTH ${isAuth} LOADED ${isLoading}`); /* eslint-disable no-nested-ternary */ return isAuth === null || isLoading ? ( <BlurCircular /> ) : // props comes afterwards so the can override the default ones. allowedRoles.includes(role) && isAuth ? ( <WrappedComponent {...(props as T)} /> ) : ( <HandleIsAuth isAuth={!!isAuth} /> ); }; return ComponentWithAuthorization; };