/** * Firebase Web Implementation * Uses Firebase JS SDK for web platform */ import { initializeApp, getApps, getApp } from 'firebase/app'; import { getAuth, onAuthStateChanged as webOnAuthStateChanged, GoogleAuthProvider, signOut as webSignOut, } from 'firebase/auth'; import type { User, ConfirmationResult } from 'firebase/auth'; import { getFirestore, collection as webCollection, doc as webDoc, getDoc, setDoc, updateDoc, deleteDoc, query, where, orderBy, limit, getDocs, onSnapshot, serverTimestamp, Timestamp as WebTimestamp, deleteField } from 'firebase/firestore'; import { getMessaging, getToken, onMessage } from 'firebase/messaging'; import { getFunctions, httpsCallable as webHttpsCallable } from 'firebase/functions'; // Firebase configuration for web const firebaseConfig = { apiKey: "AIzaSyCVprX0NvjjemRKRpG1ZJHyMwKsJmBuXHc", authDomain: "ambapaydemo.firebaseapp.com", databaseURL: "https://ambapaydemo-default-rtdb.europe-west1.firebasedatabase.app", projectId: "ambapaydemo", storageBucket: "ambapaydemo.firebasestorage.app", messagingSenderId: "613864011564", appId: "1:613864011564:web:e078c5990d3b2bff249e89", measurementId: "G-F8RVT1BHHC" }; // Initialize Firebase (only once) const app = getApps().length === 0 ? initializeApp(firebaseConfig) : getApp(); const authInstance = getAuth(app); const firestoreInstance = getFirestore(app); // Lazy initialization for messaging (requires browser support) let messagingInstance: ReturnType | null = null; const getMessagingInstanceInternal = () => { if (typeof window !== 'undefined' && 'Notification' in window) { if (!messagingInstance) { try { messagingInstance = getMessaging(app); } catch (e) { console.warn('Firebase Messaging not available:', e); } } return messagingInstance; } return null; }; const functionsInstance = getFunctions(app); // Type compatibility layer export interface FirebaseAuthTypes { User: User; ConfirmationResult: ConfirmationResult; } // Status codes compatibility (for Google Sign-In error handling) export const statusCodes = { SIGN_IN_CANCELLED: 'SIGN_IN_CANCELLED', IN_PROGRESS: 'IN_PROGRESS', PLAY_SERVICES_NOT_AVAILABLE: 'PLAY_SERVICES_NOT_AVAILABLE', }; // Auth exports export const firebaseAuth = { GoogleAuthProvider }; export const getAuthInstance = () => authInstance; export const onAuthStateChanged = (callback: (user: User | null) => void) => { return webOnAuthStateChanged(authInstance, callback); }; // Firestore exports export const firebaseFirestore = { FieldValue: { serverTimestamp: () => serverTimestamp(), delete: () => deleteField(), } }; export const getFirestoreInstance = () => firestoreInstance; export const FieldValue = { serverTimestamp: () => serverTimestamp(), delete: () => deleteField(), }; export const Timestamp = WebTimestamp; // Collection helpers that return a Firestore-like interface export const collection = (path: string) => { const collectionRef = webCollection(firestoreInstance, path); return { doc: (docId: string) => createDocRef(path, docId), where: (field: string, op: any, value: any) => { return createQueryBuilder(collectionRef, [where(field, op, value)]); }, orderBy: (field: string, direction?: 'asc' | 'desc') => { return createQueryBuilder(collectionRef, [orderBy(field, direction)]); }, get: async () => { const snapshot = await getDocs(collectionRef); return { docs: snapshot.docs.map(docItem => ({ id: docItem.id, data: () => docItem.data(), exists: docItem.exists(), })), empty: snapshot.empty, forEach: (callback: (docItem: any) => void) => { snapshot.forEach(docItem => callback({ id: docItem.id, data: () => docItem.data(), exists: docItem.exists(), })); }, }; }, }; }; // Query builder for chaining const createQueryBuilder = (collectionRef: any, constraints: any[] = []) => { return { where: (field: string, op: any, value: any) => { return createQueryBuilder(collectionRef, [...constraints, where(field, op, value)]); }, orderBy: (field: string, direction?: 'asc' | 'desc') => { return createQueryBuilder(collectionRef, [...constraints, orderBy(field, direction)]); }, limit: (n: number) => { return createQueryBuilder(collectionRef, [...constraints, limit(n)]); }, get: async () => { const q = query(collectionRef, ...constraints); const snapshot = await getDocs(q); return { docs: snapshot.docs.map(docItem => ({ id: docItem.id, data: () => docItem.data(), exists: docItem.exists(), })), empty: snapshot.empty, forEach: (callback: (docItem: any) => void) => { snapshot.forEach(docItem => callback({ id: docItem.id, data: () => docItem.data(), exists: docItem.exists(), })); }, }; }, onSnapshot: (callback: (snapshot: any) => void, errorCallback?: (error: any) => void) => { const q = query(collectionRef, ...constraints); return onSnapshot(q, (snapshot) => { callback({ docs: snapshot.docs.map(docItem => ({ id: docItem.id, data: () => docItem.data(), exists: docItem.exists(), })), empty: snapshot.empty, forEach: (cb: (docItem: any) => void) => { snapshot.forEach(docItem => cb({ id: docItem.id, data: () => docItem.data(), exists: docItem.exists(), })); }, }); }, errorCallback); }, }; }; // Document reference helper const createDocRef = (collectionPath: string, docId: string) => { const docRef = webDoc(firestoreInstance, collectionPath, docId); return { get: async () => { const snapshot = await getDoc(docRef); return { exists: snapshot.exists(), data: () => snapshot.data(), id: snapshot.id, }; }, set: async (data: any, options?: { merge?: boolean }) => { await setDoc(docRef, data, options || {}); }, update: async (data: any) => { await updateDoc(docRef, data); }, delete: async () => { await deleteDoc(docRef); }, onSnapshot: (callback: (snapshot: any) => void, errorCallback?: (error: any) => void) => { return onSnapshot(docRef, (snapshot) => { callback({ exists: snapshot.exists(), data: () => snapshot.data(), id: snapshot.id, }); }, errorCallback); }, }; }; export const doc = (collectionPath: string, docId: string) => createDocRef(collectionPath, docId); // Messaging exports (web-specific) export const firebaseMessaging = { AuthorizationStatus: { AUTHORIZED: 1, PROVISIONAL: 2, DENIED: 0, NOT_DETERMINED: -1, }, }; export const getMessagingInstance = getMessagingInstanceInternal; export const AuthorizationStatus = firebaseMessaging.AuthorizationStatus; // Functions exports export const firebaseFunctions = {}; export const getFunctionsInstance = () => functionsInstance; export const httpsCallable = (name: string) => { const callable = webHttpsCallable(functionsInstance, name); return async (data: any) => { const result = await callable(data); return result; }; }; // Google Sign-In (Web) export const signInWithGoogle = async (): Promise<{ user: User | null; isNewUser: boolean; error?: string; }> => { try { // Dynamic import for signInWithPopup (tree-shaking friendly) const authModule = await import('firebase/auth'); const signInWithPopup = (authModule as any).signInWithPopup; if (!signInWithPopup) { return { user: null, isNewUser: false, error: 'signInWithPopup not available' }; } const provider = new GoogleAuthProvider(); provider.setCustomParameters({ prompt: 'select_account' }); const result = await signInWithPopup(authInstance, provider); // Check if new user - web SDK doesn't directly expose this, check metadata const isNewUser = result.user.metadata.creationTime === result.user.metadata.lastSignInTime; return { user: result.user, isNewUser }; } catch (error: any) { console.error('Google Sign-In error:', error); if (error.code === 'auth/popup-closed-by-user') { return { user: null, isNewUser: false, error: 'Sign in was cancelled' }; } if (error.code === 'auth/popup-blocked') { return { user: null, isNewUser: false, error: 'Popup was blocked. Please allow popups for this site.' }; } return { user: null, isNewUser: false, error: error.message || 'Google Sign-In failed' }; } }; export const signOutFromGoogle = async (): Promise => { // No separate Google sign out needed on web }; // Phone Auth (Web) - requires reCAPTCHA let recaptchaVerifier: any = null; export const initRecaptcha = async (containerId: string): Promise => { if (typeof window !== 'undefined' && !recaptchaVerifier) { // Dynamic import for RecaptchaVerifier const authModule = await import('firebase/auth'); const RecaptchaVerifier = (authModule as any).RecaptchaVerifier; if (RecaptchaVerifier) { recaptchaVerifier = new RecaptchaVerifier(authInstance, containerId, { size: 'invisible', callback: () => { console.log('reCAPTCHA verified'); }, }); } } return recaptchaVerifier; }; export const signInWithPhoneNumber = async (phoneNumber: string): Promise => { if (!recaptchaVerifier) { throw new Error('reCAPTCHA not initialized. Call initRecaptcha first.'); } // Dynamic import for signInWithPhoneNumber const authModule = await import('firebase/auth'); const webSignInWithPhoneNumber = (authModule as any).signInWithPhoneNumber; if (!webSignInWithPhoneNumber) { throw new Error('signInWithPhoneNumber not available'); } return webSignInWithPhoneNumber(authInstance, phoneNumber, recaptchaVerifier); }; // Email/Password Authentication (Web) export const createUserWithEmailAndPassword = async ( email: string, password: string ): Promise<{ user: User | null; error?: string }> => { try { const authModule = await import('firebase/auth'); const createUser = (authModule as any).createUserWithEmailAndPassword; if (!createUser) { return { user: null, error: 'createUserWithEmailAndPassword not available' }; } const userCredential = await createUser(authInstance, email, password); return { user: userCredential.user }; } catch (error: any) { console.error('Email/Password signup error:', error); return { user: null, error: error.message || 'Failed to create account', }; } }; export const signInWithEmailAndPassword = async ( email: string, password: string ): Promise<{ user: User | null; error?: string }> => { try { const authModule = await import('firebase/auth'); const signIn = (authModule as any).signInWithEmailAndPassword; if (!signIn) { return { user: null, error: 'signInWithEmailAndPassword not available' }; } const userCredential = await signIn(authInstance, email, password); return { user: userCredential.user }; } catch (error: any) { console.error('Email/Password signin error:', error); return { user: null, error: error.message || 'Failed to sign in', }; } }; // Firebase Auth sign out export const signOut = async (): Promise => { await webSignOut(authInstance); }; // Web FCM helpers export const requestNotificationPermission = async (): Promise => { if (typeof window === 'undefined' || !('Notification' in window)) { return false; } try { const permission = await Notification.requestPermission(); return permission === 'granted'; } catch (error) { console.error('Error requesting notification permission:', error); return false; } }; export const getWebFCMToken = async (): Promise => { const messaging = getMessagingInstanceInternal(); if (!messaging) return null; try { // You'll need to add your VAPID key here for web push const token = await getToken(messaging, { vapidKey: 'YOUR_VAPID_KEY_HERE' // TODO: Add your VAPID key }); return token; } catch (error) { console.error('Error getting FCM token:', error); return null; } }; export const onWebMessage = (callback: (payload: any) => void) => { const messaging = getMessagingInstanceInternal(); if (!messaging) return () => { }; return onMessage(messaging, callback); }; // Platform identifier export const isNative = false; export const isWeb = true;