import React, { createContext, useContext, useState, useEffect } from 'react'; import * as Haptics from 'expo-haptics'; import * as LocalAuthentication from 'expo-local-authentication'; import { storage } from '../utils/storage'; import { Platform } from 'react-native'; import { router } from 'expo-router'; import { ApiService } from '../services/api'; import { DebugLogger } from '../utils/logger'; interface User { id: string; name: string; email: string; avatar?: string; } interface AuthContextType { user: User | null; isLoading: boolean; signIn: (email: string, pass: string) => Promise; signOut: () => void; signUp: (name: string, email: string, pass: string) => Promise; updateProfile: (name: string, email: string) => Promise; syncUser: () => Promise; } const AuthContext = createContext(undefined); // Storage helper is now imported from utils/storage export function AuthProvider({ children }: { children: React.ReactNode }) { const [user, setUser] = useState(null); const [isLoading, setIsLoading] = useState(true); useEffect(() => { loadStoredToken(); }, []); const loadStoredToken = async () => { try { const token = await storage.get('user_token'); const storedUser = await storage.get('user_data'); if (token && storedUser) { setUser(JSON.parse(storedUser)); } } catch (e) { console.warn('Authentication failed during boot:', e); await storage.remove('user_token'); await storage.remove('user_data'); setUser(null); } finally { setIsLoading(false); } }; const signIn = async (email: string, pass: string) => { setIsLoading(true); try { const response = await ApiService.login(email, pass); const userData = response.data.user; const token = response.data.token; setUser(userData); await storage.save('user_token', token); await storage.save('user_data', JSON.stringify(userData)); await storage.save('saved_email', email); await storage.save('saved_pass', pass); DebugLogger.log(`User signed in: ${email}`, 'info'); if (Platform.OS !== 'web') Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success); } catch (error: any) { DebugLogger.log(`Login failed for ${email}: ${error.message}`, 'error'); if (Platform.OS !== 'web') Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error); throw error; } finally { setIsLoading(false); } }; const signUp = async (name: string, email: string, pass: string) => { setIsLoading(true); try { const response = await ApiService.register(name, email, pass); const userData = response.data.user; const token = response.data.token; setUser(userData); await storage.save('user_token', token); await storage.save('user_data', JSON.stringify(userData)); await storage.save('saved_email', email); await storage.save('saved_pass', pass); if (Platform.OS !== 'web') Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success); } catch (error) { if (Platform.OS !== 'web') Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error); throw error; } finally { setIsLoading(false); } }; const updateProfile = async (name: string, email: string) => { setIsLoading(true); try { const response = await ApiService.updateProfile(name, email); const userData = response.data.user; setUser(userData); await storage.save('user_data', JSON.stringify(userData)); if (Platform.OS !== 'web') Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success); } catch (error) { if (Platform.OS !== 'web') Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error); throw error; } finally { setIsLoading(false); } }; const signOut = async () => { setUser(null); await storage.remove('user_token'); await storage.remove('user_data'); if (Platform.OS !== 'web') Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium); DebugLogger.log('User signed out', 'info'); router.replace('/(auth)/login'); }; const syncUser = async () => { if (!user) return; try { const response = await ApiService.getUser(); const userData = response.data.user; setUser(userData); await storage.save('user_data', JSON.stringify(userData)); } catch (error: any) { DebugLogger.log(`Sync failed: ${error.message}`, 'error'); // If it's a 401, we might want to sign out, but for now let's be silent // to avoid the "Global refresh failed" loop that annoys the user. if (error.message.includes('Unauthenticated')) { console.warn('Silent sync failure: User is unauthenticated but staying in app.'); } else { throw error; } } }; return ( {children} ); } export function useAuth() { const context = useContext(AuthContext); if (context === undefined) { return { user: null, isLoading: false, signIn: async () => {}, signOut: () => {}, signUp: async () => {}, updateProfile: async () => {}, syncUser: async () => {} } as any; } return context; }