Files
ToBePlayed-Frontend/src/components/RequireSession.jsx
T

35 lines
1.5 KiB
React

import {useEffect, useRef} from 'react';
import {Navigate, Outlet, useLocation} from 'react-router-dom';
import useSessionExpired from '../hooks/useSessionExpired.js';
import {sessionEvents} from '../utils/api.js';
import {hasSession} from '../utils/auth.js';
/**
* Layout route of the pages that need a login. Without a session it opens the login page, which comes back here;
* a 401 on one of these pages ends the session (see apiFetch).
*/
export default function RequireSession() {
const {pathname, search} = useLocation();
const expireSession = useSessionExpired();
// In a ref, so the listener is added once and not again on every navigation.
const expireRef = useRef(expireSession);
expireRef.current = expireSession;
// A render before the login page shows must not redirect again without the "session expired" state.
const expiring = useRef(false);
useEffect(() => {
// Parallel requests each fire; only the first ends the session. Another tab may have removed the flag already.
const onExpired = () => {
if (expiring.current) return;
expiring.current = true;
expireRef.current();
};
sessionEvents.addEventListener('expired', onExpired);
return () => sessionEvents.removeEventListener('expired', onExpired);
}, []);
return hasSession() || expiring.current
? <Outlet/>
: <Navigate to="/login" replace state={{from: pathname + search}}/>;
}