11"use client"
22
33import { lazy , Suspense , useCallback , useEffect , useRef , useState } from "react"
4-
5- /** Delay before showing unauthenticated overlay/redirect so auth persistence can restore (avoids sidebar flash). */
6- const AUTH_SETTLE_MS = 220
74import { usePathname } from "next/navigation"
85import { Sidebar } from "@/components/app/Sidebar"
96import { ErrorBoundary } from "@/components/app/ErrorBoundary"
@@ -14,7 +11,7 @@ import { ShellLayoutProvider } from "@/context/ShellLayoutContext"
1411import OnboardingModal from "@/components/app/OnboardingModal"
1512import { fetchUserMemory , fetchUserStatus , updateUserMemory , type UpdateUserMemoryInput } from "@/lib/api"
1613import { trackFrontendEvent } from "@/lib/frontendAnalytics"
17- import { getSettingsCache , setSettingsCache } from "@/lib/settingsCache"
14+ import { setSettingsCache } from "@/lib/settingsCache"
1815
1916const GoldenGuideModal = lazy ( ( ) => import ( "@/components/app/GoldenGuideModal" ) ) as React . LazyExoticComponent < React . ComponentType < { COLORS : ThemeColors } > >
2017const SearchModal = lazy ( ( ) => import ( "@/components/app/SearchModal" ) ) as React . LazyExoticComponent < React . ComponentType < { COLORS : ThemeColors } > >
@@ -25,78 +22,23 @@ function hasCompletedOnboarding(memory: Record<string, unknown>): boolean {
2522 return typeof completedAt === "string" && completedAt . trim ( ) . length > 0
2623}
2724
28- function readCachedOnboardingSnapshot ( ) : { required : boolean ; memory : Record < string , unknown > } {
29- const cached = getSettingsCache ( )
30- const memory = cached ?. memory ?? { }
31- return {
32- required : ! hasCompletedOnboarding ( memory ) ,
33- memory,
34- }
35- }
36-
3725function AuthGate ( { children } : { children : React . ReactNode } ) {
3826 const COLORS = useThemeColors ( )
3927 const { user, loading } = useAuth ( )
40- const [ showUnauthOverlay , setShowUnauthOverlay ] = useState ( false )
41- const settleTimerRef = useRef < ReturnType < typeof setTimeout > | null > ( null )
4228
4329 useEffect ( ( ) => {
44- if ( loading || user ) {
45- if ( settleTimerRef . current ) {
46- clearTimeout ( settleTimerRef . current )
47- settleTimerRef . current = null
48- }
49- setShowUnauthOverlay ( false )
50- return
51- }
52- settleTimerRef . current = setTimeout ( ( ) => {
53- settleTimerRef . current = null
54- setShowUnauthOverlay ( true )
30+ if ( ! loading && ! user ) {
5531 try { sessionStorage . setItem ( "fp-auth-redirect" , window . location . pathname ) } catch { }
5632 window . location . href = "/"
57- } , AUTH_SETTLE_MS )
58- return ( ) => {
59- if ( settleTimerRef . current ) {
60- clearTimeout ( settleTimerRef . current )
61- settleTimerRef . current = null
62- }
6333 }
6434 } , [ loading , user ] )
6535
66- const renderGateStatus = ( message : string ) => (
67- < div
68- style = { {
69- position : "fixed" ,
70- inset : 0 ,
71- zIndex : 60 ,
72- background : COLORS . bg ,
73- display : "flex" ,
74- alignItems : "center" ,
75- justifyContent : "center" ,
76- } }
77- >
78- < span
79- style = { {
80- color : COLORS . textSecondary ,
81- fontSize : "14px" ,
82- fontFamily : "var(--font-geist-sans), sans-serif" ,
83- letterSpacing : "0.01em" ,
84- } }
85- >
86- { message }
87- </ span >
88- </ div >
89- )
90-
91- // Always render the shell (sidebar, main, topbar, input) so it stays visible and cached.
92- // Only show redirect overlay after auth has "settled" as unauthenticated (short delay
93- // so Firebase persistence can restore the session and we don't flash-hide the sidebar).
94- return (
95- < >
96- { children }
97- { showUnauthOverlay ? renderGateStatus ( "Redirecting to sign-in..." ) : null }
98- </ >
99- )
36+ if ( user ) return < > { children } </ >
37+ // First visit, auth loading
38+ if ( loading ) return < div style = { { position : "fixed" , inset : 0 , zIndex : 60 , background : COLORS . bg } } />
39+ // Not authenticated
40+ if ( ! user ) return < div style = { { position : "fixed" , inset : 0 , zIndex : 60 , background : COLORS . bg } } />
41+ return < > { children } </ >
10042}
10143
10244function ShellInner ( { children } : { children : React . ReactNode } ) {
@@ -105,16 +47,17 @@ function ShellInner({ children }: { children: React.ReactNode }) {
10547 const { user, loading } = useAuth ( )
10648 const pathname = usePathname ( )
10749 const { guideOpen, searchOpen } = state
108- const cachedOnboarding = useRef ( readCachedOnboardingSnapshot ( ) )
10950 const prefetchedRef = useRef ( false )
110- const [ onboardingRequired , setOnboardingRequired ] = useState ( cachedOnboarding . current . required )
51+ const [ onboardingChecking , setOnboardingChecking ] = useState ( true )
52+ const [ onboardingRequired , setOnboardingRequired ] = useState ( false )
11153 const [ onboardingSubmitting , setOnboardingSubmitting ] = useState ( false )
11254 const [ onboardingError , setOnboardingError ] = useState < string | null > ( null )
113- const [ memorySnapshot , setMemorySnapshot ] = useState < Record < string , unknown > > ( cachedOnboarding . current . memory )
55+ const [ memorySnapshot , setMemorySnapshot ] = useState < Record < string , unknown > > ( { } )
11456
11557 useEffect ( ( ) => {
11658 if ( user ) return
11759 prefetchedRef . current = false
60+ setOnboardingChecking ( true )
11861 setOnboardingRequired ( false )
11962 setOnboardingError ( null )
12063 setOnboardingSubmitting ( false )
@@ -125,6 +68,7 @@ function ShellInner({ children }: { children: React.ReactNode }) {
12568 if ( loading || ! user ) return
12669 if ( prefetchedRef . current ) return
12770 prefetchedRef . current = true
71+ setOnboardingChecking ( true )
12872 void Promise . all ( [ fetchUserMemory ( ) , fetchUserStatus ( ) ] )
12973 . then ( ( [ memoryRes , statusRes ] ) => {
13074 setSettingsCache ( {
@@ -137,6 +81,9 @@ function ShellInner({ children }: { children: React.ReactNode }) {
13781 setOnboardingRequired ( ! hasCompletedOnboarding ( memoryRes . memory ) )
13882 } )
13983 . catch ( ( ) => { } )
84+ . finally ( ( ) => {
85+ setOnboardingChecking ( false )
86+ } )
14087 } , [ loading , user ] )
14188
14289 const handleOnboardingSubmit = useCallback ( async ( payload : UpdateUserMemoryInput ) => {
@@ -184,7 +131,18 @@ function ShellInner({ children }: { children: React.ReactNode }) {
184131 < DeleteConfirmModal COLORS = { COLORS } />
185132 </ Suspense >
186133
187- { ! loading && user && onboardingRequired && (
134+ { ! loading && user && onboardingChecking && (
135+ < div
136+ style = { {
137+ position : "fixed" ,
138+ inset : 0 ,
139+ zIndex : 2400 ,
140+ background : COLORS . bg ,
141+ } }
142+ />
143+ ) }
144+
145+ { ! loading && user && ! onboardingChecking && onboardingRequired && (
188146 < OnboardingModal
189147 mode = "onboarding"
190148 initialMemory = { memorySnapshot }
0 commit comments