// src/contexts/AuthContext.tsx import React, { createContext, useState, useEffect, useContext, useMemo, useCallback } from 'react'; import { Platform, ActivityIndicator, View, StyleSheet } from 'react-native'; // Import Platform import apiClient from '../api/client'; import { useTheme } from 'react-native-paper'; import { getAccessToken, getRefreshToken, storeTokens, clearTokens } from '../api/client'; // Define UserRole enum matching the backend enum UserRole { USER = 'user', ADMIN = 'admin', } interface UserData { username: string; name: string; role: UserRole; // Add other user fields if needed } interface AuthContextData { isAuthenticated: boolean; isLoading: boolean; user: UserData | null; // Add user data to context login: (username: string, password: string) => Promise; logout: () => Promise; } const AuthContext = createContext({ isAuthenticated: false, isLoading: true, user: null, // Initialize user as null login: async () => { throw new Error('AuthContext not initialized'); }, logout: async () => { throw new Error('AuthContext not initialized'); }, }); interface AuthProviderProps { children: React.ReactNode; } export const AuthProvider: React.FC = ({ children }) => { const [isLoading, setIsLoading] = useState(true); const [isAuthenticatedState, setIsAuthenticatedState] = useState(false); const [userState, setUserState] = useState(null); // State for user data // Function to fetch user data const fetchUserData = useCallback(async () => { try { console.log("[AuthContext] fetchUserData: Fetching /user/me"); const response = await apiClient.get('/user/me'); console.log("[AuthContext] fetchUserData: User data received:", response.data); setUserState(response.data); return response.data; } catch (error: any) { console.error("[AuthContext] fetchUserData: Error fetching user data:", error.response?.data || error.message); // If fetching user fails (e.g., token expired mid-session), log out await clearTokens(); setIsAuthenticatedState(false); setUserState(null); return null; } }, []); const checkAuthStatus = useCallback(async () => { const token = await getAccessToken(); const hasToken = !!token; if (hasToken) { console.log("[AuthContext] checkAuthStatus: Token found, fetching user data."); const userData = await fetchUserData(); if (userData) { setIsAuthenticatedState(true); } else { // Fetch failed, already handled logout in fetchUserData setIsAuthenticatedState(false); } } else { console.log("[AuthContext] checkAuthStatus: No token found."); setIsAuthenticatedState(false); setUserState(null); } return isAuthenticatedState; // Return the updated state }, [fetchUserData, isAuthenticatedState]); // Added isAuthenticatedState dependency const loadInitialAuth = useCallback(async () => { setIsLoading(true); try { console.log("[AuthContext] loadInitialAuth: Checking initial auth status"); await checkAuthStatus(); console.log("[AuthContext] loadInitialAuth: Initial check complete."); } catch (error) { console.error("[AuthContext] loadInitialAuth: Error during initial auth check:", error); await clearTokens(); setIsAuthenticatedState(false); setUserState(null); } finally { setIsLoading(false); } }, [checkAuthStatus]); useEffect(() => { loadInitialAuth(); }, [loadInitialAuth]); const login = useCallback(async (username: string, password: string) => { console.log("[AuthContext] login: Function called with:", username); try { // ... (existing login API call) ... const response = await apiClient.post( '/auth/login', 'grant_type=password&username=' + encodeURIComponent(username) + '&password=' + encodeURIComponent(password) + '&scope=&client_id=&client_secret=', { headers: { 'accept': 'application/json', 'Content-Type': 'application/x-www-form-urlencoded', }, } ); const { access_token, refresh_token } = response.data; // ... (existing token validation) ... if (!access_token || typeof access_token !== 'string' || !refresh_token) { console.error("[AuthContext] login: Invalid token structure received:", response.data); throw new Error('Invalid token received from server.'); } console.log('[AuthContext] login: Login successful, storing tokens.'); await storeTokens(access_token, refresh_token); // Fetch user data immediately after successful login const userData = await fetchUserData(); if (userData) { setIsAuthenticatedState(true); } else { // Should not happen if login succeeded, but handle defensively throw new Error('Failed to fetch user data after login.'); } } catch (error: any) { // ... (existing error handling) ... console.error("[AuthContext] login: Caught Error Object:", error); await clearTokens(); setIsAuthenticatedState(false); setUserState(null); throw error; } }, [fetchUserData]); // Added fetchUserData dependency const logout = useCallback(async () => { console.log('[AuthContext] logout: Logging out.'); const refreshToken = await getRefreshToken(); // ... (existing backend logout call) ... try { if (refreshToken) { console.log('[AuthContext] logout: Calling backend /auth/logout'); await apiClient.post("/auth/logout", { refresh_token: refreshToken }); console.log('[AuthContext] logout: Backend logout call successful (or ignored error).'); } } catch (error: any) { console.error('[AuthContext] logout: Error calling backend logout:', error.response?.data || error.message); } finally { await clearTokens(); setIsAuthenticatedState(false); setUserState(null); // Clear user data on logout console.log('[AuthContext] logout: Local tokens cleared and state updated.'); } }, []); const contextValue = useMemo(() => ({ isAuthenticated: isAuthenticatedState, isLoading, user: userState, // Provide user state login, logout, }), [isAuthenticatedState, isLoading, userState, login, logout]); // Added userState dependency // ... (rest of the component: Provider, useAuth, AuthLoadingScreen) ... return ( {children} ); }; export const useAuth = () => { const context = useContext(AuthContext); if (!context) { throw new Error('useAuth must be used within an AuthProvider'); } return context; }; export const AuthLoadingScreen: React.FC = () => { const theme = useTheme(); const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: theme.colors.background } }); return (); } // Export UserRole if needed elsewhere export { UserRole };