mirror of
https://github.com/zhigang1992/react-native-firebase.git
synced 2026-06-13 09:25:39 +08:00
234 lines
7.0 KiB
JavaScript
234 lines
7.0 KiB
JavaScript
/*
|
|
* 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 { NativeModules, Platform } from 'react-native';
|
|
import { APP_NATIVE_MODULE } from '../constants';
|
|
import NativeFirebaseError from '../NativeFirebaseError';
|
|
import RNFBNativeEventEmitter from '../RNFBNativeEventEmitter';
|
|
import SharedEventEmitter from '../SharedEventEmitter';
|
|
|
|
const NATIVE_MODULE_REGISTRY = {};
|
|
const NATIVE_MODULE_EVENT_SUBSCRIPTIONS = {};
|
|
|
|
function nativeModuleKey(module) {
|
|
return `${module._customUrlOrRegion || ''}:${module.app.name}:${module._config.namespace}`;
|
|
}
|
|
|
|
/**
|
|
* Wraps a native module method to provide
|
|
* auto prepended args and custom Error classes.
|
|
*
|
|
* @param namespace
|
|
* @param method
|
|
* @param argToPrepend
|
|
* @returns {Function}
|
|
*/
|
|
function nativeModuleMethodWrapped(namespace, method, argToPrepend) {
|
|
return (...args) => {
|
|
const possiblePromise = method(...[...argToPrepend, ...args]);
|
|
|
|
if (possiblePromise && possiblePromise.then) {
|
|
const jsStack = new Error().stack;
|
|
return possiblePromise.catch(nativeError =>
|
|
Promise.reject(new NativeFirebaseError(nativeError, jsStack, namespace)),
|
|
);
|
|
}
|
|
|
|
return possiblePromise;
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Prepends all arguments in prependArgs to all native method calls
|
|
*
|
|
* @param namespace
|
|
* @param NativeModule
|
|
* @param argToPrepend
|
|
*/
|
|
function nativeModuleWrapped(namespace, NativeModule, argToPrepend) {
|
|
const native = {};
|
|
if (!NativeModule) {
|
|
return NativeModule;
|
|
}
|
|
|
|
const properties = Object.keys(NativeModule);
|
|
|
|
for (let i = 0, len = properties.length; i < len; i++) {
|
|
const property = properties[i];
|
|
if (typeof NativeModule[property] === 'function') {
|
|
native[property] = nativeModuleMethodWrapped(namespace, NativeModule[property], argToPrepend);
|
|
} else {
|
|
native[property] = NativeModule[property];
|
|
}
|
|
}
|
|
|
|
return native;
|
|
}
|
|
|
|
/**
|
|
* Initialises and wraps all the native module methods.
|
|
*
|
|
* @param module
|
|
* @returns {*}
|
|
*/
|
|
function initialiseNativeModule(module) {
|
|
const config = module._config;
|
|
const key = nativeModuleKey(module);
|
|
const {
|
|
namespace,
|
|
nativeEvents,
|
|
nativeModuleName,
|
|
hasMultiAppSupport,
|
|
hasCustomUrlOrRegionSupport,
|
|
disablePrependCustomUrlOrRegion,
|
|
} = config;
|
|
const multiModuleRoot = {};
|
|
const multiModule = Array.isArray(nativeModuleName);
|
|
const nativeModuleNames = multiModule ? nativeModuleName : [nativeModuleName];
|
|
|
|
for (let i = 0; i < nativeModuleNames.length; i++) {
|
|
const nativeModule = NativeModules[nativeModuleNames[i]];
|
|
|
|
// only error if there's a single native module
|
|
// as multi modules can mean some are optional
|
|
if (!multiModule && !nativeModule) {
|
|
throw new Error(getMissingModuleHelpText(namespace));
|
|
}
|
|
|
|
if (multiModule) {
|
|
multiModuleRoot[nativeModuleNames[i]] = !!nativeModule;
|
|
}
|
|
|
|
const argToPrepend = [];
|
|
|
|
if (hasMultiAppSupport) {
|
|
argToPrepend.push(module.app.name);
|
|
}
|
|
|
|
if (hasCustomUrlOrRegionSupport && !disablePrependCustomUrlOrRegion) {
|
|
argToPrepend.push(module._customUrlOrRegion);
|
|
}
|
|
|
|
Object.assign(multiModuleRoot, nativeModuleWrapped(namespace, nativeModule, argToPrepend));
|
|
}
|
|
|
|
if (nativeEvents && nativeEvents.length) {
|
|
for (let i = 0, len = nativeEvents.length; i < len; i++) {
|
|
subscribeToNativeModuleEvent(nativeEvents[i]);
|
|
}
|
|
}
|
|
|
|
Object.freeze(multiModuleRoot);
|
|
|
|
NATIVE_MODULE_REGISTRY[key] = multiModuleRoot;
|
|
|
|
return NATIVE_MODULE_REGISTRY[key];
|
|
}
|
|
|
|
/**
|
|
* Subscribe to a native event for js side distribution by appName
|
|
* React Native events are hard set at compile - cant do dynamic event names
|
|
* so we use a single event send it to js and js then internally can prefix it
|
|
* and distribute dynamically.
|
|
*
|
|
* @param eventName
|
|
* @private
|
|
*/
|
|
function subscribeToNativeModuleEvent(eventName) {
|
|
if (!NATIVE_MODULE_EVENT_SUBSCRIPTIONS[eventName]) {
|
|
RNFBNativeEventEmitter.addListener(eventName, event => {
|
|
if (event.appName) {
|
|
// native event has an appName property - auto prefix and internally emit
|
|
SharedEventEmitter.emit(`${event.appName}-${eventName}`, event);
|
|
} else {
|
|
// standard event - no need to prefix
|
|
SharedEventEmitter.emit(eventName, event);
|
|
}
|
|
});
|
|
|
|
NATIVE_MODULE_EVENT_SUBSCRIPTIONS[eventName] = true;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Help text for integrating the native counter parts for each firebase module.
|
|
*
|
|
* @param namespace
|
|
* @returns {string}
|
|
*/
|
|
function getMissingModuleHelpText(namespace) {
|
|
const snippet = `firebase.${namespace}()`;
|
|
const nativeModule = namespace.charAt(0).toUpperCase() + namespace.slice(1);
|
|
|
|
if (Platform.OS === 'ios') {
|
|
return (
|
|
`You attempted to use a firebase module that's not installed natively on your iOS project by calling ${snippet}.` +
|
|
'\r\n\r\nEnsure you have either linked the module or added it to your projects Podfile.' +
|
|
'\r\n\r\nSee http://invertase.link/ios for full setup instructions.'
|
|
);
|
|
}
|
|
|
|
const rnFirebasePackage = `'io.invertase.firebase.${namespace}.ReactNativeFirebase${nativeModule}Package'`;
|
|
const newInstance = `'new ReactNativeFirebase${nativeModule}Package()'`;
|
|
|
|
return (
|
|
`You attempted to use a firebase module that's not installed on your Android project by calling ${snippet}.` +
|
|
`\r\n\r\nEnsure you have:\r\n\r\n1) imported the ${rnFirebasePackage} module in your 'MainApplication.java' file.\r\n\r\n2) Added the ` +
|
|
`${newInstance} line inside of the RN 'getPackages()' method list.` +
|
|
'\r\n\r\nSee http://invertase.link/android for full setup instructions.'
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Gets a wrapped native module instance for the provided firebase module.
|
|
* Will attempt to create a new instance if non previously created.
|
|
*
|
|
* @param module
|
|
* @returns {*}
|
|
*/
|
|
export function getNativeModule(module) {
|
|
const key = nativeModuleKey(module);
|
|
|
|
if (NATIVE_MODULE_REGISTRY[key]) {
|
|
return NATIVE_MODULE_REGISTRY[key];
|
|
}
|
|
|
|
return initialiseNativeModule(module);
|
|
}
|
|
|
|
/**
|
|
* Custom wrapped app module as it does not have it's own FirebaseModule based class.
|
|
*
|
|
* @returns {*}
|
|
*/
|
|
export function getAppModule() {
|
|
if (NATIVE_MODULE_REGISTRY[APP_NATIVE_MODULE]) {
|
|
return NATIVE_MODULE_REGISTRY[APP_NATIVE_MODULE];
|
|
}
|
|
|
|
const namespace = 'app';
|
|
const nativeModule = NativeModules[APP_NATIVE_MODULE];
|
|
|
|
if (!nativeModule) {
|
|
throw new Error(getMissingModuleHelpText(namespace));
|
|
}
|
|
|
|
NATIVE_MODULE_REGISTRY[APP_NATIVE_MODULE] = nativeModuleWrapped(namespace, nativeModule, []);
|
|
|
|
return NATIVE_MODULE_REGISTRY[APP_NATIVE_MODULE];
|
|
}
|