UNPKG

@react-native-firebase/app

Version:

A well tested, feature rich Firebase implementation for React Native, supporting iOS & Android. Individual module support for Admob, Analytics, Auth, Crash Reporting, Cloud Firestore, Database, Dynamic Links, Functions, Messaging (FCM), Remote Config, Sto

715 lines (665 loc) 24.1 kB
/* * Copyright (c) 2016-present Invertase Limited & Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this library except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ import { Platform } from 'react-native'; import Base64 from './Base64'; import { isString } from './validate'; export * from './id'; export * from './path'; export * from './promise'; export * from './validate'; export { default as Base64 } from './Base64'; export { default as ReferenceBase } from './ReferenceBase'; export function getDataUrlParts(dataUrlString) { const isBase64 = dataUrlString.includes(';base64'); let [mediaType, base64String] = dataUrlString.split(','); if (!mediaType || !base64String) { return { base64String: undefined, mediaType: undefined }; } mediaType = mediaType.replace('data:', '').replace(';base64', ''); if (base64String && base64String.includes('%')) { base64String = decodeURIComponent(base64String); } if (!isBase64) { base64String = Base64.btoa(base64String); } return { base64String, mediaType }; } export function once(fn, context) { let onceResult; let ranOnce = false; return function onceInner(...args) { if (!ranOnce) { ranOnce = true; onceResult = fn.apply(context || this, args); } return onceResult; }; } export function isError(value) { if (Object.prototype.toString.call(value) === '[object Error]') { return true; } return value instanceof Error; } export function hasOwnProperty(target, property) { return Object.hasOwnProperty.call(target, property); } /** * Remove a trailing forward slash from a string if it exists * * @param string * @returns {*} */ export function stripTrailingSlash(string) { if (!isString(string)) { return string; } return string.endsWith('/') ? string.slice(0, -1) : string; } export const isIOS = Platform.OS === 'ios'; export const isAndroid = Platform.OS === 'android'; export const isOther = Platform.OS !== 'ios' && Platform.OS !== 'android'; export function tryJSONParse(string) { try { return string && JSON.parse(string); } catch (_) { return string; } } export function tryJSONStringify(data) { try { return JSON.stringify(data); } catch (_) { return null; } } // Used to indicate if there is no corresponding modular function const NO_REPLACEMENT = true; const mapOfDeprecationReplacements = { analytics: { default: { logEvent: 'logEvent()', setAnalyticsCollectionEnabled: 'setAnalyticsCollectionEnabled()', setSessionTimeoutDuration: 'setSessionTimeoutDuration()', getAppInstanceId: 'getAppInstanceId()', getSessionId: 'getSessionId()', setUserId: 'setUserId()', setUserProperty: 'setUserProperty()', setUserProperties: 'setUserProperties()', resetAnalyticsData: 'resetAnalyticsData()', setDefaultEventParameters: 'setDefaultEventParameters()', initiateOnDeviceConversionMeasurementWithEmailAddress: 'initiateOnDeviceConversionMeasurementWithEmailAddress()', initiateOnDeviceConversionMeasurementWithHashedEmailAddress: 'initiateOnDeviceConversionMeasurementWithHashedEmailAddress()', initiateOnDeviceConversionMeasurementWithPhoneNumber: 'initiateOnDeviceConversionMeasurementWithPhoneNumber()', initiateOnDeviceConversionMeasurementWithHashedPhoneNumber: 'initiateOnDeviceConversionMeasurementWithHashedPhoneNumber()', setConsent: 'setConsent()', // We're deprecating all helper methods for event. e.g. `logAddPaymentInfo()` from namespaced and modular. logAddPaymentInfo: 'logEvent()', logScreenView: 'logEvent()', logAddShippingInfo: 'logEvent()', logAddToCart: 'logEvent()', logAddToWishlist: 'logEvent()', logAppOpen: 'logEvent()', logBeginCheckout: 'logEvent()', logCampaignDetails: 'logEvent()', logEarnVirtualCurrency: 'logEvent()', logGenerateLead: 'logEvent()', logJoinGroup: 'logEvent()', logLevelEnd: 'logEvent()', logLevelStart: 'logEvent()', logLevelUp: 'logEvent()', logLogin: 'logEvent()', logPostScore: 'logEvent()', logSelectContent: 'logEvent()', logPurchase: 'logEvent()', logRefund: 'logEvent()', logRemoveFromCart: 'logEvent()', logSearch: 'logEvent()', logSelectItem: 'logEvent()', logSetCheckoutOption: 'logEvent()', logSelectPromotion: 'logEvent()', logShare: 'logEvent()', logSignUp: 'logEvent()', logSpendVirtualCurrency: 'logEvent()', logTutorialBegin: 'logEvent()', logTutorialComplete: 'logEvent()', logUnlockAchievement: 'logEvent()', logViewCart: 'logEvent()', logViewItem: 'logEvent()', logViewPromotion: 'logEvent()', logViewSearchResults: 'logEvent()', }, }, appCheck: { default: { activate: 'initializeAppCheck()', setTokenAutoRefreshEnabled: 'setTokenAutoRefreshEnabled()', getToken: 'getToken()', getLimitedUseToken: 'getLimitedUseToken()', onTokenChanged: 'onTokenChanged()', }, statics: { CustomProvider: 'CustomProvider', }, }, appDistribution: { default: { isTesterSignedIn: 'isTesterSignedIn()', signInTester: 'signInTester()', checkForUpdate: 'checkForUpdate()', signOutTester: 'signOutTester()', }, }, auth: { default: { applyActionCode: 'applyActionCode()', checkActionCode: 'checkActionCode()', confirmPasswordReset: 'confirmPasswordReset()', createUserWithEmailAndPassword: 'createUserWithEmailAndPassword()', fetchSignInMethodsForEmail: 'fetchSignInMethodsForEmail()', getMultiFactorResolver: 'getMultiFactorResolver()', isSignInWithEmailLink: 'isSignInWithEmailLink()', onAuthStateChanged: 'onAuthStateChanged()', onIdTokenChanged: 'onIdTokenChanged()', sendPasswordResetEmail: 'sendPasswordResetEmail()', sendSignInLinkToEmail: 'sendSignInLinkToEmail()', signInAnonymously: 'signInAnonymously()', signInWithCredential: 'signInWithCredential()', signInWithCustomToken: 'signInWithCustomToken()', signInWithEmailAndPassword: 'signInWithEmailAndPassword()', signInWithEmailLink: 'signInWithEmailLink()', signInWithPhoneNumber: 'signInWithPhoneNumber()', signInWithRedirect: 'signInWithRedirect()', signInWithPopup: 'signInWithPopup()', signOut: 'signOut()', useUserAccessGroup: 'useUserAccessGroup()', verifyPasswordResetCode: 'verifyPasswordResetCode()', getCustomAuthDomain: 'getCustomAuthDomain()', useEmulator: 'connectAuthEmulator()', setLanguageCode: 'useDeviceLanguage()', multiFactor: 'multiFactor()', useDeviceLanguage: 'useDeviceLanguage()', updateCurrentUser: 'updateCurrentUser()', validatePassword: 'validatePassword()', }, User: { delete: 'deleteUser()', getIdToken: 'getIdToken()', getIdTokenResult: 'getIdTokenResult()', linkWithCredential: 'linkWithCredential()', linkWithPopup: 'linkWithPopup()', linkWithRedirect: 'linkWithRedirect()', reauthenticateWithCredential: 'reauthenticateWithCredential()', reauthenticateWithPopup: 'reauthenticateWithPopup()', reauthenticateWithRedirect: 'reauthenticateWithRedirect()', reload: 'reload()', sendEmailVerification: 'sendEmailVerification()', toJSON: NO_REPLACEMENT, unlink: 'unlink()', updateEmail: 'updateEmail()', updatePassword: 'updatePassword()', updatePhoneNumber: 'updatePhoneNumber()', updateProfile: 'updateProfile()', verifyBeforeUpdateEmail: 'verifyBeforeUpdateEmail()', }, statics: { AppleAuthProvider: 'AppleAuthProvider', EmailAuthProvider: 'EmailAuthProvider', PhoneAuthProvider: 'PhoneAuthProvider', GoogleAuthProvider: 'GoogleAuthProvider', GithubAuthProvider: 'GithubAuthProvider', TwitterAuthProvider: 'TwitterAuthProvider', FacebookAuthProvider: 'FacebookAuthProvider', PhoneMultiFactorGenerator: 'PhoneMultiFactorGenerator', OAuthProvider: 'OAuthProvider', OIDCAuthProvider: 'OIDCAuthProvider', PhoneAuthState: 'PhoneAuthState', getMultiFactorResolver: 'getMultiFactorResolver()', multiFactor: 'multiFactor()', }, }, crashlytics: { default: { checkForUnsentReports: 'checkForUnsentReports()', crash: 'crash()', deleteUnsentReports: 'deleteUnsentReports()', didCrashOnPreviousExecution: 'didCrashOnPreviousExecution()', log: 'log()', setAttribute: 'setAttribute()', setAttributes: 'setAttributes()', setUserId: 'setUserId()', recordError: 'recordError()', sendUnsentReports: 'sendUnsentReports()', setCrashlyticsCollectionEnabled: 'setCrashlyticsCollectionEnabled()', }, }, firestore: { default: { batch: 'writeBatch()', loadBundle: 'loadBundle()', namedQuery: 'namedQuery()', clearPersistence: 'clearIndexedDbPersistence()', waitForPendingWrites: 'waitForPendingWrites()', terminate: 'terminate()', useEmulator: 'connectFirestoreEmulator()', collection: 'collection()', collectionGroup: 'collectionGroup()', disableNetwork: 'disableNetwork()', doc: 'doc()', enableNetwork: 'enableNetwork()', runTransaction: 'runTransaction()', settings: 'settings()', persistentCacheIndexManager: 'getPersistentCacheIndexManager()', }, statics: { setLogLevel: 'setLogLevel()', Filter: 'where()', FieldValue: 'FieldValue', Timestamp: 'Timestamp', GeoPoint: 'GeoPoint', Blob: 'Bytes', FieldPath: 'FieldPath', }, FirestoreCollectionReference: { count: 'getCountFromServer()', countFromServer: 'getCountFromServer()', endAt: 'endAt()', endBefore: 'endBefore()', get: 'getDocs()', isEqual: NO_REPLACEMENT, limit: 'limit()', limitToLast: 'limitToLast()', onSnapshot: 'onSnapshot()', orderBy: 'orderBy()', startAfter: 'startAfter()', startAt: 'startAt()', where: 'where()', add: 'addDoc()', doc: 'doc()', }, FirestoreDocumentReference: { collection: 'collection()', delete: 'deleteDoc()', get: 'getDoc()', isEqual: NO_REPLACEMENT, onSnapshot: 'onSnapshot()', set: 'setDoc()', update: 'updateDoc()', }, FirestoreDocumentSnapshot: { isEqual: NO_REPLACEMENT, }, FirestoreFieldValue: { arrayRemove: 'arrayRemove()', arrayUnion: 'arrayUnion()', delete: 'deleteField()', increment: 'increment()', serverTimestamp: 'serverTimestamp()', }, Filter: { or: 'or()', and: 'and()', }, FirestorePersistentCacheIndexManager: { enableIndexAutoCreation: 'enablePersistentCacheIndexAutoCreation()', disableIndexAutoCreation: 'disablePersistentCacheIndexAutoCreation()', deleteAllIndexes: 'deleteAllPersistentCacheIndexes()', }, FirestoreTimestamp: { seconds: NO_REPLACEMENT, nanoseconds: NO_REPLACEMENT, }, }, functions: { default: { useEmulator: 'connectFirestoreEmulator()', httpsCallable: 'httpsCallable()', httpsCallableFromUrl: 'httpsCallableFromUrl()', }, statics: { HttpsErrorCode: 'HttpsErrorCode', }, }, installations: { default: { delete: 'deleteInstallations()', getId: 'getId()', getToken: 'getToken()', }, }, messaging: { default: { isAutoInitEnabled: 'isAutoInitEnabled()', isDeviceRegisteredForRemoteMessages: 'isDeviceRegisteredForRemoteMessages()', isNotificationDelegationEnabled: 'isNotificationDelegationEnabled()', isDeliveryMetricsExportToBigQueryEnabled: 'isDeliveryMetricsExportToBigQueryEnabled()', setAutoInitEnabled: 'setAutoInitEnabled()', getInitialNotification: 'getInitialNotification()', getDidOpenSettingsForNotification: 'getDidOpenSettingsForNotification()', getIsHeadless: 'getIsHeadless()', onNotificationOpenedApp: 'onNotificationOpenedApp()', onTokenRefresh: 'onTokenRefresh()', requestPermission: 'requestPermission()', registerDeviceForRemoteMessages: 'registerDeviceForRemoteMessages()', unregisterDeviceForRemoteMessages: 'unregisterDeviceForRemoteMessages()', getAPNSToken: 'getAPNSToken()', setAPNSToken: 'setAPNSToken()', hasPermission: 'hasPermission()', onDeletedMessages: 'onDeletedMessages()', onMessageSent: 'onMessageSent()', onSendError: 'onSendError()', setBackgroundMessageHandler: 'setBackgroundMessageHandler()', setOpenSettingsForNotificationsHandler: 'setOpenSettingsForNotificationsHandler()', sendMessage: 'sendMessage()', subscribeToTopic: 'subscribeToTopic()', unsubscribeFromTopic: 'unsubscribeFromTopic()', setNotificationDelegationEnabled: 'setNotificationDelegationEnabled()', // Actual firebase-js-sdk methods getToken: 'getToken()', deleteToken: 'deleteToken()', onMessage: 'onMessage()', isSupported: 'isSupported()', setDeliveryMetricsExportToBigQuery: 'experimentalSetDeliveryMetricsExportedToBigQueryEnabled()', }, statics: { AuthorizationStatus: 'AuthorizationStatus', NotificationAndroidPriority: 'NotificationAndroidPriority', NotificationAndroidVisibility: 'NotificationAndroidVisibility', }, }, perf: { default: { setPerformanceCollectionEnabled: 'initializePerformance()', newTrace: 'trace()', newHttpMetric: 'httpMetric()', newScreenTrace: 'newScreenTrace()', startScreenTrace: 'startScreenTrace()', }, }, remoteConfig: { default: { activate: 'activate()', ensureInitialized: 'ensureInitialized()', fetchAndActivate: 'fetchAndActivate()', getAll: 'getAll()', getBoolean: 'getBoolean()', getNumber: 'getNumber()', getString: 'getString()', getValue: 'getValue()', reset: 'reset()', setConfigSettings: 'setConfigSettings()', fetch: 'fetch()', setDefaults: 'setDefaults()', setDefaultsFromResource: 'setDefaultsFromResource()', onConfigUpdated: 'onConfigUpdated()', }, statics: { LastFetchStatus: 'LastFetchStatus', ValueSource: 'ValueSource', }, }, storage: { default: { useEmulator: 'connectStorageEmulator()', ref: 'ref()', refFromURL: 'refFromURL()', setMaxOperationRetryTime: 'setMaxOperationRetryTime()', setMaxUploadRetryTime: 'setMaxUploadRetryTime()', setMaxDownloadRetryTime: 'setMaxDownloadRetryTime()', }, StorageReference: { delete: 'deleteObject()', getDownloadURL: 'getDownloadURL()', getMetadata: 'getMetadata()', list: 'list()', listAll: 'listAll()', updateMetadata: 'updateMetadata()', put: 'uploadBytesResumable()', putString: 'uploadString()', putFile: 'putFile()', writeToFile: 'writeToFile()', toString: 'toString()', child: 'child()', }, statics: { StringFormat: 'StringFormat', TaskEvent: 'TaskEvent', TaskState: 'TaskState', }, }, }; const modularDeprecationMessage = 'This method is deprecated (as well as all React Native Firebase namespaced API) and will be removed in the next major release ' + 'as part of move to match Firebase Web modular SDK API. Please see migration guide for more details: https://rnfirebase.io/migrating-to-v22'; export function deprecationConsoleWarning(nameSpace, methodName, instanceName, isModularMethod) { if (!isModularMethod) { const moduleMap = mapOfDeprecationReplacements[nameSpace]; if (moduleMap) { const instanceMap = moduleMap[instanceName]; const deprecatedMethod = instanceMap[methodName]; if (instanceMap && deprecatedMethod) { if (!globalThis.RNFB_SILENCE_MODULAR_DEPRECATION_WARNINGS) { // eslint-disable-next-line no-console console.warn(createMessage(nameSpace, methodName, instanceName)); if (globalThis.RNFB_MODULAR_DEPRECATION_STRICT_MODE === true) { throw new Error('Deprecated API usage detected while in strict mode.'); } } } } } } export function createMessage( nameSpace, methodName, instanceName = 'default', uniqueMessage = null, ) { if (uniqueMessage) { // Unique deprecation message used for testing return uniqueMessage; } const moduleMap = mapOfDeprecationReplacements[nameSpace]; if (moduleMap) { const instance = moduleMap[instanceName]; if (instance) { const replacementMethodName = instance[methodName]; if (replacementMethodName !== NO_REPLACEMENT) { return ( modularDeprecationMessage + `. Method called was \`${methodName}\`. Please use \`${replacementMethodName}\` instead.` ); } else { return modularDeprecationMessage + `. Method called was \`${methodName}\``; } } } } function getNamespace(target) { if (target.GeoPoint || target.CustomProvider) { // target is statics object. GeoPoint - Firestore, CustomProvider - AppCheck return 'firestore'; } if (target._config && target._config.namespace) { return target._config.namespace; } if (target.constructor.name === 'StorageReference') { return 'storage'; } const className = target.name ? target.name : target.constructor.name; return Object.keys(mapOfDeprecationReplacements).find(key => { if (mapOfDeprecationReplacements[key][className]) { return key; } }); } function getInstanceName(target) { if (target.GeoPoint || target.CustomProvider) { // target is statics object. GeoPoint - Firestore, CustomProvider - AppCheck return 'statics'; } if (target._config) { // module class instance, we use default to store map of deprecated methods return 'default'; } if (target.constructor.name === 'StorageReference') { // if path passed into ref(), it will pass in the arg as target.name return target.constructor.name; } if (target.name) { // It's a function which has a name property unlike classes return target.name; } // It's a class instance return target.constructor.name; } export function createDeprecationProxy(instance) { return new Proxy(instance, { construct(target, args) { // needed for Timestamp which we pass as static, when we construct new instance, we need to wrap it in proxy again. return createDeprecationProxy(new target(...args)); }, get(target, prop, receiver) { const originalMethod = target[prop]; if (prop === 'constructor') { return Reflect.get(target, prop, receiver); } if (target && target.constructor && target.constructor.name === 'FirestoreTimestamp') { deprecationConsoleWarning('firestore', prop, 'FirestoreTimestamp', false); return Reflect.get(target, prop, receiver); } if (target && target.name === 'firebaseModuleWithApp') { // statics if ( prop === 'Filter' || prop === 'FieldValue' || prop === 'Timestamp' || prop === 'GeoPoint' || prop === 'Blob' || prop === 'FieldPath' ) { deprecationConsoleWarning('firestore', prop, 'statics', false); } if (prop === 'LastFetchStatus' || prop === 'ValueSource') { deprecationConsoleWarning('remoteConfig', prop, 'statics', false); } if (prop === 'CustomProvider') { deprecationConsoleWarning('appCheck', prop, 'statics', false); } if (prop === 'StringFormat' || prop === 'TaskEvent' || prop === 'TaskState') { deprecationConsoleWarning('storage', prop, 'statics', false); } if ( prop === 'PhoneAuthState' || prop === 'AppleAuthProvider' || prop === 'PhoneAuthProvider' || prop === 'GoogleAuthProvider' || prop === 'GithubAuthProvider' || prop === 'TwitterAuthProvider' || prop === 'FacebookAuthProvider' || prop === 'OAuthProvider' || prop === 'OIDCAuthProvider' || prop === 'PhoneMultiFactorGenerator' || prop === 'EmailAuthProvider' || prop === 'multiFactor' || prop === 'getMultiFactorResolver' ) { deprecationConsoleWarning('auth', prop, 'statics', false); } if ( prop === 'AuthorizationStatus' || prop === 'NotificationAndroidPriority' || prop === 'NotificationAndroidVisibility' ) { deprecationConsoleWarning('messaging', prop, 'statics', false); } if (prop !== 'setLogLevel') { // we want to capture setLogLevel function call which we do below return Reflect.get(target, prop, receiver); } } // Check if it's a getter/setter first const descriptor = Object.getOwnPropertyDescriptor(target, prop) || Object.getOwnPropertyDescriptor(Object.getPrototypeOf(target), prop); if (descriptor && (descriptor.get || descriptor.set)) { const instanceName = getInstanceName(target); const nameSpace = getNamespace(target); if (descriptor.get) { // Handle getter - call it and show deprecation warning deprecationConsoleWarning(nameSpace, prop, instanceName, _isModularCall); return descriptor.get.call(target); } if (descriptor.set) { // Handle setter - return a function that calls the setter with deprecation warning return function (value) { deprecationConsoleWarning(nameSpace, prop, instanceName, _isModularCall); descriptor.set.call(target, value); }; } } if (typeof originalMethod === 'function') { return function (...args) { const isModularMethod = args.includes(MODULAR_DEPRECATION_ARG); const instanceName = getInstanceName(target); const nameSpace = getNamespace(target); deprecationConsoleWarning(nameSpace, prop, instanceName, isModularMethod); return originalMethod.apply(target, filterModularArgument(args)); }; } return Reflect.get(target, prop, receiver); }, }); } export const MODULAR_DEPRECATION_ARG = 'react-native-firebase-modular-method-call'; // Flag to track if we're currently in a modular call let _isModularCall = false; export function withModularFlag(fn) { const previousFlag = _isModularCall; _isModularCall = true; try { return fn(); } finally { _isModularCall = previousFlag; } } export function filterModularArgument(list) { return list.filter(arg => arg !== MODULAR_DEPRECATION_ARG); } export function warnIfNotModularCall(args, replacementMethodName = '') { for (let i = 0; i < args.length; i++) { if (args[i] === MODULAR_DEPRECATION_ARG) { return; } } let message = modularDeprecationMessage; if (replacementMethodName.length > 0) { message += ` Please use \`${replacementMethodName}\` instead.`; } if (!globalThis.RNFB_SILENCE_MODULAR_DEPRECATION_WARNINGS) { // eslint-disable-next-line no-console console.warn(message); if (globalThis.RNFB_MODULAR_DEPRECATION_STRICT_MODE === true) { throw new Error('Deprecated API usage detected while in strict mode.'); } } }