mirror of
https://github.com/zhigang1992/react-native-firebase.git
synced 2026-04-24 04:24:52 +08:00
[internals] Start refactoring some of the internals to simplify, tidy up and also reduce flow type pollution
This commit is contained in:
@@ -3,41 +3,27 @@
|
||||
*/
|
||||
import { NativeModules } from 'react-native';
|
||||
|
||||
import APPS from '../../utils/apps';
|
||||
import { SharedEventEmitter } from '../../utils/events';
|
||||
import INTERNALS from '../../utils/internals';
|
||||
import { isObject, isAndroid } from '../../utils';
|
||||
import { isObject } from '../../utils';
|
||||
|
||||
import AdMob, { statics as AdMobStatics } from '../admob';
|
||||
import Auth, { statics as AuthStatics } from '../auth';
|
||||
import Analytics, { statics as AnalyticsStatics } from '../analytics';
|
||||
import Config, { statics as ConfigStatics } from '../config';
|
||||
import Crash, { statics as CrashStatics } from '../crash';
|
||||
import Crashlytics, { statics as CrashlyticsStatics } from '../fabric/crashlytics';
|
||||
import Database, { statics as DatabaseStatics } from '../database';
|
||||
import Firestore, { statics as FirestoreStatics } from '../firestore';
|
||||
import Links, { statics as LinksStatics } from '../links';
|
||||
import Messaging, { statics as MessagingStatics } from '../messaging';
|
||||
import Performance, { statics as PerformanceStatics } from '../perf';
|
||||
import Storage, { statics as StorageStatics } from '../storage';
|
||||
import Utils, { statics as UtilsStatics } from '../utils';
|
||||
import AdMob from '../admob';
|
||||
import Auth from '../auth';
|
||||
import Analytics from '../analytics';
|
||||
import Config from '../config';
|
||||
import Crash from '../crash';
|
||||
import Crashlytics from '../fabric/crashlytics';
|
||||
import Database from '../database';
|
||||
import Firestore from '../firestore';
|
||||
import Links from '../links';
|
||||
import Messaging from '../messaging';
|
||||
import Performance from '../perf';
|
||||
import Storage from '../storage';
|
||||
import Utils from '../utils';
|
||||
|
||||
import type {
|
||||
AdMobModule,
|
||||
AnalyticsModule,
|
||||
AuthModule,
|
||||
ConfigModule,
|
||||
CrashModule,
|
||||
DatabaseModule,
|
||||
FabricModule,
|
||||
FirebaseModule,
|
||||
FirebaseModuleAndStatics,
|
||||
FirebaseOptions,
|
||||
FirebaseStatics,
|
||||
FirestoreModule,
|
||||
LinksModule,
|
||||
MessagingModule,
|
||||
PerformanceModule,
|
||||
StorageModule,
|
||||
UtilsModule,
|
||||
} from '../../types';
|
||||
|
||||
const FirebaseCoreModule = NativeModules.RNFirebase;
|
||||
@@ -45,70 +31,57 @@ const FirebaseCoreModule = NativeModules.RNFirebase;
|
||||
|
||||
export default class FirebaseApp {
|
||||
_extendedProps: { [string] : boolean };
|
||||
_initialized: boolean;
|
||||
_initialized: boolean = false;
|
||||
_name: string;
|
||||
_namespaces: { [string]: FirebaseModule };
|
||||
_nativeInitialized: boolean;
|
||||
_nativeInitialized: boolean = false;
|
||||
_options: FirebaseOptions;
|
||||
admob: AdMobModule;
|
||||
analytics: AnalyticsModule;
|
||||
auth: AuthModule;
|
||||
config: ConfigModule;
|
||||
crash: CrashModule;
|
||||
database: DatabaseModule;
|
||||
fabric: FabricModule;
|
||||
firestore: FirestoreModule;
|
||||
links: LinksModule;
|
||||
messaging: MessagingModule;
|
||||
perf: PerformanceModule;
|
||||
storage: StorageModule;
|
||||
utils: UtilsModule;
|
||||
admob: () => AdMob;
|
||||
analytics: () => Analytics;
|
||||
auth: () => Auth;
|
||||
config: () => Config;
|
||||
crash: () => Crash;
|
||||
database: () => Database;
|
||||
fabric: {
|
||||
crashlytics: () => Crashlytics,
|
||||
};
|
||||
firestore: () => Firestore;
|
||||
links: () => Links;
|
||||
messaging: () => Messaging;
|
||||
perf: () => Performance;
|
||||
storage: () => Storage;
|
||||
utils: () => Utils;
|
||||
|
||||
constructor(name: string, options: FirebaseOptions) {
|
||||
constructor(name: string, options: FirebaseOptions, fromNative: boolean = false) {
|
||||
this._name = name;
|
||||
this._namespaces = {};
|
||||
this._options = Object.assign({}, options);
|
||||
|
||||
// native ios/android to confirm initialized
|
||||
this._initialized = false;
|
||||
this._nativeInitialized = false;
|
||||
|
||||
// modules
|
||||
this.admob = this._staticsOrModuleInstance(AdMobStatics, AdMob);
|
||||
this.analytics = this._staticsOrModuleInstance(AnalyticsStatics, Analytics);
|
||||
this.auth = this._staticsOrModuleInstance(AuthStatics, Auth);
|
||||
this.config = this._staticsOrModuleInstance(ConfigStatics, Config);
|
||||
this.crash = this._staticsOrModuleInstance(CrashStatics, Crash);
|
||||
this.database = this._staticsOrModuleInstance(DatabaseStatics, Database);
|
||||
this.fabric = {
|
||||
crashlytics: this._staticsOrModuleInstance(CrashlyticsStatics, Crashlytics),
|
||||
};
|
||||
this.firestore = this._staticsOrModuleInstance(FirestoreStatics, Firestore);
|
||||
this.links = this._staticsOrModuleInstance(LinksStatics, Links);
|
||||
this.messaging = this._staticsOrModuleInstance(MessagingStatics, Messaging);
|
||||
this.perf = this._staticsOrModuleInstance(PerformanceStatics, Performance);
|
||||
this.storage = this._staticsOrModuleInstance(StorageStatics, Storage);
|
||||
this.utils = this._staticsOrModuleInstance(UtilsStatics, Utils);
|
||||
this._extendedProps = {};
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param native
|
||||
* @private
|
||||
*/
|
||||
_initializeApp(native: boolean = false) {
|
||||
if (native) {
|
||||
// for apps already initialized natively that
|
||||
// we have info from RN constants
|
||||
if (fromNative) {
|
||||
this._initialized = true;
|
||||
this._nativeInitialized = true;
|
||||
} else {
|
||||
} else if (options.databaseURL && options.apiKey) {
|
||||
FirebaseCoreModule.initializeApp(this._name, this._options, (error, result) => {
|
||||
this._initialized = true;
|
||||
INTERNALS.SharedEventEmitter.emit(`AppReady:${this._name}`, { error, result });
|
||||
SharedEventEmitter.emit(`AppReady:${this._name}`, { error, result });
|
||||
});
|
||||
}
|
||||
|
||||
// modules
|
||||
this.admob = APPS.appModule(this, 'admob', AdMob);
|
||||
this.analytics = APPS.appModule(this, 'analytics', Analytics);
|
||||
this.auth = APPS.appModule(this, 'auth', Auth);
|
||||
this.config = APPS.appModule(this, 'config', Config);
|
||||
this.crash = APPS.appModule(this, 'crash', Crash);
|
||||
this.database = APPS.appModule(this, 'database', Database);
|
||||
this.fabric = {
|
||||
crashlytics: APPS.appModule(this, 'crashlytics', Crashlytics),
|
||||
};
|
||||
this.firestore = APPS.appModule(this, 'firestore', Firestore);
|
||||
this.links = APPS.appModule(this, 'links', Links);
|
||||
this.messaging = APPS.appModule(this, 'messaging', Messaging);
|
||||
this.perf = APPS.appModule(this, 'perf', Performance);
|
||||
this.storage = APPS.appModule(this, 'storage', Storage);
|
||||
this.utils = APPS.appModule(this, 'utils', Utils);
|
||||
this._extendedProps = {};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -183,38 +156,10 @@ export default class FirebaseApp {
|
||||
if (this._initialized) return Promise.resolve(this);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
INTERNALS.SharedEventEmitter.once(`AppReady:${this._name}`, ({ error }) => {
|
||||
SharedEventEmitter.once(`AppReady:${this._name}`, ({ error }) => {
|
||||
if (error) return reject(new Error(error)); // error is a string as it's from native
|
||||
return resolve(this); // return app
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param statics
|
||||
* @param InstanceClass
|
||||
* @return {function()}
|
||||
* @private
|
||||
*/
|
||||
_staticsOrModuleInstance<M: FirebaseModule, S:FirebaseStatics>(statics: S, InstanceClass: Class<M>): FirebaseModuleAndStatics<M, S> {
|
||||
const getInstance = (): M => {
|
||||
const _name = `_${InstanceClass._NAMESPACE}`;
|
||||
|
||||
if (isAndroid && InstanceClass._NAMESPACE !== Utils._NAMESPACE && !INTERNALS.FLAGS.checkedPlayServices) {
|
||||
INTERNALS.FLAGS.checkedPlayServices = true;
|
||||
this.utils().checkPlayServicesAvailability();
|
||||
}
|
||||
|
||||
if (!this._namespaces[_name]) {
|
||||
this._namespaces[_name] = new InstanceClass(this, this._options);
|
||||
}
|
||||
|
||||
return this._namespaces[_name];
|
||||
};
|
||||
|
||||
return Object.assign(getInstance, statics, {
|
||||
nativeModuleExists: !!NativeModules[InstanceClass._NATIVE_MODULE],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
* @providesModule Firebase
|
||||
* @flow
|
||||
*/
|
||||
import { NativeModules, NativeEventEmitter } from 'react-native';
|
||||
import { NativeModules } from 'react-native';
|
||||
|
||||
import APPS from '../../utils/apps';
|
||||
import INTERNALS from '../../utils/internals';
|
||||
import FirebaseApp from './firebase-app';
|
||||
import { isObject, isString } from '../../utils';
|
||||
|
||||
// module imports
|
||||
import AdMob, { statics as AdMobStatics } from '../admob';
|
||||
@@ -31,10 +31,7 @@ import type {
|
||||
CrashModule,
|
||||
DatabaseModule,
|
||||
FabricModule,
|
||||
FirebaseModule,
|
||||
FirebaseModuleAndStatics,
|
||||
FirebaseOptions,
|
||||
FirebaseStatics,
|
||||
FirestoreModule,
|
||||
LinksModule,
|
||||
MessagingModule,
|
||||
@@ -46,8 +43,6 @@ import type {
|
||||
const FirebaseCoreModule = NativeModules.RNFirebase;
|
||||
|
||||
class FirebaseCore {
|
||||
_nativeEmitters: { [string]: NativeEventEmitter };
|
||||
_nativeSubscriptions: { [string]: boolean };
|
||||
admob: AdMobModule;
|
||||
analytics: AnalyticsModule;
|
||||
auth: AuthModule;
|
||||
@@ -63,45 +58,27 @@ class FirebaseCore {
|
||||
utils: UtilsModule;
|
||||
|
||||
constructor() {
|
||||
this._nativeEmitters = {};
|
||||
this._nativeSubscriptions = {};
|
||||
|
||||
if (!FirebaseCoreModule) {
|
||||
throw (new Error(INTERNALS.STRINGS.ERROR_MISSING_CORE));
|
||||
}
|
||||
|
||||
this._initializeNativeApps();
|
||||
APPS.initializeNativeApps();
|
||||
|
||||
// modules
|
||||
this.admob = this._appNamespaceOrStatics(AdMobStatics, AdMob);
|
||||
this.analytics = this._appNamespaceOrStatics(AnalyticsStatics, Analytics);
|
||||
this.auth = this._appNamespaceOrStatics(AuthStatics, Auth);
|
||||
this.config = this._appNamespaceOrStatics(ConfigStatics, Config);
|
||||
this.crash = this._appNamespaceOrStatics(CrashStatics, Crash);
|
||||
this.database = this._appNamespaceOrStatics(DatabaseStatics, Database);
|
||||
this.admob = APPS.moduleAndStatics('admob', AdMobStatics, AdMob);
|
||||
this.analytics = APPS.moduleAndStatics('analytics', AnalyticsStatics, Analytics);
|
||||
this.auth = APPS.moduleAndStatics('auth', AuthStatics, Auth);
|
||||
this.config = APPS.moduleAndStatics('config', ConfigStatics, Config);
|
||||
this.crash = APPS.moduleAndStatics('crash', CrashStatics, Crash);
|
||||
this.database = APPS.moduleAndStatics('database', DatabaseStatics, Database);
|
||||
this.fabric = {
|
||||
crashlytics: this._appNamespaceOrStatics(CrashlyticsStatics, Crashlytics),
|
||||
crashlytics: APPS.moduleAndStatics('crashlytics', CrashlyticsStatics, Crashlytics),
|
||||
};
|
||||
this.firestore = this._appNamespaceOrStatics(FirestoreStatics, Firestore);
|
||||
this.links = this._appNamespaceOrStatics(LinksStatics, Links);
|
||||
this.messaging = this._appNamespaceOrStatics(MessagingStatics, Messaging);
|
||||
this.perf = this._appNamespaceOrStatics(PerformanceStatics, Performance);
|
||||
this.storage = this._appNamespaceOrStatics(StorageStatics, Storage);
|
||||
this.utils = this._appNamespaceOrStatics(UtilsStatics, Utils);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bootstraps all native app instances that were discovered on boot
|
||||
* @private
|
||||
*/
|
||||
_initializeNativeApps() {
|
||||
for (let i = 0, len = FirebaseCoreModule.apps.length; i < len; i++) {
|
||||
const app = FirebaseCoreModule.apps[i];
|
||||
const options = Object.assign({}, app);
|
||||
delete options.name;
|
||||
INTERNALS.APPS[app.name] = new FirebaseApp(app.name, options);
|
||||
INTERNALS.APPS[app.name]._initializeApp(true);
|
||||
}
|
||||
this.firestore = APPS.moduleAndStatics('firestore', FirestoreStatics, Firestore);
|
||||
this.links = APPS.moduleAndStatics('links', LinksStatics, Links);
|
||||
this.messaging = APPS.moduleAndStatics('messaging', MessagingStatics, Messaging);
|
||||
this.perf = APPS.moduleAndStatics('perf', PerformanceStatics, Performance);
|
||||
this.storage = APPS.moduleAndStatics('storage', StorageStatics, Storage);
|
||||
this.utils = APPS.moduleAndStatics('utils', UtilsStatics, Utils);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -112,57 +89,7 @@ class FirebaseCore {
|
||||
* @return {*}
|
||||
*/
|
||||
initializeApp(options: FirebaseOptions, name: string): FirebaseApp {
|
||||
if (name && !isString(name)) {
|
||||
throw new Error(INTERNALS.STRINGS.ERROR_INIT_STRING_NAME);
|
||||
}
|
||||
|
||||
const _name = (name || INTERNALS.STRINGS.DEFAULT_APP_NAME).toUpperCase();
|
||||
|
||||
// return an existing app if found
|
||||
// todo in v4 remove deprecation and throw an error
|
||||
if (INTERNALS.APPS[_name]) {
|
||||
console.warn(INTERNALS.STRINGS.WARN_INITIALIZE_DEPRECATION);
|
||||
return INTERNALS.APPS[_name];
|
||||
}
|
||||
|
||||
// only validate if app doesn't already exist
|
||||
// to allow apps already initialized natively
|
||||
// to still go through init without erroring (backwards compatibility)
|
||||
if (!isObject(options)) {
|
||||
throw new Error(INTERNALS.STRINGS.ERROR_INIT_OBJECT);
|
||||
}
|
||||
|
||||
if (!options.apiKey) {
|
||||
throw new Error(INTERNALS.STRINGS.ERROR_MISSING_OPT('apiKey'));
|
||||
}
|
||||
|
||||
if (!options.appId) {
|
||||
throw new Error(INTERNALS.STRINGS.ERROR_MISSING_OPT('appId'));
|
||||
}
|
||||
|
||||
if (!options.databaseURL) {
|
||||
throw new Error(INTERNALS.STRINGS.ERROR_MISSING_OPT('databaseURL'));
|
||||
}
|
||||
|
||||
if (!options.messagingSenderId) {
|
||||
throw new Error(INTERNALS.STRINGS.ERROR_MISSING_OPT('messagingSenderId'));
|
||||
}
|
||||
|
||||
if (!options.projectId) {
|
||||
throw new Error(INTERNALS.STRINGS.ERROR_MISSING_OPT('projectId'));
|
||||
}
|
||||
|
||||
if (!options.storageBucket) {
|
||||
throw new Error(INTERNALS.STRINGS.ERROR_MISSING_OPT('storageBucket'));
|
||||
}
|
||||
|
||||
INTERNALS.APPS[_name] = new FirebaseApp(_name, options);
|
||||
// only initialize if certain props are available
|
||||
if (options.databaseURL && options.apiKey) {
|
||||
INTERNALS.APPS[_name]._initializeApp();
|
||||
}
|
||||
|
||||
return INTERNALS.APPS[_name];
|
||||
return APPS.initializeApp(options, name);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -175,10 +102,7 @@ class FirebaseCore {
|
||||
* @return {*}
|
||||
*/
|
||||
app(name?: string): FirebaseApp {
|
||||
const _name = name ? name.toUpperCase() : INTERNALS.STRINGS.DEFAULT_APP_NAME;
|
||||
const app = INTERNALS.APPS[_name];
|
||||
if (!app) throw new Error(INTERNALS.STRINGS.ERROR_APP_NOT_INIT(_name));
|
||||
return app;
|
||||
return APPS.app(name);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -186,83 +110,7 @@ class FirebaseCore {
|
||||
* @return {Array}
|
||||
*/
|
||||
get apps(): Array<FirebaseApp> {
|
||||
return Object.values(INTERNALS.APPS);
|
||||
}
|
||||
|
||||
/*
|
||||
* INTERNALS
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @param nativeEmitter
|
||||
* @private
|
||||
*/
|
||||
_subscribeForDistribution(eventName: string, nativeEmitter: NativeEventEmitter) {
|
||||
if (!this._nativeSubscriptions[eventName]) {
|
||||
nativeEmitter.addListener(eventName, (event) => {
|
||||
if (event.appName) {
|
||||
// native event has an appName property - auto prefix and internally emit
|
||||
INTERNALS.SharedEventEmitter.emit(`${event.appName}-${eventName}`, event);
|
||||
} else {
|
||||
// standard event - no need to prefix
|
||||
INTERNALS.SharedEventEmitter.emit(eventName, event);
|
||||
}
|
||||
});
|
||||
|
||||
this._nativeSubscriptions[eventName] = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param statics
|
||||
* @param InstanceClass
|
||||
* @return {function(FirebaseApp=)}
|
||||
* @private
|
||||
*/
|
||||
_appNamespaceOrStatics<M: FirebaseModule, S: FirebaseStatics>(statics: S, InstanceClass: Class<M>): FirebaseModuleAndStatics<M, S> {
|
||||
const namespace = InstanceClass._NAMESPACE;
|
||||
|
||||
const getNamespace = (app?: FirebaseApp) => {
|
||||
let _app = app;
|
||||
|
||||
// throw an error if it's not a valid app instance
|
||||
if (_app && !(_app instanceof FirebaseApp)) throw new Error(INTERNALS.STRINGS.ERROR_NOT_APP(namespace));
|
||||
|
||||
// default to the 'DEFAULT' app if no arg provided - will throw an error
|
||||
// if default app not initialized
|
||||
else if (!_app) _app = this.app(INTERNALS.STRINGS.DEFAULT_APP_NAME);
|
||||
const firebaseApp = INTERNALS.APPS[_app._name];
|
||||
if (namespace === 'crashlytics') {
|
||||
return firebaseApp.fabric[namespace](_app);
|
||||
}
|
||||
return firebaseApp[namespace](_app);
|
||||
};
|
||||
|
||||
return Object.assign(getNamespace, statics, {
|
||||
nativeModuleExists: !!NativeModules[InstanceClass._NATIVE_MODULE],
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param name
|
||||
* @param nativeModule
|
||||
* @return {*}
|
||||
* @private
|
||||
*/
|
||||
_getOrSetNativeEmitter(name: string, nativeModule: Object): NativeEventEmitter {
|
||||
if (this._nativeEmitters[name]) {
|
||||
return this._nativeEmitters[name];
|
||||
}
|
||||
|
||||
return this._nativeEmitters[name] = new NativeEventEmitter(nativeModule);
|
||||
return APPS.apps();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user