diff --git a/.flowconfig b/.flowconfig index 96622d4e..2130616f 100644 --- a/.flowconfig +++ b/.flowconfig @@ -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 diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index b49108a9..d4d824c0 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,14 +1,13 @@ +BEFORE YOU MAKE AN ISSUE - +The issue list of this repo is exclusively for bug reports. - ### Issue @@ -38,4 +37,4 @@ 6. Firebase Module: \ No newline at end of file +👉 https://opencollective.com/react-native-firebase/donate --> diff --git a/ios/RNFirebase/RNFirebase.m b/ios/RNFirebase/RNFirebase.m index b9c99c9e..6f05d927 100644 --- a/ios/RNFirebase/RNFirebase.m +++ b/ios/RNFirebase/RNFirebase.m @@ -1,4 +1,5 @@ #import "RNFirebase.h" +#import "RNFirebaseUtil.h" #import @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; diff --git a/ios/RNFirebase/RNFirebaseUtil.h b/ios/RNFirebase/RNFirebaseUtil.h index ab0ef52d..8191c387 100644 --- a/ios/RNFirebase/RNFirebaseUtil.h +++ b/ios/RNFirebase/RNFirebaseUtil.h @@ -3,11 +3,15 @@ #import #import +#import @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 diff --git a/ios/RNFirebase/RNFirebaseUtil.m b/ios/RNFirebase/RNFirebaseUtil.m index 898e31cb..c97bb636 100644 --- a/ios/RNFirebase/RNFirebaseUtil.m +++ b/ios/RNFirebase/RNFirebaseUtil.m @@ -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]; } diff --git a/ios/RNFirebase/auth/RNFirebaseAuth.m b/ios/RNFirebase/auth/RNFirebaseAuth.m index 191d6330..576e8c3b 100644 --- a/ios/RNFirebase/auth/RNFirebaseAuth.m +++ b/ios/RNFirebase/auth/RNFirebaseAuth.m @@ -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 *_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, diff --git a/ios/RNFirebase/database/RNFirebaseDatabase.h b/ios/RNFirebase/database/RNFirebaseDatabase.h index 6991c4cc..e88d5b5a 100644 --- a/ios/RNFirebase/database/RNFirebaseDatabase.h +++ b/ios/RNFirebase/database/RNFirebaseDatabase.h @@ -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; diff --git a/ios/RNFirebase/database/RNFirebaseDatabase.m b/ios/RNFirebase/database/RNFirebaseDatabase.m index 30d6dedd..69a235fb 100644 --- a/ios/RNFirebase/database/RNFirebaseDatabase.m +++ b/ios/RNFirebase/database/RNFirebaseDatabase.m @@ -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 diff --git a/ios/RNFirebase/database/RNFirebaseDatabaseReference.h b/ios/RNFirebase/database/RNFirebaseDatabaseReference.h index ec02e502..fb64295c 100644 --- a/ios/RNFirebase/database/RNFirebaseDatabaseReference.h +++ b/ios/RNFirebase/database/RNFirebaseDatabaseReference.h @@ -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; diff --git a/ios/RNFirebase/database/RNFirebaseDatabaseReference.m b/ios/RNFirebase/database/RNFirebaseDatabaseReference.m index 3a3d6730..a6caf8c2 100644 --- a/ios/RNFirebase/database/RNFirebaseDatabaseReference.m +++ b/ios/RNFirebase/database/RNFirebaseDatabaseReference.m @@ -5,14 +5,14 @@ #if __has_include() - (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) { diff --git a/ios/RNFirebase/firestore/RNFirebaseFirestore.h b/ios/RNFirebase/firestore/RNFirebaseFirestore.h index e93595b1..98036471 100644 --- a/ios/RNFirebase/firestore/RNFirebaseFirestore.h +++ b/ios/RNFirebase/firestore/RNFirebaseFirestore.h @@ -13,7 +13,7 @@ + (void)promiseRejectException:(RCTPromiseRejectBlock)reject error:(NSError *)error; -+ (FIRFirestore *)getFirestoreForApp:(NSString *)appName; ++ (FIRFirestore *)getFirestoreForApp:(NSString *)appDisplayName; + (NSDictionary *)getJSError:(NSError *)nativeError; @end diff --git a/ios/RNFirebase/firestore/RNFirebaseFirestore.m b/ios/RNFirebase/firestore/RNFirebaseFirestore.m index 29d8646d..f662afb0 100644 --- a/ios/RNFirebase/firestore/RNFirebaseFirestore.m +++ b/ios/RNFirebase/firestore/RNFirebaseFirestore.m @@ -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 diff --git a/ios/RNFirebase/firestore/RNFirebaseFirestoreCollectionReference.h b/ios/RNFirebase/firestore/RNFirebaseFirestoreCollectionReference.h index 8fc07cbe..9e067ab0 100644 --- a/ios/RNFirebase/firestore/RNFirebaseFirestoreCollectionReference.h +++ b/ios/RNFirebase/firestore/RNFirebaseFirestoreCollectionReference.h @@ -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; diff --git a/ios/RNFirebase/firestore/RNFirebaseFirestoreCollectionReference.m b/ios/RNFirebase/firestore/RNFirebaseFirestoreCollectionReference.m index 4985ab8e..91af07cd 100644 --- a/ios/RNFirebase/firestore/RNFirebaseFirestoreCollectionReference.m +++ b/ios/RNFirebase/firestore/RNFirebaseFirestoreCollectionReference.m @@ -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 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"]; diff --git a/ios/RNFirebase/firestore/RNFirebaseFirestoreDocumentReference.h b/ios/RNFirebase/firestore/RNFirebaseFirestoreDocumentReference.h index 5a944ad9..9745a091 100644 --- a/ios/RNFirebase/firestore/RNFirebaseFirestoreDocumentReference.h +++ b/ios/RNFirebase/firestore/RNFirebaseFirestoreDocumentReference.h @@ -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; diff --git a/ios/RNFirebase/firestore/RNFirebaseFirestoreDocumentReference.m b/ios/RNFirebase/firestore/RNFirebaseFirestoreDocumentReference.m index d9c5a950..ba8ca21b 100644 --- a/ios/RNFirebase/firestore/RNFirebaseFirestoreDocumentReference.m +++ b/ios/RNFirebase/firestore/RNFirebaseFirestoreDocumentReference.m @@ -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"]; diff --git a/ios/RNFirebase/storage/RNFirebaseStorage.m b/ios/RNFirebase/storage/RNFirebaseStorage.m index e73038d5..ce6ea1ed 100644 --- a/ios/RNFirebase/storage/RNFirebaseStorage.m +++ b/ios/RNFirebase/storage/RNFirebaseStorage.m @@ -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}]; } /** diff --git a/lib/index.d.ts b/lib/index.d.ts index 688dd9bc..1eff94bf 100644 --- a/lib/index.d.ts +++ b/lib/index.d.ts @@ -64,6 +64,10 @@ declare module "react-native-firebase" { */ crash(): RNFirebase.crash.Crash; + static fabric: { + crashlytics(): RNFirebase.crashlytics.Crashlytics; + }; + apps: Array; 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; + } + } } } diff --git a/lib/index.js b/lib/index.js index 0d65d53e..b31d093b 100644 --- a/lib/index.js +++ b/lib/index.js @@ -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; diff --git a/lib/modules/admob/Interstitial.js b/lib/modules/admob/Interstitial.js index 617945be..06ff6eee 100644 --- a/lib/modules/admob/Interstitial.js +++ b/lib/modules/admob/Interstitial.js @@ -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; } diff --git a/lib/modules/admob/RewardedVideo.js b/lib/modules/admob/RewardedVideo.js index f34f5eec..84b02860 100644 --- a/lib/modules/admob/RewardedVideo.js +++ b/lib/modules/admob/RewardedVideo.js @@ -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; } diff --git a/lib/modules/admob/index.js b/lib/modules/admob/index.js index c044646b..2e7fd4b7 100644 --- a/lib/modules/admob/index.js +++ b/lib/modules/admob/index.js @@ -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 = { diff --git a/lib/modules/analytics/index.js b/lib/modules/analytics/index.js index cc842340..44d8464f 100644 --- a/lib/modules/analytics/index.js +++ b/lib/modules/analytics/index.js @@ -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]); } } } diff --git a/lib/modules/auth/ConfirmationResult.js b/lib/modules/auth/ConfirmationResult.js index a2b80b72..340be9cb 100644 --- a/lib/modules/auth/ConfirmationResult.js +++ b/lib/modules/auth/ConfirmationResult.js @@ -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 { - return this._auth._interceptUserValue(this._auth._native._confirmVerificationCode(verificationCode)); + return this._auth._interceptUserValue(getNativeModule(this._auth)._confirmVerificationCode(verificationCode)); } get verificationId(): string | null { diff --git a/lib/modules/auth/PhoneAuthListener.js b/lib/modules/auth/PhoneAuthListener.js index 0848b080..648b249e 100644 --- a/lib/modules/auth/PhoneAuthListener.js +++ b/lib/modules/auth/PhoneAuthListener.js @@ -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 } diff --git a/lib/modules/auth/User.js b/lib/modules/auth/User.js index aa6d5903..bcaa383f 100644 --- a/lib/modules/auth/User.js +++ b/lib/modules/auth/User.js @@ -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 { 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 { - return this._auth._native.getToken(forceRefresh); + return getNativeModule(this._auth).getToken(forceRefresh); } /** @@ -109,7 +110,7 @@ export default class User { */ linkWithCredential(credential: AuthCredential): Promise { 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 { 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 { 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 { 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.|*} */ unlink(providerId: string): Promise { - 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 { 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 { 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 { 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 { 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); } /** diff --git a/lib/modules/auth/index.js b/lib/modules/auth/index.js index b8d073db..5568aeda 100644 --- a/lib/modules/auth/index.js +++ b/lib/modules/auth/index.js @@ -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 { - 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 { - 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 { - 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 { - 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 { - 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 { 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 { - 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 { - return this._native.sendPasswordResetEmail(email, actionCodeSettings); + return getNativeModule(this).sendPasswordResetEmail(email, actionCodeSettings); } /** @@ -281,7 +293,7 @@ export default class Auth extends ModuleBase { * @return {Promise.} */ confirmPasswordReset(code: string, newPassword: string): Promise { - return this._native.confirmPasswordReset(code, newPassword); + return getNativeModule(this).confirmPasswordReset(code, newPassword); } /** @@ -292,7 +304,7 @@ export default class Auth extends ModuleBase { * @return {Promise.} */ applyActionCode(code: string): Promise { - return this._native.applyActionCode(code); + return getNativeModule(this).applyActionCode(code); } /** @@ -303,7 +315,7 @@ export default class Auth extends ModuleBase { * @return {Promise.|Promise} */ checkActionCode(code: string): Promise { - return this._native.checkActionCode(code); + return getNativeModule(this).checkActionCode(code); } /** @@ -311,7 +323,7 @@ export default class Auth extends ModuleBase { * @return {Promise} */ getCurrentUser(): Promise { - 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> { - 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')); } } diff --git a/lib/modules/config/index.js b/lib/modules/config/index.js index 999c838a..ab34b94b 100644 --- a/lib/modules/config/index.js +++ b/lib/modules/config/index.js @@ -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) { - 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.>} */ 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); } } diff --git a/lib/modules/core/firebase-app.js b/lib/modules/core/firebase-app.js index 686a0bed..a10eb140 100644 --- a/lib/modules/core/firebase-app.js +++ b/lib/modules/core/firebase-app.js @@ -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 { + onReady(): Promise { 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(statics: S, InstanceClass: Class): FirebaseModuleAndStatics { - 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], - }); - } } diff --git a/lib/modules/core/firebase.js b/lib/modules/core/firebase.js index e9fe087f..abfdc52a 100644 --- a/lib/modules/core/firebase.js +++ b/lib/modules/core/firebase.js @@ -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 { - 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(statics: S, InstanceClass: Class): FirebaseModuleAndStatics { - 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 { + return APPS.apps(); } } -export default new FirebaseCore(); +export default new Firebase(); diff --git a/lib/modules/crash/index.js b/lib/modules/crash/index.js index 2844a65c..48fbbea4 100644 --- a/lib/modules/crash/index.js +++ b/lib/modules/crash/index.js @@ -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.} */ isCrashCollectionEnabled(): Promise { - 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); } } diff --git a/lib/modules/database/disconnect.js b/lib/modules/database/disconnect.js index f1a37122..5266bc22 100644 --- a/lib/modules/database/disconnect.js +++ b/lib/modules/database/disconnect.js @@ -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 { - 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 { - 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 { - 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 { - return this._database._native.onDisconnectCancel(this.path); + return getNativeModule(this._database).onDisconnectCancel(this.path); } } diff --git a/lib/modules/database/index.js b/lib/modules/database/index.js index ddcae474..42b25cbc 100644 --- a/lib/modules/database/index.js +++ b/lib/modules/database/index.js @@ -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); } }, }; diff --git a/lib/modules/database/reference.js b/lib/modules/database/reference.js index ee0f6283..42f41a96 100644 --- a/lib/modules/database/reference.js +++ b/lib/modules/database/reference.js @@ -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) { - super(path, database); + constructor(database: Database, path: string, existingModifiers?: Array) { + 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 { - 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 { 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 { 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); } } diff --git a/lib/modules/database/transaction.js b/lib/modules/database/transaction.js index d84a1d26..1711d441 100644 --- a/lib/modules/database/transaction.js +++ b/lib/modules/database/transaction.js @@ -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]; diff --git a/lib/modules/fabric/crashlytics/index.js b/lib/modules/fabric/crashlytics/index.js index 6ab1d9f9..789c9af8 100644 --- a/lib/modules/fabric/crashlytics/index.js +++ b/lib/modules/fabric/crashlytics/index.js @@ -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); } } diff --git a/lib/modules/firestore/CollectionReference.js b/lib/modules/firestore/CollectionReference.js index 87598b57..c3cba570 100644 --- a/lib/modules/firestore/CollectionReference.js +++ b/lib/modules/firestore/CollectionReference.js @@ -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); } diff --git a/lib/modules/firestore/DocumentReference.js b/lib/modules/firestore/DocumentReference.js index 97d7c4ae..e401d4f1 100644 --- a/lib/modules/firestore/DocumentReference.js +++ b/lib/modules/firestore/DocumentReference.js @@ -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 { - return this._firestore._native + return getNativeModule(this._firestore) .documentDelete(this.path); } get(): Promise { - 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 { 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); } } diff --git a/lib/modules/firestore/Query.js b/lib/modules/firestore/Query.js index af11cae4..5d26f977 100644 --- a/lib/modules/firestore/Query.js +++ b/lib/modules/firestore/Query.js @@ -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 { - 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, diff --git a/lib/modules/firestore/WriteBatch.js b/lib/modules/firestore/WriteBatch.js index 6d687ac9..2b315de2 100644 --- a/lib/modules/firestore/WriteBatch.js +++ b/lib/modules/firestore/WriteBatch.js @@ -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 { - return this._firestore._native - .documentBatch(this._writes); + return getNativeModule(this._firestore).documentBatch(this._writes); } delete(docRef: DocumentReference): WriteBatch { diff --git a/lib/modules/firestore/index.js b/lib/modules/firestore/index.js index ac624ae4..7061ed4b 100644 --- a/lib/modules/firestore/index.js +++ b/lib/modules/firestore/index.js @@ -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); } }, }; diff --git a/lib/modules/links/index.js b/lib/modules/links/index.js index 68b332bb..1ca4f3ca 100644 --- a/lib/modules/links/index.js +++ b/lib/modules/links/index.js @@ -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.} */ getInitialLink(): Promise { - 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); } diff --git a/lib/modules/messaging/index.js b/lib/modules/messaging/index.js index 595ab402..0686f860 100644 --- a/lib/modules/messaging/index.js +++ b/lib/modules/messaging/index.js @@ -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 { - return this._native.getInitialNotification(); + return getNativeModule(this).getInitialNotification(); } /** @@ -114,7 +124,7 @@ export default class Messaging extends ModuleBase { * @returns {*|Promise.} */ getToken(): Promise { - return this._native.getToken(); + return getNativeModule(this).getToken(); } /** @@ -122,7 +132,7 @@ export default class Messaging extends ModuleBase { * @returns {*|Promise.<*>} */ deleteInstanceId(): Promise { - 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.} */ getScheduledLocalNotifications(): Promise { - return this._native.getScheduledLocalNotifications(); + return getNativeModule(this).getScheduledLocalNotifications(); } /** @@ -165,8 +175,8 @@ export default class Messaging extends ModuleBase { */ cancelLocalNotification(id: string): Promise { 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 { 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 { - 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.} */ getBadgeNumber(): Promise { - 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()); } } diff --git a/lib/modules/perf/Trace.js b/lib/modules/perf/Trace.js index 5b5614b1..5fd54608 100644 --- a/lib/modules/perf/Trace.js +++ b/lib/modules/perf/Trace.js @@ -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); } } diff --git a/lib/modules/perf/index.js b/lib/modules/perf/index.js index 81d3a5d1..165919ad 100644 --- a/lib/modules/perf/index.js +++ b/lib/modules/perf/index.js @@ -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); } /** diff --git a/lib/modules/storage/index.js b/lib/modules/storage/index.js index fdf5f520..f0f86282 100644 --- a/lib/modules/storage/index.js +++ b/lib/modules/storage/index.js @@ -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 { + 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); } } diff --git a/lib/modules/storage/reference.js b/lib/modules/storage/reference.js index d38c5d2a..8db997ad 100644 --- a/lib/modules/storage/reference.js +++ b/lib/modules/storage/reference.js @@ -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.|*} */ delete(): Promise { - 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.|*} */ getDownloadURL(): Promise { - 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.|*} */ getMetadata(): Promise { - 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.|*} */ updateMetadata(metadata: Object = {}): Promise { - 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 { - 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 { 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); } } diff --git a/lib/modules/utils/index.js b/lib/modules/utils/index.js index c7325462..6b9ade59 100644 --- a/lib/modules/utils/index.js +++ b/lib/modules/utils/index.js @@ -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 { - 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('.'), diff --git a/lib/types/index.js b/lib/types/index.js index d002a2a7..31588854 100644 --- a/lib/types/index.js +++ b/lib/types/index.js @@ -40,7 +40,18 @@ export type FirebaseError = { export type FirebaseModule = $Subtype; -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'; diff --git a/lib/utils/ModuleBase.js b/lib/utils/ModuleBase.js index 0f89825a..6f1ac1a1 100644 --- a/lib/utils/ModuleBase.js +++ b/lib/utils/ModuleBase.js @@ -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; } } diff --git a/lib/utils/ReferenceBase.js b/lib/utils/ReferenceBase.js index 4d0daf4e..80e0eede 100644 --- a/lib/utils/ReferenceBase.js +++ b/lib/utils/ReferenceBase.js @@ -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; - } } diff --git a/lib/utils/SyncTree.js b/lib/utils/SyncTree.js index 6cd4acb1..36c97c21 100644 --- a/lib/utils/SyncTree.js +++ b/lib/utils/SyncTree.js @@ -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(); diff --git a/lib/utils/apps.js b/lib/utils/apps.js new file mode 100644 index 00000000..3be466e7 --- /dev/null +++ b/lib/utils/apps.js @@ -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 { + // $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(app: App, namespace: FirebaseNamespace, InstanceClass: Class): () => 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 { + 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(namespace: FirebaseNamespace, statics: S, moduleName: FirebaseModuleName): FirebaseModuleAndStatics { + 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], + }); + }, +}; diff --git a/lib/utils/events.js b/lib/utils/events.js new file mode 100644 index 00000000..83152dba --- /dev/null +++ b/lib/utils/events.js @@ -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]); + } + } +}; diff --git a/lib/utils/index.js b/lib/utils/index.js index 6f619e4e..994bab1d 100644 --- a/lib/utils/index.js +++ b/lib/utils/index.js @@ -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); }); } diff --git a/lib/utils/internals.js b/lib/utils/internals.js index efab8da1..13b7bedc 100644 --- a/lib/utils/internals.js +++ b/lib/utils/internals.js @@ -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, 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 { - 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; - }); }, }; diff --git a/lib/utils/log.js b/lib/utils/log.js index b43dbe73..dd57df66 100644 --- a/lib/utils/log.js +++ b/lib/utils/log.js @@ -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); diff --git a/lib/utils/native.js b/lib/utils/native.js new file mode 100644 index 00000000..ed728eb1 --- /dev/null +++ b/lib/utils/native.js @@ -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]; +}; diff --git a/tests/src/firebase.js b/tests/src/firebase.js index f51fb163..4df18e2f 100644 --- a/tests/src/firebase.js +++ b/tests/src/firebase.js @@ -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'), }; diff --git a/tests/src/tests/core/coreTests.js b/tests/src/tests/core/coreTests.js index d96e5d78..5134dcbe 100644 --- a/tests/src/tests/core/coreTests.js +++ b/tests/src/tests/core/coreTests.js @@ -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(); });