Download src/lib/ui/loading-visibility.ts from asnannp/DocDoeAI: direct link, hf CLI and curl.
- Browser
- Download file 6.47 kB
-
https://huggingface.co/spaces/asnannp/DocDoeAI/resolve/main/src/lib/ui/loading-visibility.ts
- Command line
-
hf download hf://spaces/asnannp/DocDoeAI/src/lib/ui/loading-visibility.ts
-
curl -L -o loading-visibility.ts https://huggingface.co/spaces/asnannp/DocDoeAI/resolve/main/src/lib/ui/loading-visibility.ts
6.47 kB
| /** | |
| * Pure helpers for app boot / onboarding loader visibility. | |
| * Kept free of React so unit tests can assert real decision logic | |
| * without mounting the full app tree. | |
| */ | |
| export const PUBLIC_AUTH_PATHS = new Set([ | |
| "/", | |
| "/login", | |
| "/signup", | |
| "/forgot-password", | |
| "/reset-password", | |
| "/privacy", | |
| "/terms", | |
| "/refund", | |
| "/refunds", | |
| "/contact", | |
| "/feedback", | |
| "/report-bug", | |
| "/logout", | |
| "/pricing", | |
| "/preview/tuition", | |
| "/preview/chapter/sound-waves", | |
| ]); | |
| const PUBLIC_AUTH_PREFIXES = [ | |
| "/auth/", | |
| "/compare/", | |
| "/flashcards/", | |
| "/kerala-plus-one", | |
| "/kerala-plus-two", | |
| "/learn/", | |
| "/quiz/", | |
| "/revision/", | |
| ] as const; | |
| /** | |
| * Known signed-in app surfaces. Auth gates must use this allowlist so | |
| * unknown URLs fall through to Next.js `not-found` instead of login?next=. | |
| */ | |
| const PROTECTED_APP_PATHS = new Set([ | |
| "/onboarding", | |
| "/dashboard", | |
| "/home", | |
| "/home-next", | |
| "/tuition", | |
| "/study-chat", | |
| "/learn-anything", | |
| "/bookmarks", | |
| "/classes", | |
| "/notifications", | |
| "/pyq-analyzer", | |
| "/analytics", | |
| "/documents", | |
| "/exam-coach", | |
| "/first-hour", | |
| "/generate", | |
| "/last-night", | |
| "/library", | |
| "/missions", | |
| "/mode-selection", | |
| "/notes", | |
| "/previous-papers", | |
| "/profile", | |
| "/progress", | |
| "/quizzes", | |
| "/settings", | |
| "/sources", | |
| "/syllabus", | |
| "/teach", | |
| "/upload", | |
| "/video", | |
| "/videos", | |
| "/video-generator", | |
| ]); | |
| const PROTECTED_APP_PREFIXES = [ | |
| "/admin", | |
| "/onboarding", | |
| "/dashboard", | |
| "/home", | |
| "/home-next", | |
| "/tuition", | |
| "/study-chat", | |
| "/learn-anything", | |
| "/bookmarks", | |
| "/classes", | |
| "/notifications", | |
| "/pyq-analyzer", | |
| "/analytics", | |
| "/documents", | |
| "/exam-coach", | |
| "/first-hour", | |
| "/generate", | |
| "/last-night", | |
| "/library", | |
| "/missions", | |
| "/mode-selection", | |
| "/notes", | |
| "/previous-papers", | |
| "/profile", | |
| "/progress", | |
| "/quizzes", | |
| "/settings", | |
| "/sources", | |
| "/syllabus", | |
| "/teach", | |
| "/upload", | |
| "/video", | |
| "/videos", | |
| "/video-generator", | |
| ] as const; | |
| export function isPublicAuthPath(pathname: string | null | undefined): boolean { | |
| if (!pathname) return true; | |
| if (PUBLIC_AUTH_PATHS.has(pathname)) return true; | |
| if (pathname === "/learn") return true; | |
| return PUBLIC_AUTH_PREFIXES.some((prefix) => pathname.startsWith(prefix)); | |
| } | |
| function matchesPathOrPrefix(pathname: string, prefix: string): boolean { | |
| return pathname === prefix || pathname.startsWith(`${prefix}/`); | |
| } | |
| /** | |
| * True only for known workspace / admin app routes. | |
| * Unknown paths are intentionally false so RootLoadingGuard does not | |
| * intercept them into login — Next.js not-found handles those. | |
| */ | |
| export function isProtectedAppPath(pathname: string | null | undefined): boolean { | |
| if (!pathname) return false; | |
| if (isPublicAuthPath(pathname)) return false; | |
| if (PROTECTED_APP_PATHS.has(pathname)) return true; | |
| return PROTECTED_APP_PREFIXES.some((prefix) => | |
| matchesPathOrPrefix(pathname, prefix), | |
| ); | |
| } | |
| export type AppLoadingInput = { | |
| hasAuthContext: boolean; | |
| isAuthLoading: boolean; | |
| isRedirecting: boolean; | |
| pathname: string | null | undefined; | |
| }; | |
| /** | |
| * Full-screen branded boot loader. | |
| * - Always when auth context is missing (provider not ready). | |
| * - Always while an onboarding redirect is in flight. | |
| * - Never over marketing/auth pages during a routine session probe | |
| * (those pages should paint immediately). | |
| * - Never over unknown URLs (real 404 must paint, not workspace loader). | |
| * - On known protected student routes while auth is still resolving. | |
| */ | |
| export function shouldShowAppLoadingScreen(input: AppLoadingInput): boolean { | |
| if (!input.hasAuthContext) return true; | |
| if (input.isRedirecting) return true; | |
| if (!isProtectedAppPath(input.pathname)) return false; | |
| return input.isAuthLoading; | |
| } | |
| /** | |
| * Whether the real route tree may render under the boot guard. | |
| * Public pages and unknown URLs render even while auth is probing; | |
| * known protected routes wait. | |
| */ | |
| export function shouldRenderAppChildren(input: AppLoadingInput): boolean { | |
| if (!input.hasAuthContext) return false; | |
| if (input.isRedirecting) return false; | |
| if (!isProtectedAppPath(input.pathname)) return true; | |
| return !input.isAuthLoading; | |
| } | |
| export type OnboardingRedirectTarget = "/onboarding" | "/home" | null; | |
| export function resolveProtectedRouteRedirect(input: { | |
| hasUser: boolean; | |
| isAuthLoading: boolean; | |
| pathname: string | null | undefined; | |
| }): string | null { | |
| const path = input.pathname ?? "/"; | |
| if (input.isAuthLoading || input.hasUser) return null; | |
| if (!isProtectedAppPath(path)) return null; | |
| const candidate = path.startsWith("/") && !path.startsWith("//") ? path : "/home"; | |
| const authLoop = | |
| candidate === "/login" || | |
| candidate === "/signup" || | |
| candidate === "/logout" || | |
| candidate.startsWith("/login?") || | |
| candidate.startsWith("/signup?") || | |
| candidate.startsWith("/logout?") || | |
| candidate.startsWith("/login/") || | |
| candidate.startsWith("/signup/") || | |
| candidate.startsWith("/logout/"); | |
| const safeNext = authLoop ? "/home" : candidate; | |
| return `/login?next=${encodeURIComponent(safeNext)}`; | |
| } | |
| /** | |
| * Resolve post-auth onboarding redirects. | |
| * `onboardingComplete === null` means the async check is still pending. | |
| */ | |
| export function resolveOnboardingRedirect(input: { | |
| hasUser: boolean; | |
| isAuthLoading: boolean; | |
| pathname: string | null | undefined; | |
| onboardingComplete: boolean | null; | |
| }): OnboardingRedirectTarget { | |
| if (input.isAuthLoading || !input.hasUser) return null; | |
| if (input.onboardingComplete === null) return null; | |
| const path = input.pathname ?? "/"; | |
| if (!input.onboardingComplete && path !== "/onboarding" && isProtectedAppPath(path)) { | |
| return "/onboarding"; | |
| } | |
| if (input.onboardingComplete && path === "/onboarding") { | |
| return "/home"; | |
| } | |
| return null; | |
| } | |
| /** Ease fake progress toward a soft cap — never claims 100% before unmount. */ | |
| export function nextBootProgress(current: number, cap = 94): number { | |
| if (current >= cap) return cap; | |
| const step = Math.max(1, Math.round((cap - current) * 0.12)); | |
| return Math.min(cap, current + step); | |
| } | |
| export const BOOT_STATUS_MESSAGES = [ | |
| "Starting DocDoe…", | |
| "Checking your session…", | |
| "Opening your workspace…", | |
| "Almost ready…", | |
| ] as const; | |
| export function bootStatusForProgress(progress: number): string { | |
| if (progress < 28) return BOOT_STATUS_MESSAGES[0]; | |
| if (progress < 55) return BOOT_STATUS_MESSAGES[1]; | |
| if (progress < 82) return BOOT_STATUS_MESSAGES[2]; | |
| return BOOT_STATUS_MESSAGES[3]; | |
| } | |