Merge commit '36c936779b9badf72a0a3d789efafd2dcdb5ba36'
# Conflicts: # ios/RNFirebase.xcodeproj/project.pbxproj # package.json
This commit is contained in:
commit
5e12700c35
|
@ -33,6 +33,7 @@ import com.google.firebase.auth.ActionCodeResult;
|
|||
import com.google.firebase.auth.AuthCredential;
|
||||
import com.google.firebase.auth.AuthResult;
|
||||
import com.google.firebase.auth.FirebaseAuthInvalidCredentialsException;
|
||||
import com.google.firebase.auth.FirebaseAuthProvider;
|
||||
import com.google.firebase.auth.GithubAuthProvider;
|
||||
import com.google.firebase.auth.PhoneAuthCredential;
|
||||
import com.google.firebase.auth.PhoneAuthProvider;
|
||||
|
@ -54,6 +55,7 @@ import io.invertase.firebase.Utils;
|
|||
class RNFirebaseAuth extends ReactContextBaseJavaModule {
|
||||
private static final String TAG = "RNFirebaseAuth";
|
||||
private String mVerificationId;
|
||||
private PhoneAuthCredential mCredential;
|
||||
private ReactContext mReactContext;
|
||||
private HashMap<String, FirebaseAuth.AuthStateListener> mAuthListeners = new HashMap<>();
|
||||
private HashMap<String, FirebaseAuth.IdTokenListener> mIdTokenListeners = new HashMap<>();
|
||||
|
@ -738,10 +740,16 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule {
|
|||
|
||||
Log.d(TAG, "verifyPhoneNumber:" + phoneNumber);
|
||||
|
||||
// Reset the credential
|
||||
mCredential = null;
|
||||
|
||||
PhoneAuthProvider.OnVerificationStateChangedCallbacks callbacks = new PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
|
||||
|
||||
@Override
|
||||
public void onVerificationCompleted(final PhoneAuthCredential phoneAuthCredential) {
|
||||
// Cache the credential to protect against null verificationId
|
||||
mCredential = phoneAuthCredential;
|
||||
|
||||
Log.d(TAG, "verifyPhoneNumber:verification:onVerificationCompleted");
|
||||
WritableMap state = Arguments.createMap();
|
||||
|
||||
|
@ -1068,6 +1076,15 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule {
|
|||
case "github.com":
|
||||
return GithubAuthProvider.getCredential(authToken);
|
||||
case "phone":
|
||||
// If the phone number is auto-verified quickly, then the verificationId can be null
|
||||
// We cached the credential as part of the verifyPhoneNumber request to be re-used here
|
||||
// if possible
|
||||
if (authToken == null && mCredential != null) {
|
||||
PhoneAuthCredential credential = mCredential;
|
||||
// Reset the cached credential
|
||||
mCredential = null;
|
||||
return credential;
|
||||
}
|
||||
return PhoneAuthProvider.getCredential(authToken, authSecret);
|
||||
case "password":
|
||||
return EmailAuthProvider.getCredential(authToken, authSecret);
|
||||
|
@ -1282,12 +1299,12 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule {
|
|||
* @param providerData List<UserInfo> user.getProviderData()
|
||||
* @return WritableArray array
|
||||
*/
|
||||
private WritableArray convertProviderData(List<? extends UserInfo> providerData) {
|
||||
private WritableArray convertProviderData(List<? extends UserInfo> providerData, FirebaseUser user) {
|
||||
WritableArray output = Arguments.createArray();
|
||||
for (UserInfo userInfo : providerData) {
|
||||
// remove 'firebase' provider data - android fb sdk
|
||||
// should not be returning this as the ios/web ones don't
|
||||
if (!userInfo.getProviderId().equals("firebase")) {
|
||||
if (!FirebaseAuthProvider.PROVIDER_ID.equals(userInfo.getProviderId())) {
|
||||
WritableMap userInfoMap = Arguments.createMap();
|
||||
userInfoMap.putString("providerId", userInfo.getProviderId());
|
||||
userInfoMap.putString("uid", userInfo.getUid());
|
||||
|
@ -1295,20 +1312,34 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule {
|
|||
|
||||
final Uri photoUrl = userInfo.getPhotoUrl();
|
||||
|
||||
if (photoUrl != null) {
|
||||
if (photoUrl != null && !"".equals(photoUrl)) {
|
||||
userInfoMap.putString("photoURL", photoUrl.toString());
|
||||
} else {
|
||||
userInfoMap.putNull("photoURL");
|
||||
}
|
||||
|
||||
final String phoneNumber = userInfo.getPhoneNumber();
|
||||
if (phoneNumber != null) {
|
||||
// The Android SDK is missing the phone number property for the phone provider when the
|
||||
// user first signs up using their phone number. Use the phone number from the user
|
||||
// object instead
|
||||
if (PhoneAuthProvider.PROVIDER_ID.equals(userInfo.getProviderId())
|
||||
&& (userInfo.getPhoneNumber() == null || "".equals(userInfo.getPhoneNumber()))) {
|
||||
userInfoMap.putString("phoneNumber", user.getPhoneNumber());
|
||||
} else if (phoneNumber != null && !"".equals(phoneNumber)) {
|
||||
userInfoMap.putString("phoneNumber", phoneNumber);
|
||||
} else {
|
||||
userInfoMap.putNull("phoneNumber");
|
||||
}
|
||||
|
||||
userInfoMap.putString("email", userInfo.getEmail());
|
||||
// The Android SDK is missing the email property for the email provider, so we use UID instead
|
||||
if (EmailAuthProvider.PROVIDER_ID.equals(userInfo.getProviderId())
|
||||
&& (userInfo.getEmail() == null || "".equals(userInfo.getEmail()))) {
|
||||
userInfoMap.putString("email", userInfo.getUid());
|
||||
} else if (userInfo.getEmail() != null && !"".equals(userInfo.getEmail())) {
|
||||
userInfoMap.putString("email", userInfo.getEmail());
|
||||
} else {
|
||||
userInfoMap.putNull("email");
|
||||
}
|
||||
|
||||
output.pushMap(userInfoMap);
|
||||
}
|
||||
|
@ -1339,31 +1370,31 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule {
|
|||
userMap.putBoolean("emailVerified", verified);
|
||||
userMap.putBoolean("isAnonymous", user.isAnonymous());
|
||||
|
||||
if (email != null) {
|
||||
if (email != null && !"".equals(email)) {
|
||||
userMap.putString("email", email);
|
||||
} else {
|
||||
userMap.putNull("email");
|
||||
}
|
||||
|
||||
if (name != null) {
|
||||
if (name != null && !"".equals(name)) {
|
||||
userMap.putString("displayName", name);
|
||||
} else {
|
||||
userMap.putNull("displayName");
|
||||
}
|
||||
|
||||
if (photoUrl != null) {
|
||||
if (photoUrl != null && !"".equals(photoUrl)) {
|
||||
userMap.putString("photoURL", photoUrl.toString());
|
||||
} else {
|
||||
userMap.putNull("photoURL");
|
||||
}
|
||||
|
||||
if (phoneNumber != null) {
|
||||
if (phoneNumber != null && !"".equals(phoneNumber)) {
|
||||
userMap.putString("phoneNumber", phoneNumber);
|
||||
} else {
|
||||
userMap.putNull("phoneNumber");
|
||||
}
|
||||
|
||||
userMap.putArray("providerData", convertProviderData(user.getProviderData()));
|
||||
userMap.putArray("providerData", convertProviderData(user.getProviderData(), user));
|
||||
|
||||
return userMap;
|
||||
}
|
||||
|
|
|
@ -399,7 +399,7 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
|
|||
*/
|
||||
@ReactMethod
|
||||
public void on(String appName, ReadableMap props) {
|
||||
getInternalReferenceForApp(appName, props)
|
||||
getCachedInternalReferenceForApp(appName, props)
|
||||
.on(
|
||||
props.getString("eventType"),
|
||||
props.getMap("registration")
|
||||
|
@ -481,19 +481,13 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
|
|||
* @return
|
||||
*/
|
||||
private RNFirebaseDatabaseReference getInternalReferenceForApp(String appName, String key, String path, ReadableArray modifiers) {
|
||||
RNFirebaseDatabaseReference existingRef = references.get(key);
|
||||
|
||||
if (existingRef == null) {
|
||||
existingRef = new RNFirebaseDatabaseReference(
|
||||
getReactApplicationContext(),
|
||||
appName,
|
||||
key,
|
||||
path,
|
||||
modifiers
|
||||
);
|
||||
}
|
||||
|
||||
return existingRef;
|
||||
return new RNFirebaseDatabaseReference(
|
||||
getReactApplicationContext(),
|
||||
appName,
|
||||
key,
|
||||
path,
|
||||
modifiers
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -503,7 +497,7 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
|
|||
* @param props
|
||||
* @return
|
||||
*/
|
||||
private RNFirebaseDatabaseReference getInternalReferenceForApp(String appName, ReadableMap props) {
|
||||
private RNFirebaseDatabaseReference getCachedInternalReferenceForApp(String appName, ReadableMap props) {
|
||||
String key = props.getString("key");
|
||||
String path = props.getString("path");
|
||||
ReadableArray modifiers = props.getArray("modifiers");
|
||||
|
@ -511,14 +505,7 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
|
|||
RNFirebaseDatabaseReference existingRef = references.get(key);
|
||||
|
||||
if (existingRef == null) {
|
||||
existingRef = new RNFirebaseDatabaseReference(
|
||||
getReactApplicationContext(),
|
||||
appName,
|
||||
key,
|
||||
path,
|
||||
modifiers
|
||||
);
|
||||
|
||||
existingRef = getInternalReferenceForApp(appName, key, path, modifiers);
|
||||
references.put(key, existingRef);
|
||||
}
|
||||
|
||||
|
|
|
@ -31,8 +31,6 @@ import io.invertase.firebase.Utils;
|
|||
|
||||
public class FirestoreSerialize {
|
||||
private static final String TAG = "FirestoreSerialize";
|
||||
private static final DateFormat READ_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
|
||||
private static final DateFormat WRITE_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
|
||||
private static final String KEY_CHANGES = "changes";
|
||||
private static final String KEY_DATA = "data";
|
||||
private static final String KEY_DOC_CHANGE_DOCUMENT = "document";
|
||||
|
@ -43,12 +41,6 @@ public class FirestoreSerialize {
|
|||
private static final String KEY_METADATA = "metadata";
|
||||
private static final String KEY_PATH = "path";
|
||||
|
||||
static {
|
||||
// Javascript Date.toISOString is always formatted to UTC
|
||||
// We set the read TimeZone to UTC to account for this
|
||||
READ_DATE_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a DocumentSnapshot instance into a React Native WritableMap
|
||||
*
|
||||
|
@ -220,7 +212,7 @@ public class FirestoreSerialize {
|
|||
typeMap.putMap("value", geoPoint);
|
||||
} else if (value instanceof Date) {
|
||||
typeMap.putString("type", "date");
|
||||
typeMap.putString("value", WRITE_DATE_FORMAT.format((Date) value));
|
||||
typeMap.putDouble("value", ((Date) value).getTime());
|
||||
} else {
|
||||
// TODO: Changed to log an error rather than crash - is this correct?
|
||||
Log.e(TAG, "buildTypeMap: Cannot convert object of type " + value.getClass());
|
||||
|
@ -275,13 +267,8 @@ public class FirestoreSerialize {
|
|||
ReadableMap geoPoint = typeMap.getMap("value");
|
||||
return new GeoPoint(geoPoint.getDouble("latitude"), geoPoint.getDouble("longitude"));
|
||||
} else if ("date".equals(type)) {
|
||||
try {
|
||||
String date = typeMap.getString("value");
|
||||
return READ_DATE_FORMAT.parse(date);
|
||||
} catch (ParseException exception) {
|
||||
Log.e(TAG, "parseTypeMap", exception);
|
||||
return null;
|
||||
}
|
||||
Double time = typeMap.getDouble("value");
|
||||
return new Date(time.longValue());
|
||||
} else if ("fieldvalue".equals(type)) {
|
||||
String value = typeMap.getString("value");
|
||||
if ("delete".equals(value)) {
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
839D91741EF3E20B0077C7C8 /* RNFirebaseMessaging.m in Sources */ = {isa = PBXBuildFile; fileRef = 839D91651EF3E20A0077C7C8 /* RNFirebaseMessaging.m */; };
|
||||
839D91751EF3E20B0077C7C8 /* RNFirebasePerformance.m in Sources */ = {isa = PBXBuildFile; fileRef = 839D91681EF3E20A0077C7C8 /* RNFirebasePerformance.m */; };
|
||||
839D91761EF3E20B0077C7C8 /* RNFirebaseStorage.m in Sources */ = {isa = PBXBuildFile; fileRef = 839D916B1EF3E20A0077C7C8 /* RNFirebaseStorage.m */; };
|
||||
83C3EEEE1FA1EACC00B64D3C /* RNFirebaseUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = 83C3EEEC1FA1EACC00B64D3C /* RNFirebaseUtil.m */; };
|
||||
D950369E1D19C77400F7094D /* RNFirebase.m in Sources */ = {isa = PBXBuildFile; fileRef = D950369D1D19C77400F7094D /* RNFirebase.m */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
|
@ -85,6 +86,8 @@
|
|||
839D916A1EF3E20A0077C7C8 /* RNFirebaseStorage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNFirebaseStorage.h; sourceTree = "<group>"; };
|
||||
839D916B1EF3E20A0077C7C8 /* RNFirebaseStorage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFirebaseStorage.m; sourceTree = "<group>"; };
|
||||
839D91771EF3E22F0077C7C8 /* RNFirebaseEvents.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNFirebaseEvents.h; path = RNFirebase/RNFirebaseEvents.h; sourceTree = "<group>"; };
|
||||
83C3EEEC1FA1EACC00B64D3C /* RNFirebaseUtil.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNFirebaseUtil.m; path = RNFirebase/RNFirebaseUtil.m; sourceTree = "<group>"; };
|
||||
83C3EEED1FA1EACC00B64D3C /* RNFirebaseUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNFirebaseUtil.h; path = RNFirebase/RNFirebaseUtil.h; sourceTree = "<group>"; };
|
||||
D950369C1D19C77400F7094D /* RNFirebase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNFirebase.h; path = RNFirebase/RNFirebase.h; sourceTree = "<group>"; };
|
||||
D950369D1D19C77400F7094D /* RNFirebase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNFirebase.m; path = RNFirebase/RNFirebase.m; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
@ -131,11 +134,13 @@
|
|||
8376F70D1F7C141500D45A85 /* firestore */,
|
||||
839D91631EF3E20A0077C7C8 /* messaging */,
|
||||
839D91661EF3E20A0077C7C8 /* perf */,
|
||||
134814211AA4EA7D00B7C361 /* Products */,
|
||||
134814211AA4EA7D00B7C361 /* storage */,
|
||||
D950369C1D19C77400F7094D /* RNFirebase.h */,
|
||||
D950369D1D19C77400F7094D /* RNFirebase.m */,
|
||||
839D91771EF3E22F0077C7C8 /* RNFirebaseEvents.h */,
|
||||
839D91691EF3E20A0077C7C8 /* storage */,
|
||||
83C3EEED1FA1EACC00B64D3C /* RNFirebaseUtil.h */,
|
||||
83C3EEEC1FA1EACC00B64D3C /* RNFirebaseUtil.m */,
|
||||
134814211AA4EA7D00B7C361 /* Products */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
|
@ -327,6 +332,7 @@
|
|||
D950369E1D19C77400F7094D /* RNFirebase.m in Sources */,
|
||||
839D91731EF3E20B0077C7C8 /* RNFirebaseDatabase.m in Sources */,
|
||||
8323CF071F6FBD870071420B /* NativeExpressComponent.m in Sources */,
|
||||
83C3EEEE1FA1EACC00B64D3C /* RNFirebaseUtil.m in Sources */,
|
||||
839D91721EF3E20B0077C7C8 /* RNFirebaseCrash.m in Sources */,
|
||||
839D91741EF3E20B0077C7C8 /* RNFirebaseMessaging.m in Sources */,
|
||||
839D91751EF3E20B0077C7C8 /* RNFirebasePerformance.m in Sources */,
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
#ifndef RNFirebaseUtil_h
|
||||
#define RNFirebaseUtil_h
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <React/RCTEventEmitter.h>
|
||||
|
||||
@interface RNFirebaseUtil : NSObject
|
||||
|
||||
+ (void)sendJSEvent:(RCTEventEmitter *)emitter name:(NSString *)name body:(NSDictionary *)body;
|
||||
+ (void)sendJSEventWithAppName:(RCTEventEmitter *)emitter appName:(NSString *)appName name:(NSString *)name body:(NSDictionary *)body;
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
|
@ -0,0 +1,25 @@
|
|||
#import "RNFirebaseUtil.h"
|
||||
|
||||
@implementation RNFirebaseUtil
|
||||
|
||||
+ (void)sendJSEvent:(RCTEventEmitter *)emitter name:(NSString *)name body:(NSDictionary *)body {
|
||||
@try {
|
||||
// TODO: Temporary fix for https://github.com/invertase/react-native-firebase/issues/233
|
||||
// until a better solution comes around
|
||||
if (emitter.bridge) {
|
||||
[emitter sendEventWithName:name body:body];
|
||||
}
|
||||
} @catch (NSException *error) {
|
||||
NSLog(@"An error occurred in sendJSEvent: %@", [error debugDescription]);
|
||||
}
|
||||
}
|
||||
|
||||
+ (void)sendJSEventWithAppName:(RCTEventEmitter *)emitter appName:(NSString *)appName name:(NSString *)name body:(NSDictionary *)body {
|
||||
// Add the appName to the body
|
||||
NSMutableDictionary *newBody = [body mutableCopy];
|
||||
newBody[@"appName"] = appName;
|
||||
|
||||
[RNFirebaseUtil sendJSEvent:emitter name:name body:newBody];
|
||||
}
|
||||
|
||||
@end
|
|
@ -1,4 +1,5 @@
|
|||
#import "RNFirebaseAdMobInterstitial.h"
|
||||
#import "RNFirebaseUtil.h"
|
||||
|
||||
@implementation RNFirebaseAdMobInterstitial
|
||||
|
||||
|
@ -31,7 +32,7 @@
|
|||
}
|
||||
|
||||
- (void)sendJSEvent:(NSString *)type payload:(NSDictionary *)payload {
|
||||
[_delegate sendEventWithName:ADMOB_INTERSTITIAL_EVENT body:@{
|
||||
[RNFirebaseUtil sendJSEvent:self.delegate name:ADMOB_INTERSTITIAL_EVENT body:@{
|
||||
@"type": type,
|
||||
@"adUnit": _adUnitID,
|
||||
@"payload": payload
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#import "RNFirebaseAdMobRewardedVideo.h"
|
||||
#import "RNFirebaseUtil.h"
|
||||
|
||||
@implementation RNFirebaseAdMobRewardedVideo
|
||||
|
||||
|
@ -31,7 +32,7 @@
|
|||
}
|
||||
|
||||
- (void)sendJSEvent:(NSString *)type payload:(NSDictionary *)payload {
|
||||
[_delegate sendEventWithName:ADMOB_REWARDED_VIDEO_EVENT body:@{
|
||||
[RNFirebaseUtil sendJSEvent:self.delegate name:ADMOB_REWARDED_VIDEO_EVENT body:@{
|
||||
@"type": type,
|
||||
@"adUnit": _adUnitID,
|
||||
@"payload": payload
|
||||
|
@ -73,4 +74,4 @@
|
|||
|
||||
#endif
|
||||
|
||||
@end
|
||||
@end
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#import "RNFirebaseAuth.h"
|
||||
#import "RNFirebaseEvents.h"
|
||||
#import "RNFirebaseUtil.h"
|
||||
#import "RCTDefines.h"
|
||||
|
||||
|
||||
|
@ -28,9 +29,9 @@ RCT_EXPORT_METHOD(addAuthStateListener:
|
|||
FIRApp *firApp = [FIRApp appNamed:appName];
|
||||
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]} mutableCopy]];
|
||||
[RNFirebaseUtil sendJSEventWithAppName:self appName:appName name:AUTH_CHANGED_EVENT body:@{@"authenticated": @(true), @"user": [self firebaseUserToDict:user]}];
|
||||
} else {
|
||||
[self sendJSEventWithAppName:appName title:AUTH_CHANGED_EVENT props:[@{@"authenticated": @(false)} mutableCopy]];
|
||||
[RNFirebaseUtil sendJSEventWithAppName:self appName:appName name:AUTH_CHANGED_EVENT body:@{@"authenticated": @(false)}];
|
||||
}
|
||||
}];
|
||||
|
||||
|
@ -63,9 +64,9 @@ RCT_EXPORT_METHOD(addIdTokenListener:
|
|||
FIRApp *firApp = [FIRApp appNamed:appName];
|
||||
FIRIDTokenDidChangeListenerHandle newListenerHandle = [[FIRAuth authWithApp:firApp] addIDTokenDidChangeListener:^(FIRAuth * _Nonnull auth, FIRUser * _Nullable user) {
|
||||
if (user != nil) {
|
||||
[self sendJSEventWithAppName:appName title:AUTH_ID_TOKEN_CHANGED_EVENT props:[@{@"authenticated": @(true), @"user": [self firebaseUserToDict:user]} mutableCopy]];
|
||||
[RNFirebaseUtil sendJSEventWithAppName:self appName:appName name:AUTH_ID_TOKEN_CHANGED_EVENT body:@{@"authenticated": @(true), @"user": [self firebaseUserToDict:user]}];
|
||||
} else {
|
||||
[self sendJSEventWithAppName:appName title:AUTH_ID_TOKEN_CHANGED_EVENT props:[@{@"authenticated": @(false)} mutableCopy]];
|
||||
[RNFirebaseUtil sendJSEventWithAppName:self appName:appName name:AUTH_ID_TOKEN_CHANGED_EVENT body:@{@"authenticated": @(false)}];
|
||||
}
|
||||
}];
|
||||
|
||||
|
@ -248,14 +249,7 @@ RCT_EXPORT_METHOD(reload:
|
|||
FIRUser *user = [FIRAuth authWithApp:firApp].currentUser;
|
||||
|
||||
if (user) {
|
||||
[user reloadWithCompletion:^(NSError *_Nullable error) {
|
||||
if (error) {
|
||||
[self promiseRejectAuthException:reject error:error];
|
||||
} else {
|
||||
FIRUser *userAfterReload = [FIRAuth authWithApp:firApp].currentUser;
|
||||
[self promiseWithUser:resolve rejecter:reject user:userAfterReload];
|
||||
}
|
||||
}];
|
||||
[self reloadAndReturnUser:user resolver:resolve rejecter: reject];
|
||||
} else {
|
||||
[self promiseNoUser:resolve rejecter:reject isError:YES];
|
||||
}
|
||||
|
@ -315,8 +309,7 @@ RCT_EXPORT_METHOD(updateEmail:
|
|||
if (error) {
|
||||
[self promiseRejectAuthException:reject error:error];
|
||||
} else {
|
||||
FIRUser *userAfterUpdate = [FIRAuth authWithApp:firApp].currentUser;
|
||||
[self promiseWithUser:resolve rejecter:reject user:userAfterUpdate];
|
||||
[self reloadAndReturnUser:user resolver:resolve rejecter: reject];
|
||||
}
|
||||
}];
|
||||
} else {
|
||||
|
@ -399,8 +392,7 @@ RCT_EXPORT_METHOD(updateProfile:
|
|||
if (error) {
|
||||
[self promiseRejectAuthException:reject error:error];
|
||||
} else {
|
||||
FIRUser *userAfterUpdate = [FIRAuth authWithApp:firApp].currentUser;
|
||||
[self promiseWithUser:resolve rejecter:reject user:userAfterUpdate];
|
||||
[self reloadAndReturnUser:user resolver:resolve rejecter: reject];
|
||||
}
|
||||
}];
|
||||
} else {
|
||||
|
@ -686,21 +678,21 @@ RCT_EXPORT_METHOD(verifyPhoneNumber:(NSString *) appName
|
|||
[[FIRPhoneAuthProvider providerWithAuth:[FIRAuth authWithApp:firApp]] verifyPhoneNumber:phoneNumber UIDelegate:nil completion:^(NSString * _Nullable verificationID, NSError * _Nullable error) {
|
||||
if (error) {
|
||||
NSDictionary * jsError = [self getJSError:(error)];
|
||||
NSMutableDictionary * props = [@{
|
||||
@"type": @"onVerificationFailed",
|
||||
@"requestKey":requestKey,
|
||||
@"state": @{@"error": jsError},
|
||||
} mutableCopy];
|
||||
[self sendJSEventWithAppName:appName title:PHONE_AUTH_STATE_CHANGED_EVENT props: props];
|
||||
NSDictionary *body = @{
|
||||
@"type": @"onVerificationFailed",
|
||||
@"requestKey":requestKey,
|
||||
@"state": @{@"error": jsError},
|
||||
};
|
||||
[RNFirebaseUtil sendJSEventWithAppName:self appName:appName name:PHONE_AUTH_STATE_CHANGED_EVENT body:body];
|
||||
} else {
|
||||
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
||||
[defaults setObject:verificationID forKey:@"authVerificationID"];
|
||||
NSMutableDictionary * props = [@{
|
||||
@"type": @"onCodeSent",
|
||||
@"requestKey":requestKey,
|
||||
@"state": @{@"verificationId": verificationID},
|
||||
} mutableCopy];
|
||||
[self sendJSEventWithAppName:appName title:PHONE_AUTH_STATE_CHANGED_EVENT props: props];
|
||||
NSDictionary *body = @{
|
||||
@"type": @"onCodeSent",
|
||||
@"requestKey":requestKey,
|
||||
@"state": @{@"verificationId": verificationID},
|
||||
};
|
||||
[RNFirebaseUtil sendJSEventWithAppName:self appName:appName name:PHONE_AUTH_STATE_CHANGED_EVENT body:body];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
@ -794,15 +786,7 @@ RCT_EXPORT_METHOD(unlink:
|
|||
if (error) {
|
||||
[self promiseRejectAuthException:reject error:error];
|
||||
} else {
|
||||
// This is here to protect against bugs in the iOS SDK which don't
|
||||
// correctly refresh the user object when unlinking certain accounts
|
||||
[user reloadWithCompletion:^(NSError * _Nullable error) {
|
||||
if (error) {
|
||||
[self promiseRejectAuthException:reject error:error];
|
||||
} else {
|
||||
[self promiseWithUser:resolve rejecter:reject user:user];
|
||||
}
|
||||
}];
|
||||
[self reloadAndReturnUser:user resolver:resolve rejecter: reject];
|
||||
}
|
||||
}];
|
||||
} else {
|
||||
|
@ -916,6 +900,19 @@ RCT_EXPORT_METHOD(fetchProvidersForEmail:
|
|||
return credential;
|
||||
}
|
||||
|
||||
// This is here to protect against bugs in the iOS SDK which don't
|
||||
// correctly refresh the user object when performing certain operations
|
||||
- (void)reloadAndReturnUser:(FIRUser *)user
|
||||
resolver:(RCTPromiseResolveBlock)resolve
|
||||
rejecter:(RCTPromiseRejectBlock)reject {
|
||||
[user reloadWithCompletion:^(NSError * _Nullable error) {
|
||||
if (error) {
|
||||
[self promiseRejectAuthException:reject error:error];
|
||||
} else {
|
||||
[self promiseWithUser:resolve rejecter:reject user:user];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
/**
|
||||
Resolve or reject a promise based on isError value
|
||||
|
@ -1087,31 +1084,6 @@ RCT_EXPORT_METHOD(fetchProvidersForEmail:
|
|||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
wrapper for sendEventWithName for auth events
|
||||
|
||||
@param title sendEventWithName
|
||||
@param props <#props description#>
|
||||
*/
|
||||
- (void)sendJSEvent:(NSString *)title props:(NSDictionary *)props {
|
||||
@try {
|
||||
[self sendEventWithName:title body:props];
|
||||
} @catch (NSException *error) {
|
||||
NSLog(@"An error occurred in sendJSEvent: %@", [error debugDescription]);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)sendJSEventWithAppName:(NSString *)appName title:(NSString *)title props:(NSMutableDictionary *)props {
|
||||
props[@"appName"] = 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
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#import <Firebase.h>
|
||||
#import "RNFirebaseDatabaseReference.h"
|
||||
#import "RNFirebaseEvents.h"
|
||||
#import "RNFirebaseUtil.h"
|
||||
|
||||
@implementation RNFirebaseDatabase
|
||||
RCT_EXPORT_MODULE();
|
||||
|
@ -39,7 +40,7 @@ RCT_EXPORT_METHOD(keepSynced:(NSString *) appName
|
|||
path:(NSString *) path
|
||||
modifiers:(NSArray *) modifiers
|
||||
state:(BOOL) state) {
|
||||
FIRDatabaseQuery *query = [self getInternalReferenceForApp:appName key:key path:path modifiers:modifiers keep:false].query;
|
||||
FIRDatabaseQuery *query = [self getInternalReferenceForApp:appName key:key path:path modifiers:modifiers].query;
|
||||
[query keepSynced:state];
|
||||
}
|
||||
|
||||
|
@ -87,11 +88,7 @@ RCT_EXPORT_METHOD(transactionStart:(NSString *) appName
|
|||
dispatch_barrier_async(_transactionQueue, ^{
|
||||
[_transactions setValue:transactionState forKey:transactionId];
|
||||
NSDictionary *updateMap = [self createTransactionUpdateMap:appName transactionId:transactionId updatesData:currentData];
|
||||
// TODO: Temporary fix for https://github.com/invertase/react-native-firebase/issues/233
|
||||
// until a better solution comes around
|
||||
if (self.bridge) {
|
||||
[self sendEventWithName:DATABASE_TRANSACTION_EVENT body:updateMap];
|
||||
}
|
||||
[RNFirebaseUtil sendJSEvent:self name:DATABASE_TRANSACTION_EVENT body:updateMap];
|
||||
});
|
||||
|
||||
// wait for the js event handler to call tryCommitTransaction
|
||||
|
@ -118,11 +115,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];
|
||||
// TODO: Temporary fix for https://github.com/invertase/react-native-firebase/issues/233
|
||||
// until a better solution comes around
|
||||
if (self.bridge) {
|
||||
[self sendEventWithName:DATABASE_TRANSACTION_EVENT body:resultMap];
|
||||
}
|
||||
[RNFirebaseUtil sendJSEvent:self name:DATABASE_TRANSACTION_EVENT body:resultMap];
|
||||
}
|
||||
withLocalEvents:
|
||||
applyLocally];
|
||||
|
@ -233,13 +226,13 @@ RCT_EXPORT_METHOD(once:(NSString *) appName
|
|||
eventName:(NSString *) eventName
|
||||
resolver:(RCTPromiseResolveBlock) resolve
|
||||
rejecter:(RCTPromiseRejectBlock) reject) {
|
||||
RNFirebaseDatabaseReference *ref = [self getInternalReferenceForApp:appName key:key path:path modifiers:modifiers keep:false];
|
||||
RNFirebaseDatabaseReference *ref = [self getInternalReferenceForApp:appName key:key path:path modifiers:modifiers];
|
||||
[ref once:eventName resolver:resolve rejecter:reject];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(on:(NSString *) appName
|
||||
props:(NSDictionary *) props) {
|
||||
RNFirebaseDatabaseReference *ref = [self getInternalReferenceForApp:appName key:props[@"key"] path:props[@"path"] modifiers:props[@"modifiers"] keep:false];
|
||||
RNFirebaseDatabaseReference *ref = [self getCachedInternalReferenceForApp:appName props:props];
|
||||
[ref on:props[@"eventType"] registration:props[@"registration"]];
|
||||
}
|
||||
|
||||
|
@ -278,15 +271,20 @@ RCT_EXPORT_METHOD(off:(NSString *) key
|
|||
return [[RNFirebaseDatabase getDatabaseForApp:appName] referenceWithPath:path];
|
||||
}
|
||||
|
||||
- (RNFirebaseDatabaseReference *)getInternalReferenceForApp:(NSString *)appName key:(NSString *)key path:(NSString *)path modifiers:(NSArray *)modifiers keep:(BOOL)keep {
|
||||
- (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 *)getCachedInternalReferenceForApp:(NSString *)appName props:(NSDictionary *)props {
|
||||
NSString *key = props[@"key"];
|
||||
NSString *path = props[@"path"];
|
||||
NSDictionary *modifiers = props[@"modifiers"];
|
||||
|
||||
RNFirebaseDatabaseReference *ref = _dbReferences[key];
|
||||
|
||||
if (ref == nil) {
|
||||
ref = [[RNFirebaseDatabaseReference alloc] initWithPathAndModifiers:self app:appName key:key refPath:path modifiers:modifiers];
|
||||
|
||||
if (keep) {
|
||||
_dbReferences[key] = ref;
|
||||
}
|
||||
_dbReferences[key] = ref;
|
||||
}
|
||||
return ref;
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#import <FirebaseDatabase/FIRDatabase.h>
|
||||
#import "RNFirebaseDatabase.h"
|
||||
#import "RNFirebaseEvents.h"
|
||||
#import "RNFirebaseUtil.h"
|
||||
#import <React/RCTEventEmitter.h>
|
||||
|
||||
@interface RNFirebaseDatabaseReference : NSObject
|
||||
|
|
|
@ -71,11 +71,7 @@
|
|||
[event setValue:eventType forKey:@"eventType"];
|
||||
[event setValue:registration forKey:@"registration"];
|
||||
|
||||
// TODO: Temporary fix for https://github.com/invertase/react-native-firebase/issues/233
|
||||
// until a better solution comes around
|
||||
if (_emitter.bridge) {
|
||||
[_emitter sendEventWithName:DATABASE_SYNC_EVENT body:event];
|
||||
}
|
||||
[RNFirebaseUtil sendJSEvent:self.emitter name:DATABASE_SYNC_EVENT body:event];
|
||||
}
|
||||
|
||||
- (void)handleDatabaseError:(NSDictionary *) registration
|
||||
|
@ -85,11 +81,7 @@
|
|||
[event setValue:[RNFirebaseDatabase getJSError:error] forKey:@"error"];
|
||||
[event setValue:registration forKey:@"registration"];
|
||||
|
||||
// TODO: Temporary fix for https://github.com/invertase/react-native-firebase/issues/233
|
||||
// until a better solution comes around
|
||||
if (_emitter) {
|
||||
[_emitter sendEventWithName:DATABASE_SYNC_EVENT body:event];
|
||||
}
|
||||
[RNFirebaseUtil sendJSEvent:self.emitter name:DATABASE_SYNC_EVENT body:event];
|
||||
}
|
||||
|
||||
+ (NSDictionary *)snapshotToDictionary:(FIRDataSnapshot *) dataSnapshot
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#import "RNFirebaseEvents.h"
|
||||
#import "RNFirebaseFirestore.h"
|
||||
#import "RNFirebaseFirestoreDocumentReference.h"
|
||||
#import "RNFirebaseUtil.h"
|
||||
|
||||
@interface RNFirebaseFirestoreCollectionReference : NSObject
|
||||
@property RCTEventEmitter *emitter;
|
||||
|
|
|
@ -128,6 +128,9 @@ queryListenOptions:(NSDictionary *) queryListenOptions {
|
|||
if (_options[@"endBefore"]) {
|
||||
query = [query queryEndingBeforeValues:_options[@"endBefore"]];
|
||||
}
|
||||
if (_options[@"limit"]) {
|
||||
query = [query queryLimitedTo:_options[@"limit"]];
|
||||
}
|
||||
if (_options[@"offset"]) {
|
||||
// iOS doesn't support offset
|
||||
}
|
||||
|
@ -151,11 +154,7 @@ queryListenOptions:(NSDictionary *) queryListenOptions {
|
|||
[event setValue:listenerId forKey:@"listenerId"];
|
||||
[event setValue:[RNFirebaseFirestore getJSError:error] forKey:@"error"];
|
||||
|
||||
// TODO: Temporary fix for https://github.com/invertase/react-native-firebase/issues/233
|
||||
// until a better solution comes around
|
||||
if (_emitter.bridge) {
|
||||
[_emitter sendEventWithName:FIRESTORE_COLLECTION_SYNC_EVENT body:event];
|
||||
}
|
||||
[RNFirebaseUtil sendJSEvent:self.emitter name:FIRESTORE_COLLECTION_SYNC_EVENT body:event];
|
||||
}
|
||||
|
||||
- (void)handleQuerySnapshotEvent:(NSString *)listenerId
|
||||
|
@ -166,11 +165,7 @@ queryListenOptions:(NSDictionary *) queryListenOptions {
|
|||
[event setValue:listenerId forKey:@"listenerId"];
|
||||
[event setValue:[RNFirebaseFirestoreCollectionReference snapshotToDictionary:querySnapshot] forKey:@"querySnapshot"];
|
||||
|
||||
// TODO: Temporary fix for https://github.com/invertase/react-native-firebase/issues/233
|
||||
// until a better solution comes around
|
||||
if (_emitter.bridge) {
|
||||
[_emitter sendEventWithName:FIRESTORE_COLLECTION_SYNC_EVENT body:event];
|
||||
}
|
||||
[RNFirebaseUtil sendJSEvent:self.emitter name:FIRESTORE_COLLECTION_SYNC_EVENT body:event];
|
||||
}
|
||||
|
||||
+ (NSDictionary *)snapshotToDictionary:(FIRQuerySnapshot *)querySnapshot {
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#import <React/RCTEventEmitter.h>
|
||||
#import "RNFirebaseEvents.h"
|
||||
#import "RNFirebaseFirestore.h"
|
||||
#import "RNFirebaseUtil.h"
|
||||
|
||||
@interface RNFirebaseFirestoreDocumentReference : NSObject
|
||||
@property RCTEventEmitter *emitter;
|
||||
|
|
|
@ -136,11 +136,7 @@ static NSMutableDictionary *_listeners;
|
|||
[event setValue:listenerId forKey:@"listenerId"];
|
||||
[event setValue:[RNFirebaseFirestore getJSError:error] forKey:@"error"];
|
||||
|
||||
// TODO: Temporary fix for https://github.com/invertase/react-native-firebase/issues/233
|
||||
// until a better solution comes around
|
||||
if (_emitter.bridge) {
|
||||
[_emitter sendEventWithName:FIRESTORE_DOCUMENT_SYNC_EVENT body:event];
|
||||
}
|
||||
[RNFirebaseUtil sendJSEvent:self.emitter name:FIRESTORE_DOCUMENT_SYNC_EVENT body:event];
|
||||
}
|
||||
|
||||
- (void)handleDocumentSnapshotEvent:(NSString *)listenerId
|
||||
|
@ -151,11 +147,7 @@ static NSMutableDictionary *_listeners;
|
|||
[event setValue:listenerId forKey:@"listenerId"];
|
||||
[event setValue:[RNFirebaseFirestoreDocumentReference snapshotToDictionary:documentSnapshot] forKey:@"documentSnapshot"];
|
||||
|
||||
// TODO: Temporary fix for https://github.com/invertase/react-native-firebase/issues/233
|
||||
// until a better solution comes around
|
||||
if (_emitter.bridge) {
|
||||
[_emitter sendEventWithName:FIRESTORE_DOCUMENT_SYNC_EVENT body:event];
|
||||
}
|
||||
[RNFirebaseUtil sendJSEvent:self.emitter name:FIRESTORE_DOCUMENT_SYNC_EVENT body:event];
|
||||
}
|
||||
|
||||
|
||||
|
@ -205,9 +197,9 @@ static NSMutableDictionary *_listeners;
|
|||
typeMap[@"value"] = geopoint;
|
||||
} else if ([value isKindOfClass:[NSDate class]]) {
|
||||
typeMap[@"type"] = @"date";
|
||||
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
|
||||
[dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSSZ"];
|
||||
typeMap[@"value"] = [dateFormatter stringFromDate:(NSDate *)value];
|
||||
// NOTE: The round() is important as iOS ends up giving .999 otherwise,
|
||||
// and loses a millisecond when going between native and JS
|
||||
typeMap[@"value"] = @(round([(NSDate *)value timeIntervalSince1970] * 1000.0));
|
||||
} else if ([value isKindOfClass:[NSNumber class]]) {
|
||||
NSNumber *number = (NSNumber *)value;
|
||||
if (number == (void*)kCFBooleanFalse || number == (void*)kCFBooleanTrue) {
|
||||
|
@ -262,9 +254,7 @@ static NSMutableDictionary *_listeners;
|
|||
NSNumber *longitude = geopoint[@"longitude"];
|
||||
return [[FIRGeoPoint alloc] initWithLatitude:[latitude doubleValue] longitude:[longitude doubleValue]];
|
||||
} else if ([type isEqualToString:@"date"]) {
|
||||
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
|
||||
[dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSSZ"];
|
||||
return [dateFormatter dateFromString:value];
|
||||
return [NSDate dateWithTimeIntervalSince1970:([(NSNumber *)value doubleValue] / 1000.0)];
|
||||
} else if ([type isEqualToString:@"fieldvalue"]) {
|
||||
NSString *string = (NSString*)value;
|
||||
if ([string isEqualToString:@"delete"]) {
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
@import UserNotifications;
|
||||
#if __has_include(<FirebaseMessaging/FirebaseMessaging.h>)
|
||||
#import "RNFirebaseEvents.h"
|
||||
#import "RNFirebaseUtil.h"
|
||||
#import <FirebaseMessaging/FirebaseMessaging.h>
|
||||
#import <FirebaseInstanceID/FIRInstanceID.h>
|
||||
|
||||
|
@ -217,7 +218,7 @@ RCT_EXPORT_MODULE()
|
|||
data[@"_completionHandlerId"] = completionHandlerId;
|
||||
}
|
||||
|
||||
[self sendEventWithName:MESSAGING_NOTIFICATION_RECEIVED body:data];
|
||||
[RNFirebaseUtil sendJSEvent:self name:MESSAGING_NOTIFICATION_RECEIVED body:data];
|
||||
}
|
||||
|
||||
|
||||
|
@ -234,13 +235,13 @@ RCT_EXPORT_MODULE()
|
|||
// ** Start FIRMessagingDelegate methods **
|
||||
// Handle data messages in the background
|
||||
- (void)applicationReceivedRemoteMessage:(FIRMessagingRemoteMessage *)remoteMessage {
|
||||
[self sendEventWithName:MESSAGING_NOTIFICATION_RECEIVED body:[remoteMessage appData]];
|
||||
[RNFirebaseUtil sendJSEvent:self name:MESSAGING_NOTIFICATION_RECEIVED body:[remoteMessage appData]];
|
||||
}
|
||||
|
||||
// Listen for token refreshes
|
||||
- (void)messaging:(nonnull FIRMessaging *)messaging didRefreshRegistrationToken:(nonnull NSString *)fcmToken {
|
||||
NSLog(@"FCM registration token: %@", fcmToken);
|
||||
[self sendEventWithName:MESSAGING_TOKEN_REFRESHED body:fcmToken];
|
||||
[RNFirebaseUtil sendJSEvent:self name:MESSAGING_TOKEN_REFRESHED body:fcmToken];
|
||||
}
|
||||
// ** End FIRMessagingDelegate methods **
|
||||
|
||||
|
@ -297,7 +298,9 @@ RCT_EXPORT_METHOD(requestPermissions:(RCTPromiseResolveBlock)resolve rejecter:(R
|
|||
#endif
|
||||
}
|
||||
|
||||
[RCTSharedApplication() registerForRemoteNotifications];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[RCTSharedApplication() registerForRemoteNotifications];
|
||||
});
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(subscribeToTopic: (NSString*) topic) {
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#if __has_include(<FirebaseStorage/FIRStorage.h>)
|
||||
|
||||
#import "RNFirebaseEvents.h"
|
||||
#import "RNFirebaseUtil.h"
|
||||
#import <MobileCoreServices/MobileCoreServices.h>
|
||||
#import <Photos/Photos.h>
|
||||
#import <Firebase.h>
|
||||
|
@ -392,12 +393,7 @@ RCT_EXPORT_METHOD(putFile:(NSString *) appName
|
|||
}
|
||||
|
||||
- (void)sendJSEvent:(NSString *)appName type:(NSString *)type path:(NSString *)path title:(NSString *)title props:(NSDictionary *)props {
|
||||
@try {
|
||||
[self sendEventWithName:type body:@{@"eventName": title, @"appName": appName, @"path": path, @"body": props}];
|
||||
} @catch (NSException *err) {
|
||||
NSLog(@"An error occurred in sendJSEvent: %@", [err debugDescription]);
|
||||
NSLog(@"Tried to send: %@ with %@", title, props);
|
||||
}
|
||||
[RNFirebaseUtil sendJSEvent:self name:type body:@{@"eventName": title, @"appName": appName, @"path": path, @"body": props}];
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -141,18 +141,14 @@ export default class PhoneAuthListener {
|
|||
_removeAllListeners() {
|
||||
setTimeout(() => { // move to next event loop - not sure if needed
|
||||
// internal listeners
|
||||
const events = Object.values(this._internalEvents);
|
||||
|
||||
for (let i = 0, len = events.length; i < len; i++) {
|
||||
this._auth.removeAllListeners(events[i]);
|
||||
}
|
||||
Object.values(this._internalEvents).forEach((event) => {
|
||||
this._auth.removeAllListeners(event);
|
||||
});
|
||||
|
||||
// user observer listeners
|
||||
const publicEvents = Object.values(this._publicEvents);
|
||||
|
||||
for (let i = 0, len = events.length; i < len; i++) {
|
||||
this._auth.removeAllListeners(publicEvents[i]);
|
||||
}
|
||||
Object.values(this._publicEvents).forEach((publicEvent) => {
|
||||
this._auth.removeAllListeners(publicEvent);
|
||||
});
|
||||
}, 0);
|
||||
}
|
||||
|
||||
|
|
|
@ -22,33 +22,31 @@ export default class Auth extends ModuleBase {
|
|||
_native: Object;
|
||||
_getAppEventName: Function;
|
||||
_authResult: AuthResultType | null;
|
||||
authenticated: boolean;
|
||||
|
||||
constructor(firebaseApp: Object, options: Object = {}) {
|
||||
super(firebaseApp, options, true);
|
||||
this._user = null;
|
||||
this._authResult = null;
|
||||
this.authenticated = false;
|
||||
|
||||
this.addListener(
|
||||
// sub to internal native event - this fans out to
|
||||
// public event name: onAuthStateChanged
|
||||
this._getAppEventName('auth_state_changed'),
|
||||
this._onAuthStateChanged.bind(this),
|
||||
this._onInternalAuthStateChanged.bind(this),
|
||||
);
|
||||
|
||||
this.addListener(
|
||||
// sub to internal native event - this fans out to
|
||||
// public events based on event.type
|
||||
this._getAppEventName('phone_auth_state_changed'),
|
||||
this._onPhoneAuthStateChanged.bind(this),
|
||||
this._onInternalPhoneAuthStateChanged.bind(this),
|
||||
);
|
||||
|
||||
this.addListener(
|
||||
// sub to internal native event - this fans out to
|
||||
// public event name: onIdTokenChanged
|
||||
this._getAppEventName('auth_id_token_changed'),
|
||||
this._onIdTokenChanged.bind(this),
|
||||
this._onInternalIdTokenChanged.bind(this),
|
||||
);
|
||||
|
||||
this._native.addAuthStateListener();
|
||||
|
@ -60,34 +58,25 @@ export default class Auth extends ModuleBase {
|
|||
* @param event
|
||||
* @private
|
||||
*/
|
||||
_onPhoneAuthStateChanged(event: Object) {
|
||||
_onInternalPhoneAuthStateChanged(event: Object) {
|
||||
const eventKey = `phone:auth:${event.requestKey}:${event.type}`;
|
||||
this.emit(eventKey, event.state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal auth changed listener
|
||||
* @param auth
|
||||
* @param emit
|
||||
* @private
|
||||
*/
|
||||
_onAuthStateChanged(auth: AuthResultType, emit: boolean = true) {
|
||||
_setAuthState(auth: AuthResultType) {
|
||||
this._authResult = auth;
|
||||
this.authenticated = auth ? auth.authenticated || false : false;
|
||||
if (auth && auth.user && !this._user) this._user = new User(this, auth);
|
||||
else if ((!auth || !auth.user) && this._user) this._user = null;
|
||||
else if (this._user) this._user._updateValues(auth);
|
||||
if (emit) this.emit(this._getAppEventName('onAuthStateChanged'), this._user);
|
||||
return auth ? this._user : null;
|
||||
this._user = auth && auth.user ? new User(this, auth.user) : null;
|
||||
this.emit(this._getAppEventName('onUserChanged'), this._user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove auth change listener
|
||||
* @param listener
|
||||
* Internal auth changed listener
|
||||
* @param auth
|
||||
* @private
|
||||
*/
|
||||
_offAuthStateChanged(listener: Function) {
|
||||
this.log.info('Removing onAuthStateChanged listener');
|
||||
this.removeListener(this._getAppEventName('onAuthStateChanged'), listener);
|
||||
_onInternalAuthStateChanged(auth: AuthResultType) {
|
||||
this._setAuthState(auth);
|
||||
this.emit(this._getAppEventName('onAuthStateChanged'), this._user);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -96,23 +85,9 @@ export default class Auth extends ModuleBase {
|
|||
* @param emit
|
||||
* @private
|
||||
*/
|
||||
_onIdTokenChanged(auth: AuthResultType, emit: boolean = true) {
|
||||
this._authResult = auth;
|
||||
this.authenticated = auth ? auth.authenticated || false : false;
|
||||
if (auth && auth.user && !this._user) this._user = new User(this, auth);
|
||||
else if ((!auth || !auth.user) && this._user) this._user = null;
|
||||
else if (this._user) this._user._updateValues(auth);
|
||||
if (emit) this.emit(this._getAppEventName('onIdTokenChanged'), this._user);
|
||||
return auth ? this._user : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove id token change listener
|
||||
* @param listener
|
||||
*/
|
||||
_offIdTokenChanged(listener: Function) {
|
||||
this.log.info('Removing onIdTokenChanged listener');
|
||||
this.removeListener(this._getAppEventName('onIdTokenChanged'), listener);
|
||||
_onInternalIdTokenChanged(auth: AuthResultType) {
|
||||
this._setAuthState(auth);
|
||||
this.emit(this._getAppEventName('onIdTokenChanged'), this._user);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -124,10 +99,10 @@ export default class Auth extends ModuleBase {
|
|||
*/
|
||||
_interceptUserValue(promise) {
|
||||
return promise.then((result) => {
|
||||
if (!result) return this._onAuthStateChanged(null, false);
|
||||
if (result.user) return this._onAuthStateChanged(result, false);
|
||||
if (result.uid) return this._onAuthStateChanged({ authenticated: true, user: result }, false);
|
||||
return result;
|
||||
if (!result) this._setAuthState(null);
|
||||
else if (result.user) this._setAuthState(result);
|
||||
else if (result.uid) this._setAuthState({ authenticated: true, user: result });
|
||||
return this._user;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -146,6 +121,15 @@ export default class Auth extends ModuleBase {
|
|||
return this._offAuthStateChanged.bind(this, listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove auth change listener
|
||||
* @param listener
|
||||
*/
|
||||
_offAuthStateChanged(listener: Function) {
|
||||
this.log.info('Removing onAuthStateChanged listener');
|
||||
this.removeListener(this._getAppEventName('onAuthStateChanged'), listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen for id token changes.
|
||||
* @param listener
|
||||
|
@ -157,6 +141,35 @@ export default class Auth extends ModuleBase {
|
|||
return this._offIdTokenChanged.bind(this, listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove id token change listener
|
||||
* @param listener
|
||||
*/
|
||||
_offIdTokenChanged(listener: Function) {
|
||||
this.log.info('Removing onIdTokenChanged listener');
|
||||
this.removeListener(this._getAppEventName('onIdTokenChanged'), listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen for user changes.
|
||||
* @param listener
|
||||
*/
|
||||
onUserChanged(listener: Function) {
|
||||
this.log.info('Creating onUserChanged listener');
|
||||
this.on(this._getAppEventName('onUserChanged'), listener);
|
||||
if (this._authResult) listener(this._user || null);
|
||||
return this._offUserChanged.bind(this, listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove user change listener
|
||||
* @param listener
|
||||
*/
|
||||
_offUserChanged(listener: Function) {
|
||||
this.log.info('Removing onUserChanged listener');
|
||||
this.removeListener(this._getAppEventName('onUserChanged'), listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sign the current user out
|
||||
* @return {Promise}
|
||||
|
|
|
@ -7,32 +7,17 @@ export default class User {
|
|||
/**
|
||||
*
|
||||
* @param authClass Instance of Authentication class
|
||||
* @param authObj authentication result object from native
|
||||
* @param user user result object from native
|
||||
*/
|
||||
constructor(authClass, authObj) {
|
||||
constructor(authClass, userObj) {
|
||||
this._auth = authClass;
|
||||
this._user = null;
|
||||
this._updateValues(authObj);
|
||||
this._user = userObj;
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNALS
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* @param authObj
|
||||
* @private
|
||||
*/
|
||||
_updateValues(authObj) {
|
||||
this._authObj = authObj;
|
||||
if (authObj.user) {
|
||||
this._user = authObj.user;
|
||||
} else {
|
||||
this._user = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a user property or null if does not exist
|
||||
* @param prop
|
||||
|
@ -40,7 +25,6 @@ export default class User {
|
|||
* @private
|
||||
*/
|
||||
_valueOrNull(prop) {
|
||||
if (!this._user) return null;
|
||||
if (!Object.hasOwnProperty.call(this._user, prop)) return null;
|
||||
return this._user[prop];
|
||||
}
|
||||
|
@ -52,7 +36,6 @@ export default class User {
|
|||
* @private
|
||||
*/
|
||||
_valueOrFalse(prop) {
|
||||
if (!this._user) return false;
|
||||
if (!Object.hasOwnProperty.call(this._user, prop)) return false;
|
||||
return this._user[prop];
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ export default class Query {
|
|||
*/
|
||||
orderBy(name: string, key?: string) {
|
||||
this.modifiers.push({
|
||||
id: `orderBy-${name}:${key}`,
|
||||
type: 'orderBy',
|
||||
name,
|
||||
key,
|
||||
|
@ -42,6 +43,7 @@ export default class Query {
|
|||
*/
|
||||
limit(name: string, limit: number) {
|
||||
this.modifiers.push({
|
||||
id: `limit-${name}:${limit}`,
|
||||
type: 'limit',
|
||||
name,
|
||||
limit,
|
||||
|
@ -59,6 +61,7 @@ export default class Query {
|
|||
*/
|
||||
filter(name: string, value: any, key?: string) {
|
||||
this.modifiers.push({
|
||||
id: `filter-${name}:${objectToUniqueId(value)}:${key}`,
|
||||
type: 'filter',
|
||||
name,
|
||||
value,
|
||||
|
@ -82,14 +85,21 @@ export default class Query {
|
|||
* @return {*}
|
||||
*/
|
||||
queryIdentifier() {
|
||||
// convert query modifiers array into an object for generating a unique key
|
||||
const object = {};
|
||||
// sort modifiers to enforce ordering
|
||||
const sortedModifiers = this.getModifiers().sort((a, b) => {
|
||||
if (a.id < b.id) return -1;
|
||||
if (a.id > b.id) return 1;
|
||||
return 0;
|
||||
});
|
||||
|
||||
for (let i = 0, len = this.modifiers.length; i < len; i++) {
|
||||
const { name, type, value } = this.modifiers[i];
|
||||
object[`${type}-${name}`] = value;
|
||||
// Convert modifiers to unique key
|
||||
let key = '{';
|
||||
for (let i = 0; i < sortedModifiers.length; i++) {
|
||||
if (i !== 0) key += ',';
|
||||
key += sortedModifiers[i].id;
|
||||
}
|
||||
key += '}';
|
||||
|
||||
return objectToUniqueId(object);
|
||||
return key;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -736,14 +736,15 @@ export default class Reference extends ReferenceBase {
|
|||
// remove the callback.
|
||||
// Remove only a single registration
|
||||
if (eventType && originalCallback) {
|
||||
const registrations = this._syncTree.getRegistrationsByPathEvent(this.path, eventType);
|
||||
const registration = this._syncTree.getOneByPathEventListener(this.path, eventType, originalCallback);
|
||||
if (!registration) return [];
|
||||
|
||||
// remove the paired cancellation registration if any exist
|
||||
this._syncTree.removeListenersForRegistrations([`${registrations[0]}$cancelled`]);
|
||||
this._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, [registrations[0]]);
|
||||
return this._syncTree.removeListenerRegistrations(originalCallback, [registration]);
|
||||
}
|
||||
|
||||
// Firebase Docs:
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
/**
|
||||
* @flow
|
||||
* Database representation wrapper
|
||||
* Database Transaction representation wrapper
|
||||
*/
|
||||
|
||||
let transactionId = 0;
|
||||
|
||||
/**
|
||||
* @class Database
|
||||
* @class TransactionHandler
|
||||
*/
|
||||
export default class TransactionHandler {
|
||||
constructor(database: Object) {
|
||||
|
|
|
@ -67,7 +67,7 @@ const buildTypeMap = (value: any): any => {
|
|||
};
|
||||
} else if (value instanceof Date) {
|
||||
typeMap.type = 'date';
|
||||
typeMap.value = value.toISOString();
|
||||
typeMap.value = value.getTime();
|
||||
} else {
|
||||
typeMap.type = 'object';
|
||||
typeMap.value = buildNativeMap(value);
|
||||
|
|
|
@ -11,6 +11,7 @@ type Registration = {
|
|||
once?: Boolean,
|
||||
appName: String,
|
||||
eventType: String,
|
||||
listener: Function,
|
||||
eventRegistrationKey: String,
|
||||
ref: DatabaseReference,
|
||||
}
|
||||
|
@ -197,6 +198,28 @@ export default class SyncTree {
|
|||
return Object.keys(this._tree[path][eventType]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a single registration key for the specified path, eventType, and listener
|
||||
*
|
||||
* @param path
|
||||
* @param eventType
|
||||
* @param listener
|
||||
* @return {Array}
|
||||
*/
|
||||
getOneByPathEventListener(path: string, eventType: string, listener: Function): Array {
|
||||
if (!this._tree[path]) return [];
|
||||
if (!this._tree[path][eventType]) return [];
|
||||
|
||||
const registrationsForPathEvent = Object.entries(this._tree[path][eventType]);
|
||||
|
||||
for (let i = 0; i < registrationsForPathEvent.length; i++) {
|
||||
const registration = registrationsForPathEvent[i];
|
||||
if (registration[1] === listener) return registration[0];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Register a new listener.
|
||||
|
@ -211,8 +234,8 @@ export default class SyncTree {
|
|||
if (!this._tree[path]) this._tree[path] = {};
|
||||
if (!this._tree[path][eventType]) this._tree[path][eventType] = {};
|
||||
|
||||
this._tree[path][eventType][eventRegistrationKey] = 0;
|
||||
this._reverseLookup[eventRegistrationKey] = Object.assign({}, parameters);
|
||||
this._tree[path][eventType][eventRegistrationKey] = listener;
|
||||
this._reverseLookup[eventRegistrationKey] = Object.assign({ listener }, parameters);
|
||||
|
||||
if (once) {
|
||||
INTERNALS.SharedEventEmitter.once(
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "react-native-firebase",
|
||||
"version": "3.0.4",
|
||||
"version": "3.0.6",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
|
|
@ -150,7 +150,7 @@ PODS:
|
|||
- React/Core
|
||||
- React/fishhook
|
||||
- React/RCTBlob
|
||||
- RNFirebase (3.0.0):
|
||||
- RNFirebase (3.0.5):
|
||||
- React
|
||||
- yoga (0.49.1.React)
|
||||
|
||||
|
@ -208,7 +208,7 @@ SPEC CHECKSUMS:
|
|||
nanopb: 5601e6bca2dbf1ed831b519092ec110f66982ca3
|
||||
Protobuf: 03eef2ee0b674770735cf79d9c4d3659cf6908e8
|
||||
React: cf892fb84b7d06bf5fea7f328e554c6dcabe85ee
|
||||
RNFirebase: 901a473c68fcbaa28125c56a911923f2fbe5d61b
|
||||
RNFirebase: 7c86b4efd2860700048d927f34db237fbce1d5fc
|
||||
yoga: 3abf02d6d9aeeb139b4c930eb1367feae690a35a
|
||||
|
||||
PODFILE CHECKSUM: b5674be55653f5dda937c8b794d0479900643d45
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import should from 'should';
|
||||
import sinon from 'sinon';
|
||||
import 'should-sinon';
|
||||
import DatabaseContents from '../../support/DatabaseContents';
|
||||
|
||||
function issueTests({ describe, it, context, firebase }) {
|
||||
|
@ -81,6 +83,212 @@ function issueTests({ describe, it, context, firebase }) {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('issue_489', () => {
|
||||
context('long numbers should', () => {
|
||||
it('return as longs', async () => {
|
||||
// Setup
|
||||
|
||||
const long1Ref = firebase.native.database().ref('tests/issues/489/long1');
|
||||
const long2Ref = firebase.native.database().ref('tests/issues/489/long2');
|
||||
const long2 = 1234567890123456;
|
||||
|
||||
// Test
|
||||
|
||||
let snapshot = await long1Ref.once('value');
|
||||
snapshot.val().should.eql(DatabaseContents.ISSUES[489].long1);
|
||||
|
||||
|
||||
await long2Ref.set(long2);
|
||||
snapshot = await long2Ref.once('value');
|
||||
snapshot.val().should.eql(long2);
|
||||
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('issue_521', () => {
|
||||
context('orderByChild (numerical field) and limitToLast', () => {
|
||||
it('once() returns correct results', async () => {
|
||||
// Setup
|
||||
|
||||
const ref = firebase.native.database().ref('tests/issues/521');
|
||||
// Test
|
||||
|
||||
return ref
|
||||
.orderByChild('number')
|
||||
.limitToLast(1)
|
||||
.once('value')
|
||||
.then((snapshot) => {
|
||||
const val = snapshot.val();
|
||||
// Assertion
|
||||
val.key3.should.eql(DatabaseContents.ISSUES[521].key3);
|
||||
should.equal(Object.keys(val).length, 1);
|
||||
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
it('on() returns correct initial results', async () => {
|
||||
// Setup
|
||||
|
||||
const ref = firebase.native.database().ref('tests/issues/521').orderByChild('number').limitToLast(2);
|
||||
const callback = sinon.spy();
|
||||
|
||||
// Test
|
||||
|
||||
await new Promise((resolve) => {
|
||||
ref.on('value', (snapshot) => {
|
||||
callback(snapshot.val());
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
callback.should.be.calledWith({
|
||||
key2: DatabaseContents.ISSUES[521].key2,
|
||||
key3: DatabaseContents.ISSUES[521].key3,
|
||||
});
|
||||
callback.should.be.calledOnce();
|
||||
|
||||
return Promise.resolve();
|
||||
});
|
||||
|
||||
it('on() returns correct subsequent results', async () => {
|
||||
// Setup
|
||||
|
||||
const ref = firebase.native.database().ref('tests/issues/521').orderByChild('number').limitToLast(2);
|
||||
const callback = sinon.spy();
|
||||
|
||||
// Test
|
||||
|
||||
await new Promise((resolve) => {
|
||||
ref.on('value', (snapshot) => {
|
||||
callback(snapshot.val());
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
callback.should.be.calledWith({
|
||||
key2: DatabaseContents.ISSUES[521].key2,
|
||||
key3: DatabaseContents.ISSUES[521].key3,
|
||||
});
|
||||
callback.should.be.calledOnce();
|
||||
|
||||
const newDataValue = {
|
||||
name: 'Item 4',
|
||||
number: 4,
|
||||
string: 'item4',
|
||||
};
|
||||
const newRef = firebase.native.database().ref('tests/issues/521/key4');
|
||||
await newRef.set(newDataValue);
|
||||
|
||||
await new Promise((resolve) => {
|
||||
setTimeout(() => resolve(), 5);
|
||||
});
|
||||
|
||||
// Assertions
|
||||
|
||||
callback.should.be.calledWith({
|
||||
key3: DatabaseContents.ISSUES[521].key3,
|
||||
key4: newDataValue,
|
||||
});
|
||||
callback.should.be.calledTwice();
|
||||
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
context('orderByChild (string field) and limitToLast', () => {
|
||||
it('once() returns correct results', async () => {
|
||||
// Setup
|
||||
|
||||
const ref = firebase.native.database().ref('tests/issues/521');
|
||||
// Test
|
||||
|
||||
return ref
|
||||
.orderByChild('string')
|
||||
.limitToLast(1)
|
||||
.once('value')
|
||||
.then((snapshot) => {
|
||||
const val = snapshot.val();
|
||||
// Assertion
|
||||
val.key3.should.eql(DatabaseContents.ISSUES[521].key3);
|
||||
should.equal(Object.keys(val).length, 1);
|
||||
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
it('on() returns correct initial results', async () => {
|
||||
// Setup
|
||||
|
||||
const ref = firebase.native.database().ref('tests/issues/521').orderByChild('string').limitToLast(2);
|
||||
const callback = sinon.spy();
|
||||
|
||||
// Test
|
||||
|
||||
await new Promise((resolve) => {
|
||||
ref.on('value', (snapshot) => {
|
||||
callback(snapshot.val());
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
callback.should.be.calledWith({
|
||||
key2: DatabaseContents.ISSUES[521].key2,
|
||||
key3: DatabaseContents.ISSUES[521].key3,
|
||||
});
|
||||
callback.should.be.calledOnce();
|
||||
|
||||
return Promise.resolve();
|
||||
});
|
||||
|
||||
it('on() returns correct subsequent results', async () => {
|
||||
// Setup
|
||||
|
||||
const ref = firebase.native.database().ref('tests/issues/521').orderByChild('string').limitToLast(2);
|
||||
const callback = sinon.spy();
|
||||
|
||||
// Test
|
||||
|
||||
await new Promise((resolve) => {
|
||||
ref.on('value', (snapshot) => {
|
||||
callback(snapshot.val());
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
callback.should.be.calledWith({
|
||||
key2: DatabaseContents.ISSUES[521].key2,
|
||||
key3: DatabaseContents.ISSUES[521].key3,
|
||||
});
|
||||
callback.should.be.calledOnce();
|
||||
|
||||
const newDataValue = {
|
||||
name: 'Item 4',
|
||||
number: 4,
|
||||
string: 'item4',
|
||||
};
|
||||
const newRef = firebase.native.database().ref('tests/issues/521/key4');
|
||||
await newRef.set(newDataValue);
|
||||
|
||||
await new Promise((resolve) => {
|
||||
setTimeout(() => resolve(), 5);
|
||||
});
|
||||
|
||||
// Assertions
|
||||
|
||||
callback.should.be.calledWith({
|
||||
key3: DatabaseContents.ISSUES[521].key3,
|
||||
key4: newDataValue,
|
||||
});
|
||||
callback.should.be.calledTwice();
|
||||
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export default issueTests;
|
||||
|
|
|
@ -297,6 +297,167 @@ function offTests({ describe, it, xcontext, context, firebase }) {
|
|||
});
|
||||
});
|
||||
|
||||
context('when 2 different child_added callbacks on the same path', () => {
|
||||
context('that has been added and removed in the same order', () => {
|
||||
it('must be completely removed', async () => {
|
||||
// Setup
|
||||
|
||||
const spyA = sinon.spy();
|
||||
let callbackA;
|
||||
|
||||
const spyB = sinon.spy();
|
||||
let callbackB;
|
||||
|
||||
const ref = firebase.native.database().ref('tests/types/array');
|
||||
const arrayLength = DatabaseContents.DEFAULT.array.length;
|
||||
// Attach callbackA
|
||||
await new Promise((resolve) => {
|
||||
callbackA = () => {
|
||||
spyA();
|
||||
resolve();
|
||||
};
|
||||
ref.on('child_added', callbackA);
|
||||
});
|
||||
|
||||
// Attach callbackB
|
||||
await new Promise((resolve) => {
|
||||
callbackB = () => {
|
||||
spyB();
|
||||
resolve();
|
||||
};
|
||||
ref.on('child_added', callbackB);
|
||||
});
|
||||
|
||||
// Add a delay to ensure that the .on() has had time to be registered
|
||||
await new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
resolve();
|
||||
}, 15);
|
||||
});
|
||||
|
||||
spyA.should.have.callCount(arrayLength);
|
||||
spyB.should.have.callCount(arrayLength);
|
||||
|
||||
// Undo the first callback
|
||||
const resp = await ref.off('child_added', callbackA);
|
||||
should(resp, undefined);
|
||||
|
||||
// Trigger the event the callback is listening to
|
||||
await ref.push(DatabaseContents.DEFAULT.number);
|
||||
|
||||
// Add a delay to ensure that the .set() has had time to be registered
|
||||
await new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
resolve();
|
||||
}, 15);
|
||||
});
|
||||
|
||||
// CallbackA should have been called zero more times its attachment
|
||||
// has been removed, and callBackB only one more time becuase it's still attached
|
||||
spyA.should.have.callCount(arrayLength);
|
||||
spyB.should.have.callCount(arrayLength + 1);
|
||||
|
||||
// Undo the second attachment
|
||||
const resp2 = await ref.off('child_added', callbackB);
|
||||
should(resp2, undefined);
|
||||
|
||||
// Trigger the event the callback is listening to
|
||||
await ref.push(DatabaseContents.DEFAULT.number);
|
||||
|
||||
// Add a delay to ensure that the .set() has had time to be registered
|
||||
await new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
resolve();
|
||||
}, 15);
|
||||
});
|
||||
|
||||
// Both Callbacks should not have been called any more times
|
||||
spyA.should.have.callCount(arrayLength);
|
||||
spyB.should.have.callCount(arrayLength + 1);
|
||||
});
|
||||
});
|
||||
|
||||
// ******This test is failed*******
|
||||
context('that has been added and removed in reverse order', () => {
|
||||
it('must be completely removed', async () => {
|
||||
// Setup
|
||||
|
||||
const spyA = sinon.spy();
|
||||
let callbackA;
|
||||
|
||||
const spyB = sinon.spy();
|
||||
let callbackB;
|
||||
|
||||
const ref = firebase.native.database().ref('tests/types/array');
|
||||
const arrayLength = DatabaseContents.DEFAULT.array.length;
|
||||
// Attach callbackA
|
||||
await new Promise((resolve) => {
|
||||
callbackA = () => {
|
||||
spyA();
|
||||
resolve();
|
||||
};
|
||||
ref.on('child_added', callbackA);
|
||||
});
|
||||
|
||||
// Attach callbackB
|
||||
await new Promise((resolve) => {
|
||||
callbackB = () => {
|
||||
spyB();
|
||||
resolve();
|
||||
};
|
||||
ref.on('child_added', callbackB);
|
||||
});
|
||||
|
||||
// Add a delay to ensure that the .on() has had time to be registered
|
||||
await new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
resolve();
|
||||
}, 15);
|
||||
});
|
||||
|
||||
spyA.should.have.callCount(arrayLength);
|
||||
spyB.should.have.callCount(arrayLength);
|
||||
|
||||
// Undo the second callback
|
||||
const resp = await ref.off('child_added', callbackB);
|
||||
should(resp, undefined);
|
||||
|
||||
// Trigger the event the callback is listening to
|
||||
await ref.push(DatabaseContents.DEFAULT.number);
|
||||
|
||||
// Add a delay to ensure that the .set() has had time to be registered
|
||||
await new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
resolve();
|
||||
}, 15);
|
||||
});
|
||||
|
||||
// CallbackB should have been called zero more times its attachment
|
||||
// has been removed, and callBackA only one more time becuase it's still attached
|
||||
spyA.should.have.callCount(arrayLength + 1);
|
||||
spyB.should.have.callCount(arrayLength);
|
||||
|
||||
// Undo the second attachment
|
||||
const resp2 = await ref.off('child_added', callbackA);
|
||||
should(resp2, undefined);
|
||||
|
||||
// Trigger the event the callback is listening to
|
||||
await ref.push(DatabaseContents.DEFAULT.number);
|
||||
|
||||
// Add a delay to ensure that the .set() has had time to be registered
|
||||
await new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
resolve();
|
||||
}, 15);
|
||||
});
|
||||
|
||||
// Both Callbacks should not have been called any more times
|
||||
spyA.should.have.callCount(arrayLength + 1);
|
||||
spyB.should.have.callCount(arrayLength);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
xcontext('when a context', () => {
|
||||
/**
|
||||
* @todo Add tests for when a context is passed. Not sure what the intended
|
||||
|
|
|
@ -435,6 +435,7 @@ function documentReferenceTests({ describe, it, context, firebase }) {
|
|||
const doc = await docRef.get();
|
||||
doc.data().field.should.be.instanceof(Date);
|
||||
should.equal(doc.data().field.toISOString(), date.toISOString());
|
||||
should.equal(doc.data().field.getTime(), date.getTime());
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -91,5 +91,28 @@ export default {
|
|||
uid: 'aNYxLexOb2WsXGOPiEAu47q5bxH3',
|
||||
},
|
||||
},
|
||||
|
||||
489: {
|
||||
long1: 1508777379000,
|
||||
},
|
||||
|
||||
// https://github.com/invertase/react-native-firebase/issues/521
|
||||
521: {
|
||||
key1: {
|
||||
name: 'Item 1',
|
||||
number: 1,
|
||||
string: 'item1',
|
||||
},
|
||||
key3: {
|
||||
name: 'Item 3',
|
||||
number: 3,
|
||||
string: 'item3',
|
||||
},
|
||||
key2: {
|
||||
name: 'Item 2',
|
||||
number: 2,
|
||||
string: 'item2',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue