diff --git a/lib/firebase-app.js b/lib/firebase-app.js index 822616a8..6efc1f0c 100644 --- a/lib/firebase-app.js +++ b/lib/firebase-app.js @@ -26,15 +26,15 @@ export default class FirebaseApp { this._nativeInitialized = false; // modules - this.admob = this._staticsOrModuleInstance('admob', AdMobStatics, AdMob); - this.auth = this._staticsOrModuleInstance('auth', AuthStatics, Auth); - this.analytics = this._staticsOrModuleInstance('analytics', {}, Analytics); - this.config = this._staticsOrModuleInstance('config', {}, RemoteConfig); - this.crash = this._staticsOrModuleInstance('crash', {}, Crash); - this.database = this._staticsOrModuleInstance('database', DatabaseStatics, Database); - this.messaging = this._staticsOrModuleInstance('messaging', MessagingStatics, Messaging); - this.perf = this._staticsOrModuleInstance('perf', {}, Performance); - this.storage = this._staticsOrModuleInstance('storage', StorageStatics, Storage); + this.admob = this._staticsOrModuleInstance(AdMobStatics, AdMob); + this.auth = this._staticsOrModuleInstance(AuthStatics, Auth); + this.analytics = this._staticsOrModuleInstance({}, Analytics); + this.config = this._staticsOrModuleInstance({}, RemoteConfig); + this.crash = this._staticsOrModuleInstance({}, Crash); + this.database = this._staticsOrModuleInstance(DatabaseStatics, Database); + this.messaging = this._staticsOrModuleInstance(MessagingStatics, Messaging); + this.perf = this._staticsOrModuleInstance({}, Performance); + this.storage = this._staticsOrModuleInstance(StorageStatics, Storage); } /** @@ -117,9 +117,9 @@ export default class FirebaseApp { * @return {function()} * @private */ - _staticsOrModuleInstance(name, statics = {}, InstanceClass): Function { + _staticsOrModuleInstance(statics = {}, InstanceClass): Function { const getInstance = () => { - const _name = `_${name}`; + const _name = `_${InstanceClass._NAMESPACE}`; if (!this._namespaces[_name]) { this._namespaces[_name] = new InstanceClass(this); @@ -129,7 +129,7 @@ export default class FirebaseApp { }; Object.assign(getInstance, statics, { - nativeModuleExists: !!NativeModules[`RNFirebase${InstanceClass._NATIVE_MODULE || capitalizeFirstLetter(name)}`], + nativeModuleExists: !!NativeModules[InstanceClass._NATIVE_MODULE], }); return getInstance; diff --git a/lib/firebase.js b/lib/firebase.js index 7c7f1569..5dc4882c 100644 --- a/lib/firebase.js +++ b/lib/firebase.js @@ -37,15 +37,15 @@ class FirebaseCore { } // modules - this.admob = this._appNamespaceOrStatics('admob', AdMobStatics, AdMob); - this.auth = this._appNamespaceOrStatics('auth', AuthStatics, Auth); - this.analytics = this._appNamespaceOrStatics('analytics', {}, Analytics); - this.config = this._appNamespaceOrStatics('config', {}, RemoteConfig); - this.crash = this._appNamespaceOrStatics('crash', {}, Crash); - this.database = this._appNamespaceOrStatics('database', DatabaseStatics, Database); - this.messaging = this._appNamespaceOrStatics('messaging', MessagingStatics, Messaging); - this.perf = this._appNamespaceOrStatics('perf', DatabaseStatics, Performance); - this.storage = this._appNamespaceOrStatics('storage', StorageStatics, Storage); + this.admob = this._appNamespaceOrStatics(AdMobStatics, AdMob); + this.auth = this._appNamespaceOrStatics(AuthStatics, Auth); + this.analytics = this._appNamespaceOrStatics({}, Analytics); + this.config = this._appNamespaceOrStatics({}, RemoteConfig); + this.crash = this._appNamespaceOrStatics({}, Crash); + this.database = this._appNamespaceOrStatics(DatabaseStatics, Database); + this.messaging = this._appNamespaceOrStatics(MessagingStatics, Messaging); + this.perf = this._appNamespaceOrStatics(DatabaseStatics, Performance); + this.storage = this._appNamespaceOrStatics(StorageStatics, Storage); } /** @@ -205,7 +205,8 @@ class FirebaseCore { * @return {function(FirebaseApp=)} * @private */ - _appNamespaceOrStatics(namespace, statics = {}, InstanceClass): Function { + _appNamespaceOrStatics(statics = {}, InstanceClass): Function { + const namespace = InstanceClass._NAMESPACE; const getNamespace = (app?: FirebaseApp) => { let _app = app; // throw an error if it's not a valid app instance @@ -218,7 +219,7 @@ class FirebaseCore { }; Object.assign(getNamespace, statics, { - nativeModuleExists: !!NativeModules[`RNFirebase${InstanceClass._NATIVE_MODULE || capitalizeFirstLetter(namespace)}`], + nativeModuleExists: !!NativeModules[InstanceClass._NATIVE_MODULE], }); return getNamespace; } diff --git a/lib/modules/admob/index.js b/lib/modules/admob/index.js index 1961cc42..c2eb4a68 100644 --- a/lib/modules/admob/index.js +++ b/lib/modules/admob/index.js @@ -9,10 +9,11 @@ import NativeExpress from './NativeExpress'; export default class AdMob extends ModuleBase { - static _NATIVE_MODULE = 'AdMob'; + static _NAMESPACE = 'admob'; + static _NATIVE_MODULE = 'RNFirebaseAdMob'; constructor(firebaseApp: Object, options: Object = {}) { - super(firebaseApp, options, AdMob._NATIVE_MODULE, true); + super(firebaseApp, options, true); this._initialized = false; this._appId = null; diff --git a/lib/modules/analytics/index.js b/lib/modules/analytics/index.js index 278b2f0e..f468759f 100644 --- a/lib/modules/analytics/index.js +++ b/lib/modules/analytics/index.js @@ -20,8 +20,11 @@ const ReservedEventNames = [ ]; export default class Analytics extends ModuleBase { + static _NAMESPACE = 'analytics'; + static _NATIVE_MODULE = 'RNFirebaseAnalytics'; + constructor(firebaseApp: Object, options: Object = {}) { - super(firebaseApp, options, 'Analytics'); + super(firebaseApp, options); } /** diff --git a/lib/modules/auth/index.js b/lib/modules/auth/index.js index ff96797e..3adcbb9e 100644 --- a/lib/modules/auth/index.js +++ b/lib/modules/auth/index.js @@ -12,12 +12,15 @@ import TwitterAuthProvider from './providers/Twitter'; import FacebookAuthProvider from './providers/Facebook'; export default class Auth extends ModuleBase { + static _NAMESPACE = 'auth'; + static _NATIVE_MODULE = 'RNFirebaseAuth'; + _user: User | null; _authResult: AuthResultType | null; authenticated: boolean; constructor(firebaseApp: Object, options: Object = {}) { - super(firebaseApp, options, 'Auth', true); + super(firebaseApp, options, true); this._user = null; this._authResult = null; this.authenticated = false; diff --git a/lib/modules/config/index.js b/lib/modules/config/index.js index 28f7f6c8..a78f6a1d 100644 --- a/lib/modules/config/index.js +++ b/lib/modules/config/index.js @@ -7,9 +7,11 @@ import ModuleBase from './../../utils/ModuleBase'; * @class Config */ export default class RemoteConfig extends ModuleBase { - static _NATIVE_MODULE = 'RemoteConfig'; + static _NAMESPACE = 'config'; + static _NATIVE_MODULE = 'RNFirebaseRemoteConfig'; + constructor(firebaseApp: Object, options: Object = {}) { - super(firebaseApp, options, RemoteConfig._NATIVE_MODULE); + super(firebaseApp, options); this.developerModeEnabled = false; } diff --git a/lib/modules/crash/index.js b/lib/modules/crash/index.js index 7ed078db..2fa2ad74 100644 --- a/lib/modules/crash/index.js +++ b/lib/modules/crash/index.js @@ -2,8 +2,11 @@ import ModuleBase from './../../utils/ModuleBase'; export default class Crash extends ModuleBase { + static _NAMESPACE = 'crash'; + static _NATIVE_MODULE = 'RNFirebaseCrash'; + constructor(firebaseApp: Object, options: Object = {}) { - super(firebaseApp, options, 'Crash'); + super(firebaseApp, options); } /** diff --git a/lib/modules/database/index.js b/lib/modules/database/index.js index a89c655e..eb90dcc5 100644 --- a/lib/modules/database/index.js +++ b/lib/modules/database/index.js @@ -12,8 +12,11 @@ import ModuleBase from './../../utils/ModuleBase'; * @class Database */ export default class Database extends ModuleBase { + static _NAMESPACE = 'database'; + static _NATIVE_MODULE = 'RNFirebaseDatabase'; + constructor(firebaseApp: Object, options: Object = {}) { - super(firebaseApp, options, 'Database', true); + super(firebaseApp, options, true); this._transactionHandler = new TransactionHandler(this); if (this._options.persistence) { diff --git a/lib/modules/messaging/index.js b/lib/modules/messaging/index.js index 0b36e133..64307d9e 100644 --- a/lib/modules/messaging/index.js +++ b/lib/modules/messaging/index.js @@ -70,8 +70,11 @@ function finish(data) { * @class Messaging */ export default class Messaging extends ModuleBase { + static _NAMESPACE = 'messaging'; + static _NATIVE_MODULE = 'RNFirebaseMessaging'; + constructor(firebaseApp: Object, options: Object = {}) { - super(firebaseApp, options, 'Messaging', true); + super(firebaseApp, options, true); } get EVENT_TYPE() { diff --git a/lib/modules/perf/index.js b/lib/modules/perf/index.js index 43e42c8f..48f8c619 100644 --- a/lib/modules/perf/index.js +++ b/lib/modules/perf/index.js @@ -3,8 +3,11 @@ import Trace from './Trace'; import ModuleBase from '../../utils/ModuleBase'; export default class PerformanceMonitoring extends ModuleBase { + static _NAMESPACE = 'perf'; + static _NATIVE_MODULE = 'RNFirebasePerformance'; + constructor(firebaseApp: Object, options: Object = {}) { - super(firebaseApp, options, 'Performance'); + super(firebaseApp, options); } /** diff --git a/lib/modules/storage/index.js b/lib/modules/storage/index.js index 5cc4fb0a..eccfa45a 100644 --- a/lib/modules/storage/index.js +++ b/lib/modules/storage/index.js @@ -7,13 +7,15 @@ import ModuleBase from './../../utils/ModuleBase'; const FirebaseStorage = NativeModules.RNFirebaseStorage; export default class Storage extends ModuleBase { + static _NAMESPACE = 'storage'; + static _NATIVE_MODULE = 'RNFirebaseStorage'; /** * * @param firebaseApp * @param options */ constructor(firebaseApp: Object, options: Object = {}) { - super(firebaseApp, options, 'Storage', true); + super(firebaseApp, options, true); this._subscriptions = {}; this.addListener( diff --git a/lib/utils/ModuleBase.js b/lib/utils/ModuleBase.js index 0473c0c2..2cda6291 100644 --- a/lib/utils/ModuleBase.js +++ b/lib/utils/ModuleBase.js @@ -46,31 +46,31 @@ export default class ModuleBase { * @param moduleName * @param withEventEmitter */ - constructor(firebaseApp, options, moduleName, withEventEmitter = false) { - this._module = moduleName; + constructor(firebaseApp, options, withEventEmitter = false) { + this._module = this.constructor._NATIVE_MODULE.replace('RNFirebase', ''); this._firebaseApp = firebaseApp; this._appName = firebaseApp._name; this._namespace = `${this._appName}:${this._module}`; - this._options = Object.assign({}, DEFAULTS[moduleName] || {}, options); + this._options = Object.assign({}, DEFAULTS[this._module] || {}, options); // check if native module exists as all native // modules are now optionally part of build - const nativeModule = NativeModules[`RNFirebase${moduleName}`]; + const nativeModule = NativeModules[this.constructor._NATIVE_MODULE]; if (!nativeModule) { - throw new Error(INTERNALS.STRINGS.ERROR_MISSING_MODULE(moduleName)); + throw new Error(INTERNALS.STRINGS.ERROR_MISSING_MODULE(this.constructor._NATIVE_MODULE)); } // used by the modules that extend ModuleBase // to access their native module counterpart - if (!MULTI_APP_MODULES.includes(moduleName.toLowerCase())) { + if (!MULTI_APP_MODULES.includes(this._module.toLowerCase())) { this._native = nativeModule; } else { this._native = nativeWithApp(this._appName, nativeModule); } if (withEventEmitter) { - this._setupEventEmitter(nativeModule, moduleName); + this._setupEventEmitter(nativeModule, this._module); } } diff --git a/tests/src/bench.js b/tests/src/bench.js new file mode 100644 index 00000000..87085425 --- /dev/null +++ b/tests/src/bench.js @@ -0,0 +1,92 @@ +import React, { Component } from 'react'; +import { View, Button, Text } from 'react-native'; +import sinon from 'sinon'; +import 'should-sinon'; +import Promise from 'bluebird'; + +import firebase from './firebase'; +import DatabaseContents from './tests/support/DatabaseContents'; + + +export default class HomeScreen extends Component { + + constructor(props) { + super(props); + this.state = { + timeTaken: '', + }; + } + + clickMe = () => { + this.setState({ timeTaken: 'Running...' }); + let start = null; + Promise.all([ + firebase.native.database().ref('tests/types').set(DatabaseContents.DEFAULT), + firebase.native.database().ref('tests/priority').setWithPriority({ + foo: 'bar', + }, 666), + firebase.native.database().ref('tests/query').set(DatabaseContents.QUERY), + ]).then(() => { + start = Date.now(); + return Promise.each(Object.keys(DatabaseContents.DEFAULT), async (dataRef) => { + // Setup + const ref = firebase.native.database().ref(`tests/types/${dataRef}`); + const currentDataValue = DatabaseContents.DEFAULT[dataRef]; + + const callbackA = sinon.spy(); + const callbackB = sinon.spy(); + + // Test + + await new Promise((resolve) => { + ref.on('value', (snapshot) => { + callbackA(snapshot.val()); + resolve(); + }); + }); + + await new Promise((resolve) => { + ref.on('value', (snapshot) => { + callbackB(snapshot.val()); + resolve(); + }); + }); + + callbackA.should.be.calledWith(currentDataValue); + callbackA.should.be.calledOnce(); + + callbackB.should.be.calledWith(currentDataValue); + callbackB.should.be.calledOnce(); + + const newDataValue = DatabaseContents.NEW[dataRef]; + await ref.set(newDataValue); + + await new Promise((resolve) => { + setTimeout(() => resolve(), 5); + }); + + callbackA.should.be.calledWith(newDataValue); + callbackB.should.be.calledWith(newDataValue); + + callbackA.should.be.calledTwice(); + callbackB.should.be.calledTwice(); + + // Tear down + + ref.off('value'); + return Promise.resolve(); + }); + }).then(() => { + this.setState({ timeTaken: `Took ${Date.now() - start}` }); + }).catch(console.error); + }; + + render() { + return ( + +