Merge pull request #729 from invertase/master

Ignore
This commit is contained in:
Michael Diarmid 2018-01-06 12:38:11 +00:00 committed by GitHub
commit 59d9bca7b3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
61 changed files with 1427 additions and 1315 deletions

View File

@ -50,7 +50,7 @@ module.system=haste
emoji=true
munge_underscores=false
munge_underscores=true
module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub'
@ -69,6 +69,7 @@ suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(5[0-6]\\|[1
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(5[0-6]\\|[1-4][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+
suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError
suppress_comment=\\(.\\|\n\\)*\\$FlowBug.*
unsafe.enable_getters_and_setters=true

View File

@ -1,14 +1,13 @@
<!---
- BEFORE YOU MAKE AN ISSUE -
-->
BEFORE YOU MAKE AN ISSUE
<!---
1) If you're trying to request a feature then please do so via our request board:
https://react-native-firebase.canny.io/feature-requests
--->
The issue list of this repo is exclusively for bug reports.
<!---
2) If this is a setup issue then please make sure you've correctly followed the setup guides, most setup issues such as 'duplicate dex files', 'default app has not been initialized' etc are all down to an incorrect setup as the guides haven't been correctly followed.
1) For feature requests, please use our Canny board: https://react-native-firebase.canny.io/feature-requests
2) For questions and support please use our Discord chat: https://discord.gg/t6bdqMs or Stack Overflow: https://stackoverflow.com/questions/tagged/react-native-firebase
3) If this is a setup issue then please make sure you've correctly followed the setup guides, most setup issues such as 'duplicate dex files', 'default app has not been initialized' etc are all down to an incorrect setup as the guides haven't been correctly followed.
-->
### Issue
@ -38,4 +37,4 @@
6. Firebase Module:
<!-- Love react-native-firebase? Please consider supporting our collective:
👉 https://opencollective.com/react-native-firebase/donate -->
👉 https://opencollective.com/react-native-firebase/donate -->

View File

