DocDoeAI / src /components /shared /RootLoadingGuard.tsx
asnannp's picture
deploy: sync backend to Space root (learn-lesson HF cache fix)
65d68f5
Raw History Blame Contribute Delete
3.01 kB
"use client";
import { useContext, useEffect, useState } from "react";
import { AuthContext } from "@/lib/auth/AuthProvider";
import { AppLoadingScreen } from "@/components/shared/AppLoadingScreen";
import { PageTransitionLoader } from "@/components/shared/PageTransitionLoader";
import { useRouter, usePathname } from "next/navigation";
import { isOnboardingCompleteEffective } from "@/lib/student-context";
import {
isProtectedAppPath,
resolveProtectedRouteRedirect,
resolveOnboardingRedirect,
shouldRenderAppChildren,
shouldShowAppLoadingScreen,
} from "@/lib/ui/loading-visibility";
export function RootLoadingGuard({ children }: { children: React.ReactNode }) {
const auth = useContext(AuthContext);
const router = useRouter();
const pathname = usePathname();
const [isRedirecting, setIsRedirecting] = useState(false);
useEffect(() => {
if (!isProtectedAppPath(pathname)) {
const t = window.setTimeout(() => setIsRedirecting(false), 0);
return () => window.clearTimeout(t);
}
if (!auth || auth.isLoading) {
// Avoid sync setState in effect; clear any stale redirect lock.
const t = window.setTimeout(() => setIsRedirecting(false), 0);
return () => window.clearTimeout(t);
}
const authTarget = resolveProtectedRouteRedirect({
hasUser: Boolean(auth.user),
isAuthLoading: auth.isLoading,
pathname,
});
if (authTarget) {
router.replace(authTarget);
return;
}
if (!auth.user) {
const t = window.setTimeout(() => setIsRedirecting(false), 0);
return () => window.clearTimeout(t);
}
let cancelled = false;
void isOnboardingCompleteEffective().then((done) => {
if (cancelled) return;
const target = resolveOnboardingRedirect({
hasUser: Boolean(auth.user),
isAuthLoading: auth.isLoading,
pathname,
onboardingComplete: done,
});
if (target) {
setIsRedirecting(true);
router.push(target);
} else {
setIsRedirecting(false);
}
});
return () => {
cancelled = true;
};
}, [auth, pathname, router]);
const protectedGuestRedirectPending = Boolean(
auth &&
!auth.isLoading &&
!auth.user &&
isProtectedAppPath(pathname),
);
const loadingInput = {
hasAuthContext: Boolean(auth),
isAuthLoading: auth?.isLoading ?? true,
isRedirecting: isRedirecting || protectedGuestRedirectPending,
pathname,
};
const showLoader = shouldShowAppLoadingScreen(loadingInput);
const renderChildren = shouldRenderAppChildren(loadingInput);
const isOnboardingPage = pathname === "/onboarding";
return (
<>
<AppLoadingScreen isVisible={showLoader} />
{/* Top-bar transitions only once children can paint — avoids stacking
the progress bar under the full-screen boot loader. */}
{renderChildren && !isOnboardingPage ? <PageTransitionLoader /> : null}
{renderChildren ? children : null}
</>
);
}