/* eslint-disable react-hooks/exhaustive-deps */ import { notification } from 'antd'; import getLocalStorageApi from 'api/browser/localstorage/get'; import loginApi from 'api/user/login'; import { Logout } from 'api/utils'; import Spinner from 'components/Spinner'; import { LOCALSTORAGE } from 'constants/localStorage'; import ROUTES from 'constants/routes'; import history from 'lib/history'; import React, { useEffect, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { useDispatch, useSelector } from 'react-redux'; import { matchPath, Redirect, useLocation } from 'react-router-dom'; import { Dispatch } from 'redux'; import { AppState } from 'store/reducers'; import { getInitialUserTokenRefreshToken } from 'store/utils'; import AppActions from 'types/actions'; import { UPDATE_USER_IS_FETCH } from 'types/actions/app'; import AppReducer from 'types/reducer/app'; import { routePermission } from 'utils/permission'; import routes from './routes'; import afterLogin from './utils'; function PrivateRoute({ children }: PrivateRouteProps): JSX.Element { const { pathname } = useLocation(); const mapRoutes = useMemo( () => new Map( routes.map((e) => { const currentPath = matchPath(pathname, { path: e.path, }); return [currentPath === null ? null : 'current', e]; }), ), [pathname], ); const { isUserFetching, isUserFetchingError, isLoggedIn: isLoggedInState, } = useSelector((state) => state.app); const { t } = useTranslation(['common']); const dispatch = useDispatch>(); const currentRoute = mapRoutes.get('current'); const navigateToLoginIfNotLoggedIn = (isLoggedIn = isLoggedInState): void => { dispatch({ type: UPDATE_USER_IS_FETCH, payload: { isUserFetching: false, }, }); if (!isLoggedIn) { history.push(ROUTES.LOGIN); } }; // eslint-disable-next-line sonarjs/cognitive-complexity useEffect(() => { (async (): Promise => { try { const isLocalStorageLoggedIn = getLocalStorageApi(LOCALSTORAGE.IS_LOGGED_IN) === 'true'; if (currentRoute) { const { isPrivate, key } = currentRoute; if (isPrivate) { const localStorageUserAuthToken = getInitialUserTokenRefreshToken(); if ( localStorageUserAuthToken && localStorageUserAuthToken.refreshJwt && isUserFetching ) { // localstorage token is present const { refreshJwt } = localStorageUserAuthToken; // renew web access token const response = await loginApi({ refreshToken: refreshJwt, }); if (response.statusCode === 200) { const route = routePermission[key]; // get all resource and put it over redux const userResponse = await afterLogin( response.payload.userId, response.payload.accessJwt, response.payload.refreshJwt, ); if ( userResponse && route.find((e) => e === userResponse.payload.role) === undefined ) { history.push(ROUTES.UN_AUTHORIZED); } } else { Logout(); notification.error({ message: response.error || t('something_went_wrong'), }); } } else { // user does have localstorage values navigateToLoginIfNotLoggedIn(isLocalStorageLoggedIn); } } else { // no need to fetch the user and make user fetching false if (getLocalStorageApi(LOCALSTORAGE.IS_LOGGED_IN) === 'true') { history.push(ROUTES.APPLICATION); } dispatch({ type: UPDATE_USER_IS_FETCH, payload: { isUserFetching: false, }, }); } } else if (pathname === ROUTES.HOME_PAGE) { // routing to application page over root page if (isLoggedInState) { history.push(ROUTES.APPLICATION); } else { navigateToLoginIfNotLoggedIn(); } } else { // not found navigateToLoginIfNotLoggedIn(isLocalStorageLoggedIn); } } catch (error) { // something went wrong history.push(ROUTES.SOMETHING_WENT_WRONG); } })(); }, [dispatch, isLoggedInState, currentRoute]); if (isUserFetchingError) { return ; } if (isUserFetching) { return ; } // NOTE: disabling this rule as there is no need to have div // eslint-disable-next-line react/jsx-no-useless-fragment return <>{children}; } interface PrivateRouteProps { children: React.ReactChild; } export default PrivateRoute;