import { useMemo } from 'react';
import { Navigate, useLocation } from 'react-router-dom';
import { useAtom } from 'jotai';
import {
backendUserAtom,
isAuthenticatedAtom,
authInitialCheckDoneAtom,
guestSessionAtom,
guestSessionHydratedAtom,
} from '@/atoms/auth';
import { hasSessionToken } from '@/services/backend';
import { ROUTES } from '@/utils/constants';
interface ProtectedRouteProps {
children: React.ReactNode;
/** Allow access while name/age are still missing (profile completion page). */
allowIncompleteProfile?: boolean;
}
const AuthBootSpinner = () => (
);
export function ProtectedRoute({ children, allowIncompleteProfile = false }: ProtectedRouteProps) {
const [isAuthenticated] = useAtom(isAuthenticatedAtom);
const [backendUser] = useAtom(backendUserAtom);
const [authInitialCheckDone] = useAtom(authInitialCheckDoneAtom);
const [guestSession] = useAtom(guestSessionAtom);
const [guestSessionHydrated] = useAtom(guestSessionHydratedAtom);
const location = useLocation();
// Stabilize Navigate `state` — a new object every render re-triggers Navigate's
// useEffect (deps include `state`) and can blow the update depth limit.
const loginRedirectState = useMemo(
() => ({ from: { pathname: location.pathname } }),
[location.pathname],
);
const hasToken = hasSessionToken();
const canUseBackendUser = Boolean(backendUser && hasToken);
if (!authInitialCheckDone) {
return ;
}
if (!guestSessionHydrated) {
return ;
}
if (!isAuthenticated && !guestSession) {
return ;
}
// Incomplete-profile gate only when a live session exists. Stale backendUser
// without a token must not bounce / ↔ /profile forever.
if (!allowIncompleteProfile && canUseBackendUser && !backendUser!.profileComplete) {
return ;
}
return {children}
;
}