UNPKG

react-native-firebase-compiled

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

333 lines (262 loc) 9.93 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _reactNative = require("react-native"); var _events = require("./events"); var _DataSnapshot = _interopRequireDefault(require("../modules/database/DataSnapshot")); var _Reference = _interopRequireDefault(require("../modules/database/Reference")); var _ = require("."); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } /** * Internally used to manage firebase database realtime event * subscriptions and keep the listeners in sync in js vs native. */ class SyncTree { constructor() { _defineProperty(this, "_nativeEmitter", void 0); _defineProperty(this, "_reverseLookup", void 0); _defineProperty(this, "_tree", void 0); this._tree = {}; this._reverseLookup = {}; if (_reactNative.NativeModules.RNFirebaseDatabase) { this._nativeEmitter = new _reactNative.NativeEventEmitter(_reactNative.NativeModules.RNFirebaseDatabase); this._nativeEmitter.addListener('database_sync_event', this._handleSyncEvent.bind(this)); } } /** * * @param event * @private */ _handleSyncEvent(event) { if (event.error) { this._handleErrorEvent(event); } else { this._handleValueEvent(event); } } /** * Routes native database 'on' events to their js equivalent counterpart. * If there is no longer any listeners remaining for this event we internally * call the native unsub method to prevent further events coming through. * * @param event * @private */ _handleValueEvent(event) { // console.log('SyncTree.VALUE >>>', event); const _event$registration = event.registration, key = _event$registration.key, eventRegistrationKey = _event$registration.eventRegistrationKey; const registration = this.getRegistration(eventRegistrationKey); if (!registration) { // registration previously revoked // notify native that the registration // no longer exists so it can remove // the native listeners return _reactNative.NativeModules.RNFirebaseDatabase.off(key, eventRegistrationKey); } const _event$data = event.data, snapshot = _event$data.snapshot, previousChildName = _event$data.previousChildName; // forward on to users .on(successCallback <-- listener return _events.SharedEventEmitter.emit(eventRegistrationKey, new _DataSnapshot.default(registration.ref, snapshot), previousChildName); } /** * Routes native database query listener cancellation events to their js counterparts. * * @param event * @private */ _handleErrorEvent(event) { // console.log('SyncTree.ERROR >>>', event); const _event$error = event.error, code = _event$error.code, message = _event$error.message; const _event$registration2 = event.registration, eventRegistrationKey = _event$registration2.eventRegistrationKey, registrationCancellationKey = _event$registration2.registrationCancellationKey; const registration = this.getRegistration(registrationCancellationKey); if (registration) { // build a new js error - we additionally attach // the ref as a property for easier debugging const error = (0, _.nativeToJSError)(code, message, { ref: registration.ref }); // forward on to users .on(successCallback, cancellationCallback <-- listener _events.SharedEventEmitter.emit(registrationCancellationKey, error); // remove the paired event registration - if we received a cancellation // event then it's guaranteed that they'll be no further value events this.removeRegistration(eventRegistrationKey); } } /** * Returns registration information such as appName, ref, path and registration keys. * * @param registration * @return {null} */ getRegistration(registration) { return this._reverseLookup[registration] ? Object.assign({}, this._reverseLookup[registration]) : null; } /** * Removes all listeners for the specified registration keys. * * @param registrations * @return {number} */ removeListenersForRegistrations(registrations) { if ((0, _.isString)(registrations)) { this.removeRegistration(registrations); _events.SharedEventEmitter.removeAllListeners(registrations); return 1; } if (!Array.isArray(registrations)) return 0; for (let i = 0, len = registrations.length; i < len; i++) { this.removeRegistration(registrations[i]); _events.SharedEventEmitter.removeAllListeners(registrations[i]); } return registrations.length; } /** * Removes a specific listener from the specified registrations. * * @param listener * @param registrations * @return {Array} array of registrations removed */ removeListenerRegistrations(listener, registrations) { if (!Array.isArray(registrations)) return []; const removed = []; for (let i = 0, len = registrations.length; i < len; i++) { const registration = registrations[i]; const subscriptions = _events.SharedEventEmitter._subscriber.getSubscriptionsForType(registration); if (subscriptions) { for (let j = 0, l = subscriptions.length; j < l; j++) { const subscription = subscriptions[j]; // The subscription may have been removed during this event loop. // its listener matches the listener in method parameters if (subscription && subscription.listener === listener) { subscription.remove(); removed.push(registration); this.removeRegistration(registration); } } } } return removed; } /** * Returns an array of all registration keys for the specified path. * * @param path * @return {Array} */ getRegistrationsByPath(path) { const out = []; const eventKeys = Object.keys(this._tree[path] || {}); for (let i = 0, len = eventKeys.length; i < len; i++) { Array.prototype.push.apply(out, Object.keys(this._tree[path][eventKeys[i]])); } return out; } /** * Returns an array of all registration keys for the specified path and eventType. * * @param path * @param eventType * @return {Array} */ getRegistrationsByPathEvent(path, eventType) { if (!this._tree[path]) return []; if (!this._tree[path][eventType]) return []; return Object.keys(this._tree[path][eventType]); } /** * Returns a single registration key for the specified path, eventType, and listener * * @param path * @param eventType * @param listener * @return {Array} */ getOneByPathEventListener(path, eventType, listener) { if (!this._tree[path]) return null; if (!this._tree[path][eventType]) return null; const registrationsForPathEvent = Object.entries(this._tree[path][eventType]); for (let i = 0; i < registrationsForPathEvent.length; i++) { const registration = registrationsForPathEvent[i]; if (registration[1] === listener) return registration[0]; } return null; } /** * Register a new listener. * * @param parameters * @param listener * @return {String} */ addRegistration(registration) { const eventRegistrationKey = registration.eventRegistrationKey, eventType = registration.eventType, listener = registration.listener, once = registration.once, path = registration.path; if (!this._tree[path]) this._tree[path] = {}; if (!this._tree[path][eventType]) this._tree[path][eventType] = {}; this._tree[path][eventType][eventRegistrationKey] = listener; this._reverseLookup[eventRegistrationKey] = registration; if (once) { _events.SharedEventEmitter.once(eventRegistrationKey, this._onOnceRemoveRegistration(eventRegistrationKey, listener)); } else { _events.SharedEventEmitter.addListener(eventRegistrationKey, listener); } return eventRegistrationKey; } /** * Remove a registration, if it's not a `once` registration then instructs native * to also remove the underlying database query listener. * * @param registration * @return {boolean} */ removeRegistration(registration) { if (!this._reverseLookup[registration]) return false; const _this$_reverseLookup$ = this._reverseLookup[registration], path = _this$_reverseLookup$.path, eventType = _this$_reverseLookup$.eventType, once = _this$_reverseLookup$.once; if (!this._tree[path]) { delete this._reverseLookup[registration]; return false; } if (!this._tree[path][eventType]) { delete this._reverseLookup[registration]; return false; } // we don't want `once` events to notify native as they're already // automatically unsubscribed on native when the first event is sent const registrationObj = this._reverseLookup[registration]; if (registrationObj && !once) { _reactNative.NativeModules.RNFirebaseDatabase.off(registrationObj.key, registration); } delete this._tree[path][eventType][registration]; delete this._reverseLookup[registration]; return !!registrationObj; } /** * Wraps a `once` listener with a new function that self de-registers. * * @param registration * @param listener * @return {function(...[*])} * @private */ _onOnceRemoveRegistration(registration, listener) { return (...args) => { this.removeRegistration(registration); listener(...args); }; } } var _default = new SyncTree(); exports.default = _default;