♻️(frontend) Refactor Auth component for improved redirection logic

Move redirects from render
to a guarded useEffect
to avoid triggering multiple redirects
on every re-render.
This commit is contained in:
rvveber
2025-10-08 16:40:37 +02:00
committed by Anthony LC
parent af01c6e466
commit 546f97c956
5 changed files with 92 additions and 46 deletions

View File

@@ -12,6 +12,7 @@ and this project adheres to
### Changed ### Changed
- ♻️(frontend) Refactor Auth component for improved redirection logic #1461
- ♻️(frontend) replace Arial font-family with token font #1411 - ♻️(frontend) replace Arial font-family with token font #1411
- ♿(frontend) improve accessibility: - ♿(frontend) improve accessibility:
- ♿(frontend) enable enter key to open documentss #1354 - ♿(frontend) enable enter key to open documentss #1354

View File

@@ -1,8 +1,7 @@
import { Loader } from '@openfun/cunningham-react';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import { PropsWithChildren } from 'react'; import { PropsWithChildren, useEffect, useState } from 'react';
import { Box } from '@/components'; import { Loading } from '@/components';
import { useConfig } from '@/core'; import { useConfig } from '@/core';
import { HOME_URL } from '../conf'; import { HOME_URL } from '../conf';
@@ -14,57 +13,65 @@ export const Auth = ({ children }: PropsWithChildren) => {
useAuth(); useAuth();
const { replace, pathname } = useRouter(); const { replace, pathname } = useRouter();
const { data: config } = useConfig(); const { data: config } = useConfig();
const [isRedirecting, setIsRedirecting] = useState(false);
if (isLoading && !isFetchedAfterMount) {
return (
<Box $height="100vh" $width="100vw" $align="center" $justify="center">
<Loader />
</Box>
);
}
/** /**
* If the user is authenticated and wanted initially to access a document, * If the user is authenticated and initially wanted to access a specific page, redirect him to that page now.
* we redirect to the document page.
*/ */
if (authenticated) { useEffect(() => {
if (!authenticated || isRedirecting) {
return;
}
const authUrl = getAuthUrl(); const authUrl = getAuthUrl();
if (authUrl) { if (authUrl) {
void replace(authUrl); setIsRedirecting(true);
return ( void replace(authUrl).then(() => setIsRedirecting(false));
<Box $height="100vh" $width="100vw" $align="center" $justify="center">
<Loader />
</Box>
);
} }
} }, [authenticated, isRedirecting, pathname, replace]);
/** /**
* If the user is not authenticated and the path is not allowed, we redirect to the login page. * If the user is not authenticated and not on a allowed pages
*/ */
if (!authenticated && !pathAllowed) { useEffect(() => {
if (isLoading || authenticated || pathAllowed || isRedirecting) {
return;
}
/**
* The homepage feature is enabled, redirect them to the homepage
*/
if (config?.FRONTEND_HOMEPAGE_FEATURE_ENABLED) { if (config?.FRONTEND_HOMEPAGE_FEATURE_ENABLED) {
void replace(HOME_URL); if (pathname !== HOME_URL) {
} else { setIsRedirecting(true);
gotoLogin(); void replace(HOME_URL).then(() => setIsRedirecting(false));
} }
return (
<Box $height="100vh" $width="100vw" $align="center" $justify="center">
<Loader />
</Box>
);
}
/** return;
* If the user is authenticated and the path is the home page, we redirect to the index. }
*/
if (pathname === HOME_URL && authenticated) { /**
void replace('/'); * Redirect them to login page
return ( */
<Box $height="100vh" $width="100vw" $align="center" $justify="center"> setIsRedirecting(true);
<Loader /> gotoLogin();
</Box> }, [
); authenticated,
pathAllowed,
config?.FRONTEND_HOMEPAGE_FEATURE_ENABLED,
replace,
isLoading,
isRedirecting,
pathname,
]);
const shouldShowLoader =
(isLoading && !isFetchedAfterMount) ||
isRedirecting ||
(!authenticated && !pathAllowed);
if (shouldShowLoader) {
return <Loading $height="100vh" $width="100vw" />;
} }
return children; return children;

View File

@@ -1,6 +1,6 @@
import { baseApiUrl } from '@/api'; import { baseApiUrl } from '@/api';
export const HOME_URL = '/home'; export const HOME_URL: string = '/home';
export const LOGIN_URL = `${baseApiUrl()}authenticate/`; export const LOGIN_URL = `${baseApiUrl()}authenticate/`;
export const LOGOUT_URL = `${baseApiUrl()}logout/`; export const LOGOUT_URL = `${baseApiUrl()}logout/`;
export const PATH_AUTH_LOCAL_STORAGE = 'docs-path-auth'; export const PATH_AUTH_LOCAL_STORAGE = 'docs-path-auth';

View File

@@ -1,7 +1,15 @@
import { terminateCrispSession } from '@/services/Crisp'; import { terminateCrispSession } from '@/services/Crisp';
import { LOGIN_URL, LOGOUT_URL, PATH_AUTH_LOCAL_STORAGE } from './conf'; import {
HOME_URL,
LOGIN_URL,
LOGOUT_URL,
PATH_AUTH_LOCAL_STORAGE,
} from './conf';
/**
* Get the stored auth URL from local storage
*/
export const getAuthUrl = () => { export const getAuthUrl = () => {
const path_auth = localStorage.getItem(PATH_AUTH_LOCAL_STORAGE); const path_auth = localStorage.getItem(PATH_AUTH_LOCAL_STORAGE);
if (path_auth) { if (path_auth) {
@@ -10,8 +18,15 @@ export const getAuthUrl = () => {
} }
}; };
/**
* Store the current path in local storage if it's not the homepage or root
* so we can redirect the user to this path after login
*/
export const setAuthUrl = () => { export const setAuthUrl = () => {
if (window.location.pathname !== '/') { if (
window.location.pathname !== '/' &&
window.location.pathname !== `${HOME_URL}/`
) {
localStorage.setItem(PATH_AUTH_LOCAL_STORAGE, window.location.pathname); localStorage.setItem(PATH_AUTH_LOCAL_STORAGE, window.location.pathname);
} }
}; };

View File

@@ -1,7 +1,30 @@
import { useRouter } from 'next/router';
import { useEffect } from 'react';
import { Loading } from '@/components';
import { useAuth } from '@/features/auth';
import { HomeContent } from '@/features/home'; import { HomeContent } from '@/features/home';
import { NextPageWithLayout } from '@/types/next'; import { NextPageWithLayout } from '@/types/next';
const Page: NextPageWithLayout = () => { const Page: NextPageWithLayout = () => {
const { authenticated } = useAuth();
const { replace } = useRouter();
/**
* If the user is authenticated we redirect him to the index page (grid).
*/
useEffect(() => {
if (!authenticated) {
return;
}
void replace('/');
}, [authenticated, replace]);
if (authenticated) {
return <Loading $height="100vh" $width="100vw" />;
}
return <HomeContent />; return <HomeContent />;
}; };