import { create } from "zustand"; import { Platform } from "react-native"; import { onAuthStateChanged, signOut as firebaseSignOut, isWeb, getAuthInstance, } from "../firebase"; import { UserProfile, AuthService } from "../services/authServices"; import { UserWallet } from "../services/walletService"; import { useUserProfileStore } from "./userProfileStore"; import { useTransactionStore } from "./transactionStore"; import { useUserWalletStore } from "./userWalletStore"; import { withGlobalLoading } from "./uiStore"; import { awardDailyLoginPoints } from "../services/pointsService"; // Conditionally import FCMService only for native let FCMService: any = null; if (Platform.OS !== "web") { FCMService = require("../services/fcmService").FCMService; } interface AuthState { // User state user: any | null; profile: UserProfile | null; wallet: UserWallet | null; // Loading states loading: boolean; profileLoading: boolean; walletLoading: boolean; // Error states profileError: string | null; walletError: string | null; // Phone authentication state phoneSessionInfo: string | null; phoneConfirmationResult: any | null; phoneLoading: boolean; phoneError: string | null; // Computed values formattedBalance: string; // Actions setUser: (user: any | null) => void; setProfile: (profile: UserProfile | null) => void; setWallet: (wallet: UserWallet | null) => void; setLoading: (loading: boolean) => void; setProfileLoading: (loading: boolean) => void; setWalletLoading: (loading: boolean) => void; setProfileError: (error: string | null) => void; setWalletError: (error: string | null) => void; setFormattedBalance: (balance: string) => void; // Phone auth setters setPhoneSessionInfo: (sessionInfo: string | null) => void; setPhoneConfirmationResult: (confirmationResult: any | null) => void; setPhoneLoading: (loading: boolean) => void; setPhoneError: (error: string | null) => void; clearPhoneAuth: () => void; // Auth actions signOut: () => Promise; refreshProfile: () => Promise; refreshWallet: () => Promise; // Initialize auth listener initializeAuth: () => () => void; } export const useAuthStore = create((set, get) => ({ // Initial state user: null, profile: null, wallet: null, loading: true, profileLoading: false, walletLoading: false, profileError: null, walletError: null, phoneSessionInfo: null, phoneConfirmationResult: null, phoneLoading: false, phoneError: null, formattedBalance: "0.00", // Setters setUser: (user) => set({ user }), setProfile: (profile) => set({ profile }), setWallet: (wallet) => set({ wallet }), setLoading: (loading) => set({ loading }), setProfileLoading: (profileLoading) => set({ profileLoading }), setWalletLoading: (walletLoading) => set({ walletLoading }), setProfileError: (error) => set({ profileError: error }), setWalletError: (error) => set({ walletError: error }), setFormattedBalance: (formattedBalance) => set({ formattedBalance }), // Phone auth setters setPhoneSessionInfo: (phoneSessionInfo) => set({ phoneSessionInfo }), setPhoneConfirmationResult: (phoneConfirmationResult) => set({ phoneConfirmationResult }), setPhoneLoading: (phoneLoading) => set({ phoneLoading }), setPhoneError: (phoneError) => set({ phoneError }), // Phone auth actions clearPhoneAuth: () => set({ phoneSessionInfo: null, phoneConfirmationResult: null, phoneLoading: false, phoneError: null, }), // Auth actions signOut: async () => { // Get current user from our abstracted firebase module const currentUser = getAuthInstance().currentUser; // If there is no current Firebase user (common in dev with mocked flows), // just clear local state and exit without throwing. if (!currentUser) { console.warn( "AuthStore.signOut called with no current Firebase user; clearing local state only" ); get().setUser(null); get().clearPhoneAuth(); useUserProfileStore.getState().clearAll(); useTransactionStore.getState().clearAll(); useUserWalletStore.getState().clearAll(); return; } try { const uid = get().user?.uid; // Remove FCM token from Firestore before signing out (native only) if (uid && FCMService && Platform.OS !== "web") { try { await FCMService.removeTokenFromFirestore(uid); FCMService.cleanup(); } catch (fcmError) { console.error("Error removing FCM token on sign out:", fcmError); } } await withGlobalLoading(async () => { // Sign out from Google if signed in with Google await AuthService.signOutFromGoogle(); // Sign out from Firebase await firebaseSignOut(); }); get().clearPhoneAuth(); useUserProfileStore.getState().clearAll(); useTransactionStore.getState().clearAll(); useUserWalletStore.getState().clearAll(); } catch (error) { console.error("Error signing out:", error); throw error; } }, refreshProfile: async () => { const uid = get().user?.uid; if (!uid) { console.warn( "AuthStore.refreshProfile called without an authenticated user" ); return; } const profileStore = useUserProfileStore.getState(); profileStore.ensureSubscription(uid); await withGlobalLoading(() => profileStore.refreshProfile(uid)); }, refreshWallet: async () => { const uid = get().user?.uid; if (!uid) { console.warn( "AuthStore.refreshWallet called without an authenticated user" ); return; } const walletStore = useUserWalletStore.getState(); walletStore.ensureSubscription(uid); await withGlobalLoading(() => walletStore.refreshWallet(uid)); }, // Initialize auth listener initializeAuth: () => { console.log("AuthStore: Initializing auth listener"); const unsubscribe = onAuthStateChanged((fbUser: any) => { // In dev, if we've explicitly set a fake emulator user, don't let // Firebase auth (which will report null) wipe it out. if (__DEV__) { const current = get().user; if (current?.uid === "dev-emulator-user") { if (get().loading) { set({ loading: false }); } return; } } console.log("AuthStore: Auth state changed, user:", fbUser?.uid); set({ user: fbUser || null, loading: false }); if (fbUser) { awardDailyLoginPoints().catch((error) => { console.warn( "[AuthStore] Failed to maybe award daily login points", error ); }); } }); return unsubscribe; }, }));