@ -1,4 +1,5 @@
#import "RNFirebase.h"
#import "RNFirebaseUtil.h"
#import <FirebaseCore/FirebaseCore.h>
@implementation RNFirebase
@ -21,14 +22,14 @@ RCT_EXPORT_MODULE(RNFirebase);
* @return
*/
RCT_EXPORT_METHOD(initializeApp:
(NSString *) appName
(NSString *) appDisplayName
options:
(NSDictionary *) options
callback:
(RCTResponseSenderBlock) callback) {
dispatch_sync(dispatch_get_main_queue(), ^{
FIRApp *existingApp = [FIRApp appNamed:appName];
FIRApp *existingApp = [RNFirebaseUtil getApp:appDisplayName];
if (!existingApp) {
FIROptions *firOptions = [[FIROptions alloc] initWithGoogleAppID:[options valueForKey:@"appId"] GCMSenderID:[options valueForKey:@"messagingSenderId"]];
@ -43,6 +44,7 @@ RCT_EXPORT_METHOD(initializeApp:
firOptions.deepLinkURLScheme = [options valueForKey:@"deepLinkURLScheme"];
firOptions.bundleID = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleIdentifier"];
NSString *appName = [RNFirebaseUtil getAppName:appDisplayName];
[FIRApp configureWithName:appName options:firOptions];
}
@ -55,13 +57,13 @@ RCT_EXPORT_METHOD(initializeApp:
* @return
*/
RCT_EXPORT_METHOD(deleteApp:
(NSString *) appName
(NSString *) appDisplayName
resolver:
(RCTPromiseResolveBlock) resolve
rejecter:
(RCTPromiseRejectBlock) reject) {
FIRApp *existingApp = [FIRApp appNamed:appName];
FIRApp *existingApp = [RNFirebaseUtil getApp:appDisplayName];
if (!existingApp) {
return resolve([NSNull null]);
@ -90,7 +92,7 @@ RCT_EXPORT_METHOD(deleteApp:
NSMutableDictionary *appOptions = [NSMutableDictionary new];
FIRApp *firApp = firApps[key];
FIROptions *firOptions = [firApp options];
appOptions[@"name"] = firApp.name;
appOptions[@"name"] = [RNFirebaseUtil getAppDisplayName:firApp.name];
appOptions[@"apiKey"] = firOptions.APIKey;
appOptions[@"appId"] = firOptions.googleAppID;
appOptions[@"databaseURL"] = firOptions.databaseURL;

View File

@ -3,11 +3,15 @@
#import <Foundation/Foundation.h>
#import <React/RCTEventEmitter.h>
#import <Firebase.h>
@interface RNFirebaseUtil : NSObject
+ (FIRApp *)getApp:(NSString *)appDisplayName;
+ (NSString *)getAppName:(NSString *)appDisplayName;
+ (NSString *)getAppDisplayName:(NSString *)appName;
+ (void)sendJSEvent:(RCTEventEmitter *)emitter name:(NSString *)name body:(NSDictionary *)body;
+ (void)sendJSEventWithAppName:(RCTEventEmitter *)emitter appName:(NSString *)appName name:(NSString *)name body:(NSDictionary *)body;
+ (void)sendJSEventWithAppName:(RCTEventEmitter *)emitter app:(FIRApp *)app name:(NSString *)name body:(NSDictionary *)body;
@end

View File

@ -2,6 +2,28 @@
@implementation RNFirebaseUtil
static NSString *const DEFAULT_APP_DISPLAY_NAME = @"[DEFAULT]";
static NSString *const DEFAULT_APP_NAME = @"__FIRAPP_DEFAULT";
+ (FIRApp *)getApp:(NSString *)appDisplayName {
NSString *appName = [RNFirebaseUtil getAppName:appDisplayName];
return [FIRApp appNamed:appName];
}
+ (NSString *)getAppName:(NSString *)appDisplayName {
if ([appDisplayName isEqualToString:DEFAULT_APP_DISPLAY_NAME]) {
return DEFAULT_APP_NAME;
}
return appDisplayName;
}
+ (NSString *)getAppDisplayName:(NSString *)appName {
if ([appName isEqualToString:DEFAULT_APP_NAME]) {
return DEFAULT_APP_DISPLAY_NAME;
}
return appName;
}
+ (void)sendJSEvent:(RCTEventEmitter *)emitter name:(NSString *)name body:(NSDictionary *)body {
@try {
// TODO: Temporary fix for https://github.com/invertase/react-native-firebase/issues/233
@ -14,10 +36,10 @@
}
}
+ (void)sendJSEventWithAppName:(RCTEventEmitter *)emitter appName:(NSString *)appName name:(NSString *)name body:(NSDictionary *)body {
+ (void)sendJSEventWithAppName:(RCTEventEmitter *)emitter app:(FIRApp *)app name:(NSString *)name body:(NSDictionary *)body {
// Add the appName to the body
NSMutableDictionary *newBody = [body mutableCopy];
newBody[@"appName"] = appName;
newBody[@"appName"] = [RNFirebaseUtil getAppDisplayName:app.name];
[RNFirebaseUtil sendJSEvent:emitter name:name body:newBody];
}

View File

@ -23,19 +23,19 @@ RCT_EXPORT_MODULE();
*/
RCT_EXPORT_METHOD(addAuthStateListener:
(NSString *) appName) {
(NSString *) appDisplayName) {
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
if (![_authStateHandlers valueForKey:appName]) {
FIRApp *firApp = [FIRApp appNamed:appName];
if (![_authStateHandlers valueForKey:firApp.name]) {
FIRAuthStateDidChangeListenerHandle newListenerHandle = [[FIRAuth authWithApp:firApp] addAuthStateDidChangeListener:^(FIRAuth *_Nonnull auth, FIRUser *_Nullable user) {
if (user != nil) {
[RNFirebaseUtil sendJSEventWithAppName:self appName:appName name:AUTH_CHANGED_EVENT body:@{@"authenticated": @(true), @"user": [self firebaseUserToDict:user]}];
[RNFirebaseUtil sendJSEventWithAppName:self app:firApp name:AUTH_CHANGED_EVENT body:@{@"authenticated": @(true), @"user": [self firebaseUserToDict:user]}];
} else {
[RNFirebaseUtil sendJSEventWithAppName:self appName:appName name:AUTH_CHANGED_EVENT body:@{@"authenticated": @(false)}];
[RNFirebaseUtil sendJSEventWithAppName:self app:firApp name:AUTH_CHANGED_EVENT body:@{@"authenticated": @(false)}];
}
}];
_authStateHandlers[appName] = [NSValue valueWithNonretainedObject:newListenerHandle];
_authStateHandlers[firApp.name] = [NSValue valueWithNonretainedObject:newListenerHandle];
}
}
@ -44,12 +44,12 @@ RCT_EXPORT_METHOD(addAuthStateListener:
*/
RCT_EXPORT_METHOD(removeAuthStateListener:
(NSString *) appName) {
(NSString *) appDisplayName) {
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
if ([_authStateHandlers valueForKey:appName]) {
FIRApp *firApp = [FIRApp appNamed:appName];
[[FIRAuth authWithApp:firApp] removeAuthStateDidChangeListener:[_authStateHandlers valueForKey:appName]];
[_authStateHandlers removeObjectForKey:appName];
if ([_authStateHandlers valueForKey:firApp.name]) {
[[FIRAuth authWithApp:firApp] removeAuthStateDidChangeListener:[_authStateHandlers valueForKey:firApp.name]];
[_authStateHandlers removeObjectForKey:firApp.name];
}
}
@ -58,19 +58,19 @@ RCT_EXPORT_METHOD(removeAuthStateListener:
*/
RCT_EXPORT_METHOD(addIdTokenListener:
(NSString *) appName) {
(NSString *) appDisplayName) {
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
if (![_idTokenHandlers valueForKey:appName]) {
FIRApp *firApp = [FIRApp appNamed:appName];
if (![_idTokenHandlers valueForKey:firApp.name]) {
FIRIDTokenDidChangeListenerHandle newListenerHandle = [[FIRAuth authWithApp:firApp] addIDTokenDidChangeListener:^(FIRAuth * _Nonnull auth, FIRUser * _Nullable user) {
if (user != nil) {
[RNFirebaseUtil sendJSEventWithAppName:self appName:appName name:AUTH_ID_TOKEN_CHANGED_EVENT body:@{@"authenticated": @(true), @"user": [self firebaseUserToDict:user]}];
[RNFirebaseUtil sendJSEventWithAppName:self app:firApp name:AUTH_ID_TOKEN_CHANGED_EVENT body:@{@"authenticated": @(true), @"user": [self firebaseUserToDict:user]}];
} else {
[RNFirebaseUtil sendJSEventWithAppName:self appName:appName name:AUTH_ID_TOKEN_CHANGED_EVENT body:@{@"authenticated": @(false)}];
[RNFirebaseUtil sendJSEventWithAppName:self app:firApp name:AUTH_ID_TOKEN_CHANGED_EVENT body:@{@"authenticated": @(false)}];
}
}];
_idTokenHandlers[appName] = [NSValue valueWithNonretainedObject:newListenerHandle];
_idTokenHandlers[firApp.name] = [NSValue valueWithNonretainedObject:newListenerHandle];
}
}
@ -79,11 +79,12 @@ RCT_EXPORT_METHOD(addIdTokenListener:
*/
RCT_EXPORT_METHOD(removeIdTokenListener:
(NSString *) appName) {
if ([_idTokenHandlers valueForKey:appName]) {
FIRApp *firApp = [FIRApp appNamed:appName];
[[FIRAuth authWithApp:firApp] removeIDTokenDidChangeListener:[_idTokenHandlers valueForKey:appName]];
[_idTokenHandlers removeObjectForKey:appName];
(NSString *) appDisplayName) {
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
if ([_idTokenHandlers valueForKey:firApp.name]) {
[[FIRAuth authWithApp:firApp] removeIDTokenDidChangeListener:[_idTokenHandlers valueForKey:firApp.name]];
[_idTokenHandlers removeObjectForKey:firApp.name];
}
}
@ -97,12 +98,12 @@ RCT_EXPORT_METHOD(removeIdTokenListener:
@return
*/
RCT_EXPORT_METHOD(signOut:
(NSString *) appName
(NSString *) appDisplayName
resolver:
(RCTPromiseResolveBlock) resolve
rejecter:
(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName];
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
FIRUser *user = [FIRAuth authWithApp:firApp].currentUser;
@ -125,12 +126,12 @@ RCT_EXPORT_METHOD(signOut:
@return
*/
RCT_EXPORT_METHOD(signInAnonymously:
(NSString *) appName
(NSString *) appDisplayName
resolver:
(RCTPromiseResolveBlock) resolve
rejecter:
(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName];
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
[[FIRAuth authWithApp:firApp] signInAnonymouslyWithCompletion:^(FIRUser *user, NSError *error) {
if (error) {
@ -152,7 +153,7 @@ RCT_EXPORT_METHOD(signInAnonymously:
@return return
*/
RCT_EXPORT_METHOD(signInWithEmailAndPassword:
(NSString *) appName
(NSString *) appDisplayName
email:
(NSString *) email
pass:
@ -161,7 +162,7 @@ RCT_EXPORT_METHOD(signInWithEmailAndPassword:
(RCTPromiseResolveBlock) resolve
rejecter:
(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName];
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
[[FIRAuth authWithApp:firApp] signInWithEmail:email password:password completion:^(FIRUser *user, NSError *error) {
if (error) {
@ -182,7 +183,7 @@ RCT_EXPORT_METHOD(signInWithEmailAndPassword:
@return return
*/
RCT_EXPORT_METHOD(createUserWithEmailAndPassword:
(NSString *) appName
(NSString *) appDisplayName
email:
(NSString *) email
pass:
@ -191,7 +192,7 @@ RCT_EXPORT_METHOD(createUserWithEmailAndPassword:
(RCTPromiseResolveBlock) resolve
rejecter:
(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName];
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
[[FIRAuth authWithApp:firApp] createUserWithEmail:email password:password completion:^(FIRUser *user, NSError *error) {
if (error) {
@ -210,12 +211,12 @@ RCT_EXPORT_METHOD(createUserWithEmailAndPassword:
@return return
*/
RCT_EXPORT_METHOD(delete:
(NSString *) appName
(NSString *) appDisplayName
resolver:
(RCTPromiseResolveBlock) resolve
rejecter:
(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName];
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
FIRUser *user = [FIRAuth authWithApp:firApp].currentUser;
if (user) {
@ -239,12 +240,12 @@ RCT_EXPORT_METHOD(delete:
@return return
*/
RCT_EXPORT_METHOD(reload:
(NSString *) appName
(NSString *) appDisplayName
resolver:
(RCTPromiseResolveBlock) resolve
rejecter:
(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName];
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
FIRUser *user = [FIRAuth authWithApp:firApp].currentUser;
@ -262,11 +263,11 @@ RCT_EXPORT_METHOD(reload:
@param RCTPromiseRejectBlock reject
@return return
*/
RCT_EXPORT_METHOD(sendEmailVerification:(NSString *) appName
RCT_EXPORT_METHOD(sendEmailVerification:(NSString *) appDisplayName
actionCodeSettings:(NSDictionary *) actionCodeSettings
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName];
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
FIRUser *user = [FIRAuth authWithApp:firApp].currentUser;
if (user) {
@ -298,14 +299,14 @@ RCT_EXPORT_METHOD(sendEmailVerification:(NSString *) appName
@return return
*/
RCT_EXPORT_METHOD(updateEmail:
(NSString *) appName
(NSString *) appDisplayName
email:
(NSString *) email
resolver:
(RCTPromiseResolveBlock) resolve
rejecter:
(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName];
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
FIRUser *user = [FIRAuth authWithApp:firApp].currentUser;
if (user) {
@ -330,14 +331,14 @@ RCT_EXPORT_METHOD(updateEmail:
@return return
*/
RCT_EXPORT_METHOD(updatePassword:
(NSString *) appName
(NSString *) appDisplayName
password:
(NSString *) password
resolver:
(RCTPromiseResolveBlock) resolve
rejecter:
(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName];
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
FIRUser *user = [FIRAuth authWithApp:firApp].currentUser;
@ -364,14 +365,14 @@ RCT_EXPORT_METHOD(updatePassword:
@return return
*/
RCT_EXPORT_METHOD(updateProfile:
(NSString *) appName
(NSString *) appDisplayName
props:
(NSDictionary *) props
resolver:
(RCTPromiseResolveBlock) resolve
rejecter:
(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName];
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
FIRUser *user = [FIRAuth authWithApp:firApp].currentUser;
@ -412,14 +413,14 @@ RCT_EXPORT_METHOD(updateProfile:
@return
*/
RCT_EXPORT_METHOD(getToken:
(NSString *) appName
(NSString *) appDisplayName
forceRefresh:
(BOOL) forceRefresh
resolver:
(RCTPromiseResolveBlock) resolve
rejecter:
(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName];
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
FIRUser *user = [FIRAuth authWithApp:firApp].currentUser;
@ -447,7 +448,7 @@ RCT_EXPORT_METHOD(getToken:
@return
*/
RCT_EXPORT_METHOD(signInWithCredential:
(NSString *) appName
(NSString *) appDisplayName
provider:
(NSString *) provider
token:
@ -458,7 +459,7 @@ RCT_EXPORT_METHOD(signInWithCredential:
(RCTPromiseResolveBlock) resolve
rejecter:
(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName];
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
FIRAuthCredential *credential = [self getCredentialForProvider:provider token:authToken secret:authSecret];
@ -485,7 +486,7 @@ RCT_EXPORT_METHOD(signInWithCredential:
@return
*/
RCT_EXPORT_METHOD(confirmPasswordReset:
(NSString *) appName
(NSString *) appDisplayName
code:
(NSString *) code
newPassword:
@ -494,8 +495,8 @@ RCT_EXPORT_METHOD(confirmPasswordReset:
(RCTPromiseResolveBlock) resolve
rejecter:
(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName];
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
[[FIRAuth authWithApp:firApp] confirmPasswordResetWithCode:code newPassword:newPassword completion:^(NSError *_Nullable error) {
if (error) {
[self promiseRejectAuthException:reject error:error];
@ -515,15 +516,15 @@ RCT_EXPORT_METHOD(confirmPasswordReset:
* @return
*/
RCT_EXPORT_METHOD(applyActionCode:
(NSString *) appName
(NSString *) appDisplayName
code:
(NSString *) code
resolver:
(RCTPromiseResolveBlock) resolve
rejecter:
(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName];
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
[[FIRAuth authWithApp:firApp] applyActionCode:code completion:^(NSError *_Nullable error) {
if (error) {
[self promiseRejectAuthException:reject error:error];
@ -542,15 +543,15 @@ RCT_EXPORT_METHOD(applyActionCode:
* @return
*/
RCT_EXPORT_METHOD(checkActionCode:
(NSString *) appName
(NSString *) appDisplayName
code:
(NSString *) code
resolver:
(RCTPromiseResolveBlock) resolve
rejecter:
(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName];
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
[[FIRAuth authWithApp:firApp] checkActionCode:code completion:^(FIRActionCodeInfo *_Nullable info, NSError *_Nullable error) {
if (error) {
[self promiseRejectAuthException:reject error:error];
@ -586,12 +587,12 @@ RCT_EXPORT_METHOD(checkActionCode:
@param RCTPromiseRejectBlock reject
@return
*/
RCT_EXPORT_METHOD(sendPasswordResetEmail:(NSString *) appName
RCT_EXPORT_METHOD(sendPasswordResetEmail:(NSString *) appDisplayName
email:(NSString *) email
actionCodeSettings:(NSDictionary *) actionCodeSettings
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName];
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
id handler = ^(NSError *_Nullable error) {
if (error) {
@ -600,7 +601,7 @@ RCT_EXPORT_METHOD(sendPasswordResetEmail:(NSString *) appName
[self promiseNoUser:resolve rejecter:reject isError:NO];
}
};
if (actionCodeSettings) {
FIRActionCodeSettings *settings = [self buildActionCodeSettings:actionCodeSettings];
[[FIRAuth authWithApp:firApp] sendPasswordResetWithEmail:email actionCodeSettings:settings completion:handler];
@ -617,12 +618,12 @@ RCT_EXPORT_METHOD(sendPasswordResetEmail:(NSString *) appName
@return
*/
RCT_EXPORT_METHOD(getCurrentUser:
(NSString *) appName
(NSString *) appDisplayName
resolver:
(RCTPromiseResolveBlock) resolve
rejecter:
(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName];
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
FIRUser *user = [FIRAuth authWithApp:firApp].currentUser;
[self promiseWithUser:resolve rejecter:reject user:user];
@ -637,14 +638,14 @@ RCT_EXPORT_METHOD(getCurrentUser:
@return
*/
RCT_EXPORT_METHOD(signInWithCustomToken:
(NSString *) appName
(NSString *) appDisplayName
customToken:
(NSString *) customToken
resolver:
(RCTPromiseResolveBlock) resolve
rejecter:
(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName];
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
[[FIRAuth authWithApp:firApp] signInWithCustomToken:customToken completion:^(FIRUser *user, NSError *error) {
if (error) {
@ -663,11 +664,11 @@ RCT_EXPORT_METHOD(signInWithCustomToken:
@param RCTPromiseRejectBlock reject
@return
*/
RCT_EXPORT_METHOD(signInWithPhoneNumber:(NSString *) appName
RCT_EXPORT_METHOD(signInWithPhoneNumber:(NSString *) appDisplayName
phoneNumber:(NSString *) phoneNumber
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName];
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
[[FIRPhoneAuthProvider providerWithAuth:[FIRAuth authWithApp:firApp]] verifyPhoneNumber:phoneNumber UIDelegate:nil completion:^(NSString * _Nullable verificationID, NSError * _Nullable error) {
if (error) {
@ -684,17 +685,17 @@ RCT_EXPORT_METHOD(signInWithPhoneNumber:(NSString *) appName
/**
verifyPhoneNumber
@param string phoneNumber
@param RCTPromiseResolveBlock resolve
@param RCTPromiseRejectBlock reject
@return
*/
RCT_EXPORT_METHOD(verifyPhoneNumber:(NSString *) appName
RCT_EXPORT_METHOD(verifyPhoneNumber:(NSString *) appDisplayName
phoneNumber:(NSString *) phoneNumber
requestKey:(NSString *) requestKey) {
FIRApp *firApp = [FIRApp appNamed:appName];
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
[[FIRPhoneAuthProvider providerWithAuth:[FIRAuth authWithApp:firApp]] verifyPhoneNumber:phoneNumber UIDelegate:nil completion:^(NSString * _Nullable verificationID, NSError * _Nullable error) {
if (error) {
NSDictionary * jsError = [self getJSError:(error)];
@ -703,7 +704,7 @@ RCT_EXPORT_METHOD(verifyPhoneNumber:(NSString *) appName
@"requestKey":requestKey,
@"state": @{@"error": jsError},
};
[RNFirebaseUtil sendJSEventWithAppName:self appName:appName name:PHONE_AUTH_STATE_CHANGED_EVENT body:body];
[RNFirebaseUtil sendJSEventWithAppName:self app:firApp name:PHONE_AUTH_STATE_CHANGED_EVENT body:body];
} else {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:verificationID forKey:@"authVerificationID"];
@ -712,16 +713,16 @@ RCT_EXPORT_METHOD(verifyPhoneNumber:(NSString *) appName
@"requestKey":requestKey,
@"state": @{@"verificationId": verificationID},
};
[RNFirebaseUtil sendJSEventWithAppName:self appName:appName name:PHONE_AUTH_STATE_CHANGED_EVENT body:body];
[RNFirebaseUtil sendJSEventWithAppName:self app:firApp name:PHONE_AUTH_STATE_CHANGED_EVENT body:body];
}
}];
}
RCT_EXPORT_METHOD(_confirmVerificationCode:(NSString *) appName
RCT_EXPORT_METHOD(_confirmVerificationCode:(NSString *) appDisplayName
verificationCode:(NSString *) verificationCode
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName];
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *verificationId = [defaults stringForKey:@"authVerificationID"];
FIRAuthCredential *credential = [[FIRPhoneAuthProvider provider] credentialWithVerificationID:verificationId verificationCode:verificationCode];
@ -746,7 +747,7 @@ RCT_EXPORT_METHOD(_confirmVerificationCode:(NSString *) appName
@return
*/
RCT_EXPORT_METHOD(link:
(NSString *) appName
(NSString *) appDisplayName
provider:
(NSString *) provider
authToken:
@ -757,7 +758,7 @@ RCT_EXPORT_METHOD(link:
(RCTPromiseResolveBlock) resolve
rejecter:
(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName];
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
FIRAuthCredential *credential = [self getCredentialForProvider:provider token:authToken secret:authSecret];
@ -791,14 +792,14 @@ RCT_EXPORT_METHOD(link:
@return
*/
RCT_EXPORT_METHOD(unlink:
(NSString *) appName
(NSString *) appDisplayName
providerId:
(NSString *) providerId
resolver:
(RCTPromiseResolveBlock) resolve
rejecter:
(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName];
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
FIRUser *user = [FIRAuth authWithApp:firApp].currentUser;
if (user) {
@ -825,7 +826,7 @@ RCT_EXPORT_METHOD(unlink:
@return
*/
RCT_EXPORT_METHOD(reauthenticate:
(NSString *) appName
(NSString *) appDisplayName
provider:
(NSString *) provider
authToken:
@ -836,7 +837,7 @@ RCT_EXPORT_METHOD(reauthenticate:
(RCTPromiseResolveBlock) resolve
rejecter:
(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName];
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
FIRAuthCredential *credential = [self getCredentialForProvider:provider token:authToken secret:authSecret];
@ -869,14 +870,14 @@ RCT_EXPORT_METHOD(reauthenticate:
@return
*/
RCT_EXPORT_METHOD(fetchProvidersForEmail:
(NSString *) appName
(NSString *) appDisplayName
email:
(NSString *) email
resolver:
(RCTPromiseResolveBlock) resolve
rejecter:
(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [FIRApp appNamed:appName];
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
[[FIRAuth authWithApp:firApp] fetchProvidersForEmail:email completion:^(NSArray<NSString *> *_Nullable providers, NSError *_Nullable error) {
if (error) {
@ -962,14 +963,14 @@ RCT_EXPORT_METHOD(fetchProvidersForEmail:
/**
Reject a promise with an auth exception
@param error NSError
*/
- (NSDictionary *)getJSError:(NSError *)error {
NSString *code = @"auth/unknown";
NSString *message = [error localizedDescription];
NSString *nativeErrorMessage = [error localizedDescription];
switch (error.code) {
case FIRAuthErrorCodeInvalidCustomToken:
code = @"auth/invalid-custom-token";
@ -1043,7 +1044,7 @@ RCT_EXPORT_METHOD(fetchProvidersForEmail:
code = @"auth/internal-error";
message = @"An internal error has occurred, please try again.";
break;
// unsure of the below codes so leaving them as the default error message
case FIRAuthErrorCodeTooManyRequests:
code = @"auth/too-many-requests";
@ -1078,7 +1079,7 @@ RCT_EXPORT_METHOD(fetchProvidersForEmail:
default:
break;
}
return @{
@"code": code,
@"message": message,

View File

@ -16,7 +16,7 @@
+ (void)handlePromise:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject databaseError:(NSError *)databaseError;
+ (FIRDatabase *)getDatabaseForApp:(NSString *)appName;
+ (FIRDatabase *)getDatabaseForApp:(NSString *)appDisplayName;
+ (NSDictionary *)getJSError:(NSError *)nativeError;

View File

@ -22,38 +22,38 @@ RCT_EXPORT_MODULE();
return self;
}
RCT_EXPORT_METHOD(goOnline:(NSString *) appName) {
[[RNFirebaseDatabase getDatabaseForApp:appName] goOnline];
RCT_EXPORT_METHOD(goOnline:(NSString *) appDisplayName) {
[[RNFirebaseDatabase getDatabaseForApp:appDisplayName] goOnline];
}
RCT_EXPORT_METHOD(goOffline:(NSString *) appName) {
[[RNFirebaseDatabase getDatabaseForApp:appName] goOffline];
RCT_EXPORT_METHOD(goOffline:(NSString *) appDisplayName) {
[[RNFirebaseDatabase getDatabaseForApp:appDisplayName] goOffline];
}
RCT_EXPORT_METHOD(setPersistence:(NSString *) appName
RCT_EXPORT_METHOD(setPersistence:(NSString *) appDisplayName
state:(BOOL) state) {
[RNFirebaseDatabase getDatabaseForApp:appName].persistenceEnabled = state;
[RNFirebaseDatabase getDatabaseForApp:appDisplayName].persistenceEnabled = state;
}
RCT_EXPORT_METHOD(setPersistenceCacheSizeBytes:(NSString *) appName
RCT_EXPORT_METHOD(setPersistenceCacheSizeBytes:(NSString *) appDisplayName
size:(NSInteger *) size) {
[RNFirebaseDatabase getDatabaseForApp:appName].persistenceCacheSizeBytes = (NSUInteger)size;
[RNFirebaseDatabase getDatabaseForApp:appDisplayName].persistenceCacheSizeBytes = (NSUInteger)size;
}
RCT_EXPORT_METHOD(enableLogging:(BOOL) enabled) {
[FIRDatabase setLoggingEnabled:enabled];
}
RCT_EXPORT_METHOD(keepSynced:(NSString *) appName
RCT_EXPORT_METHOD(keepSynced:(NSString *) appDisplayName
key:(NSString *) key
path:(NSString *) path
modifiers:(NSArray *) modifiers
state:(BOOL) state) {
FIRDatabaseQuery *query = [self getInternalReferenceForApp:appName key:key path:path modifiers:modifiers].query;
FIRDatabaseQuery *query = [self getInternalReferenceForApp:appDisplayName key:key path:path modifiers:modifiers].query;
[query keepSynced:state];
}
RCT_EXPORT_METHOD(transactionTryCommit:(NSString *) appName
RCT_EXPORT_METHOD(transactionTryCommit:(NSString *) appDisplayName
transactionId:(nonnull NSNumber *) transactionId
updates:(NSDictionary *) updates) {
__block NSMutableDictionary *transactionState;
@ -82,7 +82,7 @@ RCT_EXPORT_METHOD(transactionTryCommit:(NSString *) appName
}
RCT_EXPORT_METHOD(transactionStart:(NSString *) appName
RCT_EXPORT_METHOD(transactionStart:(NSString *) appDisplayName
path:(NSString *) path
transactionId:(nonnull NSNumber *) transactionId
applyLocally:(BOOL) applyLocally) {
@ -90,13 +90,13 @@ RCT_EXPORT_METHOD(transactionStart:(NSString *) appName
NSMutableDictionary *transactionState = [NSMutableDictionary new];
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
transactionState[@"semaphore"] = sema;
FIRDatabaseReference *ref = [self getReferenceForAppPath:appName path:path];
FIRDatabaseReference *ref = [self getReferenceForAppPath:appDisplayName path:path];
[ref runTransactionBlock:^FIRTransactionResult * _Nonnull(FIRMutableData *
_Nonnull currentData) {
dispatch_barrier_async(_transactionQueue, ^{
[_transactions setValue:transactionState forKey:transactionId];
NSDictionary *updateMap = [self createTransactionUpdateMap:appName transactionId:transactionId updatesData:currentData];
NSDictionary *updateMap = [self createTransactionUpdateMap:appDisplayName transactionId:transactionId updatesData:currentData];
[RNFirebaseUtil sendJSEvent:self name:DATABASE_TRANSACTION_EVENT body:updateMap];
});
@ -123,7 +123,7 @@ RCT_EXPORT_METHOD(transactionStart:(NSString *) appName
}
andCompletionBlock:
^(NSError *_Nullable databaseError, BOOL committed, FIRDataSnapshot *_Nullable snapshot) {
NSDictionary *resultMap = [self createTransactionResultMap:appName transactionId:transactionId error:databaseError committed:committed snapshot:snapshot];
NSDictionary *resultMap = [self createTransactionResultMap:appDisplayName transactionId:transactionId error:databaseError committed:committed snapshot:snapshot];
[RNFirebaseUtil sendJSEvent:self name:DATABASE_TRANSACTION_EVENT body:resultMap];
}
withLocalEvents:
@ -131,117 +131,117 @@ RCT_EXPORT_METHOD(transactionStart:(NSString *) appName
});
}
RCT_EXPORT_METHOD(onDisconnectSet:(NSString *) appName
RCT_EXPORT_METHOD(onDisconnectSet:(NSString *) appDisplayName
path:(NSString *) path
props:(NSDictionary *) props
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
FIRDatabaseReference *ref = [self getReferenceForAppPath:appName path:path];
FIRDatabaseReference *ref = [self getReferenceForAppPath:appDisplayName path:path];
[ref onDisconnectSetValue:props[@"value"] withCompletionBlock:^(NSError *_Nullable error, FIRDatabaseReference *_Nonnull _ref) {
[RNFirebaseDatabase handlePromise:resolve rejecter:reject databaseError:error];
}];
}
RCT_EXPORT_METHOD(onDisconnectUpdate:(NSString *) appName
RCT_EXPORT_METHOD(onDisconnectUpdate:(NSString *) appDisplayName
path:(NSString *) path
props:(NSDictionary *) props
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
FIRDatabaseReference *ref = [self getReferenceForAppPath:appName path:path];
FIRDatabaseReference *ref = [self getReferenceForAppPath:appDisplayName path:path];
[ref onDisconnectUpdateChildValues:props withCompletionBlock:^(NSError *_Nullable error, FIRDatabaseReference *_Nonnull _ref) {
[RNFirebaseDatabase handlePromise:resolve rejecter:reject databaseError:error];
}];
}
RCT_EXPORT_METHOD(onDisconnectRemove:(NSString *) appName
RCT_EXPORT_METHOD(onDisconnectRemove:(NSString *) appDisplayName
path:(NSString *) path
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
FIRDatabaseReference *ref = [self getReferenceForAppPath:appName path:path];
FIRDatabaseReference *ref = [self getReferenceForAppPath:appDisplayName path:path];
[ref onDisconnectRemoveValueWithCompletionBlock:^(NSError *_Nullable error, FIRDatabaseReference *_Nonnull _ref) {
[RNFirebaseDatabase handlePromise:resolve rejecter:reject databaseError:error];
}];
}
RCT_EXPORT_METHOD(onDisconnectCancel:(NSString *) appName
RCT_EXPORT_METHOD(onDisconnectCancel:(NSString *) appDisplayName
path:(NSString *) path
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
FIRDatabaseReference *ref = [self getReferenceForAppPath:appName path:path];
FIRDatabaseReference *ref = [self getReferenceForAppPath:appDisplayName path:path];
[ref cancelDisconnectOperationsWithCompletionBlock:^(NSError *_Nullable error, FIRDatabaseReference *_Nonnull _ref) {
[RNFirebaseDatabase handlePromise:resolve rejecter:reject databaseError:error];
}];
}
RCT_EXPORT_METHOD(set:(NSString *) appName
RCT_EXPORT_METHOD(set:(NSString *) appDisplayName
path:(NSString *) path
props:(NSDictionary *) props
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
FIRDatabaseReference *ref = [self getReferenceForAppPath:appName path:path];
FIRDatabaseReference *ref = [self getReferenceForAppPath:appDisplayName path:path];
[ref setValue:[props valueForKey:@"value"] withCompletionBlock:^(NSError *_Nullable error, FIRDatabaseReference *_Nonnull _ref) {
[RNFirebaseDatabase handlePromise:resolve rejecter:reject databaseError:error];
}];
}
RCT_EXPORT_METHOD(setPriority:(NSString *) appName
RCT_EXPORT_METHOD(setPriority:(NSString *) appDisplayName
path:(NSString *) path
priority:(NSDictionary *) priority
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
FIRDatabaseReference *ref = [self getReferenceForAppPath:appName path:path];
FIRDatabaseReference *ref = [self getReferenceForAppPath:appDisplayName path:path];
[ref setPriority:[priority valueForKey:@"value"] withCompletionBlock:^(NSError *_Nullable error, FIRDatabaseReference *_Nonnull ref) {
[RNFirebaseDatabase handlePromise:resolve rejecter:reject databaseError:error];
}];
}
RCT_EXPORT_METHOD(setWithPriority:(NSString *) appName
RCT_EXPORT_METHOD(setWithPriority:(NSString *) appDisplayName
path:(NSString *) path
data:(NSDictionary *) data
priority:(NSDictionary *) priority
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
FIRDatabaseReference *ref = [self getReferenceForAppPath:appName path:path];
FIRDatabaseReference *ref = [self getReferenceForAppPath:appDisplayName path:path];
[ref setValue:[data valueForKey:@"value"] andPriority:[priority valueForKey:@"value"] withCompletionBlock:^(NSError *_Nullable error, FIRDatabaseReference *_Nonnull ref) {
[RNFirebaseDatabase handlePromise:resolve rejecter:reject databaseError:error];
}];
}
RCT_EXPORT_METHOD(update:(NSString *) appName
RCT_EXPORT_METHOD(update:(NSString *) appDisplayName
path:(NSString *) path
props:(NSDictionary *) props
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
FIRDatabaseReference *ref = [self getReferenceForAppPath:appName path:path];
FIRDatabaseReference *ref = [self getReferenceForAppPath:appDisplayName path:path];
[ref updateChildValues:props withCompletionBlock:^(NSError *_Nullable error, FIRDatabaseReference *_Nonnull _ref) {
[RNFirebaseDatabase handlePromise:resolve rejecter:reject databaseError:error];
}];
}
RCT_EXPORT_METHOD(remove:(NSString *) appName
RCT_EXPORT_METHOD(remove:(NSString *) appDisplayName
path:(NSString *) path
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
FIRDatabaseReference *ref = [self getReferenceForAppPath:appName path:path];
FIRDatabaseReference *ref = [self getReferenceForAppPath:appDisplayName path:path];
[ref removeValueWithCompletionBlock:^(NSError *_Nullable error, FIRDatabaseReference *_Nonnull _ref) {
[RNFirebaseDatabase handlePromise:resolve rejecter:reject databaseError:error];
}];
}
RCT_EXPORT_METHOD(once:(NSString *) appName
RCT_EXPORT_METHOD(once:(NSString *) appDisplayName
key:(NSString *) key
path:(NSString *) path
modifiers:(NSArray *) modifiers
eventName:(NSString *) eventName
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
RNFirebaseDatabaseReference *ref = [self getInternalReferenceForApp:appName key:key path:path modifiers:modifiers];
RNFirebaseDatabaseReference *ref = [self getInternalReferenceForApp:appDisplayName key:key path:path modifiers:modifiers];
[ref once:eventName resolver:resolve rejecter:reject];
}
RCT_EXPORT_METHOD(on:(NSString *) appName
RCT_EXPORT_METHOD(on:(NSString *) appDisplayName
props:(NSDictionary *) props) {
RNFirebaseDatabaseReference *ref = [self getCachedInternalReferenceForApp:appName props:props];
RNFirebaseDatabaseReference *ref = [self getCachedInternalReferenceForApp:appDisplayName props:props];
[ref on:props[@"eventType"] registration:props[@"registration"]];
}
@ -271,20 +271,20 @@ RCT_EXPORT_METHOD(off:(NSString *) key
}
}
+ (FIRDatabase *)getDatabaseForApp:(NSString *)appName {
FIRApp *app = [FIRApp appNamed:appName];
+ (FIRDatabase *)getDatabaseForApp:(NSString *)appDisplayName {
FIRApp *app = [RNFirebaseUtil getApp:appDisplayName];
return [FIRDatabase databaseForApp:app];
}
- (FIRDatabaseReference *)getReferenceForAppPath:(NSString *)appName path:(NSString *)path {
return [[RNFirebaseDatabase getDatabaseForApp:appName] referenceWithPath:path];
- (FIRDatabaseReference *)getReferenceForAppPath:(NSString *)appDisplayName path:(NSString *)path {
return [[RNFirebaseDatabase getDatabaseForApp:appDisplayName] referenceWithPath:path];
}
- (RNFirebaseDatabaseReference *)getInternalReferenceForApp:(NSString *)appName key:(NSString *)key path:(NSString *)path modifiers:(NSArray *)modifiers {
return [[RNFirebaseDatabaseReference alloc] initWithPathAndModifiers:self app:appName key:key refPath:path modifiers:modifiers];
- (RNFirebaseDatabaseReference *)getInternalReferenceForApp:(NSString *)appDisplayName key:(NSString *)key path:(NSString *)path modifiers:(NSArray *)modifiers {
return [[RNFirebaseDatabaseReference alloc] initWithPathAndModifiers:self appDisplayName:appDisplayName key:key refPath:path modifiers:modifiers];
}
- (RNFirebaseDatabaseReference *)getCachedInternalReferenceForApp:(NSString *)appName props:(NSDictionary *)props {
- (RNFirebaseDatabaseReference *)getCachedInternalReferenceForApp:(NSString *)appDisplayName props:(NSDictionary *)props {
NSString *key = props[@"key"];
NSString *path = props[@"path"];
NSDictionary *modifiers = props[@"modifiers"];
@ -292,7 +292,7 @@ RCT_EXPORT_METHOD(off:(NSString *) key
RNFirebaseDatabaseReference *ref = _dbReferences[key];
if (ref == nil) {
ref = [[RNFirebaseDatabaseReference alloc] initWithPathAndModifiers:self app:appName key:key refPath:path modifiers:modifiers];
ref = [[RNFirebaseDatabaseReference alloc] initWithPathAndModifiers:self appDisplayName:appDisplayName key:key refPath:path modifiers:modifiers];
_dbReferences[key] = ref;
}
return ref;
@ -380,20 +380,20 @@ RCT_EXPORT_METHOD(off:(NSString *) key
return errorMap;
}
- (NSDictionary *)createTransactionUpdateMap:(NSString *)appName transactionId:(NSNumber *)transactionId updatesData:(FIRMutableData *)updatesData {
- (NSDictionary *)createTransactionUpdateMap:(NSString *)appDisplayName transactionId:(NSNumber *)transactionId updatesData:(FIRMutableData *)updatesData {
NSMutableDictionary *updatesMap = [[NSMutableDictionary alloc] init];
[updatesMap setValue:transactionId forKey:@"id"];
[updatesMap setValue:@"update" forKey:@"type"];
[updatesMap setValue:appName forKey:@"appName"];
[updatesMap setValue:appDisplayName forKey:@"appName"];
[updatesMap setValue:updatesData.value forKey:@"value"];
return updatesMap;
}
- (NSDictionary *)createTransactionResultMap:(NSString *)appName transactionId:(NSNumber *)transactionId error:(NSError *)error committed:(BOOL)committed snapshot:(FIRDataSnapshot *)snapshot {
- (NSDictionary *)createTransactionResultMap:(NSString *)appDisplayName transactionId:(NSNumber *)transactionId error:(NSError *)error committed:(BOOL)committed snapshot:(FIRDataSnapshot *)snapshot {
NSMutableDictionary *resultMap = [[NSMutableDictionary alloc] init];
[resultMap setValue:transactionId forKey:@"id"];
[resultMap setValue:appName forKey:@"appName"];
[resultMap setValue:appDisplayName forKey:@"appName"];
// TODO: no timeout on iOS
[resultMap setValue:@(committed) forKey:@"committed"];
// TODO: no interrupted on iOS

View File

@ -12,12 +12,12 @@
@interface RNFirebaseDatabaseReference : NSObject
@property RCTEventEmitter *emitter;
@property FIRDatabaseQuery *query;
@property NSString *app;
@property NSString *appDisplayName;
@property NSString *key;
@property NSString *path;
@property NSMutableDictionary *listeners;
- (id)initWithPathAndModifiers:(RCTEventEmitter *)emitter app:(NSString *)app key:(NSString *)key refPath:(NSString *)refPath modifiers:(NSArray *)modifiers;
- (id)initWithPathAndModifiers:(RCTEventEmitter *)emitter appDisplayName:(NSString *)appDisplayName key:(NSString *)key refPath:(NSString *)refPath modifiers:(NSArray *)modifiers;
- (void)on:(NSString *) eventName registration:(NSDictionary *) registration;
- (void)once:(NSString *) eventType resolver:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject;
- (void)removeEventListener:(NSString *)eventRegistrationKey;

View File

@ -5,14 +5,14 @@
#if __has_include(<FirebaseDatabase/FIRDatabase.h>)
- (id)initWithPathAndModifiers:(RCTEventEmitter *)emitter
app:(NSString *) app
appDisplayName:(NSString *) appDisplayName
key:(NSString *) key
refPath:(NSString *) refPath
modifiers:(NSArray *) modifiers {
self = [super init];
if (self) {
_emitter = emitter;
_app = app;
_appDisplayName = appDisplayName;
_key = key;
_path = refPath;
_listeners = [[NSMutableDictionary alloc] init];
@ -123,7 +123,7 @@
- (FIRDatabaseQuery *)buildQueryAtPathWithModifiers:(NSString *) path
modifiers:(NSArray *)modifiers {
FIRDatabase *firebaseDatabase = [RNFirebaseDatabase getDatabaseForApp:_app];
FIRDatabase *firebaseDatabase = [RNFirebaseDatabase getDatabaseForApp:_appDisplayName];
FIRDatabaseQuery *query = [[firebaseDatabase reference] child:path];
for (NSDictionary *modifier in modifiers) {

View File

@ -13,7 +13,7 @@
+ (void)promiseRejectException:(RCTPromiseRejectBlock)reject error:(NSError *)error;
+ (FIRFirestore *)getFirestoreForApp:(NSString *)appName;
+ (FIRFirestore *)getFirestoreForApp:(NSString *)appDisplayName;
+ (NSDictionary *)getJSError:(NSError *)nativeError;
@end

View File

@ -22,17 +22,17 @@ RCT_EXPORT_METHOD(enableLogging:(BOOL) enabled) {
[FIRFirestore enableLogging:enabled];
}
RCT_EXPORT_METHOD(collectionGet:(NSString *) appName
RCT_EXPORT_METHOD(collectionGet:(NSString *) appDisplayName
path:(NSString *) path
filters:(NSArray *) filters
orders:(NSArray *) orders
options:(NSDictionary *) options
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
[[self getCollectionForAppPath:appName path:path filters:filters orders:orders options:options] get:resolve rejecter:reject];
[[self getCollectionForAppPath:appDisplayName path:path filters:filters orders:orders options:options] get:resolve rejecter:reject];
}
RCT_EXPORT_METHOD(collectionOffSnapshot:(NSString *) appName
RCT_EXPORT_METHOD(collectionOffSnapshot:(NSString *) appDisplayName
path:(NSString *) path
filters:(NSArray *) filters
orders:(NSArray *) orders
@ -41,22 +41,22 @@ RCT_EXPORT_METHOD(collectionOffSnapshot:(NSString *) appName
[RNFirebaseFirestoreCollectionReference offSnapshot:listenerId];
}
RCT_EXPORT_METHOD(collectionOnSnapshot:(NSString *) appName
RCT_EXPORT_METHOD(collectionOnSnapshot:(NSString *) appDisplayName
path:(NSString *) path
filters:(NSArray *) filters
orders:(NSArray *) orders
options:(NSDictionary *) options
listenerId:(nonnull NSString *) listenerId
queryListenOptions:(NSDictionary *) queryListenOptions) {
RNFirebaseFirestoreCollectionReference *ref = [self getCollectionForAppPath:appName path:path filters:filters orders:orders options:options];
RNFirebaseFirestoreCollectionReference *ref = [self getCollectionForAppPath:appDisplayName path:path filters:filters orders:orders options:options];
[ref onSnapshot:listenerId queryListenOptions:queryListenOptions];
}
RCT_EXPORT_METHOD(documentBatch:(NSString *) appName
RCT_EXPORT_METHOD(documentBatch:(NSString *) appDisplayName
writes:(NSArray *) writes
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
FIRFirestore *firestore = [RNFirebaseFirestore getFirestoreForApp:appName];
FIRFirestore *firestore = [RNFirebaseFirestore getFirestoreForApp:appDisplayName];
FIRWriteBatch *batch = [firestore batch];
for (NSDictionary *write in writes) {
@ -89,56 +89,56 @@ RCT_EXPORT_METHOD(documentBatch:(NSString *) appName
}];
}
RCT_EXPORT_METHOD(documentDelete:(NSString *) appName
RCT_EXPORT_METHOD(documentDelete:(NSString *) appDisplayName
path:(NSString *) path
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
[[self getDocumentForAppPath:appName path:path] delete:resolve rejecter:reject];
[[self getDocumentForAppPath:appDisplayName path:path] delete:resolve rejecter:reject];
}
RCT_EXPORT_METHOD(documentGet:(NSString *) appName
RCT_EXPORT_METHOD(documentGet:(NSString *) appDisplayName
path:(NSString *) path
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
[[self getDocumentForAppPath:appName path:path] get:resolve rejecter:reject];
[[self getDocumentForAppPath:appDisplayName path:path] get:resolve rejecter:reject];
}
RCT_EXPORT_METHOD(documentGetAll:(NSString *) appName
RCT_EXPORT_METHOD(documentGetAll:(NSString *) appDisplayName
documents:(NSString *) documents
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
// Not supported on iOS out of the box
}
RCT_EXPORT_METHOD(documentOffSnapshot:(NSString *) appName
RCT_EXPORT_METHOD(documentOffSnapshot:(NSString *) appDisplayName
path:(NSString *) path
listenerId:(nonnull NSString *) listenerId) {
[RNFirebaseFirestoreDocumentReference offSnapshot:listenerId];
}
RCT_EXPORT_METHOD(documentOnSnapshot:(NSString *) appName
RCT_EXPORT_METHOD(documentOnSnapshot:(NSString *) appDisplayName
path:(NSString *) path
listenerId:(nonnull NSString *) listenerId
docListenOptions:(NSDictionary *) docListenOptions) {
RNFirebaseFirestoreDocumentReference *ref = [self getDocumentForAppPath:appName path:path];
RNFirebaseFirestoreDocumentReference *ref = [self getDocumentForAppPath:appDisplayName path:path];
[ref onSnapshot:listenerId docListenOptions:docListenOptions];
}
RCT_EXPORT_METHOD(documentSet:(NSString *) appName
RCT_EXPORT_METHOD(documentSet:(NSString *) appDisplayName
path:(NSString *) path
data:(NSDictionary *) data
options:(NSDictionary *) options
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
[[self getDocumentForAppPath:appName path:path] set:data options:options resolver:resolve rejecter:reject];
[[self getDocumentForAppPath:appDisplayName path:path] set:data options:options resolver:resolve rejecter:reject];
}
RCT_EXPORT_METHOD(documentUpdate:(NSString *) appName
RCT_EXPORT_METHOD(documentUpdate:(NSString *) appDisplayName
path:(NSString *) path
data:(NSDictionary *) data
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
[[self getDocumentForAppPath:appName path:path] update:data resolver:resolve rejecter:reject];
[[self getDocumentForAppPath:appDisplayName path:path] update:data resolver:resolve rejecter:reject];
}
/*
@ -149,17 +149,17 @@ RCT_EXPORT_METHOD(documentUpdate:(NSString *) appName
reject([jsError valueForKey:@"code"], [jsError valueForKey:@"message"], error);
}
+ (FIRFirestore *)getFirestoreForApp:(NSString *)appName {
FIRApp *app = [FIRApp appNamed:appName];
+ (FIRFirestore *)getFirestoreForApp:(NSString *)appDisplayName {
FIRApp *app = [RNFirebaseUtil getApp:appDisplayName];
return [FIRFirestore firestoreForApp:app];
}
- (RNFirebaseFirestoreCollectionReference *)getCollectionForAppPath:(NSString *)appName path:(NSString *)path filters:(NSArray *)filters orders:(NSArray *)orders options:(NSDictionary *)options {
return [[RNFirebaseFirestoreCollectionReference alloc] initWithPathAndModifiers:self app:appName path:path filters:filters orders:orders options:options];
- (RNFirebaseFirestoreCollectionReference *)getCollectionForAppPath:(NSString *)appDisplayName path:(NSString *)path filters:(NSArray *)filters orders:(NSArray *)orders options:(NSDictionary *)options {
return [[RNFirebaseFirestoreCollectionReference alloc] initWithPathAndModifiers:self appDisplayName:appDisplayName path:path filters:filters orders:orders options:options];
}
- (RNFirebaseFirestoreDocumentReference *)getDocumentForAppPath:(NSString *)appName path:(NSString *)path {
return [[RNFirebaseFirestoreDocumentReference alloc] initWithPath:self app:appName path:path];
- (RNFirebaseFirestoreDocumentReference *)getDocumentForAppPath:(NSString *)appDisplayName path:(NSString *)path {
return [[RNFirebaseFirestoreDocumentReference alloc] initWithPath:self appDisplayName:appDisplayName path:path];
}
// TODO: Move to error util for use in other modules

View File

@ -13,14 +13,14 @@
@interface RNFirebaseFirestoreCollectionReference : NSObject
@property RCTEventEmitter *emitter;
@property NSString *app;
@property NSString *appDisplayName;
@property NSString *path;
@property NSArray *filters;
@property NSArray *orders;
@property NSDictionary *options;
@property FIRQuery *query;
- (id)initWithPathAndModifiers:(RCTEventEmitter *)emitter app:(NSString *)app path:(NSString *)path filters:(NSArray *)filters orders:(NSArray *)orders options:(NSDictionary *)options;
- (id)initWithPathAndModifiers:(RCTEventEmitter *)emitter appDisplayName:(NSString *)appDisplayName path:(NSString *)path filters:(NSArray *)filters orders:(NSArray *)orders options:(NSDictionary *)options;
- (void)get:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject;
+ (void)offSnapshot:(NSString *)listenerId;
- (void)onSnapshot:(NSString *)listenerId queryListenOptions:(NSDictionary *) queryListenOptions;

View File

@ -7,7 +7,7 @@
static NSMutableDictionary *_listeners;
- (id)initWithPathAndModifiers:(RCTEventEmitter *) emitter
app:(NSString *) app
appDisplayName:(NSString *) appDisplayName
path:(NSString *) path
filters:(NSArray *) filters
orders:(NSArray *) orders
@ -15,7 +15,7 @@ static NSMutableDictionary *_listeners;
self = [super init];
if (self) {
_emitter = emitter;
_app = app;
_appDisplayName = appDisplayName;
_path = path;
_filters = filters;
_orders = orders;
@ -64,7 +64,7 @@ queryListenOptions:(NSDictionary *) queryListenOptions {
[self handleQuerySnapshotEvent:listenerId querySnapshot:snapshot];
}
};
FIRQueryListenOptions *options = [[FIRQueryListenOptions alloc] init];
if (queryListenOptions) {
if (queryListenOptions[@"includeDocumentMetadataChanges"]) {
@ -74,14 +74,14 @@ queryListenOptions:(NSDictionary *) queryListenOptions {
[options includeQueryMetadataChanges:TRUE];
}
}
id<FIRListenerRegistration> listener = [_query addSnapshotListenerWithOptions:options listener:listenerBlock];
_listeners[listenerId] = listener;
}
}
- (FIRQuery *)buildQuery {
FIRFirestore *firestore = [RNFirebaseFirestore getFirestoreForApp:_app];
FIRFirestore *firestore = [RNFirebaseFirestore getFirestoreForApp:_appDisplayName];
FIRQuery *query = (FIRQuery*)[firestore collectionWithPath:_path];
query = [self applyFilters:firestore query:query];
query = [self applyOrders:query];
@ -152,7 +152,7 @@ queryListenOptions:(NSDictionary *) queryListenOptions {
- (void)handleQuerySnapshotError:(NSString *)listenerId
error:(NSError *)error {
NSMutableDictionary *event = [[NSMutableDictionary alloc] init];
[event setValue:_app forKey:@"appName"];
[event setValue:_appDisplayName forKey:@"appName"];
[event setValue:_path forKey:@"path"];
[event setValue:listenerId forKey:@"listenerId"];
[event setValue:[RNFirebaseFirestore getJSError:error] forKey:@"error"];
@ -163,7 +163,7 @@ queryListenOptions:(NSDictionary *) queryListenOptions {
- (void)handleQuerySnapshotEvent:(NSString *)listenerId
querySnapshot:(FIRQuerySnapshot *)querySnapshot {
NSMutableDictionary *event = [[NSMutableDictionary alloc] init];
[event setValue:_app forKey:@"appName"];
[event setValue:_appDisplayName forKey:@"appName"];
[event setValue:_path forKey:@"path"];
[event setValue:listenerId forKey:@"listenerId"];
[event setValue:[RNFirebaseFirestoreCollectionReference snapshotToDictionary:querySnapshot] forKey:@"querySnapshot"];

View File

@ -13,11 +13,11 @@
@interface RNFirebaseFirestoreDocumentReference : NSObject
@property RCTEventEmitter *emitter;
@property NSString *app;
@property NSString *appDisplayName;
@property NSString *path;
@property FIRDocumentReference *ref;
- (id)initWithPath:(RCTEventEmitter *)emitter app:(NSString *)app path:(NSString *)path;
- (id)initWithPath:(RCTEventEmitter *)emitter appDisplayName:(NSString *)appDisplayName path:(NSString *)path;
- (void)delete:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject;
- (void)get:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject;
+ (void)offSnapshot:(NSString *)listenerId;

View File

@ -7,14 +7,14 @@
static NSMutableDictionary *_listeners;
- (id)initWithPath:(RCTEventEmitter *)emitter
app:(NSString *) app
appDisplayName:(NSString *) appDisplayName
path:(NSString *) path {
self = [super init];
if (self) {
_emitter = emitter;
_app = app;
_appDisplayName = appDisplayName;
_path = path;
_ref = [[RNFirebaseFirestore getFirestoreForApp:_app] documentWithPath:_path];
_ref = [[RNFirebaseFirestore getFirestoreForApp:_appDisplayName] documentWithPath:_path];
}
// Initialise the static listeners object if required
if (!_listeners) {
@ -78,7 +78,7 @@ static NSMutableDictionary *_listeners;
options:(NSDictionary *) options
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject {
NSDictionary *dictionary = [RNFirebaseFirestoreDocumentReference parseJSMap:[RNFirebaseFirestore getFirestoreForApp:_app] jsMap:data];
NSDictionary *dictionary = [RNFirebaseFirestoreDocumentReference parseJSMap:[RNFirebaseFirestore getFirestoreForApp:_appDisplayName] jsMap:data];
if (options && options[@"merge"]) {
[_ref setData:dictionary options:[FIRSetOptions merge] completion:^(NSError * _Nullable error) {
[RNFirebaseFirestoreDocumentReference handleWriteResponse:error resolver:resolve rejecter:reject];
@ -93,7 +93,7 @@ static NSMutableDictionary *_listeners;
- (void)update:(NSDictionary *) data
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject {
NSDictionary *dictionary = [RNFirebaseFirestoreDocumentReference parseJSMap:[RNFirebaseFirestore getFirestoreForApp:_app] jsMap:data];
NSDictionary *dictionary = [RNFirebaseFirestoreDocumentReference parseJSMap:[RNFirebaseFirestore getFirestoreForApp:_appDisplayName] jsMap:data];
[_ref updateData:dictionary completion:^(NSError * _Nullable error) {
[RNFirebaseFirestoreDocumentReference handleWriteResponse:error resolver:resolve rejecter:reject];
}];
@ -131,7 +131,7 @@ static NSMutableDictionary *_listeners;
- (void)handleDocumentSnapshotError:(NSString *)listenerId
error:(NSError *)error {
NSMutableDictionary *event = [[NSMutableDictionary alloc] init];
[event setValue:_app forKey:@"appName"];
[event setValue:_appDisplayName forKey:@"appName"];
[event setValue:_path forKey:@"path"];
[event setValue:listenerId forKey:@"listenerId"];
[event setValue:[RNFirebaseFirestore getJSError:error] forKey:@"error"];
@ -142,7 +142,7 @@ static NSMutableDictionary *_listeners;
- (void)handleDocumentSnapshotEvent:(NSString *)listenerId
documentSnapshot:(FIRDocumentSnapshot *)documentSnapshot {
NSMutableDictionary *event = [[NSMutableDictionary alloc] init];
[event setValue:_app forKey:@"appName"];
[event setValue:_appDisplayName forKey:@"appName"];
[event setValue:_path forKey:@"path"];
[event setValue:listenerId forKey:@"listenerId"];
[event setValue:[RNFirebaseFirestoreDocumentReference snapshotToDictionary:documentSnapshot] forKey:@"documentSnapshot"];

View File

@ -23,11 +23,11 @@ RCT_EXPORT_MODULE(RNFirebaseStorage);
@url https://firebase.google.com/docs/reference/js/firebase.storage.Reference#delete
@param NSString path
*/
RCT_EXPORT_METHOD(delete:(NSString *) appName
RCT_EXPORT_METHOD(delete:(NSString *) appDisplayName
path:(NSString *) path
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
FIRStorageReference *fileRef = [self getReference:path appName:appName];
FIRStorageReference *fileRef = [self getReference:path appDisplayName:appDisplayName];
[fileRef deleteWithCompletion:^(NSError *_Nullable error) {
if (error != nil) {
@ -44,11 +44,11 @@ RCT_EXPORT_METHOD(delete:(NSString *) appName
@url https://firebase.google.com/docs/reference/js/firebase.storage.Reference#getDownloadURL
@param NSString path
*/
RCT_EXPORT_METHOD(getDownloadURL:(NSString *) appName
RCT_EXPORT_METHOD(getDownloadURL:(NSString *) appDisplayName
path:(NSString *) path
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
FIRStorageReference *fileRef = [self getReference:path appName:appName];
FIRStorageReference *fileRef = [self getReference:path appDisplayName:appDisplayName];
[fileRef downloadURLWithCompletion:^(NSURL *_Nullable URL, NSError *_Nullable error) {
if (error != nil) {
@ -65,11 +65,11 @@ RCT_EXPORT_METHOD(getDownloadURL:(NSString *) appName
@url https://firebase.google.com/docs/reference/js/firebase.storage.Reference#getMetadata
@param NSString path
*/
RCT_EXPORT_METHOD(getMetadata:(NSString *) appName
RCT_EXPORT_METHOD(getMetadata:(NSString *) appDisplayName
path:(NSString *) path
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
FIRStorageReference *fileRef = [self getReference:path appName:appName];
FIRStorageReference *fileRef = [self getReference:path appDisplayName:appDisplayName];
[fileRef metadataWithCompletion:^(FIRStorageMetadata *_Nullable metadata, NSError *_Nullable error) {
if (error != nil) {
@ -87,12 +87,12 @@ RCT_EXPORT_METHOD(getMetadata:(NSString *) appName
@param NSString path
@param NSDictionary metadata
*/
RCT_EXPORT_METHOD(updateMetadata:(NSString *) appName
RCT_EXPORT_METHOD(updateMetadata:(NSString *) appDisplayName
path:(NSString *) path
metadata:(NSDictionary *) metadata
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
FIRStorageReference *fileRef = [self getReference:path appName:appName];
FIRStorageReference *fileRef = [self getReference:path appDisplayName:appDisplayName];
FIRStorageMetadata *firmetadata = [self buildMetadataFromMap:metadata];
[fileRef updateMetadata:firmetadata completion:^(FIRStorageMetadata *_Nullable metadata, NSError *_Nullable error) {
@ -111,12 +111,12 @@ RCT_EXPORT_METHOD(updateMetadata:(NSString *) appName
@param NSString path
@param NSString localPath
*/
RCT_EXPORT_METHOD(downloadFile:(NSString *) appName
RCT_EXPORT_METHOD(downloadFile:(NSString *) appDisplayName
path:(NSString *) path
localPath:(NSString *) localPath
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
FIRStorageReference *fileRef = [self getReference:path appName:appName];
FIRStorageReference *fileRef = [self getReference:path appDisplayName:appDisplayName];
NSURL *localFile = [NSURL fileURLWithPath:localPath];
FIRStorageDownloadTask *downloadTask = [fileRef writeToFile:localFile];
@ -124,25 +124,25 @@ RCT_EXPORT_METHOD(downloadFile:(NSString *) appName
[downloadTask observeStatus:FIRStorageTaskStatusResume handler:^(FIRStorageTaskSnapshot *snapshot) {
// download resumed, also fires when the upload starts
NSDictionary *event = [self getDownloadTaskAsDictionary:snapshot];
[self sendJSEvent:appName type:STORAGE_EVENT path:path title:STORAGE_STATE_CHANGED props:event];
[self sendJSEvent:appDisplayName type:STORAGE_EVENT path:path title:STORAGE_STATE_CHANGED props:event];
}];
[downloadTask observeStatus:FIRStorageTaskStatusPause handler:^(FIRStorageTaskSnapshot *snapshot) {
// download paused
NSDictionary *event = [self getDownloadTaskAsDictionary:snapshot];
[self sendJSEvent:appName type:STORAGE_EVENT path:path title:STORAGE_STATE_CHANGED props:event];
[self sendJSEvent:appDisplayName type:STORAGE_EVENT path:path title:STORAGE_STATE_CHANGED props:event];
}];
[downloadTask observeStatus:FIRStorageTaskStatusProgress handler:^(FIRStorageTaskSnapshot *snapshot) {
// download reported progress
NSDictionary *event = [self getDownloadTaskAsDictionary:snapshot];
[self sendJSEvent:appName type:STORAGE_EVENT path:path title:STORAGE_STATE_CHANGED props:event];
[self sendJSEvent:appDisplayName type:STORAGE_EVENT path:path title:STORAGE_STATE_CHANGED props:event];
}];
[downloadTask observeStatus:FIRStorageTaskStatusSuccess handler:^(FIRStorageTaskSnapshot *snapshot) {
// download completed successfully
NSDictionary *resp = [self getDownloadTaskAsDictionary:snapshot];
[self sendJSEvent:appName type:STORAGE_EVENT path:path title:STORAGE_DOWNLOAD_SUCCESS props:resp];
[self sendJSEvent:appDisplayName type:STORAGE_EVENT path:path title:STORAGE_DOWNLOAD_SUCCESS props:resp];
resolve(resp);
}];
@ -161,9 +161,10 @@ RCT_EXPORT_METHOD(downloadFile:(NSString *) appName
@url https://firebase.google.com/docs/reference/js/firebase.storage.Storage#setMaxDownloadRetryTime
@param NSNumber milliseconds
*/
RCT_EXPORT_METHOD(setMaxDownloadRetryTime:(NSString *) appName
RCT_EXPORT_METHOD(setMaxDownloadRetryTime:(NSString *) appDisplayName
milliseconds:(NSNumber *) milliseconds) {
[[FIRStorage storageForApp:[FIRApp appNamed:appName]] setMaxDownloadRetryTime:[milliseconds doubleValue]];
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
[[FIRStorage storageForApp:firApp] setMaxDownloadRetryTime:[milliseconds doubleValue]];
}
/**
@ -172,9 +173,10 @@ RCT_EXPORT_METHOD(setMaxDownloadRetryTime:(NSString *) appName
@url https://firebase.google.com/docs/reference/js/firebase.storage.Storage#setMaxOperationRetryTime
@param NSNumber milliseconds
*/
RCT_EXPORT_METHOD(setMaxOperationRetryTime:(NSString *) appName
RCT_EXPORT_METHOD(setMaxOperationRetryTime:(NSString *) appDisplayName
milliseconds:(NSNumber *) milliseconds) {
[[FIRStorage storageForApp:[FIRApp appNamed:appName]] setMaxOperationRetryTime:[milliseconds doubleValue]];
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
[[FIRStorage storageForApp:firApp] setMaxOperationRetryTime:[milliseconds doubleValue]];
}
/**
@ -182,9 +184,10 @@ RCT_EXPORT_METHOD(setMaxOperationRetryTime:(NSString *) appName
@url https://firebase.google.com/docs/reference/js/firebase.storage.Storage#setMaxUploadRetryTime
*/
RCT_EXPORT_METHOD(setMaxUploadRetryTime:(NSString *) appName
RCT_EXPORT_METHOD(setMaxUploadRetryTime:(NSString *) appDisplayName
milliseconds:(NSNumber *) milliseconds) {
[[FIRStorage storageForApp:[FIRApp appNamed:appName]] setMaxUploadRetryTime:[milliseconds doubleValue]];
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
[[FIRStorage storageForApp:firApp] setMaxUploadRetryTime:[milliseconds doubleValue]];
}
/**
@ -195,7 +198,7 @@ RCT_EXPORT_METHOD(setMaxUploadRetryTime:(NSString *) appName
@param NSString localPath
@param NSDictionary metadata
*/
RCT_EXPORT_METHOD(putFile:(NSString *) appName
RCT_EXPORT_METHOD(putFile:(NSString *) appDisplayName
path:(NSString *) path
localPath:(NSString *) localPath
metadata:(NSDictionary *) metadata
@ -224,7 +227,7 @@ RCT_EXPORT_METHOD(putFile:(NSString *) appName
if (info[PHImageErrorKey] == nil) {
if (UTTypeConformsTo((__bridge CFStringRef)dataUTI, kUTTypeJPEG)) {
firmetadata.contentType = [self utiToMimeType:dataUTI];
[self uploadData:appName data:imageData firmetadata:firmetadata path:path resolver:resolve rejecter:reject];
[self uploadData:appDisplayName data:imageData firmetadata:firmetadata path:path resolver:resolve rejecter:reject];
} else {
// if the image UTI is not JPEG then convert to JPEG, e.g. HEI
CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)imageData, NULL);
@ -236,7 +239,7 @@ RCT_EXPORT_METHOD(putFile:(NSString *) appName
CGImageDestinationFinalize(destination);
// Manually set mimetype to JPEG
firmetadata.contentType = @"image/jpeg";
[self uploadData:appName data:[NSData dataWithData:imageDataJPEG] firmetadata:firmetadata path:path resolver:resolve rejecter:reject];
[self uploadData:appDisplayName data:[NSData dataWithData:imageDataJPEG] firmetadata:firmetadata path:path resolver:resolve rejecter:reject];
}
} else {
reject(@"storage/request-image-data-failed", @"Could not obtain image data for the specified file.", nil);
@ -260,7 +263,7 @@ RCT_EXPORT_METHOD(putFile:(NSString *) appName
[exportSession exportAsynchronouslyWithCompletionHandler:^{
if (exportSession.status == AVAssetExportSessionStatusCompleted) {
firmetadata.contentType = [self utiToMimeType:exportSession.outputFileType];
[self uploadFile:appName url:tempUrl firmetadata:firmetadata path:path resolver:resolve rejecter:reject];
[self uploadFile:appDisplayName url:tempUrl firmetadata:firmetadata path:path resolver:resolve rejecter:reject];
// we're not cleaning up the temporary file at the moment, just relying on the OS to do that in it's own time - todo?
} else {
reject(@"storage/temporary-file-failure", @"Unable to create temporary file for upload.", nil);
@ -274,7 +277,7 @@ RCT_EXPORT_METHOD(putFile:(NSString *) appName
} else {
// TODO: Content type for file?
NSData *data = [[NSFileManager defaultManager] contentsAtPath:localPath];
[self uploadData:appName data:data firmetadata:firmetadata path:path resolver:resolve rejecter:reject];
[self uploadData:appDisplayName data:data firmetadata:firmetadata path:path resolver:resolve rejecter:reject];
}
}
@ -288,42 +291,42 @@ RCT_EXPORT_METHOD(putFile:(NSString *) appName
return (__bridge_transfer NSString *)UTTypeCopyPreferredTagWithClass((__bridge CFStringRef)dataUTI, kUTTagClassMIMEType);
}
- (void)uploadFile:(NSString *)appName url:(NSURL *)url firmetadata:(FIRStorageMetadata *)firmetadata path:(NSString *)path resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject {
FIRStorageReference *fileRef = [self getReference:path appName:appName];
- (void)uploadFile:(NSString *)appDisplayName url:(NSURL *)url firmetadata:(FIRStorageMetadata *)firmetadata path:(NSString *)path resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject {
FIRStorageReference *fileRef = [self getReference:path appDisplayName:appDisplayName];
FIRStorageUploadTask *uploadTask = [fileRef putFile:url metadata:firmetadata];
[self addUploadObservers:appName uploadTask:uploadTask path:path resolver:resolve rejecter:reject];
[self addUploadObservers:appDisplayName uploadTask:uploadTask path:path resolver:resolve rejecter:reject];
}
- (void)uploadData:(NSString *)appName data:(NSData *)data firmetadata:(FIRStorageMetadata *)firmetadata path:(NSString *)path resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject {
FIRStorageReference *fileRef = [self getReference:path appName:appName];
- (void)uploadData:(NSString *)appDisplayName data:(NSData *)data firmetadata:(FIRStorageMetadata *)firmetadata path:(NSString *)path resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject {
FIRStorageReference *fileRef = [self getReference:path appDisplayName:appDisplayName];
FIRStorageUploadTask *uploadTask = [fileRef putData:data metadata:firmetadata];
[self addUploadObservers:appName uploadTask:uploadTask path:path resolver:resolve rejecter:reject];
[self addUploadObservers:appDisplayName uploadTask:uploadTask path:path resolver:resolve rejecter:reject];
}
- (void)addUploadObservers:(NSString *)appName uploadTask:(FIRStorageUploadTask *)uploadTask path:(NSString *)path resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject {
- (void)addUploadObservers:(NSString *)appDisplayName uploadTask:(FIRStorageUploadTask *)uploadTask path:(NSString *)path resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject {
// listen for state changes, errors, and completion of the upload.
[uploadTask observeStatus:FIRStorageTaskStatusResume handler:^(FIRStorageTaskSnapshot *snapshot) {
// upload resumed, also fires when the upload starts
NSDictionary *event = [self getUploadTaskAsDictionary:snapshot];
[self sendJSEvent:appName type:STORAGE_EVENT path:path title:STORAGE_STATE_CHANGED props:event];
[self sendJSEvent:appDisplayName type:STORAGE_EVENT path:path title:STORAGE_STATE_CHANGED props:event];
}];
[uploadTask observeStatus:FIRStorageTaskStatusPause handler:^(FIRStorageTaskSnapshot *snapshot) {
// upload paused
NSDictionary *event = [self getUploadTaskAsDictionary:snapshot];
[self sendJSEvent:appName type:STORAGE_EVENT path:path title:STORAGE_STATE_CHANGED props:event];
[self sendJSEvent:appDisplayName type:STORAGE_EVENT path:path title:STORAGE_STATE_CHANGED props:event];
}];
[uploadTask observeStatus:FIRStorageTaskStatusProgress handler:^(FIRStorageTaskSnapshot *snapshot) {
// upload reported progress
NSDictionary *event = [self getUploadTaskAsDictionary:snapshot];
[self sendJSEvent:appName type:STORAGE_EVENT path:path title:STORAGE_STATE_CHANGED props:event];
[self sendJSEvent:appDisplayName type:STORAGE_EVENT path:path title:STORAGE_STATE_CHANGED props:event];
}];
[uploadTask observeStatus:FIRStorageTaskStatusSuccess handler:^(FIRStorageTaskSnapshot *snapshot) {
// upload completed successfully
NSDictionary *resp = [self getUploadTaskAsDictionary:snapshot];
[self sendJSEvent:appName type:STORAGE_EVENT path:path title:STORAGE_STATE_CHANGED props:resp];
[self sendJSEvent:appName type:STORAGE_EVENT path:path title:STORAGE_UPLOAD_SUCCESS props:resp];
[self sendJSEvent:appDisplayName type:STORAGE_EVENT path:path title:STORAGE_STATE_CHANGED props:resp];
[self sendJSEvent:appDisplayName type:STORAGE_EVENT path:path title:STORAGE_UPLOAD_SUCCESS props:resp];
resolve(resp);
}];
@ -335,12 +338,13 @@ RCT_EXPORT_METHOD(putFile:(NSString *) appName
}
- (FIRStorageReference *)getReference:(NSString *)path
appName:(NSString *)appName {
appDisplayName:(NSString *)appDisplayName {
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
if ([path hasPrefix:@"url::"]) {
NSString *url = [path substringFromIndex:5];
return [[FIRStorage storageForApp:[FIRApp appNamed:appName]] referenceForURL:url];
return [[FIRStorage storageForApp:firApp] referenceForURL:url];
} else {
return [[FIRStorage storageForApp:[FIRApp appNamed:appName]] referenceWithPath:path];
return [[FIRStorage storageForApp:firApp] referenceWithPath:path];
}
}
@ -387,13 +391,13 @@ RCT_EXPORT_METHOD(putFile:(NSString *) appName
return @[STORAGE_EVENT, STORAGE_ERROR];
}
- (void)sendJSError:(NSString *)appName error:(NSError *)error path:(NSString *)path {
- (void)sendJSError:(NSString *)appDisplayName error:(NSError *)error path:(NSString *)path {
NSDictionary *evt = @{@"path": path, @"message": [error debugDescription]};
[self sendJSEvent:appName type:STORAGE_ERROR path:path title:STORAGE_ERROR props:evt];
[self sendJSEvent:appDisplayName type:STORAGE_ERROR path:path title:STORAGE_ERROR props:evt];
}
- (void)sendJSEvent:(NSString *)appName type:(NSString *)type path:(NSString *)path title:(NSString *)title props:(NSDictionary *)props {
[RNFirebaseUtil sendJSEvent:self name:type body:@{@"eventName": title, @"appName": appName, @"path": path, @"body": props}];
- (void)sendJSEvent:(NSString *)appDisplayName type:(NSString *)type path:(NSString *)path title:(NSString *)title props:(NSDictionary *)props {
[RNFirebaseUtil sendJSEvent:self name:type body:@{@"eventName": title, @"appName": appDisplayName, @"path": path, @"body": props}];
}
/**

49
lib/index.d.ts vendored
View File

@ -64,6 +64,10 @@ declare module "react-native-firebase" {
*/
crash(): RNFirebase.crash.Crash;
static fabric: {
crashlytics(): RNFirebase.crashlytics.Crashlytics;
};
apps: Array<string>;
googleApiAvailability: RNFirebase.GoogleApiAvailabilityType;
@ -846,5 +850,50 @@ declare module "react-native-firebase" {
[key: string]: any;
}
}
namespace crashlytics {
interface Crashlytics {
/**
* Forces a crash. Useful for testing your application is set up correctly.
*/
crash(): void;
/**
* Logs a message that will appear in any subsequent crash reports.
*/
log(message: string): void;
/**
* Logs a non fatal exception.
*/
recordError(code: number, message: string): void;
/**
* Set a boolean value to show alongside any subsequent crash reports.
*/
setBoolValue(key: string, value: boolean): void;
/**
* Set a float value to show alongside any subsequent crash reports.
*/
setFloatValue(key: string, value: number): void;
/**
* Set an integer value to show alongside any subsequent crash reports.
*/
setIntValue(key: string, value: number): void;
/**
* Set a string value to show alongside any subsequent crash reports.
*/
setStringValue(key: string, value: string): void;
/**
* Set the user ID to show alongside any subsequent crash reports.
*/
setUserIdentifier(userId: string): void;
}
}
}
}

View File

@ -1,8 +1,8 @@
/**
* @flow
*/
import Firebase from './modules/core/firebase';
import firebase from './modules/core/firebase';
export const AdMob = require('./modules/admob');
export type { default as User } from './modules/auth/User';
export default Firebase;
export default firebase;

View File

@ -1,6 +1,7 @@
import { NativeModules, Platform } from 'react-native';
import { statics } from './';
import AdRequest from './AdRequest';
import { SharedEventEmitter } from '../../utils/events';
import { nativeToJSError } from '../../utils';
const FirebaseAdMob = NativeModules.RNFirebaseAdMob;
@ -23,8 +24,8 @@ export default class Interstitial {
this.admob = admob;
this.adUnit = adUnit;
this.loaded = false;
this.admob.removeAllListeners(`interstitial_${adUnit}`);
this.admob.on(`interstitial_${adUnit}`, this._onInterstitialEvent);
SharedEventEmitter.removeAllListeners(`interstitial_${adUnit}`);
SharedEventEmitter.addListener(`interstitial_${adUnit}`, this._onInterstitialEvent);
}
/**
@ -48,8 +49,8 @@ export default class Interstitial {
default:
}
this.admob.emit(eventType, emitData);
this.admob.emit(`interstitial:${this.adUnit}:*`, emitData);
SharedEventEmitter.emit(eventType, emitData);
SharedEventEmitter.emit(`interstitial:${this.adUnit}:*`, emitData);
};
/**
@ -97,7 +98,7 @@ export default class Interstitial {
return null;
}
const sub = this.admob.on(`interstitial:${this.adUnit}:${eventType}`, listenerCb);
const sub = SharedEventEmitter.addListener(`interstitial:${this.adUnit}:${eventType}`, listenerCb);
subscriptions.push(sub);
return sub;
}

View File

@ -1,6 +1,7 @@
import { NativeModules } from 'react-native';
import { statics } from './';
import AdRequest from './AdRequest';
import { SharedEventEmitter } from '../../utils/events';
import { nativeToJSError } from '../../utils';
const FirebaseAdMob = NativeModules.RNFirebaseAdMob;
@ -18,8 +19,8 @@ export default class RewardedVideo {
this.admob = admob;
this.adUnit = adUnit;
this.loaded = false;
this.admob.removeAllListeners(`rewarded_video_${adUnit}`);
this.admob.on(`rewarded_video_${adUnit}`, this._onRewardedVideoEvent);
SharedEventEmitter.removeAllListeners(`rewarded_video_${adUnit}`);
SharedEventEmitter.addListener(`rewarded_video_${adUnit}`, this._onRewardedVideoEvent);
}
/**
@ -43,8 +44,8 @@ export default class RewardedVideo {
default:
}
this.admob.emit(eventType, emitData);
this.admob.emit(`rewarded_video:${this.adUnit}:*`, emitData);
SharedEventEmitter.emit(eventType, emitData);
SharedEventEmitter.emit(`rewarded_video:${this.adUnit}:*`, emitData);
};
/**
@ -97,7 +98,7 @@ export default class RewardedVideo {
return null;
}
const sub = this.admob.on(`rewarded_video:${this.adUnit}:${eventType}`, listenerCb);
const sub = SharedEventEmitter.addListener(`rewarded_video:${this.adUnit}:${eventType}`, listenerCb);
subscriptions.push(sub);
return sub;
}

View File

@ -2,7 +2,10 @@
* @flow
* AdMob representation wrapper
*/
import ModuleBase from './../../utils/ModuleBase';
import { SharedEventEmitter } from '../../utils/events';
import { getLogger } from '../../utils/log';
import { getNativeModule } from '../../utils/native';
import ModuleBase from '../../utils/ModuleBase';
import Interstitial from './Interstitial';
import RewardedVideo from './RewardedVideo';
@ -16,7 +19,7 @@ import EventTypes, {
RewardedVideoEventTypes,
} from './EventTypes';
import type FirebaseApp from '../core/firebase-app';
import type App from '../core/firebase-app';
type NativeEvent = {
adUnit: string,
@ -24,61 +27,70 @@ type NativeEvent = {
type: string,
}
export default class AdMob extends ModuleBase {
static _NAMESPACE = 'admob';
static _NATIVE_MODULE = 'RNFirebaseAdMob';
const NATIVE_EVENTS = [
'interstitial_event',
'rewarded_video_event',
];
export const MODULE_NAME = 'RNFirebaseAdmob';
export const NAMESPACE = 'admob';
export default class AdMob extends ModuleBase {
_appId: ?string;
_initialized: boolean;
constructor(firebaseApp: FirebaseApp, options: Object = {}) {
super(firebaseApp, options, true);
constructor(app: App) {
super(app, {
events: NATIVE_EVENTS,
moduleName: MODULE_NAME,
namespace: NAMESPACE,
});
this._initialized = false;
this._appId = null;
this._eventEmitter.addListener('interstitial_event', this._onInterstitialEvent.bind(this));
this._eventEmitter.addListener('rewarded_video_event', this._onRewardedVideoEvent.bind(this));
SharedEventEmitter.addListener('interstitial_event', this._onInterstitialEvent.bind(this));
SharedEventEmitter.addListener('rewarded_video_event', this._onRewardedVideoEvent.bind(this));
}
_onInterstitialEvent(event: NativeEvent): void {
const { adUnit } = event;
const jsEventType = `interstitial_${adUnit}`;
if (!this.hasListeners(jsEventType)) {
if (!SharedEventEmitter.hasListeners(jsEventType)) {
// TODO
}
this.emit(jsEventType, event);
SharedEventEmitter.emit(jsEventType, event);
}
_onRewardedVideoEvent(event: NativeEvent): void {
const { adUnit } = event;
const jsEventType = `rewarded_video_${adUnit}`;
if (!this.hasListeners(jsEventType)) {
if (!SharedEventEmitter.hasListeners(jsEventType)) {
// TODO
}
this.emit(jsEventType, event);
SharedEventEmitter.emit(jsEventType, event);
}
initialize(appId: string): void {
if (this._initialized) {
this.log.warn('AdMob has already been initialized!');
getLogger(this).warn('AdMob has already been initialized!');
} else {
this._initialized = true;
this._appId = appId;
this._native.initialize(appId);
getNativeModule(this).initialize(appId);
}
}
openDebugMenu(): void {
if (!this._initialized) {
this.log.warn('AdMob needs to be initialized before opening the dev menu!');
getLogger(this).warn('AdMob needs to be initialized before opening the dev menu!');
} else {
this.log.info('Opening debug menu');
this._native.openDebugMenu(this._appId);
getLogger(this).info('Opening debug menu');
getNativeModule(this).openDebugMenu(this._appId);
}
}
@ -89,10 +101,6 @@ export default class AdMob extends ModuleBase {
rewarded(adUnit: string): RewardedVideo {
return new RewardedVideo(this, adUnit);
}
get namespace(): string {
return 'firebase:admob';
}
}
export const statics = {

View File

@ -2,9 +2,10 @@
* @flow
* Analytics representation wrapper
*/
import ModuleBase from './../../utils/ModuleBase';
import ModuleBase from '../../utils/ModuleBase';
import { getNativeModule } from '../../utils/native';
import type FirebaseApp from '../core/firebase-app';
import type App from '../core/firebase-app';
const AlphaNumericUnderscore = /^[a-zA-Z0-9_]+$/;
@ -24,12 +25,15 @@ const ReservedEventNames = [
'user_engagement',
];
export default class Analytics extends ModuleBase {
static _NAMESPACE = 'analytics';
static _NATIVE_MODULE = 'RNFirebaseAnalytics';
export const MODULE_NAME = 'RNFirebaseAnalytics';
export const NAMESPACE = 'analytics';
constructor(firebaseApp: FirebaseApp, options: Object = {}) {
super(firebaseApp, options);
export default class Analytics extends ModuleBase {
constructor(app: App) {
super(app, {
moduleName: MODULE_NAME,
namespace: NAMESPACE,
});
}
/**
@ -57,7 +61,7 @@ export default class Analytics extends ModuleBase {
// types are supported. String parameter values can be up to 36 characters long. The "firebase_"
// prefix is reserved and should not be used for parameter names.
this._native.logEvent(name, params);
getNativeModule(this).logEvent(name, params);
}
/**
@ -65,7 +69,7 @@ export default class Analytics extends ModuleBase {
* @param enabled
*/
setAnalyticsCollectionEnabled(enabled: boolean): void {
this._native.setAnalyticsCollectionEnabled(enabled);
getNativeModule(this).setAnalyticsCollectionEnabled(enabled);
}
/**
@ -74,7 +78,7 @@ export default class Analytics extends ModuleBase {
* @param screenClassOverride
*/
setCurrentScreen(screenName: string, screenClassOverride: string): void {
this._native.setCurrentScreen(screenName, screenClassOverride);
getNativeModule(this).setCurrentScreen(screenName, screenClassOverride);
}
/**
@ -82,7 +86,7 @@ export default class Analytics extends ModuleBase {
* @param milliseconds
*/
setMinimumSessionDuration(milliseconds: number = 10000): void {
this._native.setMinimumSessionDuration(milliseconds);
getNativeModule(this).setMinimumSessionDuration(milliseconds);
}
/**
@ -90,7 +94,7 @@ export default class Analytics extends ModuleBase {
* @param milliseconds
*/
setSessionTimeoutDuration(milliseconds: number = 1800000): void {
this._native.setSessionTimeoutDuration(milliseconds);
getNativeModule(this).setSessionTimeoutDuration(milliseconds);
}
/**
@ -98,7 +102,7 @@ export default class Analytics extends ModuleBase {
* @param id
*/
setUserId(id: string): void {
this._native.setUserId(id);
getNativeModule(this).setUserId(id);
}
/**
@ -107,7 +111,7 @@ export default class Analytics extends ModuleBase {
* @param value
*/
setUserProperty(name: string, value: string): void {
this._native.setUserProperty(name, value);
getNativeModule(this).setUserProperty(name, value);
}
/**
@ -117,7 +121,7 @@ export default class Analytics extends ModuleBase {
*/
setUserProperties(object: Object): void {
for (const property of Object.keys(object)) {
this._native.setUserProperty(property, object[property]);
getNativeModule(this).setUserProperty(property, object[property]);
}
}
}

View File

@ -2,6 +2,7 @@
* @flow
* ConfirmationResult representation wrapper
*/
import { getNativeModule } from '../../utils/native';
import type Auth from './';
import type User from './User';
@ -25,7 +26,7 @@ export default class ConfirmationResult {
* @return {*}
*/
confirm(verificationCode: string): Promise<?User> {
return this._auth._interceptUserValue(this._auth._native._confirmVerificationCode(verificationCode));
return this._auth._interceptUserValue(getNativeModule(this._auth)._confirmVerificationCode(verificationCode));
}
get verificationId(): string | null {

View File

@ -1,6 +1,8 @@
// @flow
import INTERNALS from '../../utils/internals';
import { generatePushID, isFunction, isAndroid, isIOS, isString, nativeToJSError } from './../../utils';
import { SharedEventEmitter } from '../../utils/events';
import { generatePushID, isFunction, isAndroid, isIOS, isString, nativeToJSError } from '../../utils';
import { getNativeModule } from '../../utils/native';
import type Auth from './';
@ -19,7 +21,7 @@ type PhoneAuthError = {
};
export default class PhoneAuthListener {
_auth: Object;
_auth: Auth;
_timeout: number;
_publicEvents: Object;
_internalEvents: Object;
@ -68,7 +70,7 @@ export default class PhoneAuthListener {
// start verification flow natively
if (isAndroid) {
this._auth._native.verifyPhoneNumber(
getNativeModule(this._auth).verifyPhoneNumber(
phoneNumber,
this._phoneAuthRequestKey,
this._timeout,
@ -76,7 +78,7 @@ export default class PhoneAuthListener {
}
if (isIOS) {
this._auth._native.verifyPhoneNumber(
getNativeModule(this._auth).verifyPhoneNumber(
phoneNumber,
this._phoneAuthRequestKey,
);
@ -92,7 +94,8 @@ export default class PhoneAuthListener {
for (let i = 0, len = events.length; i < len; i++) {
const type = events[i];
this._auth.once(this._internalEvents[type], this[`_${type}Handler`].bind(this));
// $FlowBug: Flow doesn't support indexable signatures on classes: https://github.com/facebook/flow/issues/1323
SharedEventEmitter.once(this._internalEvents[type], this[`_${type}Handler`].bind(this));
}
}
@ -102,7 +105,7 @@ export default class PhoneAuthListener {
* @private
*/
_addUserObserver(observer) {
this._auth.on(this._publicEvents.event, observer);
SharedEventEmitter.addListener(this._publicEvents.event, observer);
}
/**
@ -111,7 +114,7 @@ export default class PhoneAuthListener {
* @private
*/
_emitToObservers(snapshot: PhoneAuthSnapshot) {
this._auth.emit(this._publicEvents.event, snapshot);
SharedEventEmitter.emit(this._publicEvents.event, snapshot);
}
/**
@ -120,9 +123,9 @@ export default class PhoneAuthListener {
* @private
*/
_emitToErrorCb(snapshot) {
const error = snapshot.error;
const { error } = snapshot;
if (this._reject) this._reject(error);
this._auth.emit(this._publicEvents.error, error);
SharedEventEmitter.emit(this._publicEvents.error, error);
}
/**
@ -132,7 +135,7 @@ export default class PhoneAuthListener {
*/
_emitToSuccessCb(snapshot) {
if (this._resolve) this._resolve(snapshot);
this._auth.emit(this._publicEvents.success, snapshot);
SharedEventEmitter.emit(this._publicEvents.success, snapshot);
}
/**
@ -143,12 +146,12 @@ export default class PhoneAuthListener {
setTimeout(() => { // move to next event loop - not sure if needed
// internal listeners
Object.values(this._internalEvents).forEach((event) => {
this._auth.removeAllListeners(event);
SharedEventEmitter.removeAllListeners(event);
});
// user observer listeners
Object.values(this._publicEvents).forEach((publicEvent) => {
this._auth.removeAllListeners(publicEvent);
SharedEventEmitter.removeAllListeners(publicEvent);
});
}, 0);
}
@ -279,11 +282,11 @@ export default class PhoneAuthListener {
this._addUserObserver(observer);
if (isFunction(errorCb)) {
this._auth.once(this._publicEvents.error, errorCb);
SharedEventEmitter.once(this._publicEvents.error, errorCb);
}
if (isFunction(successCb)) {
this._auth.once(this._publicEvents.success, successCb);
SharedEventEmitter.once(this._publicEvents.success, successCb);
}
return this;
@ -295,6 +298,7 @@ export default class PhoneAuthListener {
*/
then(fn: () => PhoneAuthSnapshot) {
this._promiseDeferred();
// $FlowFixMe: Unsure how to annotate `bind` here
if (this._promise) return this._promise.then.bind(this._promise)(fn);
return undefined; // will never get here - just to keep flow happy
}
@ -305,6 +309,7 @@ export default class PhoneAuthListener {
*/
catch(fn: () => Error) {
this._promiseDeferred();
// $FlowFixMe: Unsure how to annotate `bind` here
if (this._promise) return this._promise.catch.bind(this._promise)(fn);
return undefined; // will never get here - just to keep flow happy
}

View File

@ -3,6 +3,7 @@
* User representation wrapper
*/
import INTERNALS from '../../utils/internals';
import { getNativeModule } from '../../utils/native';
import type Auth from './';
import type { ActionCodeSettings, AuthCredential } from '../../types';
@ -92,7 +93,7 @@ export default class User {
*/
delete(): Promise<void> {
return this._auth
._interceptUndefinedUserValue(this._auth._native.delete());
._interceptUndefinedUserValue(getNativeModule(this._auth).delete());
}
/**
@ -100,7 +101,7 @@ export default class User {
* @return {Promise}
*/
getIdToken(forceRefresh: boolean = false): Promise<string> {
return this._auth._native.getToken(forceRefresh);
return getNativeModule(this._auth).getToken(forceRefresh);
}
/**
@ -109,7 +110,7 @@ export default class User {
*/
linkWithCredential(credential: AuthCredential): Promise<User> {
return this._auth
._interceptUserValue(this._auth._native.link(credential.providerId, credential.token, credential.secret));
._interceptUserValue(getNativeModule(this._auth).link(credential.providerId, credential.token, credential.secret));
}
/**
@ -118,7 +119,7 @@ export default class User {
*/
reauthenticateWithCredential(credential: AuthCredential): Promise<void> {
return this._auth
._interceptUndefinedUserValue(this._auth._native.reauthenticate(credential.providerId, credential.token, credential.secret));
._interceptUndefinedUserValue(getNativeModule(this._auth).reauthenticate(credential.providerId, credential.token, credential.secret));
}
/**
@ -127,7 +128,7 @@ export default class User {
*/
reload(): Promise<void> {
return this._auth
._interceptUndefinedUserValue(this._auth._native.reload());
._interceptUndefinedUserValue(getNativeModule(this._auth).reload());
}
/**
@ -135,7 +136,7 @@ export default class User {
*/
sendEmailVerification(actionCodeSettings?: ActionCodeSettings): Promise<void> {
return this._auth
._interceptUndefinedUserValue(this._auth._native.sendEmailVerification(actionCodeSettings));
._interceptUndefinedUserValue(getNativeModule(this._auth).sendEmailVerification(actionCodeSettings));
}
toJSON(): Object {
@ -148,7 +149,7 @@ export default class User {
* @return {Promise.<TResult>|*}
*/
unlink(providerId: string): Promise<User> {
return this._auth._interceptUserValue(this._auth._native.unlink(providerId));
return this._auth._interceptUserValue(getNativeModule(this._auth).unlink(providerId));
}
/**
@ -159,7 +160,7 @@ export default class User {
*/
updateEmail(email: string): Promise<void> {
return this._auth
._interceptUndefinedUserValue(this._auth._native.updateEmail(email));
._interceptUndefinedUserValue(getNativeModule(this._auth).updateEmail(email));
}
/**
@ -169,7 +170,7 @@ export default class User {
*/
updatePassword(password: string): Promise<void> {
return this._auth
._interceptUndefinedUserValue(this._auth._native.updatePassword(password));
._interceptUndefinedUserValue(getNativeModule(this._auth).updatePassword(password));
}
/**
@ -179,7 +180,7 @@ export default class User {
*/
updateProfile(updates: Object = {}): Promise<void> {
return this._auth
._interceptUndefinedUserValue(this._auth._native.updateProfile(updates));
._interceptUndefinedUserValue(getNativeModule(this._auth).updateProfile(updates));
}
/**
@ -189,7 +190,7 @@ export default class User {
*/
getToken(forceRefresh: boolean = false): Promise<Object> {
console.warn('Deprecated firebase.User.prototype.getToken in favor of firebase.User.prototype.getIdToken.');
return this._auth._native.getToken(forceRefresh);
return getNativeModule(this._auth).getToken(forceRefresh);
}
/**

View File

@ -4,6 +4,9 @@
*/
import User from './User';
import ModuleBase from '../../utils/ModuleBase';
import { getAppEventName, SharedEventEmitter } from '../../utils/events';
import { getLogger } from '../../utils/log';
import { getNativeModule } from '../../utils/native';
import INTERNALS from '../../utils/internals';
import ConfirmationResult from './ConfirmationResult';
@ -18,48 +21,57 @@ import FacebookAuthProvider from './providers/FacebookAuthProvider';
import PhoneAuthListener from './PhoneAuthListener';
import type { ActionCodeSettings, AuthCredential } from '../../types';
import type FirebaseApp from '../core/firebase-app';
import type App from '../core/firebase-app';
type AuthResult = {
authenticated: boolean,
user: Object|null
} | null;
export default class Auth extends ModuleBase {
static _NAMESPACE = 'auth';
static _NATIVE_MODULE = 'RNFirebaseAuth';
const NATIVE_EVENTS = [
'auth_state_changed',
'phone_auth_state_changed',
];
export const MODULE_NAME = 'RNFirebaseAuth';
export const NAMESPACE = 'auth';
export default class Auth extends ModuleBase {
_authResult: AuthResult | null;
_user: User | null;
constructor(firebaseApp: FirebaseApp, options: Object = {}) {
super(firebaseApp, options, true);
constructor(app: App) {
super(app, {
events: NATIVE_EVENTS,
moduleName: MODULE_NAME,
namespace: NAMESPACE,
});
this._user = null;
this._authResult = null;
this.addListener(
SharedEventEmitter.addListener(
// sub to internal native event - this fans out to
// public event name: onAuthStateChanged
super._getAppEventName('auth_state_changed'),
getAppEventName(this, 'auth_state_changed'),
this._onInternalAuthStateChanged.bind(this),
);
this.addListener(
SharedEventEmitter.addListener(
// sub to internal native event - this fans out to
// public events based on event.type
super._getAppEventName('phone_auth_state_changed'),
getAppEventName(this, 'phone_auth_state_changed'),
this._onInternalPhoneAuthStateChanged.bind(this),
);
this.addListener(
SharedEventEmitter.addListener(
// sub to internal native event - this fans out to
// public event name: onIdTokenChanged
super._getAppEventName('auth_id_token_changed'),
getAppEventName(this, 'auth_id_token_changed'),
this._onInternalIdTokenChanged.bind(this),
);
this._native.addAuthStateListener();
this._native.addIdTokenListener();
getNativeModule(this).addAuthStateListener();
getNativeModule(this).addIdTokenListener();
}
/**
@ -69,13 +81,13 @@ export default class Auth extends ModuleBase {
*/
_onInternalPhoneAuthStateChanged(event: Object) {
const eventKey = `phone:auth:${event.requestKey}:${event.type}`;
this.emit(eventKey, event.state);
SharedEventEmitter.emit(eventKey, event.state);
}
_setAuthState(auth: AuthResult) {
this._authResult = auth;
this._user = auth && auth.user ? new User(this, auth.user) : null;
this.emit(this._getAppEventName('onUserChanged'), this._user);
SharedEventEmitter.emit(getAppEventName(this, 'onUserChanged'), this._user);
}
/**
@ -85,7 +97,7 @@ export default class Auth extends ModuleBase {
*/
_onInternalAuthStateChanged(auth: AuthResult) {
this._setAuthState(auth);
this.emit(this._getAppEventName('onAuthStateChanged'), this._user);
SharedEventEmitter.emit(getAppEventName(this, 'onAuthStateChanged'), this._user);
}
/**
@ -96,7 +108,7 @@ export default class Auth extends ModuleBase {
*/
_onInternalIdTokenChanged(auth: AuthResult) {
this._setAuthState(auth);
this.emit(this._getAppEventName('onIdTokenChanged'), this._user);
SharedEventEmitter.emit(getAppEventName(this, 'onIdTokenChanged'), this._user);
}
/**
@ -129,8 +141,8 @@ export default class Auth extends ModuleBase {
* @param listener
*/
onAuthStateChanged(listener: Function) {
this.log.info('Creating onAuthStateChanged listener');
this.on(this._getAppEventName('onAuthStateChanged'), listener);
getLogger(this).info('Creating onAuthStateChanged listener');
SharedEventEmitter.addListener(getAppEventName(this, 'onAuthStateChanged'), listener);
if (this._authResult) listener(this._user || null);
return this._offAuthStateChanged.bind(this, listener);
}
@ -140,8 +152,8 @@ export default class Auth extends ModuleBase {
* @param listener
*/
_offAuthStateChanged(listener: Function) {
this.log.info('Removing onAuthStateChanged listener');
this.removeListener(this._getAppEventName('onAuthStateChanged'), listener);
getLogger(this).info('Removing onAuthStateChanged listener');
SharedEventEmitter.removeListener(getAppEventName(this, 'onAuthStateChanged'), listener);
}
/**
@ -149,8 +161,8 @@ export default class Auth extends ModuleBase {
* @param listener
*/
onIdTokenChanged(listener: Function) {
this.log.info('Creating onIdTokenChanged listener');
this.on(this._getAppEventName('onIdTokenChanged'), listener);
getLogger(this).info('Creating onIdTokenChanged listener');
SharedEventEmitter.addListener(getAppEventName(this, 'onIdTokenChanged'), listener);
if (this._authResult) listener(this._user || null);
return this._offIdTokenChanged.bind(this, listener);
}
@ -160,8 +172,8 @@ export default class Auth extends ModuleBase {
* @param listener
*/
_offIdTokenChanged(listener: Function) {
this.log.info('Removing onIdTokenChanged listener');
this.removeListener(this._getAppEventName('onIdTokenChanged'), listener);
getLogger(this).info('Removing onIdTokenChanged listener');
SharedEventEmitter.removeListener(getAppEventName(this, 'onIdTokenChanged'), listener);
}
/**
@ -169,8 +181,8 @@ export default class Auth extends ModuleBase {
* @param listener
*/
onUserChanged(listener: Function) {
this.log.info('Creating onUserChanged listener');
this.on(this._getAppEventName('onUserChanged'), listener);
getLogger(this).info('Creating onUserChanged listener');
SharedEventEmitter.addListener(getAppEventName(this, 'onUserChanged'), listener);
if (this._authResult) listener(this._user || null);
return this._offUserChanged.bind(this, listener);
}
@ -180,8 +192,8 @@ export default class Auth extends ModuleBase {
* @param listener
*/
_offUserChanged(listener: Function) {
this.log.info('Removing onUserChanged listener');
this.removeListener(this._getAppEventName('onUserChanged'), listener);
getLogger(this).info('Removing onUserChanged listener');
SharedEventEmitter.removeListener(getAppEventName(this, 'onUserChanged'), listener);
}
/**
@ -189,7 +201,7 @@ export default class Auth extends ModuleBase {
* @return {Promise}
*/
signOut(): Promise<void> {
return this._interceptUndefinedUserValue(this._native.signOut());
return this._interceptUndefinedUserValue(getNativeModule(this).signOut());
}
/**
@ -197,7 +209,7 @@ export default class Auth extends ModuleBase {
* @return {Promise} A promise resolved upon completion
*/
signInAnonymously(): Promise<User> {
return this._interceptUserValue(this._native.signInAnonymously());
return this._interceptUserValue(getNativeModule(this).signInAnonymously());
}
/**
@ -207,7 +219,7 @@ export default class Auth extends ModuleBase {
* @return {Promise} A promise indicating the completion
*/
createUserWithEmailAndPassword(email: string, password: string): Promise<User> {
return this._interceptUserValue(this._native.createUserWithEmailAndPassword(email, password));
return this._interceptUserValue(getNativeModule(this).createUserWithEmailAndPassword(email, password));
}
/**
@ -217,7 +229,7 @@ export default class Auth extends ModuleBase {
* @return {Promise} A promise that is resolved upon completion
*/
signInWithEmailAndPassword(email: string, password: string): Promise<User> {
return this._interceptUserValue(this._native.signInWithEmailAndPassword(email, password));
return this._interceptUserValue(getNativeModule(this).signInWithEmailAndPassword(email, password));
}
/**
@ -226,7 +238,7 @@ export default class Auth extends ModuleBase {
* @return {Promise} A promise resolved upon completion
*/
signInWithCustomToken(customToken: string): Promise<User> {
return this._interceptUserValue(this._native.signInWithCustomToken(customToken));
return this._interceptUserValue(getNativeModule(this).signInWithCustomToken(customToken));
}
/**
@ -235,7 +247,7 @@ export default class Auth extends ModuleBase {
*/
signInWithCredential(credential: AuthCredential): Promise<User> {
return this._interceptUserValue(
this._native.signInWithCredential(
getNativeModule(this).signInWithCredential(
credential.providerId, credential.token, credential.secret,
),
);
@ -246,7 +258,7 @@ export default class Auth extends ModuleBase {
*
*/
signInWithPhoneNumber(phoneNumber: string): Promise<ConfirmationResult> {
return this._native.signInWithPhoneNumber(phoneNumber).then((result) => {
return getNativeModule(this).signInWithPhoneNumber(phoneNumber).then((result) => {
return new ConfirmationResult(this, result.verificationId);
});
}
@ -269,7 +281,7 @@ export default class Auth extends ModuleBase {
* @param {string} email The email to send password reset instructions
*/
sendPasswordResetEmail(email: string, actionCodeSettings?: ActionCodeSettings): Promise<void> {
return this._native.sendPasswordResetEmail(email, actionCodeSettings);
return getNativeModule(this).sendPasswordResetEmail(email, actionCodeSettings);
}
/**
@ -281,7 +293,7 @@ export default class Auth extends ModuleBase {
* @return {Promise.<Null>}
*/
confirmPasswordReset(code: string, newPassword: string): Promise<void> {
return this._native.confirmPasswordReset(code, newPassword);
return getNativeModule(this).confirmPasswordReset(code, newPassword);
}
/**
@ -292,7 +304,7 @@ export default class Auth extends ModuleBase {
* @return {Promise.<Null>}
*/
applyActionCode(code: string): Promise<void> {
return this._native.applyActionCode(code);
return getNativeModule(this).applyActionCode(code);
}
/**
@ -303,7 +315,7 @@ export default class Auth extends ModuleBase {
* @return {Promise.<any>|Promise<ActionCodeInfo>}
*/
checkActionCode(code: string): Promise<void> {
return this._native.checkActionCode(code);
return getNativeModule(this).checkActionCode(code);
}
/**
@ -311,7 +323,7 @@ export default class Auth extends ModuleBase {
* @return {Promise}
*/
getCurrentUser(): Promise<User | null> {
return this._interceptUserValue(this._native.getCurrentUser());
return this._interceptUserValue(getNativeModule(this).getCurrentUser());
}
/**
@ -319,7 +331,7 @@ export default class Auth extends ModuleBase {
* @return {Promise}
*/
fetchProvidersForEmail(email: string): Promise<Array<String>> {
return this._native.fetchProvidersForEmail(email);
return getNativeModule(this).fetchProvidersForEmail(email);
}
/**
@ -330,32 +342,28 @@ export default class Auth extends ModuleBase {
return this._user;
}
get namespace(): string {
return 'firebase:auth';
}
/**
* KNOWN UNSUPPORTED METHODS
*/
getRedirectResult() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD(Auth, 'getRedirectResult'));
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD('auth', 'getRedirectResult'));
}
setPersistence() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD(Auth, 'setPersistence'));
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD('auth', 'setPersistence'));
}
signInAndRetrieveDataWithCredential() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD(Auth, 'signInAndRetrieveDataWithCredential'));
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD('auth', 'signInAndRetrieveDataWithCredential'));
}
signInWithPopup() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD(Auth, 'signInWithPopup'));
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD('auth', 'signInWithPopup'));
}
signInWithRedirect() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD(Auth, 'signInWithRedirect'));
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD('auth', 'signInWithRedirect'));
}
}

View File

@ -2,9 +2,11 @@
* @flow
* Remote Config representation wrapper
*/
import ModuleBase from './../../utils/ModuleBase';
import { getLogger } from '../../utils/log';
import ModuleBase from '../../utils/ModuleBase';
import { getNativeModule } from '../../utils/native';
import type FirebaseApp from '../core/firebase-app';
import type App from '../core/firebase-app';
type NativeValue = {
stringValue?: string,
@ -14,17 +16,20 @@ type NativeValue = {
source: 'remoteConfigSourceRemote' | 'remoteConfigSourceDefault' | ' remoteConfigSourceStatic',
}
export const MODULE_NAME = 'RNFirebaseRemoteConfig';
export const NAMESPACE = 'config';
/**
* @class Config
*/
export default class RemoteConfig extends ModuleBase {
static _NAMESPACE = 'config';
static _NATIVE_MODULE = 'RNFirebaseRemoteConfig';
_developerModeEnabled: boolean;
constructor(firebaseApp: FirebaseApp, options: Object = {}) {
super(firebaseApp, options);
constructor(app: App) {
super(app, {
moduleName: MODULE_NAME,
namespace: NAMESPACE,
});
this._developerModeEnabled = false;
}
@ -39,7 +44,7 @@ export default class RemoteConfig extends ModuleBase {
source: nativeValue.source,
val() {
if (nativeValue.boolValue !== null && (nativeValue.stringValue === 'true' || nativeValue.stringValue === 'false' || nativeValue.stringValue === null)) return nativeValue.boolValue;
if (nativeValue.numberValue !== null && (nativeValue.stringValue == null || nativeValue.stringValue === '' || `${nativeValue.numberValue}` === nativeValue.stringValue)) return nativeValue.numberValue;
if (nativeValue.numberValue !== null && nativeValue.numberValue !== undefined && (nativeValue.stringValue == null || nativeValue.stringValue === '' || nativeValue.numberValue.toString() === nativeValue.stringValue)) return nativeValue.numberValue;
if (nativeValue.dataValue !== nativeValue.stringValue && (nativeValue.stringValue == null || nativeValue.stringValue === '')) return nativeValue.dataValue;
return nativeValue.stringValue;
},
@ -51,8 +56,8 @@ export default class RemoteConfig extends ModuleBase {
*/
enableDeveloperMode() {
if (!this._developerModeEnabled) {
this.log.debug('Enabled developer mode');
this._native.enableDeveloperMode();
getLogger(this).debug('Enabled developer mode');
getNativeModule(this).enableDeveloperMode();
this._developerModeEnabled = true;
}
}
@ -64,11 +69,11 @@ export default class RemoteConfig extends ModuleBase {
*/
fetch(expiration?: number) {
if (expiration !== undefined) {
this.log.debug(`Fetching remote config data with expiration ${expiration.toString()}`);
return this._native.fetchWithExpirationDuration(expiration);
getLogger(this).debug(`Fetching remote config data with expiration ${expiration.toString()}`);
return getNativeModule(this).fetchWithExpirationDuration(expiration);
}
this.log.debug('Fetching remote config data');
return this._native.fetch();
getLogger(this).debug('Fetching remote config data');
return getNativeModule(this).fetch();
}
/**
@ -78,8 +83,8 @@ export default class RemoteConfig extends ModuleBase {
* rejects if no Fetched Config was found, or the Fetched Config was already activated.
*/
activateFetched() {
this.log.debug('Activating remote config');
return this._native.activateFetched();
getLogger(this).debug('Activating remote config');
return getNativeModule(this).activateFetched();
}
/**
@ -96,7 +101,7 @@ export default class RemoteConfig extends ModuleBase {
* }
*/
getValue(key: String) {
return this._native
return getNativeModule(this)
.getValue(key || '')
.then(this._nativeValueToJS);
}
@ -116,7 +121,7 @@ export default class RemoteConfig extends ModuleBase {
* }
*/
getValues(keys: Array<String>) {
return this._native
return getNativeModule(this)
.getValues(keys || [])
.then((nativeValues) => {
const values: { [String]: Object } = {};
@ -133,7 +138,7 @@ export default class RemoteConfig extends ModuleBase {
* @returns {*|Promise.<Array<String>>}
*/
getKeysByPrefix(prefix?: String) {
return this._native.getKeysByPrefix(prefix);
return getNativeModule(this).getKeysByPrefix(prefix);
}
/**
@ -141,7 +146,7 @@ export default class RemoteConfig extends ModuleBase {
* @param defaults: A dictionary mapping a String key to a Object values.
*/
setDefaults(defaults: Object) {
this._native.setDefaults(defaults);
getNativeModule(this).setDefaults(defaults);
}
/**
@ -149,7 +154,7 @@ export default class RemoteConfig extends ModuleBase {
* @param resource: The plist file name or resource ID
*/
setDefaultsFromResource(resource: String | number) {
this._native.setDefaultsFromResource(resource);
getNativeModule(this).setDefaultsFromResource(resource);
}
}

View File

@ -3,112 +3,85 @@
*/
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, { NAMESPACE as AdmobNamespace } from '../admob';
import Auth, { NAMESPACE as AuthNamespace } from '../auth';
import Analytics, { NAMESPACE as AnalyticsNamespace } from '../analytics';
import Config, { NAMESPACE as ConfigNamespace } from '../config';
import Crash, { NAMESPACE as CrashNamespace } from '../crash';
import Crashlytics, { NAMESPACE as CrashlyticsNamespace } from '../fabric/crashlytics';
import Database, { NAMESPACE as DatabaseNamespace } from '../database';
import Firestore, { NAMESPACE as FirestoreNamespace } from '../firestore';
import Links, { NAMESPACE as LinksNamespace } from '../links';
import Messaging, { NAMESPACE as MessagingNamespace } from '../messaging';
import Performance, { NAMESPACE as PerfNamespace } from '../perf';
import Storage, { NAMESPACE as StorageNamespace } from '../storage';
import Utils, { NAMESPACE as UtilsNamespace } 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;
export default class FirebaseApp {
export default class App {
_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, AdmobNamespace, AdMob);
this.analytics = APPS.appModule(this, AnalyticsNamespace, Analytics);
this.auth = APPS.appModule(this, AuthNamespace, Auth);
this.config = APPS.appModule(this, ConfigNamespace, Config);
this.crash = APPS.appModule(this, CrashNamespace, Crash);
this.database = APPS.appModule(this, DatabaseNamespace, Database);
this.fabric = {
crashlytics: APPS.appModule(this, CrashlyticsNamespace, Crashlytics),
};
this.firestore = APPS.appModule(this, FirestoreNamespace, Firestore);
this.links = APPS.appModule(this, LinksNamespace, Links);
this.messaging = APPS.appModule(this, MessagingNamespace, Messaging);
this.perf = APPS.appModule(this, PerfNamespace, Performance);
this.storage = APPS.appModule(this, StorageNamespace, Storage);
this.utils = APPS.appModule(this, UtilsNamespace, Utils);
this._extendedProps = {};
}
/**
@ -116,13 +89,6 @@ export default class FirebaseApp {
* @return {*}
*/
get name(): string {
if (this._name === INTERNALS.STRINGS.DEFAULT_APP_NAME) {
// ios and android firebase sdk's return different
// app names - so we just return what the web sdk
// would if it was default.
return '[DEFAULT]';
}
return this._name;
}
@ -153,6 +119,7 @@ export default class FirebaseApp {
throw new Error(INTERNALS.STRINGS.ERROR_PROTECTED_PROP(key));
}
// $FlowBug: Flow doesn't support indexable signatures on classes: https://github.com/facebook/flow/issues/1323
this[key] = props[key];
this._extendedProps[key] = true;
}
@ -165,7 +132,7 @@ export default class FirebaseApp {
delete() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD('app', 'delete'));
// TODO only the ios sdk currently supports delete, add back in when android also supports it
// if (this._name === INTERNALS.STRINGS.DEFAULT_APP_NAME && this._nativeInitialized) {
// if (this._name === APPS.DEFAULT_APP_NAME && this._nativeInitialized) {
// return Promise.reject(
// new Error('Unable to delete the default native firebase app instance.'),
// );
@ -179,42 +146,14 @@ export default class FirebaseApp {
*
* @return {*}
*/
onReady(): Promise<FirebaseApp> {
onReady(): Promise<App> {
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],
});
}
}

View File

@ -2,26 +2,26 @@
* @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';
import App from './firebase-app';
// module imports
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 { statics as AdMobStatics, MODULE_NAME as AdmobModuleName } from '../admob';
import { statics as AuthStatics, MODULE_NAME as AuthModuleName } from '../auth';
import { statics as AnalyticsStatics, MODULE_NAME as AnalyticsModuleName } from '../analytics';
import { statics as ConfigStatics, MODULE_NAME as ConfigModuleName } from '../config';
import { statics as CrashStatics, MODULE_NAME as CrashModuleName } from '../crash';
import { statics as CrashlyticsStatics, MODULE_NAME as CrashlyticsModuleName } from '../fabric/crashlytics';
import { statics as DatabaseStatics, MODULE_NAME as DatabaseModuleName } from '../database';
import { statics as FirestoreStatics, MODULE_NAME as FirestoreModuleName } from '../firestore';
import { statics as LinksStatics, MODULE_NAME as LinksModuleName } from '../links';
import { statics as MessagingStatics, MODULE_NAME as MessagingModuleName } from '../messaging';
import { statics as PerformanceStatics, MODULE_NAME as PerfModuleName } from '../perf';
import { statics as StorageStatics, MODULE_NAME as StorageModuleName } from '../storage';
import { statics as UtilsStatics, MODULE_NAME as UtilsModuleName } from '../utils';
import type {
AdMobModule,
@ -31,10 +31,7 @@ import type {
CrashModule,
DatabaseModule,
FabricModule,
FirebaseModule,
FirebaseModuleAndStatics,
FirebaseOptions,
FirebaseStatics,
FirestoreModule,
LinksModule,
MessagingModule,
@ -45,9 +42,7 @@ import type {
const FirebaseCoreModule = NativeModules.RNFirebase;
class FirebaseCore {
_nativeEmitters: { [string]: NativeEventEmitter };
_nativeSubscriptions: { [string]: boolean };
class Firebase {
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, AdmobModuleName);
this.analytics = APPS.moduleAndStatics('analytics', AnalyticsStatics, AnalyticsModuleName);
this.auth = APPS.moduleAndStatics('auth', AuthStatics, AuthModuleName);
this.config = APPS.moduleAndStatics('config', ConfigStatics, ConfigModuleName);
this.crash = APPS.moduleAndStatics('crash', CrashStatics, CrashModuleName);
this.database = APPS.moduleAndStatics('database', DatabaseStatics, DatabaseModuleName);
this.fabric = {
crashlytics: this._appNamespaceOrStatics(CrashlyticsStatics, Crashlytics),
crashlytics: APPS.moduleAndStatics('crashlytics', CrashlyticsStatics, CrashlyticsModuleName),
};
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, FirestoreModuleName);
this.links = APPS.moduleAndStatics('links', LinksStatics, LinksModuleName);
this.messaging = APPS.moduleAndStatics('messaging', MessagingStatics, MessagingModuleName);
this.perf = APPS.moduleAndStatics('perf', PerformanceStatics, PerfModuleName);
this.storage = APPS.moduleAndStatics('storage', StorageStatics, StorageModuleName);
this.utils = APPS.moduleAndStatics('utils', UtilsStatics, UtilsModuleName);
}
/**
@ -111,58 +88,8 @@ class FirebaseCore {
* @param name
* @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];
initializeApp(options: FirebaseOptions, name: string): App {
return APPS.initializeApp(options, name);
}
/**
@ -174,96 +101,17 @@ class FirebaseCore {
* @param name
* @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;
app(name?: string): App {
return APPS.app(name);
}
/**
* A (read-only) array of all initialized apps.
* @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);
get apps(): Array<App> {
return APPS.apps();
}
}
export default new FirebaseCore();
export default new Firebase();

View File

@ -3,16 +3,20 @@
* Crash Reporting representation wrapper
*/
import ModuleBase from '../../utils/ModuleBase';
import { getNativeModule } from '../../utils/native';
import type FirebaseApp from '../core/firebase-app';
import type App from '../core/firebase-app';
import type { FirebaseError } from '../../types';
export default class Crash extends ModuleBase {
static _NAMESPACE = 'crash';
static _NATIVE_MODULE = 'RNFirebaseCrash';
export const MODULE_NAME = 'RNFirebaseCrash';
export const NAMESPACE = 'crash';
constructor(firebaseApp: FirebaseApp, options: Object = {}) {
super(firebaseApp, options);
export default class Crash extends ModuleBase {
constructor(app: App) {
super(app, {
moduleName: MODULE_NAME,
namespace: NAMESPACE,
});
}
/**
@ -20,7 +24,7 @@ export default class Crash extends ModuleBase {
* @param enabled
*/
setCrashCollectionEnabled(enabled: boolean): void {
this._native.setCrashCollectionEnabled(enabled);
getNativeModule(this).setCrashCollectionEnabled(enabled);
}
/**
@ -28,7 +32,7 @@ export default class Crash extends ModuleBase {
* @returns {Promise.<boolean>}
*/
isCrashCollectionEnabled(): Promise<boolean> {
return this._native.isCrashCollectionEnabled();
return getNativeModule(this).isCrashCollectionEnabled();
}
/**
@ -36,7 +40,7 @@ export default class Crash extends ModuleBase {
* @param {string} message
*/
log(message: string): void {
this._native.log(message);
getNativeModule(this).log(message);
}
/**
@ -47,7 +51,7 @@ export default class Crash extends ModuleBase {
* @param {string} tag
*/
logcat(level: number, tag: string, message: string): void {
this._native.logcat(level, tag, message);
getNativeModule(this).logcat(level, tag, message);
}
/**
@ -74,7 +78,7 @@ export default class Crash extends ModuleBase {
errorMessage = `${errorMessage} - ${stackRows[i]}\r\n`;
}
this._native.report(errorMessage);
getNativeModule(this).report(errorMessage);
}
}

View File

@ -2,7 +2,8 @@
* @flow
* Disconnect representation wrapper
*/
import { typeOf } from './../../utils';
import { typeOf } from '../../utils';
import { getNativeModule } from '../../utils/native';
import type Database from './';
import type Reference from './reference';
@ -32,7 +33,7 @@ export default class Disconnect {
* @returns {*}
*/
set(value: string | Object): Promise<void> {
return this._database._native.onDisconnectSet(this.path, { type: typeOf(value), value });
return getNativeModule(this._database).onDisconnectSet(this.path, { type: typeOf(value), value });
}
/**
@ -41,7 +42,7 @@ export default class Disconnect {
* @returns {*}
*/
update(values: Object): Promise<void> {
return this._database._native.onDisconnectUpdate(this.path, values);
return getNativeModule(this._database).onDisconnectUpdate(this.path, values);
}
/**
@ -49,7 +50,7 @@ export default class Disconnect {
* @returns {*}
*/
remove(): Promise<void> {
return this._database._native.onDisconnectRemove(this.path);
return getNativeModule(this._database).onDisconnectRemove(this.path);
}
/**
@ -57,6 +58,6 @@ export default class Disconnect {
* @returns {*}
*/
cancel(): Promise<void> {
return this._database._native.onDisconnectCancel(this.path);
return getNativeModule(this._database).onDisconnectCancel(this.path);
}
}

View File

@ -6,27 +6,37 @@ import { NativeModules } from 'react-native';
import Reference from './reference';
import TransactionHandler from './transaction';
import ModuleBase from './../../utils/ModuleBase';
import ModuleBase from '../../utils/ModuleBase';
import { getNativeModule } from '../../utils/native';
import type FirebaseApp from '../core/firebase-app';
import type App from '../core/firebase-app';
const NATIVE_EVENTS = [
'database_transaction_event',
// 'database_server_offset', // TODO
];
export const MODULE_NAME = 'RNFirebaseDatabase';
export const NAMESPACE = 'database';
/**
* @class Database
*/
export default class Database extends ModuleBase {
static _NAMESPACE = 'database';
static _NATIVE_MODULE = 'RNFirebaseDatabase';
_offsetRef: Reference;
_serverTimeOffset: number;
_transactionHandler: TransactionHandler;
constructor(firebaseApp: FirebaseApp, options: Object = {}) {
super(firebaseApp, options, true);
constructor(app: App, options: Object = {}) {
super(app, {
events: NATIVE_EVENTS,
moduleName: MODULE_NAME,
namespace: NAMESPACE,
});
this._transactionHandler = new TransactionHandler(this);
if (this._options.persistence) {
this._native.setPersistence(this._options.persistence);
if (options.persistence) {
getNativeModule(this).setPersistence(options.persistence);
}
// server time listener
@ -54,14 +64,14 @@ export default class Database extends ModuleBase {
*
*/
goOnline(): void {
this._native.goOnline();
getNativeModule(this).goOnline();
}
/**
*
*/
goOffline(): void {
this._native.goOffline();
getNativeModule(this).goOffline();
}
/**
@ -79,8 +89,8 @@ export const statics = {
TIMESTAMP: NativeModules.RNFirebaseDatabase.serverValueTimestamp || { '.sv': 'timestamp' },
} : {},
enableLogging(enabled: boolean) {
if (NativeModules[Database._NATIVE_MODULE]) {
NativeModules[Database._NATIVE_MODULE].enableLogging(enabled);
if (NativeModules[MODULE_NAME]) {
NativeModules[MODULE_NAME].enableLogging(enabled);
}
},
};

View File

@ -5,6 +5,8 @@
import Query from './query.js';
import Snapshot from './snapshot';
import Disconnect from './disconnect';
import { getLogger } from '../../utils/log';
import { getNativeModule } from '../../utils/native';
import ReferenceBase from '../../utils/ReferenceBase';
import {
@ -17,10 +19,10 @@ import {
generatePushID,
} from '../../utils';
import INTERNALS from '../../utils/internals';
import SyncTree from '../../utils/SyncTree';
import type Database from './';
import type { DatabaseModifier, FirebaseError } from '../../types';
import type SyncTree from '../../utils/SyncTree';
// track all event registrations by path
let listeners = 0;
@ -73,18 +75,18 @@ type DatabaseListener = {
* @extends ReferenceBase
*/
export default class Reference extends ReferenceBase {
_database: Object;
_database: Database;
_promise: ?Promise<*>;
_query: Query;
_refListeners: { [listenerId: number]: DatabaseListener };
constructor(database: Object, path: string, existingModifiers?: Array<DatabaseModifier>) {
super(path, database);
constructor(database: Database, path: string, existingModifiers?: Array<DatabaseModifier>) {
super(path);
this._promise = null;
this._refListeners = {};
this._database = database;
this._query = new Query(this, path, existingModifiers);
this.log.debug('Created new Reference', this._getRefKey());
getLogger(database).debug('Created new Reference', this._getRefKey());
}
/**
@ -98,7 +100,7 @@ export default class Reference extends ReferenceBase {
* @returns {*}
*/
keepSynced(bool: boolean): Promise<void> {
return this._database._native.keepSynced(this._getRefKey(), this.path, this._query.getModifiers(), bool);
return getNativeModule(this._database).keepSynced(this._getRefKey(), this.path, this._query.getModifiers(), bool);
}
/**
@ -111,7 +113,7 @@ export default class Reference extends ReferenceBase {
*/
set(value: any, onComplete?: Function): Promise<void> {
return promiseOrCallback(
this._database._native.set(this.path, this._serializeAnyType(value)),
getNativeModule(this._database).set(this.path, this._serializeAnyType(value)),
onComplete,
);
}
@ -128,7 +130,7 @@ export default class Reference extends ReferenceBase {
const _priority = this._serializeAnyType(priority);
return promiseOrCallback(
this._database._native.setPriority(this.path, _priority),
getNativeModule(this._database).setPriority(this.path, _priority),
onComplete,
);
}
@ -147,7 +149,7 @@ export default class Reference extends ReferenceBase {
const _priority = this._serializeAnyType(priority);
return promiseOrCallback(
this._database._native.setWithPriority(this.path, _value, _priority),
getNativeModule(this._database).setWithPriority(this.path, _value, _priority),
onComplete,
);
}
@ -164,7 +166,7 @@ export default class Reference extends ReferenceBase {
const value = this._serializeObject(val);
return promiseOrCallback(
this._database._native.update(this.path, value),
getNativeModule(this._database).update(this.path, value),
onComplete,
);
}
@ -178,7 +180,7 @@ export default class Reference extends ReferenceBase {
*/
remove(onComplete?: Function): Promise<void> {
return promiseOrCallback(
this._database._native.remove(this.path),
getNativeModule(this._database).remove(this.path),
onComplete,
);
}
@ -234,7 +236,7 @@ export default class Reference extends ReferenceBase {
cancelOrContext: (error: FirebaseError) => void,
context?: Object,
) {
return this._database._native.once(this._getRefKey(), this.path, this._query.getModifiers(), eventName)
return getNativeModule(this._database).once(this._getRefKey(), this.path, this._query.getModifiers(), eventName)
.then(({ snapshot }) => {
const _snapshot = new Snapshot(this, snapshot);
@ -269,7 +271,9 @@ export default class Reference extends ReferenceBase {
// if callback provided then internally call the set promise with value
if (isFunction(onComplete)) {
return promise
// $FlowBug: Reports that onComplete can change to null despite the null check: https://github.com/facebook/flow/issues/1655
.then(() => onComplete(null, newRef))
// $FlowBug: Reports that onComplete can change to null despite the null check: https://github.com/facebook/flow/issues/1655
.catch(error => onComplete(error, null));
}
@ -535,7 +539,7 @@ export default class Reference extends ReferenceBase {
* @return {string}
*/
_getRegistrationKey(eventType: string): string {
return `$${this._database._appName}$/${this.path}$${this._query.queryIdentifier()}$${listeners}$${eventType}`;
return `$${this._database.app.name}$/${this.path}$${this._query.queryIdentifier()}$${listeners}$${eventType}`;
}
/**
@ -546,14 +550,7 @@ export default class Reference extends ReferenceBase {
* @private
*/
_getRefKey(): string {
return `$${this._database._appName}$/${this.path}$${this._query.queryIdentifier()}`;
}
/**
* Return instance of db logger
*/
get log() {
return this._database.log;
return `$${this._database.app.name}$/${this.path}$${this._query.queryIdentifier()}`;
}
/**
@ -626,7 +623,7 @@ export default class Reference extends ReferenceBase {
*
* {@link https://firebase.google.com/docs/reference/js/firebase.database.Reference#on}
*/
on(eventType: string, callback: () => any, cancelCallbackOrContext?: () => any, context?: Object): Function {
on(eventType: string, callback: (Snapshot) => any, cancelCallbackOrContext?: (Object) => any | Object, context?: Object): Function {
if (!eventType) {
throw new Error('Query.on failed: Function called with 0 arguments. Expects at least 2.');
}
@ -659,36 +656,37 @@ export default class Reference extends ReferenceBase {
ref: this,
path: this.path,
key: this._getRefKey(),
appName: this._database._appName,
appName: this._database.app.name,
eventRegistrationKey,
};
this._syncTree.addRegistration(registrationObj, _context ? callback.bind(_context) : callback);
SyncTree.addRegistration({
...registrationObj,
listener: _context ? callback.bind(_context) : callback,
});
if (isFunction(cancelCallbackOrContext)) {
if (cancelCallbackOrContext && isFunction(cancelCallbackOrContext)) {
// cancellations have their own separate registration
// as these are one off events, and they're not guaranteed
// to occur either, only happens on failure to register on native
this._syncTree.addRegistration(
{
ref: this,
once: true,
path: this.path,
key: this._getRefKey(),
appName: this._database._appName,
eventType: `${eventType}$cancelled`,
eventRegistrationKey: registrationCancellationKey,
},
_context ? cancelCallbackOrContext.bind(_context) : cancelCallbackOrContext,
);
SyncTree.addRegistration({
ref: this,
once: true,
path: this.path,
key: this._getRefKey(),
appName: this._database.app.name,
eventType: `${eventType}$cancelled`,
eventRegistrationKey: registrationCancellationKey,
listener: _context ? cancelCallbackOrContext.bind(_context) : cancelCallbackOrContext,
});
}
// initialise the native listener if not already listening
this._database._native.on({
getNativeModule(this._database).on({
eventType,
path: this.path,
key: this._getRefKey(),
appName: this._database._appName,
appName: this._database.app.name,
modifiers: this._query.getModifiers(),
hasCancellationCallback: isFunction(cancelCallbackOrContext),
registration: {
@ -726,7 +724,7 @@ export default class Reference extends ReferenceBase {
if (!arguments.length) {
// Firebase Docs:
// if no eventType or callback is specified, all callbacks for the Reference will be removed.
return this._syncTree.removeListenersForRegistrations(this._syncTree.getRegistrationsByPath(this.path));
return SyncTree.removeListenersForRegistrations(SyncTree.getRegistrationsByPath(this.path));
}
/*
@ -747,29 +745,25 @@ export default class Reference extends ReferenceBase {
// remove the callback.
// Remove only a single registration
if (eventType && originalCallback) {
const registration = this._syncTree.getOneByPathEventListener(this.path, eventType, originalCallback);
const registration = SyncTree.getOneByPathEventListener(this.path, eventType, originalCallback);
if (!registration) return [];
// remove the paired cancellation registration if any exist
this._syncTree.removeListenersForRegistrations([`${registration}$cancelled`]);
SyncTree.removeListenersForRegistrations([`${registration}$cancelled`]);
// remove only the first registration to match firebase web sdk
// call multiple times to remove multiple registrations
return this._syncTree.removeListenerRegistrations(originalCallback, [registration]);
return SyncTree.removeListenerRegistrations(originalCallback, [registration]);
}
// Firebase Docs:
// If a callback is not specified, all callbacks for the specified eventType will be removed.
const registrations = this._syncTree.getRegistrationsByPathEvent(this.path, eventType);
const registrations = SyncTree.getRegistrationsByPathEvent(this.path, eventType);
this._syncTree.removeListenersForRegistrations(
this._syncTree.getRegistrationsByPathEvent(this.path, `${eventType}$cancelled`),
SyncTree.removeListenersForRegistrations(
SyncTree.getRegistrationsByPathEvent(this.path, `${eventType}$cancelled`),
);
return this._syncTree.removeListenersForRegistrations(registrations);
}
get _syncTree(): SyncTree {
return INTERNALS.SyncTree;
return SyncTree.removeListenersForRegistrations(registrations);
}
}

View File

@ -2,24 +2,36 @@
* @flow
* Database Transaction representation wrapper
*/
import { getAppEventName, SharedEventEmitter } from '../../utils/events';
import { getLogger } from '../../utils/log';
import { getNativeModule } from '../../utils/native';
import type Database from './';
let transactionId = 0;
/**
* Uses the push id generator to create a transaction id
* @returns {number}
* @private
*/
const generateTransactionId = (): number => {
return transactionId++;
};
/**
* @class TransactionHandler
*/
export default class TransactionHandler {
_database: Database;
_transactionListener: Function;
_transactions: { [string]: Object }
_transactions: { [number]: Object }
constructor(database: Database) {
this._transactions = {};
this._database = database;
this._transactionListener = this._database.addListener(
this._database._getAppEventName('database_transaction_event'),
this._transactionListener = SharedEventEmitter.addListener(
getAppEventName(this._database, 'database_transaction_event'),
this._handleTransactionEvent.bind(this),
);
}
@ -32,7 +44,7 @@ export default class TransactionHandler {
* @param applyLocally
*/
add(reference: Object, transactionUpdater: Function, onComplete?: Function, applyLocally?: boolean = false) {
const id = this._generateTransactionId();
const id = generateTransactionId();
this._transactions[id] = {
id,
@ -44,22 +56,13 @@ export default class TransactionHandler {
started: true,
};
this._database._native.transactionStart(reference.path, id, applyLocally);
getNativeModule(this._database).transactionStart(reference.path, id, applyLocally);
}
/**
* INTERNALS
*/
/**
* Uses the push id generator to create a transaction id
* @returns {string}
* @private
*/
_generateTransactionId(): string {
return transactionId++;
}
/**
*
* @param event
@ -75,7 +78,7 @@ export default class TransactionHandler {
case 'complete':
return this._handleComplete(event);
default:
this.log.warn(`Unknown transaction event type: '${event.type}'`, event);
getLogger(this._database).warn(`Unknown transaction event type: '${event.type}'`, event);
return undefined;
}
}
@ -101,7 +104,7 @@ export default class TransactionHandler {
abort = true;
}
this._database._native.transactionTryCommit(id, { value: newValue, abort });
getNativeModule(this._database).transactionTryCommit(id, { value: newValue, abort });
}
}
@ -115,7 +118,7 @@ export default class TransactionHandler {
if (transaction && !transaction.completed) {
transaction.completed = true;
try {
transaction.onComplete(new Error(event.error.message, event.error.code), null);
transaction.onComplete(event.error, false, null);
} finally {
setImmediate(() => {
delete this._transactions[event.id];

View File

@ -3,22 +3,26 @@
* Crash Reporting representation wrapper
*/
import ModuleBase from '../../../utils/ModuleBase';
import { getNativeModule } from '../../../utils/native';
import type FirebaseApp from '../../core/firebase-app';
import type App from '../../core/firebase-app';
export const MODULE_NAME = 'RNFirebaseCrashlytics';
export const NAMESPACE = 'crashlytics';
export default class Crashlytics extends ModuleBase {
static _NAMESPACE = 'crashlytics';
static _NATIVE_MODULE = 'RNFirebaseCrashlytics';
constructor(firebaseApp: FirebaseApp, options: Object = {}) {
super(firebaseApp, options);
constructor(app: App) {
super(app, {
moduleName: MODULE_NAME,
namespace: NAMESPACE,
});
}
/**
* Forces a crash. Useful for testing your application is set up correctly.
*/
crash(): void {
this._native.crash();
getNativeModule(this).crash();
}
/**
@ -26,7 +30,7 @@ export default class Crashlytics extends ModuleBase {
* @param {string} message
*/
log(message: string): void {
this._native.log(message);
getNativeModule(this).log(message);
}
/**
@ -35,42 +39,42 @@ export default class Crashlytics extends ModuleBase {
* @param {string} message
*/
recordError(code: number, message: string): void {
this._native.recordError(code, message);
getNativeModule(this).recordError(code, message);
}
/**
* Set a boolean value to show alongside any subsequent crash reports.
*/
setBoolValue(key: string, value: boolean): void {
this._native.setBoolValue(key, value);
getNativeModule(this).setBoolValue(key, value);
}
/**
* Set a float value to show alongside any subsequent crash reports.
*/
setFloatValue(key: string, value: number): void {
this._native.setFloatValue(key, value);
getNativeModule(this).setFloatValue(key, value);
}
/**
* Set an integer value to show alongside any subsequent crash reports.
*/
setIntValue(key: string, value: number): void {
this._native.setIntValue(key, value);
getNativeModule(this).setIntValue(key, value);
}
/**
* Set a string value to show alongside any subsequent crash reports.
*/
setStringValue(key: string, value: string): void {
this._native.setStringValue(key, value);
getNativeModule(this).setStringValue(key, value);
}
/**
* Set the user ID to show alongside any subsequent crash reports.
*/
setUserIdentifier(userId: string): void {
this._native.setUserIdentifier(userId);
getNativeModule(this).setUserIdentifier(userId);
}
}

View File

@ -6,11 +6,10 @@ import DocumentReference from './DocumentReference';
import Query from './Query';
import { firestoreAutoId } from '../../utils';
import type DocumentSnapshot from './DocumentSnapshot';
import type Firestore from './';
import type { FirestoreQueryDirection, FirestoreQueryOperator } from '../../types';
import type Path from './Path';
import type { Observer, QueryListenOptions } from './Query';
import type { Observer, ObserverOnError, ObserverOnNext, QueryListenOptions } from './Query';
import type QuerySnapshot from './QuerySnapshot';
/**
@ -75,9 +74,9 @@ export default class CollectionReference {
}
onSnapshot(
optionsOrObserverOrOnNext: QueryListenOptions | Observer | (DocumentSnapshot) => void,
observerOrOnNextOrOnError?: Observer | (DocumentSnapshot) => void | (Object) => void,
onError?: (Object) => void,
optionsOrObserverOrOnNext: QueryListenOptions | Observer | ObserverOnNext,
observerOrOnNextOrOnError?: Observer | ObserverOnNext | ObserverOnError,
onError?: ObserverOnError,
): () => void {
return this._query.onSnapshot(optionsOrObserverOrOnNext, observerOrOnNextOrOnError, onError);
}

View File

@ -5,7 +5,10 @@
import CollectionReference from './CollectionReference';
import DocumentSnapshot from './DocumentSnapshot';
import { buildNativeMap } from './utils/serialize';
import { getAppEventName, SharedEventEmitter } from '../../utils/events';
import { getLogger } from '../../utils/log';
import { firestoreAutoId, isFunction, isObject, isString } from '../../utils';
import { getNativeModule } from '../../utils/native';
import type Firestore from './';
import type { FirestoreNativeDocumentSnapshot, FirestoreWriteOptions } from '../../types';
@ -65,12 +68,12 @@ export default class DocumentReference {
}
delete(): Promise<void> {
return this._firestore._native
return getNativeModule(this._firestore)
.documentDelete(this.path);
}
get(): Promise<DocumentSnapshot> {
return this._firestore._native
return getNativeModule(this._firestore)
.documentGet(this.path)
.then(result => new DocumentSnapshot(this._firestore, result));
}
@ -80,15 +83,18 @@ export default class DocumentReference {
observerOrOnNextOrOnError?: Observer | ObserverOnNext | ObserverOnError,
onError?: ObserverOnError,
) {
let observer = {};
let observer: Observer;
let docListenOptions = {};
// Called with: onNext, ?onError
if (isFunction(optionsOrObserverOrOnNext)) {
observer.next = optionsOrObserverOrOnNext;
if (observerOrOnNextOrOnError && !isFunction(observerOrOnNextOrOnError)) {
throw new Error('DocumentReference.onSnapshot failed: Second argument must be a valid function.');
}
observer.error = observerOrOnNextOrOnError;
// $FlowBug: Not coping with the overloaded method signature
observer = {
next: optionsOrObserverOrOnNext,
error: observerOrOnNextOrOnError,
};
} else if (optionsOrObserverOrOnNext && isObject(optionsOrObserverOrOnNext)) {
// Called with: Observer
if (optionsOrObserverOrOnNext.next) {
@ -96,7 +102,11 @@ export default class DocumentReference {
if (optionsOrObserverOrOnNext.error && !isFunction(optionsOrObserverOrOnNext.error)) {
throw new Error('DocumentReference.onSnapshot failed: Observer.error must be a valid function.');
}
observer = optionsOrObserverOrOnNext;
// $FlowBug: Not coping with the overloaded method signature
observer = {
next: optionsOrObserverOrOnNext.next,
error: optionsOrObserverOrOnNext.error,
};
} else {
throw new Error('DocumentReference.onSnapshot failed: Observer.next must be a valid function.');
}
@ -104,18 +114,24 @@ export default class DocumentReference {
docListenOptions = optionsOrObserverOrOnNext;
// Called with: Options, onNext, ?onError
if (isFunction(observerOrOnNextOrOnError)) {
observer.next = observerOrOnNextOrOnError;
if (onError && !isFunction(onError)) {
throw new Error('DocumentReference.onSnapshot failed: Third argument must be a valid function.');
}
observer.error = onError;
// $FlowBug: Not coping with the overloaded method signature
observer = {
next: observerOrOnNextOrOnError,
error: onError,
};
// Called with Options, Observer
} else if (observerOrOnNextOrOnError && isObject(observerOrOnNextOrOnError) && observerOrOnNextOrOnError.next) {
if (isFunction(observerOrOnNextOrOnError.next)) {
if (observerOrOnNextOrOnError.error && !isFunction(observerOrOnNextOrOnError.error)) {
throw new Error('DocumentReference.onSnapshot failed: Observer.error must be a valid function.');
}
observer = observerOrOnNextOrOnError;
observer = {
next: observerOrOnNextOrOnError.next,
error: observerOrOnNextOrOnError.error,
};
} else {
throw new Error('DocumentReference.onSnapshot failed: Observer.next must be a valid function.');
}
@ -136,21 +152,21 @@ export default class DocumentReference {
};
// Listen to snapshot events
this._firestore.on(
this._firestore._getAppEventName(`onDocumentSnapshot:${listenerId}`),
SharedEventEmitter.addListener(
getAppEventName(this._firestore, `onDocumentSnapshot:${listenerId}`),
listener,
);
// Listen for snapshot error events
if (observer.error) {
this._firestore.on(
this._firestore._getAppEventName(`onDocumentSnapshotError:${listenerId}`),
SharedEventEmitter.addListener(
getAppEventName(this._firestore, `onDocumentSnapshotError:${listenerId}`),
observer.error,
);
}
// Add the native listener
this._firestore._native
getNativeModule(this._firestore)
.documentOnSnapshot(this.path, listenerId, docListenOptions);
// Return an unsubscribe method
@ -159,7 +175,7 @@ export default class DocumentReference {
set(data: Object, writeOptions?: FirestoreWriteOptions): Promise<void> {
const nativeData = buildNativeMap(data);
return this._firestore._native
return getNativeModule(this._firestore)
.documentSet(this.path, nativeData, writeOptions);
}
@ -183,7 +199,7 @@ export default class DocumentReference {
}
}
const nativeData = buildNativeMap(data);
return this._firestore._native
return getNativeModule(this._firestore)
.documentUpdate(this.path, nativeData);
}
@ -196,10 +212,10 @@ export default class DocumentReference {
* @param listener
*/
_offDocumentSnapshot(listenerId: string, listener: Function) {
this._firestore.log.info('Removing onDocumentSnapshot listener');
this._firestore.removeListener(this._firestore._getAppEventName(`onDocumentSnapshot:${listenerId}`), listener);
this._firestore.removeListener(this._firestore._getAppEventName(`onDocumentSnapshotError:${listenerId}`), listener);
this._firestore._native
getLogger(this._firestore).info('Removing onDocumentSnapshot listener');
SharedEventEmitter.removeListener(getAppEventName(this._firestore, `onDocumentSnapshot:${listenerId}`), listener);
SharedEventEmitter.removeListener(getAppEventName(this._firestore, `onDocumentSnapshotError:${listenerId}`), listener);
getNativeModule(this._firestore)
.documentOffSnapshot(this.path, listenerId);
}
}

View File

@ -5,7 +5,10 @@
import DocumentSnapshot from './DocumentSnapshot';
import QuerySnapshot from './QuerySnapshot';
import { buildNativeArray, buildTypeMap } from './utils/serialize';
import { getAppEventName, SharedEventEmitter } from '../../utils/events';
import { getLogger } from '../../utils/log';
import { firestoreAutoId, isFunction, isObject } from '../../utils';
import { getNativeModule } from '../../utils/native';
import type Firestore from './';
import type { FirestoreQueryDirection, FirestoreQueryOperator } from '../../types';
@ -51,9 +54,12 @@ export type QueryListenOptions = {
includeQueryMetadataChanges: boolean,
}
export type ObserverOnError = (Object) => void;
export type ObserverOnNext = (QuerySnapshot) => void;
export type Observer = {
next: (DocumentSnapshot) => void,
error?: (Object) => void,
error?: ObserverOnError,
next: ObserverOnNext,
}
/**
@ -117,7 +123,7 @@ export default class Query {
}
get(): Promise<QuerySnapshot> {
return this._firestore._native
return getNativeModule(this._firestore)
.collectionGet(
this._referencePath.relativeName,
this._fieldFilters,
@ -145,19 +151,22 @@ export default class Query {
}
onSnapshot(
optionsOrObserverOrOnNext: QueryListenOptions | Observer | (DocumentSnapshot) => void,
observerOrOnNextOrOnError?: Observer | (DocumentSnapshot) => void | (Object) => void,
onError?: (Object) => void,
optionsOrObserverOrOnNext: QueryListenOptions | Observer | ObserverOnNext,
observerOrOnNextOrOnError?: Observer | ObserverOnNext | ObserverOnError,
onError?: ObserverOnError,
) {
let observer = {};
let observer: Observer;
let queryListenOptions = {};
// Called with: onNext, ?onError
if (isFunction(optionsOrObserverOrOnNext)) {
observer.next = optionsOrObserverOrOnNext;
if (observerOrOnNextOrOnError && !isFunction(observerOrOnNextOrOnError)) {
throw new Error('Query.onSnapshot failed: Second argument must be a valid function.');
}
observer.error = observerOrOnNextOrOnError;
// $FlowBug: Not coping with the overloaded method signature
observer = {
next: optionsOrObserverOrOnNext,
error: observerOrOnNextOrOnError,
};
} else if (optionsOrObserverOrOnNext && isObject(optionsOrObserverOrOnNext)) {
// Called with: Observer
if (optionsOrObserverOrOnNext.next) {
@ -165,7 +174,11 @@ export default class Query {
if (optionsOrObserverOrOnNext.error && !isFunction(optionsOrObserverOrOnNext.error)) {
throw new Error('Query.onSnapshot failed: Observer.error must be a valid function.');
}
observer = optionsOrObserverOrOnNext;
// $FlowBug: Not coping with the overloaded method signature
observer = {
next: optionsOrObserverOrOnNext.next,
error: optionsOrObserverOrOnNext.error,
};
} else {
throw new Error('Query.onSnapshot failed: Observer.next must be a valid function.');
}
@ -173,18 +186,24 @@ export default class Query {
queryListenOptions = optionsOrObserverOrOnNext;
// Called with: Options, onNext, ?onError
if (isFunction(observerOrOnNextOrOnError)) {
observer.next = observerOrOnNextOrOnError;
if (onError && !isFunction(onError)) {
throw new Error('Query.onSnapshot failed: Third argument must be a valid function.');
}
observer.error = onError;
// $FlowBug: Not coping with the overloaded method signature
observer = {
next: observerOrOnNextOrOnError,
error: onError,
};
// Called with Options, Observer
} else if (observerOrOnNextOrOnError && isObject(observerOrOnNextOrOnError) && observerOrOnNextOrOnError.next) {
if (isFunction(observerOrOnNextOrOnError.next)) {
if (observerOrOnNextOrOnError.error && !isFunction(observerOrOnNextOrOnError.error)) {
throw new Error('Query.onSnapshot failed: Observer.error must be a valid function.');
}
observer = observerOrOnNextOrOnError;
observer = {
next: observerOrOnNextOrOnError.next,
error: observerOrOnNextOrOnError.error,
};
} else {
throw new Error('Query.onSnapshot failed: Observer.next must be a valid function.');
}
@ -197,7 +216,6 @@ export default class Query {
} else {
throw new Error('Query.onSnapshot failed: Called with invalid arguments.');
}
const listenerId = firestoreAutoId();
const listener = (nativeQuerySnapshot) => {
@ -206,21 +224,21 @@ export default class Query {
};
// Listen to snapshot events
this._firestore.on(
this._firestore._getAppEventName(`onQuerySnapshot:${listenerId}`),
SharedEventEmitter.addListener(
getAppEventName(this._firestore, `onQuerySnapshot:${listenerId}`),
listener,
);
// Listen for snapshot error events
if (observer.error) {
this._firestore.on(
this._firestore._getAppEventName(`onQuerySnapshotError:${listenerId}`),
SharedEventEmitter.addListener(
getAppEventName(this._firestore, `onQuerySnapshotError:${listenerId}`),
observer.error,
);
}
// Add the native listener
this._firestore._native
getNativeModule(this._firestore)
.collectionOnSnapshot(
this._referencePath.relativeName,
this._fieldFilters,
@ -334,10 +352,10 @@ export default class Query {
* @param listener
*/
_offCollectionSnapshot(listenerId: string, listener: Function) {
this._firestore.log.info('Removing onQuerySnapshot listener');
this._firestore.removeListener(this._firestore._getAppEventName(`onQuerySnapshot:${listenerId}`), listener);
this._firestore.removeListener(this._firestore._getAppEventName(`onQuerySnapshotError:${listenerId}`), listener);
this._firestore._native
getLogger(this._firestore).info('Removing onQuerySnapshot listener');
SharedEventEmitter.removeListener(getAppEventName(this._firestore, `onQuerySnapshot:${listenerId}`), listener);
SharedEventEmitter.removeListener(getAppEventName(this._firestore, `onQuerySnapshotError:${listenerId}`), listener);
getNativeModule(this._firestore)
.collectionOffSnapshot(
this._referencePath.relativeName,
this._fieldFilters,

View File

@ -4,6 +4,7 @@
*/
import { buildNativeMap } from './utils/serialize';
import { isObject, isString } from '../../utils';
import { getNativeModule } from '../../utils/native';
import type DocumentReference from './DocumentReference';
import type Firestore from './';
@ -29,8 +30,7 @@ export default class WriteBatch {
}
commit(): Promise<void> {
return this._firestore._native
.documentBatch(this._writes);
return getNativeModule(this._firestore).documentBatch(this._writes);
}
delete(docRef: DocumentReference): WriteBatch {

View File

@ -4,7 +4,8 @@
*/
import { NativeModules } from 'react-native';
import ModuleBase from './../../utils/ModuleBase';
import { getAppEventName, SharedEventEmitter } from '../../utils/events';
import ModuleBase from '../../utils/ModuleBase';
import CollectionReference from './CollectionReference';
import DocumentReference from './DocumentReference';
import FieldValue from './FieldValue';
@ -14,7 +15,7 @@ import WriteBatch from './WriteBatch';
import INTERNALS from '../../utils/internals';
import type DocumentSnapshot from './DocumentSnapshot';
import type FirebaseApp from '../core/firebase-app';
import type App from '../core/firebase-app';
import type QuerySnapshot from './QuerySnapshot';
type CollectionSyncEvent = {
@ -33,64 +34,41 @@ type DocumentSyncEvent = {
path: string,
}
class FirestoreInternalModule extends ModuleBase {
static _NAMESPACE = 'firestore';
static _NATIVE_MODULE = 'RNFirebaseFirestore';
const NATIVE_EVENTS = [
'firestore_collection_sync_event',
'firestore_document_sync_event',
];
_referencePath: Path;
constructor(firebaseApp: FirebaseApp, options: Object = {}) {
super(firebaseApp, options, true);
this._referencePath = new Path([]);
super.addListener(
// sub to internal native event - this fans out to
// public event name: onCollectionSnapshot
super._getAppEventName('firestore_collection_sync_event'),
this._onCollectionSyncEvent.bind(this),
);
super.addListener(
// sub to internal native event - this fans out to
// public event name: onDocumentSnapshot
super._getAppEventName('firestore_document_sync_event'),
this._onDocumentSyncEvent.bind(this),
);
}
/**
* Internal collection sync listener
* @param event
* @private
*/
_onCollectionSyncEvent(event: CollectionSyncEvent) {
if (event.error) {
this.emit(super._getAppEventName(`onQuerySnapshotError:${event.listenerId}`), event.error);
} else {
this.emit(super._getAppEventName(`onQuerySnapshot:${event.listenerId}`), event.querySnapshot);
}
}
/**
* Internal document sync listener
* @param event
* @private
*/
_onDocumentSyncEvent(event: DocumentSyncEvent) {
if (event.error) {
this.emit(super._getAppEventName(`onDocumentSnapshotError:${event.listenerId}`), event.error);
} else {
this.emit(super._getAppEventName(`onDocumentSnapshot:${event.listenerId}`), event.documentSnapshot);
}
}
}
export const MODULE_NAME = 'RNFirebaseFirestore';
export const NAMESPACE = 'firestore';
/**
* @class Firestore
*/
export default class Firestore extends FirestoreInternalModule {
constructor(firebaseApp: FirebaseApp, options: Object = {}) {
super(firebaseApp, options);
export default class Firestore extends ModuleBase {
_referencePath: Path;
constructor(app: App) {
super(app, {
events: NATIVE_EVENTS,
moduleName: MODULE_NAME,
namespace: NAMESPACE,
});
this._referencePath = new Path([]);
SharedEventEmitter.addListener(
// sub to internal native event - this fans out to
// public event name: onCollectionSnapshot
getAppEventName(this, 'firestore_collection_sync_event'),
this._onCollectionSyncEvent.bind(this),
);
SharedEventEmitter.addListener(
// sub to internal native event - this fans out to
// public event name: onDocumentSnapshot
getAppEventName(this, 'firestore_document_sync_event'),
this._onDocumentSyncEvent.bind(this),
);
}
batch(): WriteBatch {
@ -134,20 +112,46 @@ export default class Firestore extends FirestoreInternalModule {
}
setLogLevel(): void {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD(Firestore, 'setLogLevel'));
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD('firestore', 'setLogLevel'));
}
settings(): void {
throw new Error('firebase.firestore().settings() coming soon');
}
/**
* Internal collection sync listener
* @param event
* @private
*/
_onCollectionSyncEvent(event: CollectionSyncEvent) {
if (event.error) {
SharedEventEmitter.emit(getAppEventName(this, `onQuerySnapshotError:${event.listenerId}`), event.error);
} else {
SharedEventEmitter.emit(getAppEventName(this, `onQuerySnapshot:${event.listenerId}`), event.querySnapshot);
}
}
/**
* Internal document sync listener
* @param event
* @private
*/
_onDocumentSyncEvent(event: DocumentSyncEvent) {
if (event.error) {
SharedEventEmitter.emit(getAppEventName(this, `onDocumentSnapshotError:${event.listenerId}`), event.error);
} else {
SharedEventEmitter.emit(getAppEventName(this, `onDocumentSnapshot:${event.listenerId}`), event.documentSnapshot);
}
}
}
export const statics = {
FieldValue,
GeoPoint,
enableLogging(bool) {
if (NativeModules[Firestore._NATIVE_MODULE]) {
NativeModules[Firestore._NATIVE_MODULE].enableLogging(bool);
enableLogging(enabled: boolean) {
if (NativeModules[MODULE_NAME]) {
NativeModules[MODULE_NAME].enableLogging(enabled);
}
},
};

View File

@ -2,15 +2,24 @@
* @flow
* Dynamic Links representation wrapper
*/
import ModuleBase from './../../utils/ModuleBase';
import { areObjectKeysContainedInOther, isObject, isString } from './../../utils';
import { SharedEventEmitter } from '../../utils/events';
import ModuleBase from '../../utils/ModuleBase';
import { areObjectKeysContainedInOther, isObject, isString } from '../../utils';
import { getNativeModule } from '../../utils/native';
import type FirebaseApp from '../core/firebase-app';
import type App from '../core/firebase-app';
const EVENT_TYPE = {
Link: 'dynamic_link_received',
};
const NATIVE_EVENTS = [
EVENT_TYPE.Link,
];
export const MODULE_NAME = 'RNFirebaseLinks';
export const NAMESPACE = 'links';
function validateParameters(parameters: Object): void {
const suportedParametersObject = {
dynamicLinkDomain: 'string',
@ -62,11 +71,12 @@ function checkForMandatoryParameters(parameters: Object): void {
* @class Links
*/
export default class Links extends ModuleBase {
static _NAMESPACE = 'links';
static _NATIVE_MODULE = 'RNFirebaseLinks';
constructor(firebaseApp: FirebaseApp, options: Object = {}) {
super(firebaseApp, options, true);
constructor(app: App) {
super(app, {
events: NATIVE_EVENTS,
moduleName: MODULE_NAME,
namespace: NAMESPACE,
});
}
get EVENT_TYPE(): Object {
@ -78,7 +88,7 @@ export default class Links extends ModuleBase {
* @returns {Promise.<String>}
*/
getInitialLink(): Promise<string> {
return this._native.getInitialLink();
return getNativeModule(this).getInitialLink();
}
/**
@ -87,7 +97,7 @@ export default class Links extends ModuleBase {
* @returns {Function}
*/
onLink(listener: Function): () => any {
const rnListener = this._eventEmitter.addListener(EVENT_TYPE.Link, listener);
const rnListener = SharedEventEmitter.addListener(EVENT_TYPE.Link, listener);
return () => rnListener.remove();
}
@ -100,7 +110,7 @@ export default class Links extends ModuleBase {
try {
checkForMandatoryParameters(parameters);
validateParameters(parameters);
return this._native.createDynamicLink(parameters);
return getNativeModule(this).createDynamicLink(parameters);
} catch (error) {
return Promise.reject(error);
}
@ -115,7 +125,7 @@ export default class Links extends ModuleBase {
try {
checkForMandatoryParameters(parameters);
validateParameters(parameters);
return this._native.createShortDynamicLink(parameters);
return getNativeModule(this).createShortDynamicLink(parameters);
} catch (error) {
return Promise.reject(error);
}

View File

@ -3,10 +3,12 @@
* Messaging representation wrapper
*/
import { Platform, NativeModules } from 'react-native';
import ModuleBase from './../../utils/ModuleBase';
import { SharedEventEmitter } from '../../utils/events';
import ModuleBase from '../../utils/ModuleBase';
import RemoteMessage from './RemoteMessage';
import { getNativeModule } from '../../utils/native';
import type FirebaseApp from '../core/firebase-app';
import type App from '../core/firebase-app';
const EVENT_TYPE = {
RefreshToken: 'messaging_token_refreshed',
@ -31,6 +33,11 @@ const WILL_PRESENT_RESULT = {
None: 'UNNotificationPresentationOptionNone',
};
const NATIVE_EVENTS = [
EVENT_TYPE.RefreshToken,
EVENT_TYPE.Notification,
];
const FirebaseMessaging = NativeModules.RNFirebaseMessaging;
/**
@ -73,16 +80,19 @@ function finish(data) {
}
}
export const MODULE_NAME = 'RNFirebaseMessaging';
export const NAMESPACE = 'messaging';
/**
* @class Messaging
*/
export default class Messaging extends ModuleBase {
static _NAMESPACE = 'messaging';
static _NATIVE_MODULE = 'RNFirebaseMessaging';
constructor(firebaseApp: FirebaseApp, options: Object = {}) {
super(firebaseApp, options, true);
constructor(app: App) {
super(app, {
events: NATIVE_EVENTS,
moduleName: MODULE_NAME,
namespace: NAMESPACE,
});
}
get EVENT_TYPE(): Object {
@ -106,7 +116,7 @@ export default class Messaging extends ModuleBase {
* @returns {*}
*/
getInitialNotification(): Promise<Object> {
return this._native.getInitialNotification();
return getNativeModule(this).getInitialNotification();
}
/**
@ -114,7 +124,7 @@ export default class Messaging extends ModuleBase {
* @returns {*|Promise.<String>}
*/
getToken(): Promise<string> {
return this._native.getToken();
return getNativeModule(this).getToken();
}
/**
@ -122,7 +132,7 @@ export default class Messaging extends ModuleBase {
* @returns {*|Promise.<*>}
*/
deleteInstanceId(): Promise<void> {
return this._native.deleteInstanceId();
return getNativeModule(this).deleteInstanceId();
}
/**
@ -134,7 +144,7 @@ export default class Messaging extends ModuleBase {
const _notification = Object.assign({}, notification);
_notification.id = _notification.id || new Date().getTime().toString();
_notification.local_notification = true;
return this._native.createLocalNotification(_notification);
return getNativeModule(this).createLocalNotification(_notification);
}
/**
@ -146,7 +156,7 @@ export default class Messaging extends ModuleBase {
const _notification = Object.assign({}, notification);
if (!notification.id) return Promise.reject(new Error('An id is required to schedule a local notification.'));
_notification.local_notification = true;
return this._native.scheduleLocalNotification(_notification);
return getNativeModule(this).scheduleLocalNotification(_notification);
}
/**
@ -154,7 +164,7 @@ export default class Messaging extends ModuleBase {
* @returns {Promise.<Array>}
*/
getScheduledLocalNotifications(): Promise<Object[]> {
return this._native.getScheduledLocalNotifications();
return getNativeModule(this).getScheduledLocalNotifications();
}
/**
@ -165,8 +175,8 @@ export default class Messaging extends ModuleBase {
*/
cancelLocalNotification(id: string): Promise<void> {
if (!id) return Promise.reject(new Error('Missing notification id'));
if (id === '*') return this._native.cancelAllLocalNotifications();
return this._native.cancelLocalNotification(id);
if (id === '*') return getNativeModule(this).cancelAllLocalNotifications();
return getNativeModule(this).cancelLocalNotification(id);
}
/**
@ -177,8 +187,8 @@ export default class Messaging extends ModuleBase {
*/
removeDeliveredNotification(id: string): Promise<void> {
if (!id) return Promise.reject(new Error('Missing notification id'));
if (id === '*') return this._native.removeAllDeliveredNotifications();
return this._native.removeDeliveredNotification(id);
if (id === '*') return getNativeModule(this).removeAllDeliveredNotifications();
return getNativeModule(this).removeDeliveredNotification(id);
}
/**
@ -187,7 +197,7 @@ export default class Messaging extends ModuleBase {
* @returns {*|Promise.<*>}
*/
requestPermissions(): Promise<void> {
return this._native.requestPermissions();
return getNativeModule(this).requestPermissions();
}
@ -196,7 +206,7 @@ export default class Messaging extends ModuleBase {
* @param n
*/
setBadgeNumber(n: number): void {
this._native.setBadgeNumber(n);
getNativeModule(this).setBadgeNumber(n);
}
/**
@ -204,7 +214,7 @@ export default class Messaging extends ModuleBase {
* @returns {Promise.<Number>}
*/
getBadgeNumber(): Promise<number> {
return this._native.getBadgeNumber();
return getNativeModule(this).getBadgeNumber();
}
/**
@ -213,7 +223,7 @@ export default class Messaging extends ModuleBase {
* @returns {*}
*/
onMessage(listener: (Object) => any): () => any {
const rnListener = this._eventEmitter.addListener(
const rnListener = SharedEventEmitter.addListener(
EVENT_TYPE.Notification,
async (event) => {
const data = {
@ -236,7 +246,7 @@ export default class Messaging extends ModuleBase {
* @returns {*}
*/
onTokenRefresh(listener: (string) => any): () => any {
const rnListener = this._eventEmitter.addListener(EVENT_TYPE.RefreshToken, listener);
const rnListener = SharedEventEmitter.addListener(EVENT_TYPE.RefreshToken, listener);
return () => rnListener.remove();
}
@ -245,7 +255,7 @@ export default class Messaging extends ModuleBase {
* @param topic
*/
subscribeToTopic(topic: string): void {
this._native.subscribeToTopic(topic);
getNativeModule(this).subscribeToTopic(topic);
}
/**
@ -253,7 +263,7 @@ export default class Messaging extends ModuleBase {
* @param topic
*/
unsubscribeFromTopic(topic: string): void {
this._native.unsubscribeFromTopic(topic);
getNativeModule(this).unsubscribeFromTopic(topic);
}
/**
@ -265,7 +275,7 @@ export default class Messaging extends ModuleBase {
throw new Error('messaging().send requires an instance of RemoteMessage as the first argument.');
}
return this._native.send(remoteMessage.toJSON());
return getNativeModule(this).send(remoteMessage.toJSON());
}
}

View File

@ -2,26 +2,27 @@
* @flow
* Trace representation wrapper
*/
import { getNativeModule } from '../../utils/native';
import type PerformanceMonitoring from './';
export default class Trace {
identifier: string;
perf: PerformanceMonitoring;
_perf: PerformanceMonitoring;
constructor(perf: PerformanceMonitoring, identifier: string) {
this.perf = perf;
this._perf = perf;
this.identifier = identifier;
}
start(): void {
this.perf._native.start(this.identifier);
getNativeModule(this._perf).start(this.identifier);
}
stop(): void {
this.perf._native.stop(this.identifier);
getNativeModule(this._perf).stop(this.identifier);
}
incrementCounter(event: string): void {
this.perf._native.incrementCounter(this.identifier, event);
getNativeModule(this._perf).incrementCounter(this.identifier, event);
}
}

View File

@ -4,15 +4,19 @@
*/
import Trace from './Trace';
import ModuleBase from '../../utils/ModuleBase';
import { getNativeModule } from '../../utils/native';
import type FirebaseApp from '../core/firebase-app';
import type App from '../core/firebase-app';
export const MODULE_NAME = 'RNFirebasePerformance';
export const NAMESPACE = 'perf';
export default class PerformanceMonitoring extends ModuleBase {
static _NAMESPACE = 'perf';
static _NATIVE_MODULE = 'RNFirebasePerformance';
constructor(firebaseApp: FirebaseApp, options: Object = {}) {
super(firebaseApp, options);
constructor(app: App) {
super(app, {
moduleName: MODULE_NAME,
namespace: NAMESPACE,
});
}
/**
@ -21,7 +25,7 @@ export default class PerformanceMonitoring extends ModuleBase {
* @returns {*}
*/
setPerformanceCollectionEnabled(enabled: boolean): void {
this._native.setPerformanceCollectionEnabled(enabled);
getNativeModule(this).setPerformanceCollectionEnabled(enabled);
}
/**

View File

@ -5,31 +5,43 @@
import { NativeModules } from 'react-native';
import StorageRef from './reference';
import ModuleBase from './../../utils/ModuleBase';
import { getAppEventName, SharedEventEmitter } from '../../utils/events';
import { getLogger } from '../../utils/log';
import ModuleBase from '../../utils/ModuleBase';
import { getNativeModule } from '../../utils/native';
import type FirebaseApp from '../core/firebase-app';
import type App from '../core/firebase-app';
const FirebaseStorage = NativeModules.RNFirebaseStorage;
export default class Storage extends ModuleBase {
static _NAMESPACE = 'storage';
static _NATIVE_MODULE = 'RNFirebaseStorage';
const NATIVE_EVENTS = [
'storage_event',
'storage_error',
];
export const MODULE_NAME = 'RNFirebaseStorage';
export const NAMESPACE = 'storage';
export default class Storage extends ModuleBase {
/**
*
* @param firebaseApp
* @param app
* @param options
*/
constructor(firebaseApp: FirebaseApp, options: Object = {}) {
super(firebaseApp, options, true);
constructor(app: App) {
super(app, {
events: NATIVE_EVENTS,
moduleName: MODULE_NAME,
namespace: NAMESPACE,
});
this.addListener(
this._getAppEventName('storage_event'),
SharedEventEmitter.addListener(
getAppEventName(this, 'storage_event'),
this._handleStorageEvent.bind(this),
);
this.addListener(
this._getAppEventName('storage_error'),
SharedEventEmitter.addListener(
getAppEventName(this, 'storage_error'),
this._handleStorageEvent.bind(this),
);
}
@ -50,7 +62,7 @@ export default class Storage extends ModuleBase {
* @param url
* @returns {StorageReference}
*/
refFromURL(url: string): Promise<StorageRef> {
refFromURL(url: string): StorageRef {
// TODO don't think this is correct?
return new StorageRef(this, `url::${url}`);
}
@ -61,7 +73,7 @@ export default class Storage extends ModuleBase {
* @param time The new maximum operation retry time in milliseconds.
*/
setMaxOperationRetryTime(time: number): void {
this._native.setMaxOperationRetryTime(time);
getNativeModule(this).setMaxOperationRetryTime(time);
}
/**
@ -70,7 +82,7 @@ export default class Storage extends ModuleBase {
* @param time The new maximum upload retry time in milliseconds.
*/
setMaxUploadRetryTime(time: number): void {
this._native.setMaxUploadRetryTime(time);
getNativeModule(this).setMaxUploadRetryTime(time);
}
/**
@ -79,38 +91,38 @@ export default class Storage extends ModuleBase {
* @param time The new maximum download retry time in milliseconds.
*/
setMaxDownloadRetryTime(time: number): void {
this._native.setMaxDownloadRetryTime(time);
getNativeModule(this).setMaxDownloadRetryTime(time);
}
/**
* INTERNALS
*/
_getSubEventName(path: string, eventName: string) {
return this._getAppEventName(`${path}-${eventName}`);
return getAppEventName(this, `${path}-${eventName}`);
}
_handleStorageEvent(event: Object) {
const { path, eventName } = event;
const body = event.body || {};
this.log.debug('_handleStorageEvent: ', path, eventName, body);
this.emit(this._getSubEventName(path, eventName), body);
getLogger(this).debug('_handleStorageEvent: ', path, eventName, body);
SharedEventEmitter.emit(this._getSubEventName(path, eventName), body);
}
_handleStorageError(err: Object) {
const { path, eventName } = err;
const body = err.body || {};
this.log.debug('_handleStorageError ->', err);
this.emit(this._getSubEventName(path, eventName), body);
getLogger(this).debug('_handleStorageError ->', err);
SharedEventEmitter.emit(this._getSubEventName(path, eventName), body);
}
_addListener(path: string, eventName: string, cb: (evt: Object) => Object): void {
this.on(this._getSubEventName(path, eventName), cb);
SharedEventEmitter.addListener(this._getSubEventName(path, eventName), cb);
}
_removeListener(path: string, eventName: string, origCB: (evt: Object) => Object): void {
this.removeListener(this._getSubEventName(path, eventName), origCB);
SharedEventEmitter.removeListener(this._getSubEventName(path, eventName), origCB);
}
}

View File

@ -4,6 +4,7 @@
*/
import ReferenceBase from '../../utils/ReferenceBase';
import StorageTask, { UPLOAD_TASK, DOWNLOAD_TASK } from './task';
import { getNativeModule } from '../../utils/native';
import type Storage from './';
@ -14,7 +15,7 @@ export default class StorageReference extends ReferenceBase {
_storage: Storage;
constructor(storage: Storage, path: string) {
super(path, storage);
super(path);
this._storage = storage;
}
@ -40,7 +41,7 @@ export default class StorageReference extends ReferenceBase {
* @returns {Promise.<T>|*}
*/
delete(): Promise<void> {
return this._storage._native.delete(this.path);
return getNativeModule(this._storage).delete(this.path);
}
/**
@ -48,7 +49,7 @@ export default class StorageReference extends ReferenceBase {
* @returns {Promise.<T>|*}
*/
getDownloadURL(): Promise<string> {
return this._storage._native.getDownloadURL(this.path);
return getNativeModule(this._storage).getDownloadURL(this.path);
}
/**
@ -56,7 +57,7 @@ export default class StorageReference extends ReferenceBase {
* @returns {Promise.<T>|*}
*/
getMetadata(): Promise<Object> {
return this._storage._native.getMetadata(this.path);
return getNativeModule(this._storage).getMetadata(this.path);
}
/**
@ -65,7 +66,7 @@ export default class StorageReference extends ReferenceBase {
* @returns {Promise.<T>|*}
*/
updateMetadata(metadata: Object = {}): Promise<Object> {
return this._storage._native.updateMetadata(this.path, metadata);
return getNativeModule(this._storage).updateMetadata(this.path, metadata);
}
/**
@ -74,7 +75,7 @@ export default class StorageReference extends ReferenceBase {
* @return {Promise}
*/
downloadFile(filePath: string): Promise<Object> {
return new StorageTask(DOWNLOAD_TASK, this._storage._native.downloadFile(this.path, filePath), this);
return new StorageTask(DOWNLOAD_TASK, getNativeModule(this._storage).downloadFile(this.path, filePath), this);
}
/**
@ -93,6 +94,6 @@ export default class StorageReference extends ReferenceBase {
*/
putFile(filePath: Object, metadata: Object = {}): Promise<Object> {
const _filePath = filePath.replace('file://', '');
return new StorageTask(UPLOAD_TASK, this._module._native.putFile(this.path, _filePath, metadata), this);
return new StorageTask(UPLOAD_TASK, getNativeModule(this._storage).putFile(this.path, _filePath, metadata), this);
}
}

View File

@ -2,7 +2,6 @@
import { NativeModules } from 'react-native';
// import { version as ReactVersion } from 'react';
// import ReactNativeVersion from 'react-native/Libraries/Core/ReactNativeVersion';
import INTERNALS from '../../utils/internals';
import { isIOS } from '../../utils';
import ModuleBase from '../../utils/ModuleBase';
@ -18,34 +17,31 @@ type GoogleApiAvailabilityType = {
error?: string
}
export default class RNFirebaseUtils extends ModuleBase {
static _NAMESPACE = 'utils';
static _NATIVE_DISABLED = true;
static _NATIVE_MODULE = 'RNFirebaseUtils';
export const MODULE_NAME = 'RNFirebaseUtils';
export const NAMESPACE = 'utils';
export default class RNFirebaseUtils extends ModuleBase {
/**
*
*/
checkPlayServicesAvailability() {
if (isIOS) return null;
if (isIOS) return;
const code = this.playServicesAvailability.code;
const { status } = this.playServicesAvailability;
if (!this.playServicesAvailability.isAvailable) {
if (INTERNALS.OPTIONS.promptOnMissingPlayServices && this.playServicesAvailability.isUserResolvableError) {
this.promptForPlayServices();
} else {
const error = INTERNALS.STRINGS.ERROR_PLAY_SERVICES(code);
const error = INTERNALS.STRINGS.ERROR_PLAY_SERVICES(status);
if (INTERNALS.OPTIONS.errorOnMissingPlayServices) {
if (code === 2) console.warn(error); // only warn if it exists but may need an update
if (status === 2) console.warn(error); // only warn if it exists but may need an update
else throw new Error(error);
} else {
console.warn(error);
}
}
}
return null;
}
promptForPlayServices() {
@ -63,41 +59,13 @@ export default class RNFirebaseUtils extends ModuleBase {
return FirebaseCoreModule.makePlayServicesAvailable();
}
get sharedEventEmitter(): Object {
return INTERNALS.SharedEventEmitter;
}
/**
* Set the global logging level for all logs.
*
* @param booleanOrDebugString
* @param logLevel
*/
set logLevel(booleanOrDebugString) {
INTERNALS.OPTIONS.logLevel = booleanOrDebugString;
}
/**
* Returns an array of all current database registrations id strings
*/
get databaseRegistrations(): Array<string> {
return Object.keys(INTERNALS.SyncTree._reverseLookup);
}
/**
* Call with a registration id string to get the details off this reg
*/
get getDatabaseRegistrationDetails(): Function {
return INTERNALS.SyncTree.getRegistration.bind(INTERNALS.SyncTree);
}
/**
* Accepts an array or a single string of registration ids.
* This will remove the refs on both the js and native sides and their listeners.
* @return {function(this:T)}
*/
get removeDatabaseRegistration(): Function {
return INTERNALS.SyncTree.removeListenersForRegistrations.bind(INTERNALS.SyncTree);
set logLevel(logLevel: string) {
INTERNALS.OPTIONS.logLevel = logLevel;
}
/**
@ -130,7 +98,6 @@ export default class RNFirebaseUtils extends ModuleBase {
export const statics = {
DEFAULT_APP_NAME: INTERNALS.STRINGS.DEFAULT_APP_NAME,
// VERSIONS: {
// react: ReactVersion,
// 'react-native': Object.values(ReactNativeVersion.version).slice(0, 3).join('.'),

View File

@ -40,7 +40,18 @@ export type FirebaseError = {
export type FirebaseModule = $Subtype<ModuleBase>;
export type FirebaseModuleName = 'admob' | 'analytics' | 'auth' | 'config' | 'crash'
export type FirebaseModuleConfig = {
events?: string[],
moduleName: FirebaseModuleName,
namespace: FirebaseNamespace,
}
export type FirebaseModuleName = 'RNFirebaseAdmob' | 'RNFirebaseAnalytics' | 'RNFirebaseAuth'
| 'RNFirebaseRemoteConfig' | 'RNFirebaseCrash' | 'RNFirebaseCrashlytics' | 'RNFirebaseDatabase'
| 'RNFirebaseFirestore' | 'RNFirebaseLinks' | 'RNFirebaseMessaging' | 'RNFirebasePerformance'
| 'RNFirebaseStorage' | 'RNFirebaseUtils';
export type FirebaseNamespace = 'admob' | 'analytics' | 'auth' | 'config' | 'crash'
| 'crashlytics' | 'database' | 'firestore' | 'links' | 'messaging' | 'perf' | 'storage'
| 'utils';

View File

@ -1,174 +1,42 @@
/**
* @flow
*/
import { NativeModules } from 'react-native';
import { initialiseLogger } from './log';
import { initialiseNativeModule } from './native';
import Log from './log';
import INTERNALS from './internals';
import FirebaseCore from '../modules/core/firebase';
import { nativeWithApp } from '../utils';
import type FirebaseApp from '../modules/core/firebase-app';
import type { FirebaseModuleName } from '../types';
const logs = {};
// Firebase Native SDKs that support multiple app instances
const MULTI_APP_MODULES = [
'auth',
'database',
'firestore',
'storage',
];
const NATIVE_MODULE_EVENTS = {
Storage: [
'storage_event',
'storage_error',
],
Auth: [
'auth_state_changed',
'phone_auth_state_changed',
],
Database: [
'database_transaction_event',
// 'database_server_offset', // TODO
],
Firestore: [
'firestore_collection_sync_event',
'firestore_document_sync_event',
],
};
const DEFAULTS = {
Database: {
persistence: false,
},
};
import type App from '../modules/core/firebase-app';
import type { FirebaseModuleConfig, FirebaseNamespace } from '../types';
export default class ModuleBase {
_native: Object;
_module: string;
_options: Object;
_appName: string;
_namespace: string;
_firebaseApp: FirebaseApp;
_eventEmitter: Object;
static _NAMESPACE: FirebaseModuleName;
static _NATIVE_MODULE: string;
_app: App;
namespace: FirebaseNamespace;
/**
*
* @param firebaseApp
* @param options
* @param withEventEmitter
* @param app
* @param config
*/
constructor(firebaseApp: FirebaseApp, options: Object, withEventEmitter: boolean = 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[this._module] || {}, options);
constructor(app: App, config: FirebaseModuleConfig) {
if (!config.moduleName) {
throw new Error('Missing module name');
}
if (!config.namespace) {
throw new Error('Missing namespace');
}
const { moduleName } = config;
this._app = app;
this.namespace = config.namespace;
// check if native module exists as all native
// modules are now optionally part of build
const nativeModule = NativeModules[this.constructor._NATIVE_MODULE];
if (!nativeModule && !this.constructor._NATIVE_DISABLED) {
throw new Error(INTERNALS.STRINGS.ERROR_MISSING_MODULE(this.constructor._NAMESPACE, this.constructor._NATIVE_MODULE));
}
// used by the modules that extend ModuleBase
// to access their native module counterpart
if (!MULTI_APP_MODULES.includes(this._module.toLowerCase())) {
this._native = nativeModule;
} else {
this._native = nativeWithApp(this._appName, nativeModule);
}
if (withEventEmitter) {
this._setupEventEmitter(nativeModule, this._module);
}
initialiseNativeModule(this, config);
initialiseLogger(this, `${app.name}:${moduleName.replace('RNFirebase', '')}`);
}
/**
*
* @param nativeModule
* @param moduleName
* @private
*/
_setupEventEmitter(nativeModule: Object, moduleName: string) {
this._eventEmitter = FirebaseCore._getOrSetNativeEmitter(`${this._appName}-${this._module}`, nativeModule);
const events = NATIVE_MODULE_EVENTS[moduleName];
if (events && events.length) {
for (let i = 0, len = events.length; i < len; i++) {
FirebaseCore._subscribeForDistribution(events[i], this._eventEmitter);
}
}
}
/**
*
* @param eventName
* @return {string}
* @private
*/
_getAppEventName(eventName: string) {
return `${this._appName}-${eventName}`;
}
/**
* Returns the FirebaseApp instance for current module
* Returns the App instance for current module
* @return {*}
*/
get app(): FirebaseApp {
return this._firebaseApp;
}
get log(): Log {
if (logs[this._namespace]) return logs[this._namespace];
return logs[this._namespace] = Log.createLogger(`🔥 ${this._namespace.toUpperCase()}`);
}
/*
* Proxy functions to shared event emitter instance
* https://github.com/facebook/react-native/blob/master/Libraries/EventEmitter/EventEmitter.js
*/
get sharedEventEmitter(): Object {
return INTERNALS.SharedEventEmitter;
}
get addListener(): Function {
return INTERNALS.SharedEventEmitter.addListener.bind(INTERNALS.SharedEventEmitter);
}
get once(): Function {
return INTERNALS.SharedEventEmitter.once.bind(INTERNALS.SharedEventEmitter);
}
get on(): Function {
return INTERNALS.SharedEventEmitter.addListener.bind(INTERNALS.SharedEventEmitter);
}
get emit(): Function {
return INTERNALS.SharedEventEmitter.emit.bind(INTERNALS.SharedEventEmitter);
}
get listeners(): Function {
return INTERNALS.SharedEventEmitter.listeners.bind(INTERNALS.SharedEventEmitter);
}
hasListeners(eventType: string): Boolean {
const subscriptions = INTERNALS.SharedEventEmitter._subscriber.getSubscriptionsForType(eventType);
return subscriptions && subscriptions.length;
}
get removeListener(): Function {
return INTERNALS.SharedEventEmitter.removeListener.bind(INTERNALS.SharedEventEmitter);
}
get removeAllListeners(): Function {
return INTERNALS.SharedEventEmitter.removeAllListeners.bind(INTERNALS.SharedEventEmitter);
get app(): App {
return this._app;
}
}

View File

@ -1,17 +1,10 @@
/**
* @flow
*/
import Log from './log';
import type Database from '../modules/database';
import type Storage from '../modules/storage';
export default class ReferenceBase {
_module: Database | Storage;
path: string;
constructor(path: string, module: Database | Storage) {
this._module = module;
constructor(path: string) {
this.path = path || '/';
}
@ -24,8 +17,4 @@ export default class ReferenceBase {
get key(): string | null {
return this.path === '/' ? null : this.path.substring(this.path.lastIndexOf('/') + 1);
}
get log(): Log {
return this._module.log;
}
}

View File

@ -1,20 +1,22 @@
/**
* @flow
*/
import { NativeEventEmitter } from 'react-native';
import { NativeEventEmitter, NativeModules } from 'react-native';
import INTERNALS from './internals';
import { SharedEventEmitter } from './events';
import DatabaseSnapshot from '../modules/database/snapshot';
import DatabaseReference from '../modules/database/reference';
import { isString, nativeToJSError } from '../utils';
type Listener = (DatabaseSnapshot) => any;
type Registration = {
key: string,
path: string,
once?: boolean,
appName: string,
eventType: string,
listener: Function,
listener: Listener,
eventRegistrationKey: string,
ref: DatabaseReference,
}
@ -23,21 +25,21 @@ type Registration = {
* Internally used to manage firebase database realtime event
* subscriptions and keep the listeners in sync in js vs native.
*/
export default class SyncTree {
_databaseNative: Object;
class SyncTree {
_nativeEmitter: NativeEventEmitter;
_reverseLookup: { [string]: Registration };
_tree: { [string]: { [string]: Array }};
_tree: { [string]: { [string]: { [string]: Listener }}};
constructor(databaseNative: Object) {
constructor() {
this._tree = {};
this._reverseLookup = {};
this._databaseNative = databaseNative;
this._nativeEmitter = new NativeEventEmitter(databaseNative);
this._nativeEmitter.addListener(
'database_sync_event',
this._handleSyncEvent.bind(this),
);
if (NativeModules.RNFirebaseDatabase) {
this._nativeEmitter = new NativeEventEmitter(NativeModules.RNFirebaseDatabase);
this._nativeEmitter.addListener(
'database_sync_event',
this._handleSyncEvent.bind(this),
);
}
}
/**
@ -71,13 +73,13 @@ export default class SyncTree {
// notify native that the registration
// no longer exists so it can remove
// the native listeners
return this._databaseNative.off(key, eventRegistrationKey);
return NativeModules.RNFirebaseDatabase.off(key, eventRegistrationKey);
}
const { snapshot, previousChildName } = event.data;
// forward on to users .on(successCallback <-- listener
return INTERNALS.SharedEventEmitter.emit(
return SharedEventEmitter.emit(
eventRegistrationKey,
new DatabaseSnapshot(registration.ref, snapshot),
previousChildName,
@ -104,7 +106,7 @@ export default class SyncTree {
const error = nativeToJSError(code, message, { ref: registration.ref });
// forward on to users .on(successCallback, cancellationCallback <-- listener
INTERNALS.SharedEventEmitter.emit(registrationCancellationKey, error);
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
@ -128,17 +130,17 @@ export default class SyncTree {
* @param registrations
* @return {number}
*/
removeListenersForRegistrations(registrations: string | string[]) {
removeListenersForRegistrations(registrations: string | string[]): number {
if (isString(registrations)) {
this.removeRegistration(registrations);
INTERNALS.SharedEventEmitter.removeAllListeners(registrations);
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]);
INTERNALS.SharedEventEmitter.removeAllListeners(registrations[i]);
SharedEventEmitter.removeAllListeners(registrations[i]);
}
return registrations.length;
@ -151,13 +153,13 @@ export default class SyncTree {
* @param registrations
* @return {Array} array of registrations removed
*/
removeListenerRegistrations(listener, registrations: string[]) {
removeListenerRegistrations(listener: () => any, registrations: string[]) {
if (!Array.isArray(registrations)) return [];
const removed = [];
for (let i = 0, len = registrations.length; i < len; i++) {
const registration = registrations[i];
const subscriptions = INTERNALS.SharedEventEmitter._subscriber.getSubscriptionsForType(registration);
const subscriptions = SharedEventEmitter._subscriber.getSubscriptionsForType(registration);
if (subscriptions) {
for (let j = 0, l = subscriptions.length; j < l; j++) {
const subscription = subscriptions[j];
@ -181,7 +183,7 @@ export default class SyncTree {
* @param path
* @return {Array}
*/
getRegistrationsByPath(path: string): Array {
getRegistrationsByPath(path: string): string[] {
const out = [];
const eventKeys = Object.keys(this._tree[path] || {});
@ -199,7 +201,7 @@ export default class SyncTree {
* @param eventType
* @return {Array}
*/
getRegistrationsByPathEvent(path: string, eventType: string): Array {
getRegistrationsByPathEvent(path: string, eventType: string): string[] {
if (!this._tree[path]) return [];
if (!this._tree[path][eventType]) return [];
@ -214,9 +216,9 @@ export default class SyncTree {
* @param listener
* @return {Array}
*/
getOneByPathEventListener(path: string, eventType: string, listener: Function): Array {
if (!this._tree[path]) return [];
if (!this._tree[path][eventType]) return [];
getOneByPathEventListener(path: string, eventType: string, listener: Function): ?string {
if (!this._tree[path]) return null;
if (!this._tree[path][eventType]) return null;
const registrationsForPathEvent = Object.entries(this._tree[path][eventType]);
@ -236,27 +238,28 @@ export default class SyncTree {
* @param listener
* @return {String}
*/
addRegistration(parameters: Registration, listener: Function): string {
addRegistration(registration: Registration): string {
const {
path,
eventType,
eventRegistrationKey,
eventType,
listener,
once,
} = parameters;
path,
} = registration;
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] = Object.assign({ listener }, parameters);
this._reverseLookup[eventRegistrationKey] = registration;
if (once) {
INTERNALS.SharedEventEmitter.once(
SharedEventEmitter.once(
eventRegistrationKey,
this._onOnceRemoveRegistration(eventRegistrationKey, listener),
);
} else {
INTERNALS.SharedEventEmitter.addListener(eventRegistrationKey, listener);
SharedEventEmitter.addListener(eventRegistrationKey, listener);
}
return eventRegistrationKey;
@ -287,7 +290,7 @@ export default class SyncTree {
// automatically unsubscribed on native when the first event is sent
const registrationObj = this._reverseLookup[registration];
if (registrationObj && !once) {
this._databaseNative.off(registrationObj.key, registration);
NativeModules.RNFirebaseDatabase.off(registrationObj.key, registration);
}
delete this._tree[path][eventType][registration];
@ -305,9 +308,11 @@ export default class SyncTree {
* @private
*/
_onOnceRemoveRegistration(registration, listener) {
return (...args) => {
return (...args: any[]) => {
this.removeRegistration(registration);
listener(...args);
};
}
}
export default new SyncTree();

173
lib/utils/apps.js Normal file
View File

@ -0,0 +1,173 @@
/**
* @flow
*/
import { NativeModules } from 'react-native';
import App from '../modules/core/firebase-app';
import INTERNALS from './internals';
import { isAndroid, isObject, isString } from './';
import type {
FirebaseModule,
FirebaseModuleAndStatics,
FirebaseModuleName,
FirebaseNamespace,
FirebaseOptions,
FirebaseStatics,
} from '../types';
const FirebaseCoreModule = NativeModules.RNFirebase;
const APPS: { [string]: App } = {};
const APP_MODULES: { [App]: { [string]: FirebaseModule }} = {};
const DEFAULT_APP_NAME = '[DEFAULT]';
export default {
DEFAULT_APP_NAME,
app(name?: string): App {
const _name = name ? name.toUpperCase() : DEFAULT_APP_NAME;
const app = APPS[_name];
if (!app) throw new Error(INTERNALS.STRINGS.ERROR_APP_NOT_INIT(_name));
return app;
},
apps(): Array<App> {
// $FlowBug: Object.values always returns mixed type: https://github.com/facebook/flow/issues/2221
return Object.values(APPS);
},
/**
*
* @param statics
* @param InstanceClass
* @return {function()}
* @private
*/
appModule<M: FirebaseModule>(app: App, namespace: FirebaseNamespace, InstanceClass: Class<M>): () => FirebaseModule {
return (): M => {
if (!APP_MODULES[app]) {
APP_MODULES[app] = {};
}
if (isAndroid && namespace !== 'utils' && !INTERNALS.FLAGS.checkedPlayServices) {
INTERNALS.FLAGS.checkedPlayServices = true;
this.utils().checkPlayServicesAvailability();
}
if (!APP_MODULES[app][namespace]) {
APP_MODULES[app][namespace] = new InstanceClass(app, app.options);
}
return APP_MODULES[app][namespace];
};
},
deleteApp(name: string): Promise<boolean> {
const app = APPS[name];
if (!app) return Promise.resolve(true);
// https://firebase.google.com/docs/reference/js/firebase.app.App#delete
return app.delete().then(() => {
delete APPS[name];
return true;
});
},
/**
* Web SDK initializeApp
*
* @param options
* @param name
* @return {*}
*/
initializeApp(options: FirebaseOptions, name: string): App {
if (name && !isString(name)) {
throw new Error(INTERNALS.STRINGS.ERROR_INIT_STRING_NAME);
}
const _name = (name || DEFAULT_APP_NAME).toUpperCase();
// return an existing app if found
// todo in v4 remove deprecation and throw an error
if (APPS[_name]) {
console.warn(INTERNALS.STRINGS.WARN_INITIALIZE_DEPRECATION);
return 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'));
}
APPS[_name] = new App(_name, options);
return APPS[_name];
},
/**
* Bootstraps all native app instances that were discovered on boot
*/
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;
APPS[app.name] = new App(app.name, options, true);
}
},
/**
*
* @param statics
* @param InstanceClass
* @return {function(App=)}
*/
moduleAndStatics<M: FirebaseModule, S: FirebaseStatics>(namespace: FirebaseNamespace, statics: S, moduleName: FirebaseModuleName): FirebaseModuleAndStatics<M, S> {
const getModule = (app?: App): FirebaseModule => {
let _app = app;
// throw an error if it's not a valid app instance
if (_app && !(_app instanceof App)) 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(DEFAULT_APP_NAME);
if (namespace === 'crashlytics') {
return _app.fabric[namespace]();
}
// $FlowBug: Flow doesn't support indexable signatures on classes: https://github.com/facebook/flow/issues/1323
const module = _app[namespace];
return module();
};
return Object.assign(getModule, statics, {
nativeModuleExists: !!NativeModules[moduleName],
});
},
};

62
lib/utils/events.js Normal file
View File

@ -0,0 +1,62 @@
/**
* @flow
*/
import { NativeEventEmitter, NativeModules } from 'react-native';
import EventEmitter from './emitter/EventEmitter';
import type ModuleBase from './ModuleBase';
import type { FirebaseModuleConfig, FirebaseModuleName } from '../types';
const NATIVE_EMITTERS: { [string]: NativeEventEmitter } = {};
const NATIVE_SUBSCRIPTIONS: { [string]: boolean } = {};
export const SharedEventEmitter = new EventEmitter();
export const getAppEventName = (module: ModuleBase, eventName: string): string => {
return `${module.app.name}-${eventName}`;
};
const getNativeEmitter = (moduleName: FirebaseModuleName, module: ModuleBase): NativeEventEmitter => {
const name = `${module.app.name}-${moduleName}`;
const nativeModule = NativeModules[moduleName];
if (!NATIVE_EMITTERS[name]) {
NATIVE_EMITTERS[name] = new NativeEventEmitter(nativeModule);
}
return NATIVE_EMITTERS[name];
};
/**
* 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 module
* @param eventName
* @private
*/
const subscribeToNativeModuleEvents = (moduleName: FirebaseModuleName, module: ModuleBase, eventName: string): void => {
if (!NATIVE_SUBSCRIPTIONS[eventName]) {
const nativeEmitter = getNativeEmitter(moduleName, module);
nativeEmitter.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_SUBSCRIPTIONS[eventName] = true;
}
};
export const initialiseNativeModuleEventEmitter = (module: ModuleBase, config: FirebaseModuleConfig): void => {
const { events, moduleName } = config;
if (events && events.length) {
for (let i = 0, len = events.length; i < len; i++) {
subscribeToNativeModuleEvents(moduleName, module, events[i]);
}
}
};

View File

@ -340,25 +340,6 @@ export function nativeToJSError(code: string, message: string, additionalProps?:
return error;
}
/**
* Prepends appName arg to all native method calls
* @param appName
* @param NativeModule
*/
export function nativeWithApp(appName: string, NativeModule: Object) {
const native = {};
const methods = Object.keys(NativeModule);
for (let i = 0, len = methods.length; i < len; i++) {
const method = methods[i];
native[method] = (...args) => {
return NativeModule[method](...[appName, ...args]);
};
}
return native;
}
/**
*
* @param object
@ -390,21 +371,21 @@ export function objectToUniqueId(object: Object): string {
* @param optionalCallback
* @return {Promise}
*/
export function promiseOrCallback(promise: Promise<*>, optionalCallback?: Function) {
export function promiseOrCallback(promise: Promise<*>, optionalCallback?: Function): Promise<*> {
if (!isFunction(optionalCallback)) return promise;
return promise.then((result) => {
// some of firebase internal tests & methods only check/return one arg
// see https://github.com/firebase/firebase-js-sdk/blob/master/src/utils/promise.ts#L62
if (optionalCallback.length === 1) {
if (optionalCallback && optionalCallback.length === 1) {
optionalCallback(null);
} else {
} else if (optionalCallback) {
optionalCallback(null, result);
}
return Promise.resolve(result);
}).catch((error) => {
optionalCallback(error);
if (optionalCallback) optionalCallback(error);
return Promise.reject(error);
});
}

View File

@ -1,15 +1,7 @@
/**
* @flow
*/
import { Platform, NativeModules } from 'react-native';
import EventEmitter from './emitter/EventEmitter';
import ModuleBase from './ModuleBase';
import SyncTree from './SyncTree';
import type FirebaseApp from '../modules/core/firebase-app';
const DEFAULT_APP_NAME = Platform.OS === 'ios' ? '__FIRAPP_DEFAULT' : '[DEFAULT]';
import { Platform } from 'react-native';
const NAMESPACE_PODS = {
admob: 'Firebase/AdMob',
@ -29,34 +21,38 @@ const GRADLE_DEPS = {
};
const PLAY_SERVICES_CODES = {
// $FlowBug: Doesn't like numerical object keys: https://github.com/facebook/flow/issues/380
1: {
code: 'SERVICE_MISSING',
message: 'Google Play services is missing on this device.',
},
// $FlowBug: Doesn't like numerical object keys: https://github.com/facebook/flow/issues/380
2: {
code: 'SERVICE_VERSION_UPDATE_REQUIRED',
message: 'The installed version of Google Play services on this device is out of date.',
},
// $FlowBug: Doesn't like numerical object keys: https://github.com/facebook/flow/issues/380
3: {
code: 'SERVICE_DISABLED',
message: 'The installed version of Google Play services has been disabled on this device.',
},
// $FlowBug: Doesn't like numerical object keys: https://github.com/facebook/flow/issues/380
9: {
code: 'SERVICE_INVALID',
message: 'The version of the Google Play services installed on this device is not authentic.',
},
// $FlowBug: Doesn't like numerical object keys: https://github.com/facebook/flow/issues/380
18: {
code: 'SERVICE_UPDATING',
message: 'Google Play services is currently being updated on this device.',
},
// $FlowBug: Doesn't like numerical object keys: https://github.com/facebook/flow/issues/380
19: {
code: 'SERVICE_MISSING_PERMISSION',
message: 'Google Play service doesn\'t have one or more required permissions.',
},
};
const APPS: { [string]: FirebaseApp } = {};
export default {
// default options
OPTIONS: {
@ -69,9 +65,6 @@ export default {
checkedPlayServices: false,
},
// track all initialized firebase apps
APPS,
STRINGS: {
WARN_INITIALIZE_DEPRECATION: 'Deprecation: Calling \'initializeApp()\' for apps that are already initialised natively ' +
'is unnecessary, use \'firebase.app()\' instead to access the already initialized default app instance.',
@ -174,7 +167,7 @@ export default {
* @return {string}
*/
ERROR_NOT_APP(namespace: string) {
return `Invalid FirebaseApp instance passed to firebase.${namespace}(app <--).`;
return `Invalid App instance passed to firebase.${namespace}(app <--).`;
},
/**
@ -194,8 +187,8 @@ export default {
/**
* @return {string}
*/
ERROR_UNSUPPORTED_MODULE_METHOD(module: Class<ModuleBase>, method: string) {
return `firebase.${module._NAMESPACE}().${method}() is unsupported by the native Firebase SDKs.`;
ERROR_UNSUPPORTED_MODULE_METHOD(namespace: string, method: string) {
return `firebase.${namespace}().${method}() is unsupported by the native Firebase SDKs.`;
},
@ -222,24 +215,5 @@ export default {
'For more information on how to resolve this issue, configure Play Services checks or for guides on how to validate Play Services on your users devices see the link below:' +
'\r\n\r\nhttp://invertase.link/play-services';
},
DEFAULT_APP_NAME,
},
SharedEventEmitter: new EventEmitter(),
SyncTree: NativeModules.RNFirebaseDatabase ? new SyncTree(NativeModules.RNFirebaseDatabase) : null,
// internal utils
deleteApp(name: String): Promise<boolean> {
const app = this.APPS[name];
if (!app) return Promise.resolve(true);
// https://firebase.google.com/docs/reference/js/firebase.app.App#delete
return app.delete().then(() => {
delete this.APPS[name];
return true;
});
},
};

View File

@ -1,12 +1,34 @@
/*
* @flow
*/
import { windowOrGlobal } from './';
import type ModuleBase from './ModuleBase';
((base) => {
window = base || window;
// $FlowFixMe: Why are we using localStorage at all?
if (!window.localStorage) window.localStorage = {};
})(windowOrGlobal);
// clean up time
const NATIVE_LOGGERS: { [string]: Object } = {};
const getModuleKey = (module: ModuleBase): string => `${module.app.name}:${module.namespace}`;
export const getLogger = (module: ModuleBase) => {
const key = getModuleKey(module);
return NATIVE_LOGGERS[key];
};
export const initialiseLogger = (module: ModuleBase, logNamespace: string) => {
const key = getModuleKey(module);
if (!NATIVE_LOGGERS[key]) {
NATIVE_LOGGERS[key] = require('bows')(`🔥 ${logNamespace.toUpperCase()}`);
}
};
export default class Log {
static createLogger(namespace) {
return require('bows')(namespace);

67
lib/utils/native.js Normal file
View File

@ -0,0 +1,67 @@
/*
* @flow
*/
import { NativeModules } from 'react-native';
import { initialiseNativeModuleEventEmitter } from './events';
import INTERNALS from './internals';
import type ModuleBase from './ModuleBase';
import type { FirebaseModuleConfig } from '../types';
// Firebase Native SDKs that support multiple app instances
const MULTI_APP_MODULES = [
'RNFirebaseAuth',
'RNFirebaseDatabase',
'RNFirebaseFirestore',
'RNFirebaseStorage',
];
const NATIVE_MODULES: { [string]: Object } = {};
/**
* Prepends appName arg to all native method calls
* @param appName
* @param NativeModule
*/
const nativeWithApp = (appName: string, NativeModule: Object): Object => {
const native = {};
const methods = Object.keys(NativeModule);
for (let i = 0, len = methods.length; i < len; i++) {
const method = methods[i];
native[method] = (...args) => {
return NativeModule[method](...[appName, ...args]);
};
}
return native;
};
const getModuleKey = (module: ModuleBase): string => `${module.app.name}:${module.namespace}`;
export const getNativeModule = (module: ModuleBase): Object => {
const key = getModuleKey(module);
return NATIVE_MODULES[key];
};
export const initialiseNativeModule = (module: ModuleBase, config: FirebaseModuleConfig): Object => {
const { moduleName, namespace } = config;
const nativeModule = NativeModules[moduleName];
const key = getModuleKey(module);
if (!nativeModule && namespace !== 'utils') {
throw new Error(INTERNALS.STRINGS.ERROR_MISSING_MODULE(namespace, moduleName));
}
// used by the modules that extend ModuleBase
// to access their native module counterpart
if (!MULTI_APP_MODULES.includes(moduleName)) {
NATIVE_MODULES[key] = nativeModule;
} else {
NATIVE_MODULES[key] = nativeWithApp(module.app.name, nativeModule);
}
initialiseNativeModuleEventEmitter(module, config);
return NATIVE_MODULES[key];
};

View File

@ -153,7 +153,7 @@ PODS:
- React/Core
- React/fishhook
- React/RCTBlob
- RNFirebase (3.1.1):
- RNFirebase (3.2.0):
- React
- yoga (0.49.1.React)
@ -215,7 +215,7 @@ SPEC CHECKSUMS:
nanopb: 5601e6bca2dbf1ed831b519092ec110f66982ca3
Protobuf: 03eef2ee0b674770735cf79d9c4d3659cf6908e8
React: cf892fb84b7d06bf5fea7f328e554c6dcabe85ee
RNFirebase: 976f3b35d112017c69da5ada20cf1f15fc2c327e
RNFirebase: 22b1917fec663706907bc901ed665ac4f8b9bfd6
yoga: 3abf02d6d9aeeb139b4c930eb1367feae690a35a
PODFILE CHECKSUM: f17a538903249834df5049668d10174810db4c4c

View File

@ -40,7 +40,7 @@ const ios = {
const instances = {
web: firebase.initializeApp(config),
native: RNfirebase.app(),
native: RNfirebase,
another: RNfirebase.initializeApp(Platform.OS === 'ios' ? ios : android, 'anotherApp'),
};

View File

@ -53,8 +53,7 @@ function coreTests({ describe, it }) {
it('it should provide an array of apps', () => {
should.equal(!!RNFirebase.apps.length, true);
should.equal(RNFirebase.apps[0]._name, RNFirebase.utils.DEFAULT_APP_NAME);
should.equal(RNFirebase.apps[0].name, '[DEFAULT]');
should.equal(RNFirebase.apps.includes(RNFirebase.app('[DEFAULT]')), true);
return Promise.resolve();
});