Amba-Agent-App/lib/stores/authStore.ts
2026-01-16 00:22:35 +03:00

224 lines
6.6 KiB
TypeScript

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<void>;
refreshProfile: () => Promise<void>;
refreshWallet: () => Promise<void>;
// Initialize auth listener
initializeAuth: () => () => void;
}
export const useAuthStore = create<AuthState>((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;
},
}));