"use client" import { useEffect, useRef, useState, type ReactNode } from "react" import { usePathname } from "next/navigation" import { isAuthPublicPath } from "@/lib/auth/public-paths" import { ensureNativeAccessToken } from "@/lib/auth/native-session" import { hydrateNativeRuntimeConfig } from "@/lib/runtime-config/native" import { getRuntimeConfig } from "@/lib/runtime-config" import { useNativeRuntime } from "@/lib/platform" import { NativeLogin } from "@/components/mobile/native-login" import { ServerPicker } from "@/components/mobile/server-picker" function withTimeout(promise: Promise, ms: number): Promise { return Promise.race([ promise, new Promise((_, reject) => { setTimeout(() => reject(new Error("timeout")), ms) }), ]) } type AuthScreen = "picker" | "login" | "none" function initialAuthScreen(pathname: string): AuthScreen { if (isAuthPublicPath(pathname)) return "none" // SSR-safe: never read localStorage here (hydration mismatch breaks touch on Android). return "picker" } function NativeAuthScreen({ children }: { children: ReactNode }) { return (
{children}
) } /** * On native shells, show server picker / login inline (no client router redirects). */ export function NativeAuthGate({ children }: { children: ReactNode }) { const native = useNativeRuntime() const pathname = usePathname() const booted = useRef(false) const [authScreen, setAuthScreen] = useState(() => native ? initialAuthScreen(pathname) : "none" ) const [returnTo, setReturnTo] = useState("/mail/inbox") useEffect(() => { if (!native) return const dest = pathname.startsWith("/") ? pathname : "/mail/inbox" setReturnTo(dest) if (isAuthPublicPath(pathname)) { setAuthScreen("none") } }, [native, pathname]) useEffect(() => { if (!native || booted.current) return let cancelled = false async function bootstrap() { try { await withTimeout(hydrateNativeRuntimeConfig(), 3_000) } catch { /* localStorage fallback */ } if (cancelled) return if (isAuthPublicPath(pathname)) { setAuthScreen("none") return } if (!getRuntimeConfig()) { setAuthScreen("picker") return } let token: string | null = null try { token = await withTimeout(ensureNativeAccessToken(), 8_000) } catch { token = null } if (cancelled) return setAuthScreen(token ? "none" : "login") } void bootstrap().finally(() => { if (!cancelled) booted.current = true }) return () => { cancelled = true } }, [native, pathname]) if (!native) return children if (isAuthPublicPath(pathname) || authScreen === "none") { return children } if (authScreen === "picker") { return ( { setAuthScreen("login") }} /> ) } return ( ) }