diff --git a/android/src/main/java/io/invertase/firebase/RNFirebaseModule.java b/android/src/main/java/io/invertase/firebase/RNFirebaseModule.java index e32bcc56..04589a15 100644 --- a/android/src/main/java/io/invertase/firebase/RNFirebaseModule.java +++ b/android/src/main/java/io/invertase/firebase/RNFirebaseModule.java @@ -7,6 +7,7 @@ import java.util.HashMap; // react import com.facebook.react.bridge.Arguments; +import com.facebook.react.bridge.ReadableMap; import com.facebook.react.bridge.WritableMap; import com.facebook.react.bridge.LifecycleEventListener; import com.facebook.react.bridge.ReactApplicationContext; @@ -43,6 +44,12 @@ public class RNFirebaseModule extends ReactContextBaseJavaModule implements Life } } + @ReactMethod + public void initializeApp(String name, ReadableMap options) { + // todo https://firebase.google.com/docs/reference/android/com/google/firebase/FirebaseOptions + + } + private WritableMap getPlayServicesStatus() { GoogleApiAvailability gapi = GoogleApiAvailability.getInstance(); final int status = gapi.isGooglePlayServicesAvailable(getReactApplicationContext()); diff --git a/android/src/main/java/io/invertase/firebase/auth/RNFirebaseAuth.java b/android/src/main/java/io/invertase/firebase/auth/RNFirebaseAuth.java index c223144c..3aa2b8c5 100644 --- a/android/src/main/java/io/invertase/firebase/auth/RNFirebaseAuth.java +++ b/android/src/main/java/io/invertase/firebase/auth/RNFirebaseAuth.java @@ -5,6 +5,7 @@ import android.net.Uri; import android.support.annotation.NonNull; import java.util.List; +import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -42,14 +43,14 @@ import com.google.firebase.auth.EmailAuthProvider; import io.invertase.firebase.Utils; @SuppressWarnings("ThrowableResultOfMethodCallIgnored") -public class RNFirebaseAuth extends ReactContextBaseJavaModule { +class RNFirebaseAuth extends ReactContextBaseJavaModule { private static final String TAG = "RNFirebaseAuth"; private ReactContext mReactContext; private FirebaseAuth mAuth; - private FirebaseAuth.AuthStateListener mAuthListener; + private Map mAuthListeners; - public RNFirebaseAuth(ReactApplicationContext reactContext) { + RNFirebaseAuth(ReactApplicationContext reactContext) { super(reactContext); mReactContext = reactContext; mAuth = FirebaseAuth.getInstance(); @@ -65,16 +66,18 @@ public class RNFirebaseAuth extends ReactContextBaseJavaModule { * Add a new auth state listener - if one doesn't exist already */ @ReactMethod - public void addAuthStateListener() { + public void addAuthStateListener(final String appName) { Log.d(TAG, "addAuthStateListener"); + FirebaseAuth.AuthStateListener mAuthListener = mAuthListeners.get(appName); if (mAuthListener == null) { - mAuthListener = new FirebaseAuth.AuthStateListener() { + FirebaseAuth.AuthStateListener newAuthListener = new FirebaseAuth.AuthStateListener() { @Override public void onAuthStateChanged(@NonNull FirebaseAuth firebaseAuth) { FirebaseUser user = firebaseAuth.getCurrentUser(); WritableMap msgMap = Arguments.createMap(); if (user != null) { msgMap.putBoolean("authenticated", true); + msgMap.putString("appName", appName); // for js side distribution msgMap.putMap("user", firebaseUserToMap(user)); Utils.sendEvent(mReactContext, "onAuthStateChanged", msgMap); } else { @@ -83,7 +86,9 @@ public class RNFirebaseAuth extends ReactContextBaseJavaModule { } } }; - mAuth.addAuthStateListener(mAuthListener); + + mAuth.addAuthStateListener(newAuthListener); + mAuthListeners.put(appName, newAuthListener); } } @@ -91,8 +96,10 @@ public class RNFirebaseAuth extends ReactContextBaseJavaModule { * Removes the current auth state listener */ @ReactMethod - public void removeAuthStateListener() { + public void removeAuthStateListener(String appName) { Log.d(TAG, "removeAuthStateListener"); + FirebaseAuth.AuthStateListener mAuthListener = mAuthListeners.get(appName); + if (mAuthListener != null) { mAuth.removeAuthStateListener(mAuthListener); } diff --git a/ios/RNFirebase/RNFirebase.m b/ios/RNFirebase/RNFirebase.m index 803a283c..4149c5fb 100644 --- a/ios/RNFirebase/RNFirebase.m +++ b/ios/RNFirebase/RNFirebase.m @@ -1,4 +1,5 @@ #import "RNFirebase.h" +#import "FirebaseCore/FirebaseCore.h" @implementation RNFirebase RCT_EXPORT_MODULE(RNFirebase); @@ -15,4 +16,43 @@ RCT_EXPORT_MODULE(RNFirebase); return @[]; } +/** + * Initialize a new firebase app instance or ignore if currently exists. + * @return + */ +RCT_EXPORT_METHOD(initializeApp: + (NSString *) name + options: + (NSDictionary *) options + callback: + (RCTResponseSenderBlock) callback) { + + dispatch_sync(dispatch_get_main_queue(), ^{ + FIRApp *existingApp = [FIRApp appNamed:name]; + + if (!existingApp) { + FIROptions *firOptions = [ + [FIROptions alloc] + initWithGoogleAppID:[options valueForKey:@"iosAppId"] + GCMSenderID:[options valueForKey:@"messagingSenderId"] + ]; + + firOptions.APIKey = [options valueForKey:@"apiKey"]; + firOptions.projectID = [options valueForKey:@"projectId"]; + firOptions.clientID = [options valueForKey:@"iosClientId"]; + firOptions.trackingID = [options valueForKey:@"trackingId"]; + firOptions.databaseURL = [options valueForKey:@"databaseURL"]; + firOptions.storageBucket = [options valueForKey:@"storageBucket"]; + firOptions.androidClientID = [options valueForKey:@"androidClientId"]; + firOptions.deepLinkURLScheme = [options valueForKey:@"deepLinkURLScheme"]; + firOptions.bundleID = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleIdentifier"]; + + [FIRApp configureWithName:name options:firOptions]; + } + + // todo expand on callback result + callback(@[[NSNull null], @{@"result": @"success"}]); + }); +} + @end diff --git a/ios/RNFirebase/auth/RNFirebaseAuth.h b/ios/RNFirebase/auth/RNFirebaseAuth.h index 1c991a7f..fd1dc5b8 100644 --- a/ios/RNFirebase/auth/RNFirebaseAuth.h +++ b/ios/RNFirebase/auth/RNFirebaseAuth.h @@ -9,7 +9,7 @@ @interface RNFirebaseAuth : RCTEventEmitter { FIRAuthStateDidChangeListenerHandle authListenerHandle; - Boolean listening; + NSMutableDictionary *authStateHandlers; } @end diff --git a/ios/RNFirebase/auth/RNFirebaseAuth.m b/ios/RNFirebase/auth/RNFirebaseAuth.m index 520707d2..1126cdf6 100644 --- a/ios/RNFirebase/auth/RNFirebaseAuth.m +++ b/ios/RNFirebase/auth/RNFirebaseAuth.m @@ -1,8 +1,10 @@ #import "RNFirebaseAuth.h" #import "RNFirebaseEvents.h" +#import "RCTDefines.h" #if __has_include() + @implementation RNFirebaseAuth RCT_EXPORT_MODULE(); @@ -10,28 +12,38 @@ RCT_EXPORT_MODULE(); addAuthStateListener */ -RCT_EXPORT_METHOD(addAuthStateListener) { - self->listening = true; - self->authListenerHandle = [[FIRAuth auth] addAuthStateDidChangeListener:^(FIRAuth *_Nonnull auth, FIRUser *_Nullable user) { - if (user != nil) { - [self sendJSEvent:AUTH_CHANGED_EVENT props: @{ @"authenticated": @(true),@"user": [self firebaseUserToDict:user] }]; - } else { - [self sendJSEvent:AUTH_CHANGED_EVENT props:@{ @"authenticated": @(false) }]; - } - }]; +RCT_EXPORT_METHOD(addAuthStateListener: + (NSString *) appName) { + FIRApp *firApp = [FIRApp appNamed:appName]; + // todo +// FIRAuthStateDidChangeListenerHandle *listenerBlock = [authStateHandlers valueForKey:appName]; +// +// if (!listenerBlock) { +// FIRAuthStateDidChangeListenerHandle *newlistenerHandle = [[FIRAuth authWithApp:firApp] addAuthStateDidChangeListener:^(FIRAuth *_Nonnull auth, FIRUser *_Nullable user) { +// if (user != nil) { +// [self sendJSEventWithAppName:appName title:AUTH_CHANGED_EVENT props:@{@"authenticated": @(true), @"user": [self firebaseUserToDict:user]}]; +// } else { +// [self sendJSEventWithAppName:appName title:AUTH_CHANGED_EVENT props:@{@"authenticated": @(false)}]; +// } +// }]; +// } } /** removeAuthStateListener */ -RCT_EXPORT_METHOD(removeAuthStateListener) { +RCT_EXPORT_METHOD(removeAuthStateListener: + (NSString *) appName) { + FIRApp *firApp = [FIRApp appNamed:appName]; + if (self->authListenerHandle != nil) { - [[FIRAuth auth] removeAuthStateDidChangeListener:self->authListenerHandle]; - self->listening = false; + [[FIRAuth authWithApp:firApp] removeAuthStateDidChangeListener:self->authListenerHandle]; } } +// TODO refactor remaining methods to accept appName arg - just testing listeners for now + /** signOut @@ -39,7 +51,10 @@ RCT_EXPORT_METHOD(removeAuthStateListener) { @param RCTPromiseRejectBlock reject @return */ -RCT_EXPORT_METHOD(signOut:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject) { +RCT_EXPORT_METHOD(signOut: + (RCTPromiseResolveBlock) resolve + rejecter: + (RCTPromiseRejectBlock) reject) { FIRUser *user = [FIRAuth auth].currentUser; if (user) { @@ -60,7 +75,10 @@ RCT_EXPORT_METHOD(signOut:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseR @param RCTPromiseRejectBlock reject @return */ -RCT_EXPORT_METHOD(signInAnonymously:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject) { +RCT_EXPORT_METHOD(signInAnonymously: + (RCTPromiseResolveBlock) resolve + rejecter: + (RCTPromiseRejectBlock) reject) { [[FIRAuth auth] signInAnonymouslyWithCompletion:^(FIRUser *user, NSError *error) { if (error) { [self promiseRejectAuthException:reject error:error]; @@ -80,7 +98,14 @@ RCT_EXPORT_METHOD(signInAnonymously:(RCTPromiseResolveBlock) resolve rejecter:(R @param RCTPromiseRejectBlock reject @return return */ -RCT_EXPORT_METHOD(signInWithEmailAndPassword:(NSString *)email pass:(NSString *)password resolver:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject) { +RCT_EXPORT_METHOD(signInWithEmailAndPassword: + (NSString *) email + pass: + (NSString *) password + resolver: + (RCTPromiseResolveBlock) resolve + rejecter: + (RCTPromiseRejectBlock) reject) { [[FIRAuth auth] signInWithEmail:email password:password completion:^(FIRUser *user, NSError *error) { if (error) { [self promiseRejectAuthException:reject error:error]; @@ -99,7 +124,14 @@ RCT_EXPORT_METHOD(signInWithEmailAndPassword:(NSString *)email pass:(NSString *) @param RCTPromiseRejectBlock reject @return return */ -RCT_EXPORT_METHOD(createUserWithEmailAndPassword:(NSString *)email pass:(NSString *)password resolver:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject) { +RCT_EXPORT_METHOD(createUserWithEmailAndPassword: + (NSString *) email + pass: + (NSString *) password + resolver: + (RCTPromiseResolveBlock) resolve + rejecter: + (RCTPromiseRejectBlock) reject) { [[FIRAuth auth] createUserWithEmail:email password:password completion:^(FIRUser *user, NSError *error) { if (error) { [self promiseRejectAuthException:reject error:error]; @@ -116,7 +148,10 @@ RCT_EXPORT_METHOD(createUserWithEmailAndPassword:(NSString *)email pass:(NSStrin @param RCTPromiseRejectBlock reject @return return */ -RCT_EXPORT_METHOD(delete:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject) { +RCT_EXPORT_METHOD(delete: + (RCTPromiseResolveBlock) resolve + rejecter: + (RCTPromiseRejectBlock) reject) { FIRUser *user = [FIRAuth auth].currentUser; if (user) { @@ -139,7 +174,10 @@ RCT_EXPORT_METHOD(delete:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRe @param RCTPromiseRejectBlock reject @return return */ -RCT_EXPORT_METHOD(reload:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject) { +RCT_EXPORT_METHOD(reload: + (RCTPromiseResolveBlock) resolve + rejecter: + (RCTPromiseRejectBlock) reject) { FIRUser *user = [FIRAuth auth].currentUser; if (user) { @@ -163,7 +201,10 @@ RCT_EXPORT_METHOD(reload:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRe @param RCTPromiseRejectBlock reject @return return */ -RCT_EXPORT_METHOD(sendEmailVerification:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject) { +RCT_EXPORT_METHOD(sendEmailVerification: + (RCTPromiseResolveBlock) resolve + rejecter: + (RCTPromiseRejectBlock) reject) { FIRUser *user = [FIRAuth auth].currentUser; if (user) { @@ -188,7 +229,12 @@ RCT_EXPORT_METHOD(sendEmailVerification:(RCTPromiseResolveBlock) resolve rejecte @param RCTPromiseRejectBlock reject @return return */ -RCT_EXPORT_METHOD(updateEmail:(NSString *) email resolver:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject) { +RCT_EXPORT_METHOD(updateEmail: + (NSString *) email + resolver: + (RCTPromiseResolveBlock) resolve + rejecter: + (RCTPromiseRejectBlock) reject) { FIRUser *user = [FIRAuth auth].currentUser; if (user) { @@ -213,7 +259,12 @@ RCT_EXPORT_METHOD(updateEmail:(NSString *) email resolver:(RCTPromiseResolveBloc @param RCTPromiseRejectBlock reject @return return */ -RCT_EXPORT_METHOD(updatePassword:(NSString *) password resolver:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject) { +RCT_EXPORT_METHOD(updatePassword: + (NSString *) password + resolver: + (RCTPromiseResolveBlock) resolve + rejecter: + (RCTPromiseRejectBlock) reject) { FIRUser *user = [FIRAuth auth].currentUser; if (user) { @@ -238,7 +289,12 @@ RCT_EXPORT_METHOD(updatePassword:(NSString *) password resolver:(RCTPromiseResol @param RCTPromiseRejectBlock reject @return return */ -RCT_EXPORT_METHOD(updateProfile:(NSDictionary *) props resolver:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject) { +RCT_EXPORT_METHOD(updateProfile: + (NSDictionary *) props + resolver: + (RCTPromiseResolveBlock) resolve + rejecter: + (RCTPromiseRejectBlock) reject) { FIRUser *user = [FIRAuth auth].currentUser; if (user) { @@ -279,7 +335,12 @@ RCT_EXPORT_METHOD(updateProfile:(NSDictionary *) props resolver:(RCTPromiseResol @param RCTPromiseRejectBlock reject @return */ -RCT_EXPORT_METHOD(getToken:(BOOL)forceRefresh resolver:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject) { +RCT_EXPORT_METHOD(getToken: + (BOOL) forceRefresh + resolver: + (RCTPromiseResolveBlock) resolve + rejecter: + (RCTPromiseRejectBlock) reject) { FIRUser *user = [FIRAuth auth].currentUser; if (user) { @@ -305,7 +366,16 @@ RCT_EXPORT_METHOD(getToken:(BOOL)forceRefresh resolver:(RCTPromiseResolveBlock) @param RCTPromiseRejectBlock reject @return */ -RCT_EXPORT_METHOD(signInWithCredential:(NSString *)provider token:(NSString *)authToken secret:(NSString *)authSecret resolver:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject) { +RCT_EXPORT_METHOD(signInWithCredential: + (NSString *) provider + token: + (NSString *) authToken + secret: + (NSString *) authSecret + resolver: + (RCTPromiseResolveBlock) resolve + rejecter: + (RCTPromiseRejectBlock) reject) { FIRAuthCredential *credential = [self getCredentialForProvider:provider token:authToken secret:authSecret]; if (credential == nil) { @@ -329,7 +399,12 @@ RCT_EXPORT_METHOD(signInWithCredential:(NSString *)provider token:(NSString *)au @param RCTPromiseRejectBlock reject @return */ -RCT_EXPORT_METHOD(sendPasswordResetEmail:(NSString *)email resolver:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject) { +RCT_EXPORT_METHOD(sendPasswordResetEmail: + (NSString *) email + resolver: + (RCTPromiseResolveBlock) resolve + rejecter: + (RCTPromiseRejectBlock) reject) { [[FIRAuth auth] sendPasswordResetWithEmail:email completion:^(NSError *_Nullable error) { if (error) { [self promiseRejectAuthException:reject error:error]; @@ -346,7 +421,10 @@ RCT_EXPORT_METHOD(sendPasswordResetEmail:(NSString *)email resolver:(RCTPromiseR @param RCTPromiseRejectBlock reject @return */ -RCT_EXPORT_METHOD(getCurrentUser:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject) { +RCT_EXPORT_METHOD(getCurrentUser: + (RCTPromiseResolveBlock) resolve + rejecter: + (RCTPromiseRejectBlock) reject) { FIRUser *user = [FIRAuth auth].currentUser; [self promiseWithUser:resolve rejecter:reject user:user]; } @@ -359,7 +437,12 @@ RCT_EXPORT_METHOD(getCurrentUser:(RCTPromiseResolveBlock) resolve rejecter:(RCTP @param RCTPromiseRejectBlock reject @return */ -RCT_EXPORT_METHOD(signInWithCustomToken: (NSString *)customToken resolver:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject) { +RCT_EXPORT_METHOD(signInWithCustomToken: + (NSString *) customToken + resolver: + (RCTPromiseResolveBlock) resolve + rejecter: + (RCTPromiseRejectBlock) reject) { [[FIRAuth auth] signInWithCustomToken:customToken completion:^(FIRUser *user, NSError *error) { if (error) { [self promiseRejectAuthException:reject error:error]; @@ -379,7 +462,16 @@ RCT_EXPORT_METHOD(signInWithCustomToken: (NSString *)customToken resolver:(RCTPr @param RCTPromiseRejectBlock reject @return */ -RCT_EXPORT_METHOD(link:(NSString *)provider authToken:(NSString *)authToken authSecret:(NSString *)authSecret resolver:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject) { +RCT_EXPORT_METHOD(link: + (NSString *) provider + authToken: + (NSString *) authToken + authSecret: + (NSString *) authSecret + resolver: + (RCTPromiseResolveBlock) resolve + rejecter: + (RCTPromiseRejectBlock) reject) { FIRAuthCredential *credential = [self getCredentialForProvider:provider token:authToken secret:authSecret]; if (credential == nil) { @@ -411,7 +503,16 @@ RCT_EXPORT_METHOD(link:(NSString *)provider authToken:(NSString *)authToken auth @param RCTPromiseRejectBlock reject @return */ -RCT_EXPORT_METHOD(reauthenticate:(NSString *)provider authToken:(NSString *)authToken authSecret:(NSString *)authSecret resolver:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject) { +RCT_EXPORT_METHOD(reauthenticate: + (NSString *) provider + authToken: + (NSString *) authToken + authSecret: + (NSString *) authSecret + resolver: + (RCTPromiseResolveBlock) resolve + rejecter: + (RCTPromiseRejectBlock) reject) { FIRAuthCredential *credential = [self getCredentialForProvider:provider token:authToken secret:authSecret]; if (credential == nil) { @@ -442,7 +543,12 @@ RCT_EXPORT_METHOD(reauthenticate:(NSString *)provider authToken:(NSString *)auth @param RCTPromiseRejectBlock reject @return */ -RCT_EXPORT_METHOD(fetchProvidersForEmail:(NSString *)email resolver:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject) { +RCT_EXPORT_METHOD(fetchProvidersForEmail: + (NSString *) email + resolver: + (RCTPromiseResolveBlock) resolve + rejecter: + (RCTPromiseRejectBlock) reject) { [[FIRAuth auth] fetchProvidersForEmail:email completion:^(NSArray *_Nullable providers, NSError *_Nullable error) { if (error) { [self promiseRejectAuthException:reject error:error]; @@ -484,7 +590,6 @@ RCT_EXPORT_METHOD(fetchProvidersForEmail:(NSString *)email resolver:(RCTPromiseR } - /** Resolve or reject a promise based on isError value @@ -492,7 +597,7 @@ RCT_EXPORT_METHOD(fetchProvidersForEmail:(NSString *)email resolver:(RCTPromiseR @param reject RCTPromiseRejectBlock @param isError BOOL */ -- (void) promiseNoUser:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject isError:(BOOL) isError { +- (void)promiseNoUser:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject isError:(BOOL)isError { if (isError) { reject(@"auth/no-current-user", @"No user currently signed in.", nil); } else { @@ -506,7 +611,7 @@ RCT_EXPORT_METHOD(fetchProvidersForEmail:(NSString *)email resolver:(RCTPromiseR @param reject RCTPromiseRejectBlock @param error NSError */ -- (void) promiseRejectAuthException:(RCTPromiseRejectBlock) reject error:(NSError *)error { +- (void)promiseRejectAuthException:(RCTPromiseRejectBlock)reject error:(NSError *)error { NSString *code = @"auth/unknown"; NSString *message = [error localizedDescription]; @@ -629,7 +734,7 @@ RCT_EXPORT_METHOD(fetchProvidersForEmail:(NSString *)email resolver:(RCTPromiseR @param reject RCTPromiseRejectBlock @param user FIRUser */ -- (void) promiseWithUser:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject user:(FIRUser *) user { +- (void)promiseWithUser:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject user:(FIRUser *)user { if (user) { NSDictionary *userDict = [self firebaseUserToDict:user]; resolve(userDict); @@ -646,17 +751,22 @@ RCT_EXPORT_METHOD(fetchProvidersForEmail:(NSString *)email resolver:(RCTPromiseR @param title sendEventWithName @param props <#props description#> */ -- (void) sendJSEvent:(NSString *)title props:(NSDictionary *)props { +- (void)sendJSEvent:(NSString *)title props:(NSDictionary *)props { @try { - if (self->listening) { - [self sendEventWithName:title body:props]; - } - } - @catch (NSException *error) { + [self sendEventWithName:title body:props]; + } @catch (NSException *error) { NSLog(@"An error occurred in sendJSEvent: %@", [error debugDescription]); } } +- (void)sendJSEventWithAppName:(NSString *)appName title:(NSString *)title props:(NSDictionary *)props { + [props setValue:appName forKey:@"appName"]; + @try { + [self sendEventWithName:title body:props]; + } @catch (NSException *error) { + NSLog(@"An error occurred in sendJSEvent: %@", [error debugDescription]); + } +} /** Converts an array of FIRUserInfo instances into the correct format to match the web sdk @@ -664,30 +774,30 @@ RCT_EXPORT_METHOD(fetchProvidersForEmail:(NSString *)email resolver:(RCTPromiseR @param providerData FIRUser.providerData @return NSArray */ -- (NSArray *) convertProviderData:(NSArray > *) providerData { +- (NSArray *)convertProviderData:(NSArray > *)providerData { NSMutableArray *output = [NSMutableArray array]; - for (id userInfo in providerData) { + for (id userInfo in providerData) { NSMutableDictionary *pData = [NSMutableDictionary dictionary]; if (userInfo.providerID != nil) { - [pData setValue: userInfo.providerID forKey:@"providerId"]; + [pData setValue:userInfo.providerID forKey:@"providerId"]; } if (userInfo.uid != nil) { - [pData setValue: userInfo.uid forKey:@"uid"]; + [pData setValue:userInfo.uid forKey:@"uid"]; } if (userInfo.displayName != nil) { - [pData setValue: userInfo.displayName forKey:@"displayName"]; + [pData setValue:userInfo.displayName forKey:@"displayName"]; } if (userInfo.photoURL != nil) { - [pData setValue: [userInfo.photoURL absoluteString] forKey:@"photoURL"]; + [pData setValue:[userInfo.photoURL absoluteString] forKey:@"photoURL"]; } if (userInfo.email != nil) { - [pData setValue: userInfo.email forKey:@"email"]; + [pData setValue:userInfo.email forKey:@"email"]; } [output addObject:pData]; @@ -702,21 +812,11 @@ RCT_EXPORT_METHOD(fetchProvidersForEmail:(NSString *)email resolver:(RCTPromiseR @param user FIRUser @return NSDictionary */ -- (NSDictionary *) firebaseUserToDict:(FIRUser *) user { - NSMutableDictionary *userDict = [ - @{ @"uid": user.uid, - @"email": user.email ? user.email : [NSNull null], - @"emailVerified": @(user.emailVerified), - @"isAnonymous": @(user.anonymous), - @"displayName": user.displayName ? user.displayName : [NSNull null], - @"refreshToken": user.refreshToken, - @"providerId": [user.providerID lowercaseString], - @"providerData": [self convertProviderData: user.providerData] - } mutableCopy - ]; +- (NSDictionary *)firebaseUserToDict:(FIRUser *)user { + NSMutableDictionary *userDict = [@{@"uid": user.uid, @"email": user.email ? user.email : [NSNull null], @"emailVerified": @(user.emailVerified), @"isAnonymous": @(user.anonymous), @"displayName": user.displayName ? user.displayName : [NSNull null], @"refreshToken": user.refreshToken, @"providerId": [user.providerID lowercaseString], @"providerData": [self convertProviderData:user.providerData]} mutableCopy]; if ([user valueForKey:@"photoURL"] != nil) { - [userDict setValue: [user.photoURL absoluteString] forKey:@"photoURL"]; + [userDict setValue:[user.photoURL absoluteString] forKey:@"photoURL"]; } return userDict; diff --git a/lib/constants.js b/lib/constants.js deleted file mode 100644 index 8e24dc81..00000000 --- a/lib/constants.js +++ /dev/null @@ -1,26 +0,0 @@ -import { reverseKeyValues } from './utils'; - -export const ConnectionResult = { - SUCCESS: 0, - SERVICE_MISSING: 1, - SERVICE_VERSION_UPDATE_REQUIRED: 2, - SERVICE_DISABLED: 3, - SIGN_IN_REQUIRED: 4, - INVALID_ACCOUNT: 5, - RESOLUTION_REQUIRED: 6, - NETWORK_ERROR: 7, - INTERNAL_ERROR: 8, - SERVICE_INVALID: 9, - DEVELOPER_ERROR: 10, - LICENSE_CHECK_FAILED: 11, - CANCELED: 13, - TIMEOUT: 14, - INTERRUPTED: 15, - API_UNAVAILABLE: 16, - SIGN_IN_FAILED: 17, - SERVICE_UPDATING: 18, - SERVICE_MISSING_PERMISSION: 19, - RESTRICTED_PROFILE: 20, -}; - -export const ConnectionResultReverse = reverseKeyValues(ConnectionResult); diff --git a/lib/firebase-app.js b/lib/firebase-app.js new file mode 100644 index 00000000..33344136 --- /dev/null +++ b/lib/firebase-app.js @@ -0,0 +1,94 @@ +import { NativeModules } from 'react-native'; + +import INTERNALS from './internals'; + +// modules +import Crash from './modules/crash'; +import Performance from './modules/perf'; +import RemoteConfig from './modules/config'; +import Analytics from './modules/analytics'; +import Auth, { statics as AuthStatics } from './modules/auth'; +import AdMob, { statics as AdMobStatics } from './modules/admob'; +import Storage, { statics as StorageStatics } from './modules/storage'; +import Database, { statics as DatabaseStatics } from './modules/database'; +import Messaging, { statics as MessagingStatics } from './modules/messaging'; + + +const FirebaseCoreModule = NativeModules.RNFirebase; + + +export default class FirebaseApp { + constructor(name: string, options: Object = {}) { + this._name = name; + this._namespaces = {}; + this._options = Object.assign({}, options); + + // native ios/android to confirm initialized + this._intialized = false; + } + + _initializeApp() { + FirebaseCoreModule.initializeApp(this._name, this._options, (error, result) => { + // todo check error/result + this._initialized = true; + }); + } + + get name() { + return this._name; + } + + get options() { + return Object.assign({}, this._options); + } + + delete() { + // todo + return Promise.resolve(); + } + + + /* + * MODULES + */ + + get auth() { + return this._staticsOrModuleInstance('auth', AuthStatics, Auth); + } + + get database() { + return this._staticsOrModuleInstance('database', DatabaseStatics, Database); + } + + get messaging() { + return this._staticsOrModuleInstance('messaging', MessagingStatics, Messaging); + } + + get storage() { + return this._staticsOrModuleInstance('storage', StorageStatics, Storage); + } + + + /** + * + * @param name + * @param statics + * @param InstanceClass + * @return {function()} + * @private + */ + _staticsOrModuleInstance(name, statics = {}, InstanceClass): Function { + const getInstance = () => { + const _name = `_${name}`; + + if (!this._namespaces[_name]) { + this._namespaces[_name] = new InstanceClass(this); + } + + return this._namespaces[_name]; + }; + + Object.assign(getInstance, statics); + return getInstance; + } +} diff --git a/lib/firebase.js b/lib/firebase.js index 95170e00..112b28bd 100644 --- a/lib/firebase.js +++ b/lib/firebase.js @@ -2,171 +2,186 @@ * @providesModule Firebase * @flow */ -import { NativeModules } from 'react-native'; +import { NativeModules, NativeEventEmitter } from 'react-native'; -import Log from './utils/log'; -import { isObject } from './utils'; +import { isObject, isString } from './utils'; -// modules +import INTERNALS from './internals'; +import PACKAGE from './../package.json'; +import FirebaseApp from './firebase-app'; + +// module imports import Auth, { statics as AuthStatics } from './modules/auth'; import Storage, { statics as StorageStatics } from './modules/storage'; import Database, { statics as DatabaseStatics } from './modules/database'; import Messaging, { statics as MessagingStatics } from './modules/messaging'; -import Analytics from './modules/analytics'; -import Crash from './modules/crash'; -import RemoteConfig from './modules/config'; -import Performance from './modules/perf'; -import AdMob, { statics as AdMobStatics } from './modules/admob'; -const instances: Object = { default: null }; -const FirebaseModule = NativeModules.RNFirebase; +const FirebaseCoreModule = NativeModules.RNFirebase; -/** - * @class Firebase - */ -export default class Firebase { - _log: ?Object; - _auth: ?Object; - _store: ?Object; - _storage: ?Object; - _database: ?Object; - _presence: ?Object; - _analytics: ?Object; - _constants: ?Object; - _messaging: ?Object; - _config: ?Object; - _crash: ?Object; - _perf: ?Object; - _admob: ?Object; +class FirebaseCore { + constructor() { + this._nativeEmitters = {}; - auth: Function; - crash: Function; - storage: Function; - database: Function; - analytics: Function; - messaging: Function; - config: Function; - perf: Function; - admob: Function; - - eventHandlers: Object; - debug: boolean; - options: { - errorOnMissingPlayServices: boolean, - debug?: boolean, - persistence?: boolean - }; - - /** - * - * @param options - */ - constructor(options: Object = {}) { - this.eventHandlers = {}; - this.debug = options.debug || false; - this.options = Object.assign({ errorOnMissingPlayServices: true, promptOnMissingPlayServices: true }, options); - - if (this.debug) { - Log.enable(this.debug); - } - - this._log = new Log('firebase'); - - if (!this.googleApiAvailability.isAvailable) { - if (this.options.promptOnMissingPlayServices && this.googleApiAvailability.isUserResolvableError) { - FirebaseModule.promptPlayServices(); - } else { - const error = `Google Play Services is required to run this application but no valid installation was found (Code ${this.googleApiAvailability.status}).`; - if (this.options.errorOnMissingPlayServices) { - throw new Error(error); - } else { - console.warn(error); - } - } - } - - this.auth = this._staticsOrInstance('auth', AuthStatics, Auth); - this.storage = this._staticsOrInstance('storage', StorageStatics, Storage); - this.database = this._staticsOrInstance('database', DatabaseStatics, Database); - this.messaging = this._staticsOrInstance('messaging', MessagingStatics, Messaging); - this.analytics = this._staticsOrInstance('analytics', {}, Analytics); - this.crash = this._staticsOrInstance('crash', {}, Crash); - this.config = this._staticsOrInstance('config', {}, RemoteConfig); - this.perf = this._staticsOrInstance('perf', {}, Performance); - this.admob = this._staticsOrInstance('admob', AdMobStatics, AdMob); - - // init auth to start listeners + // todo init other modules if available on native + // todo generalise this so adding other modules isn't so repetitive: if (NativeModules.RNFirebaseAuth) { - this.auth(); + this._nativeEmitters.auth = new NativeEventEmitter(NativeModules.RNFirebaseAuth); + this._subscribeForDistribution('onAuthStateChanged', this._nativeEmitters.auth); } + // ... } /** - * Support web version of initApp. + * Web SDK initializeApp + * * @param options * @param name - * @returns {*} + * @return {*} */ - static initializeApp(options: Object = {}, name: string = 'default') { + initializeApp(options: Object = {}, name: string): FirebaseApp { if (!isObject(options)) { - throw new Error('Firebase.initializeApp(options <- requires a configuration object'); + throw new Error(INTERNALS.STRINGS.ERROR_INIT_OBJECT); } - if (typeof name !== 'string') { - throw new Error('Firebase.initializeApp(options, name <- requires a string value'); + // todo validate required options + + if (name && !isString(name)) { + throw new Error(INTERNALS.STRINGS.ERROR_INIT_STRING_NAME); } - if (name !== 'default') { - throw new Error('RNFirebase currently only supports one instance of firebase - the default one.'); + const _name = (name || INTERNALS.STRINGS.DEFAULT_APP_NAME).toUpperCase(); + + if (!INTERNALS.APPS[_name]) { + INTERNALS.APPS[_name] = new FirebaseApp(_name, options); + INTERNALS.APPS[_name]._initializeApp(); } - if (!instances[name]) instances[name] = new Firebase(options); - return instances[name]; - } - - get apps(): Array { - return Object.keys(instances); - } - - /** - * Returns androids GoogleApiAvailability status and message if available. - * @returns {GoogleApiAvailabilityType|{isAvailable: boolean, status: number}} - */ - get googleApiAvailability(): GoogleApiAvailabilityType { - // if not available then return a fake object for ios - saves doing platform specific logic. - return FirebaseModule.googleApiAvailability || { isAvailable: true, status: 0 }; - } - - /** - * Logger - */ - get log(): Log { - return this._log; + return INTERNALS.APPS[_name]; } /** + * Retrieves a Firebase app instance. + * + * When called with no arguments, the default app is returned. + * When an app name is provided, the app corresponding to that name is returned. * * @param name - * @param statics - * @param InstanceClass - * @returns {function()} + * @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; + } + + /** + * A (read-only) array of all initialized apps. + * @return {Array} + */ + get apps(): Array { + return Object.values(INTERNALS.APPS); + } + + /** + * The current RNFirebase SDK version. + */ + get SDK_VERSION() { + return PACKAGE.version; + } + + /** + * Returns props from the android GoogleApiAvailability sdk + * @android + * @return {RNFirebase.GoogleApiAvailabilityType|{isAvailable: boolean, status: number}} + */ + get googleApiAvailabilty(): GoogleApiAvailabilityType { + return FirebaseCoreModule.googleApiAvailability || { isAvailable: true, status: 0 }; + } + + /* + * MODULES + */ + + get auth() { + return this._appNamespaceOrStatics('auth', AuthStatics, Auth); + } + + get database() { + return this._appNamespaceOrStatics('database', DatabaseStatics, Database); + } + + get messaging() { + return this._appNamespaceOrStatics('messaging', MessagingStatics, Messaging); + } + + get storage() { + return this._appNamespaceOrStatics('storage', StorageStatics, Storage); + } + + + /* + * CONFIG METHODS + */ + + /** + * Set the global logging level for all logs. + * + * @param booleanOrDebugString + */ + setLogLevel(booleanOrDebugString) { + INTERNALS.OPTIONS.logLevel = booleanOrDebugString; + Log.setLevel(booleanOrDebugString); + } + + /* + * 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 */ - _staticsOrInstance(name, statics, InstanceClass): Function { - const getInstance = () => { - const internalPropName = `_${name}`; - - // $FlowFixMe - if (!this[internalPropName]) { - // $FlowFixMe - this[internalPropName] = new InstanceClass(this); + _subscribeForDistribution(eventName, nativeEmitter) { + 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); } + }); + } - // $FlowFixMe - return this[internalPropName]; + /** + * + * @param namespace + * @param statics + * @return {function(FirebaseApp=)} + * @private + */ + _appNamespaceOrStatics(namespace, statics = {}): Function { + 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); + return INTERNALS.APPS[_app.name][namespace](_app); }; - Object.assign(getInstance, statics || {}); - return getInstance; + Object.assign(getNamespace, statics); + return getNamespace; } + } + +export default new FirebaseCore(); diff --git a/lib/internals.js b/lib/internals.js new file mode 100644 index 00000000..3d7fd96b --- /dev/null +++ b/lib/internals.js @@ -0,0 +1,61 @@ +import EventEmitter from 'EventEmitter'; + +const DEFAULT_APP_NAME = 'DEFAULT'; + +// am still thinking ;p +export default { + // default options + OPTIONS: { + logLevel: 'warn', + }, + + // track all initialized firebase apps + APPS: { + [DEFAULT_APP_NAME]: null, + }, + + STRINGS: { + ERROR_INIT_OBJECT: 'Firebase.initializeApp(options <-- requires a valid configuration object.', + ERROR_INIT_STRING_NAME: 'Firebase.initializeApp(options, name <-- requires a valid string value.', + + /** + * @param moduleName + * @return {string} + */ + ERROR_MISSING_MODULE(moduleName) { + return `Attempted to call a method for a module that is not installed natively (${moduleName}).`; + }, + + /** + * @return {string} + */ + ERROR_APP_NOT_INIT(appName) { + return `The [${{ appName }}] firebase app has not been initialized!`; + }, + + /** + * @return {string} + */ + ERROR_NOT_APP(namespace) { + return `Invalid FirebaseApp instance passed to firebase.${namespace}(app <--).`; + }, + + DEFAULT_APP_NAME, + }, + + + SharedEventEmitter: new EventEmitter(), + + + // internal utils + deleteApp(name: String) { + const app = this.APPS[name]; + if (!app) return Promise.resolve(); + + // 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/modules/auth/index.js b/lib/modules/auth/index.js index e9621e30..706c1ab2 100644 --- a/lib/modules/auth/index.js +++ b/lib/modules/auth/index.js @@ -2,8 +2,7 @@ import { NativeModules, NativeEventEmitter } from 'react-native'; import User from './user'; -import { Base } from './../base'; -import { nativeSDKMissing } from './../../utils'; +import ModuleBase from './../../utils/ModuleBase'; // providers import EmailAuthProvider from './providers/Email'; @@ -13,26 +12,19 @@ import TwitterAuthProvider from './providers/Twitter'; import GithubAuthProvider from './providers/Github'; const FirebaseAuth = NativeModules.RNFirebaseAuth; -const FirebaseAuthEvt = FirebaseAuth && new NativeEventEmitter(FirebaseAuth); -export default class Auth extends Base { +export default class Auth extends ModuleBase { _user: User | null; _authResult: AuthResultType | null; authenticated: boolean; - constructor(firebase: Object, options: Object = {}) { - super(firebase, options); - if (!FirebaseAuth) { - return nativeSDKMissing('auth'); - } - + constructor(firebaseApp: Object, options: Object = {}) { + super(firebaseApp, options, 'auth'); this._user = null; this._authResult = null; this.authenticated = false; - - // start listening immediately for auth changes - FirebaseAuthEvt.addListener('onAuthStateChanged', this._onAuthStateChanged.bind(this)); - FirebaseAuth.addAuthStateListener(); + this.addListener('onAuthStateChanged', this._onAuthStateChanged.bind(this)); + this._native.addAuthStateListener(); // this is the native function on ios/android: } /** diff --git a/lib/modules/base.js b/lib/modules/base.js index 1565645b..f1ea818f 100644 --- a/lib/modules/base.js +++ b/lib/modules/base.js @@ -2,64 +2,7 @@ * @flow */ -import EventEmitter from 'react-native/Libraries/EventEmitter/EventEmitter'; -import Log from '../utils/log'; - -const logs = {}; -const SharedEventEmitter = new EventEmitter(); - -export class Base { - - /** - * Return a namespaced instance of Log - * @returns {*} - */ - get log(): Log { - if (logs[this.namespace]) return logs[this.namespace]; - - // todo grab log level from global config provider (still todo); - return logs[this.namespace] = new Log(this.namespace, '*'); - } - - /* - * Proxy functions to shared event emitter instance - * https://github.com/facebook/react-native/blob/master/Libraries/EventEmitter/EventEmitter.js - */ - - get sharedEventEmitter () { - return SharedEventEmitter; - } - - get addListener() { - return SharedEventEmitter.addListener.bind(SharedEventEmitter); - } - - get on() { - return SharedEventEmitter.addListener.bind(SharedEventEmitter); - } - - get emit() { - return SharedEventEmitter.emit.bind(SharedEventEmitter); - } - - get listeners() { - return SharedEventEmitter.listeners.bind(SharedEventEmitter); - } - - hasListeners(eventType: string): Boolean { - const subscriptions = SharedEventEmitter._subscriber.getSubscriptionsForType(eventType); - return subscriptions && subscriptions.length; - } - - get removeListener() { - return SharedEventEmitter.removeListener.bind(SharedEventEmitter); - } - - get removeAllListeners() { - return SharedEventEmitter.removeAllListeners.bind(SharedEventEmitter); - } -} - +// todo move out export class ReferenceBase extends Base { constructor(path: string) { super(); @@ -72,7 +15,7 @@ export class ReferenceBase extends Base { * @type {String} * {@link https://firebase.google.com/docs/reference/js/firebase.database.Reference#key} */ - get key(): string|null { + get key(): string | null { return this.path === '/' ? null : this.path.substring(this.path.lastIndexOf('/') + 1); } } diff --git a/lib/utils/ModuleBase.js b/lib/utils/ModuleBase.js new file mode 100644 index 00000000..f75e2f34 --- /dev/null +++ b/lib/utils/ModuleBase.js @@ -0,0 +1,83 @@ +/** + * @flow + */ +import { NativeModules } from 'react-native'; +import Log from '../utils/log'; +import { nativeWithApp } from './../utils'; +import INTERNALS from './../internals'; + +// logging instances + +const logs = {}; + +export default class ModuleBase { + constructor(firebaseApp, options, moduleName) { + this._options = Object.assign({}, options); + this._module = moduleName; + this._firebaseApp = firebaseApp; + this._appName = firebaseApp.name; + this._namespace = `${this._appName}:${this._module}`; + + // check if native module exists as all native modules are now optionally part of build + const nativeModule = NativeModules[`RNFirebase${moduleName}`]; + + if (!nativeModule) { + throw new Error(INTERNALS.STRINGS.ERROR_MISSING_MODULE(moduleName)); + } + + // used by the modules that extend ModuleBase to access their native module counterpart + this._native = nativeWithApp(this._appName, nativeModule); + } + + /** + * Returns the FirebaseApp instance for current module + * @return {*} + */ + get app() { + 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() { + return INTERNALS.SharedEventEmitter; + } + + get addListener() { + return INTERNALS.SharedEventEmitter.addListener.bind(INTERNALS.SharedEventEmitter); + } + + get on() { + return INTERNALS.SharedEventEmitter.addListener.bind(INTERNALS.SharedEventEmitter); + } + + get emit() { + return INTERNALS.SharedEventEmitter.emit.bind(INTERNALS.SharedEventEmitter); + } + + get listeners() { + return INTERNALS.SharedEventEmitter.listeners.bind(INTERNALS.SharedEventEmitter); + } + + hasListeners(eventType: string): Boolean { + const subscriptions = INTERNALS.SharedEventEmitter._subscriber.getSubscriptionsForType(eventType); + return subscriptions && subscriptions.length; + } + + get removeListener() { + return INTERNALS.SharedEventEmitter.removeListener.bind(INTERNALS.SharedEventEmitter); + } + + get removeAllListeners() { + return INTERNALS.SharedEventEmitter.removeAllListeners.bind(INTERNALS.SharedEventEmitter); + } +} diff --git a/lib/utils/index.js b/lib/utils/index.js index 40dcc3ce..f5cd1389 100644 --- a/lib/utils/index.js +++ b/lib/utils/index.js @@ -1,5 +1,6 @@ import { Platform } from 'react-native'; +// todo cleanup unused utilities from legacy code /** * @flow */ @@ -109,6 +110,15 @@ export function isFunction(item?: any): boolean { return Boolean(item && typeof item === 'function'); } +/** + * Simple is string check + * @param value + * @return {boolean} + */ +export function isString(value): Boolean { + return typeof value === 'string'; +} + /** * * @param string @@ -334,3 +344,23 @@ export function nativeToJSError(code: string, message: string) { error.code = code; return error; } + +/** + * Prepends appName arg to all native method calls + * @param appName + * @param NativeModule + */ +export function nativeWithApp(appName, NativeModule) { + 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; +} + diff --git a/lib/utils/log.js b/lib/utils/log.js index 3ddab6d6..b43dbe73 100644 --- a/lib/utils/log.js +++ b/lib/utils/log.js @@ -5,36 +5,15 @@ import { windowOrGlobal } from './'; if (!window.localStorage) window.localStorage = {}; })(windowOrGlobal); +// clean up time + export default class Log { - constructor(namespace) { - this._namespace = namespace || 'RNFirebase'; - require('bows').config({ padLength: 20 }); - this.loggers = {}; + static createLogger(namespace) { + return require('bows')(namespace); } - get warn() { - return this._createOrGetLogger('warn'); - } - - get info() { - return this._createOrGetLogger('info'); - } - - get error() { - return this._createOrGetLogger('error'); - } - - get debug() { - return this._createOrGetLogger('debug'); - } - - static enable(booleanOrStringDebug) { - window.localStorage.debug = booleanOrStringDebug; - window.localStorage.debugColors = !!window.localStorage.debug; - } - - _createOrGetLogger(level) { - if (!this.loggers[level]) this.loggers[level] = require('bows')(this._namespace, `[${level}]`); - return this.loggers[level]; + static setLevel(booleanOrDebugString) { + window.localStorage.debug = booleanOrDebugString; + window.localStorage.debugColors = !!booleanOrDebugString; } }