UNPKG

@react-native-enhanced-notifications/app

Version:
232 lines (195 loc) 7.01 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 { NativeModules, Platform } from 'react-native'; import { APP_NATIVE_MODULE } from '../constants'; import NativeFirebaseError from '../NativeFirebaseError'; import RNENNativeEventEmitter from '../RNENNativeEventEmitter'; 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]) { RNENNativeEventEmitter.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.' ); } const rnFirebasePackage = `'it.enhancers.firebase.${namespace}.ReactNativeEnhancedNotifications${nativeModule}Package'`; const newInstance = `'new ReactNativeEnhancedNotifications${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.` ); } /** * 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]; }