import * as React from 'react'; import type { AppCheck } from 'firebase/app-check'; import type { Auth } from 'firebase/auth'; import type { Analytics } from 'firebase/analytics'; import type { Database } from 'firebase/database'; import type { Firestore } from 'firebase/firestore'; import type { Functions } from 'firebase/functions'; import type { FirebasePerformance } from 'firebase/performance'; import type { FirebaseStorage } from 'firebase/storage'; import type { RemoteConfig } from 'firebase/remote-config'; import { useFirebaseApp } from './firebaseApp'; import { FirebaseApp } from 'firebase/app'; import { ObservableStatus, useObservable } from './useObservable'; import { from } from 'rxjs'; import { ReactFireOptions } from '.'; export const AppCheckSdkContext = React.createContext(undefined); export const AuthSdkContext = React.createContext(undefined); export const AnalyticsSdkContext = React.createContext(undefined); export const DatabaseSdkContext = React.createContext(undefined); export const FirestoreSdkContext = React.createContext(undefined); export const FunctionsSdkContext = React.createContext(undefined); export const StorageSdkContext = React.createContext(undefined); export const PerformanceSdkContext = React.createContext(undefined); export const RemoteConfigSdkContext = React.createContext(undefined); type FirebaseSdks = Analytics | AppCheck | Auth | Database | Firestore | FirebasePerformance | FirebaseStorage | Functions | RemoteConfig; function getSdkProvider(SdkContext: React.Context) { return function SdkProvider(props: React.PropsWithChildren<{ sdk: Sdk }>) { if (!props.sdk) throw new Error('no sdk provided'); const contextualAppName = useFirebaseApp().name; const sdkAppName = props?.sdk?.app?.name; if (sdkAppName !== contextualAppName) throw new Error('sdk was initialized with a different firebase app'); return ; }; } function useSdk(SdkContext: React.Context): Sdk { const sdk = React.useContext(SdkContext); if (!sdk) { throw new Error('SDK not found. useSdk must be called from within a provider'); } return sdk; } function useInitSdk( sdkName: string, SdkContext: React.Context, sdkInitializer: (firebaseApp: FirebaseApp) => Promise, options?: ReactFireOptions ) { const firebaseApp = useFirebaseApp(); // Some initialization functions (like Firestore's `enableIndexedDbPersistence`) // can only be called before anything else. So if an sdk is already available in context, // it isn't safe to call initialization functions again. if (React.useContext(SdkContext)) { throw new Error(`Cannot initialize SDK ${sdkName} because it already exists in Context`); } const initializeSdk = React.useMemo(() => sdkInitializer(firebaseApp), [firebaseApp]); return useObservable(`firebase-sdk:${sdkName}:${firebaseApp.name}`, from(initializeSdk), options); } export const AppCheckProvider = getSdkProvider(AppCheckSdkContext); export const AuthProvider = getSdkProvider(AuthSdkContext); export const AnalyticsProvider = getSdkProvider(AnalyticsSdkContext); export const DatabaseProvider = getSdkProvider(DatabaseSdkContext); export const FirestoreProvider = getSdkProvider(FirestoreSdkContext); export const FunctionsProvider = getSdkProvider(FunctionsSdkContext); export const PerformanceProvider = getSdkProvider(PerformanceSdkContext); export const StorageProvider = getSdkProvider(StorageSdkContext); export const RemoteConfigProvider = getSdkProvider(RemoteConfigSdkContext); export const useAppCheck = () => useSdk(AppCheckSdkContext); export const useAuth = () => useSdk(AuthSdkContext); export const useAnalytics = () => useSdk(AnalyticsSdkContext); export const useDatabase = () => useSdk(DatabaseSdkContext); export const useFirestore = () => useSdk(FirestoreSdkContext); export const useFunctions = () => useSdk(FunctionsSdkContext); export const usePerformance = () => useSdk(PerformanceSdkContext); export const useStorage = () => useSdk(StorageSdkContext); export const useRemoteConfig = () => useSdk(RemoteConfigSdkContext); type InitSdkHook = ( initializer: (firebaseApp: FirebaseApp) => Promise, options?: ReactFireOptions ) => ObservableStatus; export const useInitAppCheck: InitSdkHook = (initializer, options) => useInitSdk('appcheck', AppCheckSdkContext, initializer, options); export const useInitAuth: InitSdkHook = (initializer, options) => useInitSdk('auth', AuthSdkContext, initializer, options); export const useInitAnalytics: InitSdkHook = (initializer, options) => useInitSdk('analytics', AnalyticsSdkContext, initializer, options); export const useInitDatabase: InitSdkHook = (initializer, options) => useInitSdk('database', DatabaseSdkContext, initializer, options); export const useInitFirestore: InitSdkHook = (initializer, options) => useInitSdk('firestore', FirestoreSdkContext, initializer, options); export const useInitFunctions: InitSdkHook = (initializer, options) => useInitSdk('functions', FunctionsSdkContext, initializer, options); export const useInitPerformance: InitSdkHook = (initializer, options) => useInitSdk('performance', PerformanceSdkContext, initializer, options); export const useInitRemoteConfig: InitSdkHook = (initializer, options) => useInitSdk('remoteconfig', RemoteConfigSdkContext, initializer, options); export const useInitStorage: InitSdkHook = (initializer, options) => useInitSdk('storage', StorageSdkContext, initializer, options);