started work on multiple app initialization

This commit is contained in:
Salakar
2017-06-29 17:24:34 +01:00
parent 7db8b07f34
commit 80ae8425ce
14 changed files with 651 additions and 326 deletions

View File

@@ -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());

View File

@@ -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<String, FirebaseAuth.AuthStateListener> 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);
}

View File

@@ -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

View File

@@ -9,7 +9,7 @@
@interface RNFirebaseAuth : RCTEventEmitter <RCTBridgeModule> {
FIRAuthStateDidChangeListenerHandle authListenerHandle;
Boolean listening;
NSMutableDictionary *authStateHandlers;
}
@end

View File

@@ -1,8 +1,10 @@
#import "RNFirebaseAuth.h"
#import "RNFirebaseEvents.h"
#import "RCTDefines.h"
#if __has_include(<FirebaseAuth/FIRAuth.h>)
@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<NSString *> *_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 <NSObject *> *) convertProviderData:(NSArray <id<FIRUserInfo>> *) providerData {
- (NSArray <NSObject *> *)convertProviderData:(NSArray <id <FIRUserInfo>> *)providerData {
NSMutableArray *output = [NSMutableArray array];
for (id<FIRUserInfo> userInfo in providerData) {
for (id <FIRUserInfo> 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;

View File

@@ -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);

94
lib/firebase-app.js Normal file
View File

@@ -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;
}
}

View File

@@ -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<string> {
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<Object> {
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();

61
lib/internals.js Normal file
View File

@@ -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;
});
},
};

View File

@@ -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:
}
/**

View File

@@ -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);
}
}

83
lib/utils/ModuleBase.js Normal file
View File

@@ -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);
}
}

View File

@@ -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;
}

View File

@@ -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;
}
}