[js][internals] _NAMESPACE & _NATIVE_MODULE static props for all modules

This commit is contained in:
Salakar 2017-08-17 17:58:28 +01:00
parent 75c6a8f787
commit 9825226665
14 changed files with 287 additions and 42 deletions

View File

@ -26,15 +26,15 @@ export default class FirebaseApp {
this._nativeInitialized = false; this._nativeInitialized = false;
// modules // modules
this.admob = this._staticsOrModuleInstance('admob', AdMobStatics, AdMob); this.admob = this._staticsOrModuleInstance(AdMobStatics, AdMob);
this.auth = this._staticsOrModuleInstance('auth', AuthStatics, Auth); this.auth = this._staticsOrModuleInstance(AuthStatics, Auth);
this.analytics = this._staticsOrModuleInstance('analytics', {}, Analytics); this.analytics = this._staticsOrModuleInstance({}, Analytics);
this.config = this._staticsOrModuleInstance('config', {}, RemoteConfig); this.config = this._staticsOrModuleInstance({}, RemoteConfig);
this.crash = this._staticsOrModuleInstance('crash', {}, Crash); this.crash = this._staticsOrModuleInstance({}, Crash);
this.database = this._staticsOrModuleInstance('database', DatabaseStatics, Database); this.database = this._staticsOrModuleInstance(DatabaseStatics, Database);
this.messaging = this._staticsOrModuleInstance('messaging', MessagingStatics, Messaging); this.messaging = this._staticsOrModuleInstance(MessagingStatics, Messaging);
this.perf = this._staticsOrModuleInstance('perf', {}, Performance); this.perf = this._staticsOrModuleInstance({}, Performance);
this.storage = this._staticsOrModuleInstance('storage', StorageStatics, Storage); this.storage = this._staticsOrModuleInstance(StorageStatics, Storage);
} }
/** /**
@ -117,9 +117,9 @@ export default class FirebaseApp {
* @return {function()} * @return {function()}
* @private * @private
*/ */
_staticsOrModuleInstance(name, statics = {}, InstanceClass): Function { _staticsOrModuleInstance(statics = {}, InstanceClass): Function {
const getInstance = () => { const getInstance = () => {
const _name = `_${name}`; const _name = `_${InstanceClass._NAMESPACE}`;
if (!this._namespaces[_name]) { if (!this._namespaces[_name]) {
this._namespaces[_name] = new InstanceClass(this); this._namespaces[_name] = new InstanceClass(this);
@ -129,7 +129,7 @@ export default class FirebaseApp {
}; };
Object.assign(getInstance, statics, { Object.assign(getInstance, statics, {
nativeModuleExists: !!NativeModules[`RNFirebase${InstanceClass._NATIVE_MODULE || capitalizeFirstLetter(name)}`], nativeModuleExists: !!NativeModules[InstanceClass._NATIVE_MODULE],
}); });
return getInstance; return getInstance;

View File

@ -37,15 +37,15 @@ class FirebaseCore {
} }
// modules // modules
this.admob = this._appNamespaceOrStatics('admob', AdMobStatics, AdMob); this.admob = this._appNamespaceOrStatics(AdMobStatics, AdMob);
this.auth = this._appNamespaceOrStatics('auth', AuthStatics, Auth); this.auth = this._appNamespaceOrStatics(AuthStatics, Auth);
this.analytics = this._appNamespaceOrStatics('analytics', {}, Analytics); this.analytics = this._appNamespaceOrStatics({}, Analytics);
this.config = this._appNamespaceOrStatics('config', {}, RemoteConfig); this.config = this._appNamespaceOrStatics({}, RemoteConfig);
this.crash = this._appNamespaceOrStatics('crash', {}, Crash); this.crash = this._appNamespaceOrStatics({}, Crash);
this.database = this._appNamespaceOrStatics('database', DatabaseStatics, Database); this.database = this._appNamespaceOrStatics(DatabaseStatics, Database);
this.messaging = this._appNamespaceOrStatics('messaging', MessagingStatics, Messaging); this.messaging = this._appNamespaceOrStatics(MessagingStatics, Messaging);
this.perf = this._appNamespaceOrStatics('perf', DatabaseStatics, Performance); this.perf = this._appNamespaceOrStatics(DatabaseStatics, Performance);
this.storage = this._appNamespaceOrStatics('storage', StorageStatics, Storage); this.storage = this._appNamespaceOrStatics(StorageStatics, Storage);
} }
/** /**
@ -205,7 +205,8 @@ class FirebaseCore {
* @return {function(FirebaseApp=)} * @return {function(FirebaseApp=)}
* @private * @private
*/ */
_appNamespaceOrStatics(namespace, statics = {}, InstanceClass): Function { _appNamespaceOrStatics(statics = {}, InstanceClass): Function {
const namespace = InstanceClass._NAMESPACE;
const getNamespace = (app?: FirebaseApp) => { const getNamespace = (app?: FirebaseApp) => {
let _app = app; let _app = app;
// throw an error if it's not a valid app instance // throw an error if it's not a valid app instance
@ -218,7 +219,7 @@ class FirebaseCore {
}; };
Object.assign(getNamespace, statics, { Object.assign(getNamespace, statics, {
nativeModuleExists: !!NativeModules[`RNFirebase${InstanceClass._NATIVE_MODULE || capitalizeFirstLetter(namespace)}`], nativeModuleExists: !!NativeModules[InstanceClass._NATIVE_MODULE],
}); });
return getNamespace; return getNamespace;
} }

View File

@ -9,10 +9,11 @@ import NativeExpress from './NativeExpress';
export default class AdMob extends ModuleBase { export default class AdMob extends ModuleBase {
static _NATIVE_MODULE = 'AdMob'; static _NAMESPACE = 'admob';
static _NATIVE_MODULE = 'RNFirebaseAdMob';
constructor(firebaseApp: Object, options: Object = {}) { constructor(firebaseApp: Object, options: Object = {}) {
super(firebaseApp, options, AdMob._NATIVE_MODULE, true); super(firebaseApp, options, true);
this._initialized = false; this._initialized = false;
this._appId = null; this._appId = null;

View File

@ -20,8 +20,11 @@ const ReservedEventNames = [
]; ];
export default class Analytics extends ModuleBase { export default class Analytics extends ModuleBase {
static _NAMESPACE = 'analytics';
static _NATIVE_MODULE = 'RNFirebaseAnalytics';
constructor(firebaseApp: Object, options: Object = {}) { constructor(firebaseApp: Object, options: Object = {}) {
super(firebaseApp, options, 'Analytics'); super(firebaseApp, options);
} }
/** /**

View File

@ -12,12 +12,15 @@ import TwitterAuthProvider from './providers/Twitter';
import FacebookAuthProvider from './providers/Facebook'; import FacebookAuthProvider from './providers/Facebook';
export default class Auth extends ModuleBase { export default class Auth extends ModuleBase {
static _NAMESPACE = 'auth';
static _NATIVE_MODULE = 'RNFirebaseAuth';
_user: User | null; _user: User | null;
_authResult: AuthResultType | null; _authResult: AuthResultType | null;
authenticated: boolean; authenticated: boolean;
constructor(firebaseApp: Object, options: Object = {}) { constructor(firebaseApp: Object, options: Object = {}) {
super(firebaseApp, options, 'Auth', true); super(firebaseApp, options, true);
this._user = null; this._user = null;
this._authResult = null; this._authResult = null;
this.authenticated = false; this.authenticated = false;

View File

@ -7,9 +7,11 @@ import ModuleBase from './../../utils/ModuleBase';
* @class Config * @class Config
*/ */
export default class RemoteConfig extends ModuleBase { export default class RemoteConfig extends ModuleBase {
static _NATIVE_MODULE = 'RemoteConfig'; static _NAMESPACE = 'config';
static _NATIVE_MODULE = 'RNFirebaseRemoteConfig';
constructor(firebaseApp: Object, options: Object = {}) { constructor(firebaseApp: Object, options: Object = {}) {
super(firebaseApp, options, RemoteConfig._NATIVE_MODULE); super(firebaseApp, options);
this.developerModeEnabled = false; this.developerModeEnabled = false;
} }

View File

@ -2,8 +2,11 @@
import ModuleBase from './../../utils/ModuleBase'; import ModuleBase from './../../utils/ModuleBase';
export default class Crash extends ModuleBase { export default class Crash extends ModuleBase {
static _NAMESPACE = 'crash';
static _NATIVE_MODULE = 'RNFirebaseCrash';
constructor(firebaseApp: Object, options: Object = {}) { constructor(firebaseApp: Object, options: Object = {}) {
super(firebaseApp, options, 'Crash'); super(firebaseApp, options);
} }
/** /**

View File

@ -12,8 +12,11 @@ import ModuleBase from './../../utils/ModuleBase';
* @class Database * @class Database
*/ */
export default class Database extends ModuleBase { export default class Database extends ModuleBase {
static _NAMESPACE = 'database';
static _NATIVE_MODULE = 'RNFirebaseDatabase';
constructor(firebaseApp: Object, options: Object = {}) { constructor(firebaseApp: Object, options: Object = {}) {
super(firebaseApp, options, 'Database', true); super(firebaseApp, options, true);
this._transactionHandler = new TransactionHandler(this); this._transactionHandler = new TransactionHandler(this);
if (this._options.persistence) { if (this._options.persistence) {

View File

@ -70,8 +70,11 @@ function finish(data) {
* @class Messaging * @class Messaging
*/ */
export default class Messaging extends ModuleBase { export default class Messaging extends ModuleBase {
static _NAMESPACE = 'messaging';
static _NATIVE_MODULE = 'RNFirebaseMessaging';
constructor(firebaseApp: Object, options: Object = {}) { constructor(firebaseApp: Object, options: Object = {}) {
super(firebaseApp, options, 'Messaging', true); super(firebaseApp, options, true);
} }
get EVENT_TYPE() { get EVENT_TYPE() {

View File

@ -3,8 +3,11 @@ import Trace from './Trace';
import ModuleBase from '../../utils/ModuleBase'; import ModuleBase from '../../utils/ModuleBase';
export default class PerformanceMonitoring extends ModuleBase { export default class PerformanceMonitoring extends ModuleBase {
static _NAMESPACE = 'perf';
static _NATIVE_MODULE = 'RNFirebasePerformance';
constructor(firebaseApp: Object, options: Object = {}) { constructor(firebaseApp: Object, options: Object = {}) {
super(firebaseApp, options, 'Performance'); super(firebaseApp, options);
} }
/** /**

View File

@ -7,13 +7,15 @@ import ModuleBase from './../../utils/ModuleBase';
const FirebaseStorage = NativeModules.RNFirebaseStorage; const FirebaseStorage = NativeModules.RNFirebaseStorage;
export default class Storage extends ModuleBase { export default class Storage extends ModuleBase {
static _NAMESPACE = 'storage';
static _NATIVE_MODULE = 'RNFirebaseStorage';
/** /**
* *
* @param firebaseApp * @param firebaseApp
* @param options * @param options
*/ */
constructor(firebaseApp: Object, options: Object = {}) { constructor(firebaseApp: Object, options: Object = {}) {
super(firebaseApp, options, 'Storage', true); super(firebaseApp, options, true);
this._subscriptions = {}; this._subscriptions = {};
this.addListener( this.addListener(

View File

@ -46,31 +46,31 @@ export default class ModuleBase {
* @param moduleName * @param moduleName
* @param withEventEmitter * @param withEventEmitter
*/ */
constructor(firebaseApp, options, moduleName, withEventEmitter = false) { constructor(firebaseApp, options, withEventEmitter = false) {
this._module = moduleName; this._module = this.constructor._NATIVE_MODULE.replace('RNFirebase', '');
this._firebaseApp = firebaseApp; this._firebaseApp = firebaseApp;
this._appName = firebaseApp._name; this._appName = firebaseApp._name;
this._namespace = `${this._appName}:${this._module}`; 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 // check if native module exists as all native
// modules are now optionally part of build // modules are now optionally part of build
const nativeModule = NativeModules[`RNFirebase${moduleName}`]; const nativeModule = NativeModules[this.constructor._NATIVE_MODULE];
if (!nativeModule) { 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 // used by the modules that extend ModuleBase
// to access their native module counterpart // to access their native module counterpart
if (!MULTI_APP_MODULES.includes(moduleName.toLowerCase())) { if (!MULTI_APP_MODULES.includes(this._module.toLowerCase())) {
this._native = nativeModule; this._native = nativeModule;
} else { } else {
this._native = nativeWithApp(this._appName, nativeModule); this._native = nativeWithApp(this._appName, nativeModule);
} }
if (withEventEmitter) { if (withEventEmitter) {
this._setupEventEmitter(nativeModule, moduleName); this._setupEventEmitter(nativeModule, this._module);
} }
} }

92
tests/src/bench.js Normal file
View File

@ -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 (
<View style={{ marginTop: 15, backgroundColor: '#000' }}>
<Button title="Run Test" onPress={this.clickMe} />
<Text style={{ color: '#fff' }}>{this.state.timeTaken || ''}</Text>
</View>
);
}
}

View File

@ -1,5 +1,5 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import { View, Text, Button } from 'react-native'; import { View, SectionList, Text, Button } from 'react-native';
export default class HomeScreen extends Component { export default class HomeScreen extends Component {
@ -28,3 +28,132 @@ export default class HomeScreen extends Component {
); );
} }
} }
const sampleData = {
somePostId1: {
title: 'today now',
timestamp: Date.now(),
startOfDay: 1502838000,
},
somePostId3: {
title: 'today but older',
timestamp: Date.now() - 10000000,
startOfDay: 1502838000,
},
somePostId4: {
title: 'today but even older',
timestamp: Date.now() - 60000000,
startOfDay: 1502838000,
},
somePostId2: {
title: 'hello yesterday',
timestamp: Date.now() - 82000000, // minus 23 hours - just to make it yesterday ;p
startOfDay: 1502751600, // yesterday ;p
},
somePostId5: {
title: 'hello yesterday but older',
timestamp: Date.now() - 82800000, // minus 23 hours - just to make it yesterday ;p
startOfDay: 1502751600, // yesterday ;p
},
};
// export default class PostsScreen extends Component {
// constructor(props) {
// super(props);
// this.ref = null;
// this.state = {
// postSections: [],
// };
// }
//
// componentDidMount() {
// // this.ref = firebase.database().ref('posts');
// // this.ref.on('value', this._onPostsUpdate);
// // just fake it to test
// this._onPostsUpdate({
// val() {
// return sampleData;
// },
// });
// }
//
// componentWillUnmount() {
// // always unsubscribe from realtime events when component unmounts
// // if (this.ref) {
// // this.ref.off('value', this._onPostsUpdate);
// // }
// }
//
// _onPostsUpdate(snapshot) {
// const value = snapshot.val() || {};
// const keys = Object.keys(value);
// const sections = {};
//
// // we'll group them now by date
// for (let i = 0, len = keys.length; i < len; i++) {
// const key = keys[i];
// const post = value[key];
//
// // assuming post will have a 'timestamp' field and a `startOfDay` field
// // start of day can be calculated as above `startOfToday`
//
// if (!sections[post.startOfDay]) {
// sections[post.startOfDay] = {
// title: 'Header - I will leave this up to you', // todo today/yesterday/3 days ago etc
// // will use this later to sort the sections so today is on top
// key: post.startOfDay,
// data: [],
// };
// }
//
// const data = Object.assign({ key }, post);
// // add a post to a specific section date
// // we'll push/unshift depending on the date, so they' appear in order
// if (!sections[post.startOfDay].data.length) {
// // array is empty so nothing to compare sort, just push it
// sections[post.startOfDay].data.push(data);
// } else {
// const previousTimestamp = sections[post.startOfDay].data[sections[post.startOfDay].data.length - 1].timestamp;
// if (previousTimestamp < data.timestamp) sections[post.startOfDay].data.unshift(data);
// else sections[post.startOfDay].data.push(data);
// }
// }
//
// this.setState({
// postSections: Object.values(sections).sort((a, b) => a.key > b.key).reverse(),
// });
// }
//
// _renderSectionItem = ({ item }) => {
// // todo your custom section item component
// // return (
// // <EventCell
// // userName={item.userName}
// // postTitle={item.postTitle}
// // />
// // );
//
// return <Text>{`${item.title} - ${item.timestamp}`}</Text>;
// };
//
// _renderSectionHeader = ({ section }) => {
// // todo your custom section header
// return (
// <Text style={{ backgroundColor: '#000', color: '#fff' }}>{section.title}</Text>
// );
// };
//
// render() {
// return (
// <SectionList
// sections={this.state.postSections}
// renderItem={this._renderSectionItem}
// renderSectionHeader={this._renderSectionHeader}
// />
// );
// }
// }