jan/web-app/src/providers/AuthProvider.tsx
Dinh Long Nguyen 9f72debc17 fix: thread item overfetching (#6699)
* fix: thread item overfetching

* chore: cleanup left over import
2025-10-01 22:53:53 +07:00

137 lines
4.1 KiB
TypeScript

/**
* Authentication Provider
* Initializes the auth service and sets up event listeners
*/
import { useCallback, useEffect, useState, ReactNode } from 'react'
import { PlatformFeature } from '@/lib/platform/types'
import { PlatformFeatures } from '@/lib/platform/const'
import { initializeAuthStore, getAuthStore } from '@/hooks/useAuth'
import { useThreads } from '@/hooks/useThreads'
import { useMessages } from '@/hooks/useMessages'
import { usePrompt } from '@/hooks/usePrompt'
import { useAppState } from '@/hooks/useAppState'
import { useNavigate } from '@tanstack/react-router'
import { useServiceHub } from '@/hooks/useServiceHub'
interface AuthProviderProps {
children: ReactNode
}
export function AuthProvider({ children }: AuthProviderProps) {
const [isReady, setIsReady] = useState(false)
const navigate = useNavigate()
const serviceHub = useServiceHub()
// Check if authentication is enabled for this platform
const isAuthenticationEnabled =
PlatformFeatures[PlatformFeature.AUTHENTICATION]
// Fetch user data when user logs in
const fetchUserData = useCallback(async () => {
try {
const { setThreads } = useThreads.getState()
// Fetch threads first
const threads = await serviceHub.threads().fetchThreads()
setThreads(threads)
} catch (error) {
console.error('Failed to fetch user data:', error)
}
}, [serviceHub])
// Reset all app data when user logs out
const resetAppData = useCallback(() => {
// Clear all threads (including favorites)
const { clearAllThreads, setCurrentThreadId } = useThreads.getState()
clearAllThreads()
setCurrentThreadId(undefined)
// Clear all messages
const { clearAllMessages } = useMessages.getState()
clearAllMessages()
// Reset prompt
const { resetPrompt } = usePrompt.getState()
resetPrompt()
// Clear app state (streaming, tokens, errors, etc.)
const { clearAppState } = useAppState.getState()
clearAppState()
// Navigate back to home to ensure clean state
navigate({ to: '/', replace: true })
}, [navigate])
useEffect(() => {
if (!isAuthenticationEnabled) {
setIsReady(true)
return
}
const initializeAuth = async () => {
try {
const { getSharedAuthService } = await import('@jan/extensions-web')
const authService = getSharedAuthService()
await initializeAuthStore(authService)
setIsReady(true)
} catch (error) {
console.error('Failed to initialize auth service:', error)
setIsReady(true) // Still render to show error state
}
}
initializeAuth()
}, [isAuthenticationEnabled])
// Listen for auth state changes across tabs - setup after auth service is ready
useEffect(() => {
if (!isAuthenticationEnabled || !isReady) {
return
}
const handleAuthEvent = (event: MessageEvent) => {
// Listen for all auth events, not just login/logout
if (event.data?.type?.startsWith('auth:')) {
const authStore = getAuthStore()
// Handle different auth events
if (event.data.type === 'auth:logout') {
// Reset all app data first on logout
resetAppData()
}
// Reload auth state when auth events are received
// For login events, force refresh the user profile
if (event.data.type === 'auth:login') {
// Force refresh user profile on login events (forceRefresh=true)
authStore.loadAuthState(true).then(() => {
// Also fetch user data (threads, messages)
fetchUserData()
})
} else {
// For other events, just reload auth state without forcing refresh
authStore.loadAuthState()
}
}
}
// Use the auth store's subscribeToAuthEvents method
const authStore = getAuthStore()
if (!authStore.authService) {
return
}
const cleanupAuthListener = authStore.subscribeToAuthEvents(handleAuthEvent)
return () => {
cleanupAuthListener()
}
}, [isAuthenticationEnabled, isReady, fetchUserData, resetAppData])
return <>{isReady && children}</>
}