2017-09-28 12:48:28 +00:00
|
|
|
#import "RNFirebaseFirestoreCollectionReference.h"
|
|
|
|
|
|
|
|
@implementation RNFirebaseFirestoreCollectionReference
|
|
|
|
|
2017-10-03 16:23:28 +00:00
|
|
|
#if __has_include(<FirebaseFirestore/FirebaseFirestore.h>)
|
2017-09-28 12:48:28 +00:00
|
|
|
|
2017-10-03 09:12:25 +00:00
|
|
|
static NSMutableDictionary *_listeners;
|
|
|
|
|
|
|
|
- (id)initWithPathAndModifiers:(RCTEventEmitter *) emitter
|
2018-01-03 20:00:38 +00:00
|
|
|
appDisplayName:(NSString *) appDisplayName
|
2017-09-28 12:48:28 +00:00
|
|
|
path:(NSString *) path
|
|
|
|
filters:(NSArray *) filters
|
|
|
|
orders:(NSArray *) orders
|
|
|
|
options:(NSDictionary *) options {
|
|
|
|
self = [super init];
|
|
|
|
if (self) {
|
2017-10-03 09:12:25 +00:00
|
|
|
_emitter = emitter;
|
2018-01-03 20:00:38 +00:00
|
|
|
_appDisplayName = appDisplayName;
|
2017-09-28 12:48:28 +00:00
|
|
|
_path = path;
|
|
|
|
_filters = filters;
|
|
|
|
_orders = orders;
|
|
|
|
_options = options;
|
|
|
|
_query = [self buildQuery];
|
|
|
|
}
|
2017-10-03 09:12:25 +00:00
|
|
|
// Initialise the static listeners object if required
|
|
|
|
if (!_listeners) {
|
|
|
|
_listeners = [[NSMutableDictionary alloc] init];
|
|
|
|
}
|
2017-09-28 12:48:28 +00:00
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
2018-07-03 15:03:43 +00:00
|
|
|
- (void)get:(NSDictionary *) getOptions
|
|
|
|
resolver:(RCTPromiseResolveBlock) resolve
|
2017-09-28 12:48:28 +00:00
|
|
|
rejecter:(RCTPromiseRejectBlock) reject {
|
2018-07-03 15:03:43 +00:00
|
|
|
FIRFirestoreSource source;
|
|
|
|
if (getOptions && getOptions[@"source"]) {
|
|
|
|
if ([getOptions[@"source"] isEqualToString:@"server"]) {
|
|
|
|
source = FIRFirestoreSourceServer;
|
|
|
|
} else if ([getOptions[@"source"] isEqualToString:@"cache"]) {
|
|
|
|
source = FIRFirestoreSourceCache;
|
|
|
|
} else {
|
|
|
|
source = FIRFirestoreSourceDefault;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
source = FIRFirestoreSourceDefault;
|
|
|
|
}
|
|
|
|
[_query getDocumentsWithSource:source completion:^(FIRQuerySnapshot * _Nullable snapshot, NSError * _Nullable error) {
|
2017-09-28 12:48:28 +00:00
|
|
|
if (error) {
|
|
|
|
[RNFirebaseFirestore promiseRejectException:reject error:error];
|
|
|
|
} else {
|
|
|
|
NSDictionary *data = [RNFirebaseFirestoreCollectionReference snapshotToDictionary:snapshot];
|
|
|
|
resolve(data);
|
|
|
|
}
|
|
|
|
}];
|
|
|
|
}
|
|
|
|
|
2017-10-03 09:12:25 +00:00
|
|
|
+ (void)offSnapshot:(NSString *) listenerId {
|
|
|
|
id<FIRListenerRegistration> listener = _listeners[listenerId];
|
|
|
|
if (listener) {
|
|
|
|
[_listeners removeObjectForKey:listenerId];
|
|
|
|
[listener remove];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-06 11:00:40 +00:00
|
|
|
- (void)onSnapshot:(NSString *) listenerId
|
|
|
|
queryListenOptions:(NSDictionary *) queryListenOptions {
|
2017-10-03 09:12:25 +00:00
|
|
|
if (_listeners[listenerId] == nil) {
|
|
|
|
id listenerBlock = ^(FIRQuerySnapshot * _Nullable snapshot, NSError * _Nullable error) {
|
|
|
|
if (error) {
|
|
|
|
id<FIRListenerRegistration> listener = _listeners[listenerId];
|
|
|
|
if (listener) {
|
|
|
|
[_listeners removeObjectForKey:listenerId];
|
|
|
|
[listener remove];
|
|
|
|
}
|
|
|
|
[self handleQuerySnapshotError:listenerId error:error];
|
|
|
|
} else {
|
|
|
|
[self handleQuerySnapshotEvent:listenerId querySnapshot:snapshot];
|
|
|
|
}
|
|
|
|
};
|
2018-01-03 20:00:38 +00:00
|
|
|
|
2018-05-16 16:22:47 +00:00
|
|
|
bool includeMetadataChanges;
|
2018-05-08 14:36:40 +00:00
|
|
|
if (queryListenOptions && queryListenOptions[@"includeMetadataChanges"]) {
|
2018-05-16 16:22:47 +00:00
|
|
|
includeMetadataChanges = true;
|
2018-05-08 14:36:40 +00:00
|
|
|
} else {
|
2018-05-16 16:22:47 +00:00
|
|
|
includeMetadataChanges = false;
|
2017-10-06 11:00:40 +00:00
|
|
|
}
|
2018-01-03 20:00:38 +00:00
|
|
|
|
2018-05-16 16:22:47 +00:00
|
|
|
id<FIRListenerRegistration> listener = [_query addSnapshotListenerWithIncludeMetadataChanges:includeMetadataChanges listener:listenerBlock];
|
2017-10-03 09:12:25 +00:00
|
|
|
_listeners[listenerId] = listener;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-28 12:48:28 +00:00
|
|
|
- (FIRQuery *)buildQuery {
|
2018-01-03 20:00:38 +00:00
|
|
|
FIRFirestore *firestore = [RNFirebaseFirestore getFirestoreForApp:_appDisplayName];
|
2017-10-31 15:32:08 +00:00
|
|
|
FIRQuery *query = (FIRQuery*)[firestore collectionWithPath:_path];
|
|
|
|
query = [self applyFilters:firestore query:query];
|
2017-09-28 12:48:28 +00:00
|
|
|
query = [self applyOrders:query];
|
2017-10-31 22:18:07 +00:00
|
|
|
query = [self applyOptions:firestore query:query];
|
2017-10-03 16:23:28 +00:00
|
|
|
|
2017-09-28 12:48:28 +00:00
|
|
|
return query;
|
|
|
|
}
|
|
|
|
|
2017-10-31 15:32:08 +00:00
|
|
|
- (FIRQuery *)applyFilters:(FIRFirestore *) firestore
|
|
|
|
query:(FIRQuery *) query {
|
2017-09-28 12:48:28 +00:00
|
|
|
for (NSDictionary *filter in _filters) {
|
2018-01-11 18:28:14 +00:00
|
|
|
NSDictionary *fieldPathDictionary = filter[@"fieldPath"];
|
|
|
|
NSString *fieldPathType = fieldPathDictionary[@"type"];
|
2017-09-28 12:48:28 +00:00
|
|
|
NSString *operator = filter[@"operator"];
|
2017-10-31 15:32:08 +00:00
|
|
|
NSDictionary *jsValue = filter[@"value"];
|
|
|
|
id value = [RNFirebaseFirestoreDocumentReference parseJSTypeMap:firestore jsTypeMap:jsValue];
|
2017-10-03 16:23:28 +00:00
|
|
|
|
2018-01-11 18:28:14 +00:00
|
|
|
if ([fieldPathType isEqualToString:@"string"]) {
|
|
|
|
NSString *fieldPath = fieldPathDictionary[@"string"];
|
|
|
|
if ([operator isEqualToString:@"EQUAL"]) {
|
|
|
|
query = [query queryWhereField:fieldPath isEqualTo:value];
|
|
|
|
} else if ([operator isEqualToString:@"GREATER_THAN"]) {
|
|
|
|
query = [query queryWhereField:fieldPath isGreaterThan:value];
|
|
|
|
} else if ([operator isEqualToString:@"GREATER_THAN_OR_EQUAL"]) {
|
|
|
|
query = [query queryWhereField:fieldPath isGreaterThanOrEqualTo:value];
|
|
|
|
} else if ([operator isEqualToString:@"LESS_THAN"]) {
|
|
|
|
query = [query queryWhereField:fieldPath isLessThan:value];
|
|
|
|
} else if ([operator isEqualToString:@"LESS_THAN_OR_EQUAL"]) {
|
|
|
|
query = [query queryWhereField:fieldPath isLessThanOrEqualTo:value];
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
NSArray *fieldPathElements = fieldPathDictionary[@"elements"];
|
|
|
|
FIRFieldPath *fieldPath = [[FIRFieldPath alloc] initWithFields:fieldPathElements];
|
|
|
|
if ([operator isEqualToString:@"EQUAL"]) {
|
|
|
|
query = [query queryWhereFieldPath:fieldPath isEqualTo:value];
|
|
|
|
} else if ([operator isEqualToString:@"GREATER_THAN"]) {
|
|
|
|
query = [query queryWhereFieldPath:fieldPath isGreaterThan:value];
|
|
|
|
} else if ([operator isEqualToString:@"GREATER_THAN_OR_EQUAL"]) {
|
|
|
|
query = [query queryWhereFieldPath:fieldPath isGreaterThanOrEqualTo:value];
|
|
|
|
} else if ([operator isEqualToString:@"LESS_THAN"]) {
|
|
|
|
query = [query queryWhereFieldPath:fieldPath isLessThan:value];
|
|
|
|
} else if ([operator isEqualToString:@"LESS_THAN_OR_EQUAL"]) {
|
|
|
|
query = [query queryWhereFieldPath:fieldPath isLessThanOrEqualTo:value];
|
|
|
|
}
|
2017-09-28 12:48:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return query;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (FIRQuery *)applyOrders:(FIRQuery *) query {
|
|
|
|
for (NSDictionary *order in _orders) {
|
|
|
|
NSString *direction = order[@"direction"];
|
2018-01-11 18:28:14 +00:00
|
|
|
NSDictionary *fieldPathDictionary = order[@"fieldPath"];
|
|
|
|
NSString *fieldPathType = fieldPathDictionary[@"type"];
|
2017-10-03 16:23:28 +00:00
|
|
|
|
2018-01-11 18:28:14 +00:00
|
|
|
if ([fieldPathType isEqualToString:@"string"]) {
|
|
|
|
NSString *fieldPath = fieldPathDictionary[@"string"];
|
|
|
|
query = [query queryOrderedByField:fieldPath descending:([direction isEqualToString:@"DESCENDING"])];
|
|
|
|
} else {
|
|
|
|
NSArray *fieldPathElements = fieldPathDictionary[@"elements"];
|
|
|
|
FIRFieldPath *fieldPath = [[FIRFieldPath alloc] initWithFields:fieldPathElements];
|
|
|
|
query = [query queryOrderedByFieldPath:fieldPath descending:([direction isEqualToString:@"DESCENDING"])];
|
|
|
|
}
|
2017-09-28 12:48:28 +00:00
|
|
|
}
|
|
|
|
return query;
|
|
|
|
}
|
|
|
|
|
2017-10-31 22:18:07 +00:00
|
|
|
- (FIRQuery *)applyOptions:(FIRFirestore *) firestore
|
|
|
|
query:(FIRQuery *) query {
|
2017-09-28 12:48:28 +00:00
|
|
|
if (_options[@"endAt"]) {
|
2017-10-31 22:18:07 +00:00
|
|
|
query = [query queryEndingAtValues:[RNFirebaseFirestoreDocumentReference parseJSArray:firestore jsArray:_options[@"endAt"]]];
|
2017-09-28 12:48:28 +00:00
|
|
|
}
|
|
|
|
if (_options[@"endBefore"]) {
|
2017-10-31 22:18:07 +00:00
|
|
|
query = [query queryEndingBeforeValues:[RNFirebaseFirestoreDocumentReference parseJSArray:firestore jsArray:_options[@"endBefore"]]];
|
2017-09-28 12:48:28 +00:00
|
|
|
}
|
2017-10-31 07:34:41 +00:00
|
|
|
if (_options[@"limit"]) {
|
2017-10-31 14:29:23 +00:00
|
|
|
query = [query queryLimitedTo:[_options[@"limit"] intValue]];
|
2017-10-31 07:34:41 +00:00
|
|
|
}
|
2017-09-28 12:48:28 +00:00
|
|
|
if (_options[@"offset"]) {
|
|
|
|
// iOS doesn't support offset
|
|
|
|
}
|
|
|
|
if (_options[@"selectFields"]) {
|
|
|
|
// iOS doesn't support selectFields
|
|
|
|
}
|
|
|
|
if (_options[@"startAfter"]) {
|
2017-10-31 22:18:07 +00:00
|
|
|
query = [query queryStartingAfterValues:[RNFirebaseFirestoreDocumentReference parseJSArray:firestore jsArray:_options[@"startAfter"]]];
|
2017-09-28 12:48:28 +00:00
|
|
|
}
|
|
|
|
if (_options[@"startAt"]) {
|
2017-10-31 22:18:07 +00:00
|
|
|
query = [query queryStartingAtValues:[RNFirebaseFirestoreDocumentReference parseJSArray:firestore jsArray:_options[@"startAt"]]];
|
2017-09-28 12:48:28 +00:00
|
|
|
}
|
|
|
|
return query;
|
|
|
|
}
|
|
|
|
|
2017-10-03 09:12:25 +00:00
|
|
|
- (void)handleQuerySnapshotError:(NSString *)listenerId
|
|
|
|
error:(NSError *)error {
|
|
|
|
NSMutableDictionary *event = [[NSMutableDictionary alloc] init];
|
2018-01-03 20:00:38 +00:00
|
|
|
[event setValue:_appDisplayName forKey:@"appName"];
|
2017-10-03 09:12:25 +00:00
|
|
|
[event setValue:_path forKey:@"path"];
|
|
|
|
[event setValue:listenerId forKey:@"listenerId"];
|
|
|
|
[event setValue:[RNFirebaseFirestore getJSError:error] forKey:@"error"];
|
2017-10-03 16:23:28 +00:00
|
|
|
|
2017-10-26 10:55:07 +00:00
|
|
|
[RNFirebaseUtil sendJSEvent:self.emitter name:FIRESTORE_COLLECTION_SYNC_EVENT body:event];
|
2017-10-03 09:12:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)handleQuerySnapshotEvent:(NSString *)listenerId
|
|
|
|
querySnapshot:(FIRQuerySnapshot *)querySnapshot {
|
|
|
|
NSMutableDictionary *event = [[NSMutableDictionary alloc] init];
|
2018-01-03 20:00:38 +00:00
|
|
|
[event setValue:_appDisplayName forKey:@"appName"];
|
2017-10-03 09:12:25 +00:00
|
|
|
[event setValue:_path forKey:@"path"];
|
|
|
|
[event setValue:listenerId forKey:@"listenerId"];
|
|
|
|
[event setValue:[RNFirebaseFirestoreCollectionReference snapshotToDictionary:querySnapshot] forKey:@"querySnapshot"];
|
2017-10-03 16:23:28 +00:00
|
|
|
|
2017-10-26 10:55:07 +00:00
|
|
|
[RNFirebaseUtil sendJSEvent:self.emitter name:FIRESTORE_COLLECTION_SYNC_EVENT body:event];
|
2017-10-03 09:12:25 +00:00
|
|
|
}
|
|
|
|
|
2017-09-28 12:48:28 +00:00
|
|
|
+ (NSDictionary *)snapshotToDictionary:(FIRQuerySnapshot *)querySnapshot {
|
|
|
|
NSMutableDictionary *snapshot = [[NSMutableDictionary alloc] init];
|
|
|
|
[snapshot setValue:[self documentChangesToArray:querySnapshot.documentChanges] forKey:@"changes"];
|
|
|
|
[snapshot setValue:[self documentSnapshotsToArray:querySnapshot.documents] forKey:@"documents"];
|
2017-10-05 09:18:24 +00:00
|
|
|
if (querySnapshot.metadata) {
|
|
|
|
NSMutableDictionary *metadata = [[NSMutableDictionary alloc] init];
|
|
|
|
[metadata setValue:@(querySnapshot.metadata.fromCache) forKey:@"fromCache"];
|
|
|
|
[metadata setValue:@(querySnapshot.metadata.hasPendingWrites) forKey:@"hasPendingWrites"];
|
|
|
|
[snapshot setValue:metadata forKey:@"metadata"];
|
|
|
|
}
|
2017-10-03 16:23:28 +00:00
|
|
|
|
2017-09-28 12:48:28 +00:00
|
|
|
return snapshot;
|
|
|
|
}
|
|
|
|
|
|
|
|
+ (NSArray *)documentChangesToArray:(NSArray<FIRDocumentChange *> *) documentChanges {
|
|
|
|
NSMutableArray *changes = [[NSMutableArray alloc] init];
|
|
|
|
for (FIRDocumentChange *change in documentChanges) {
|
|
|
|
[changes addObject:[self documentChangeToDictionary:change]];
|
|
|
|
}
|
2017-10-03 16:23:28 +00:00
|
|
|
|
2017-09-28 12:48:28 +00:00
|
|
|
return changes;
|
|
|
|
}
|
|
|
|
|
|
|
|
+ (NSDictionary *)documentChangeToDictionary:(FIRDocumentChange *)documentChange {
|
|
|
|
NSMutableDictionary *change = [[NSMutableDictionary alloc] init];
|
|
|
|
[change setValue:[RNFirebaseFirestoreDocumentReference snapshotToDictionary:documentChange.document] forKey:@"document"];
|
|
|
|
[change setValue:@(documentChange.newIndex) forKey:@"newIndex"];
|
|
|
|
[change setValue:@(documentChange.oldIndex) forKey:@"oldIndex"];
|
2017-10-03 16:23:28 +00:00
|
|
|
|
2017-09-28 12:48:28 +00:00
|
|
|
if (documentChange.type == FIRDocumentChangeTypeAdded) {
|
|
|
|
[change setValue:@"added" forKey:@"type"];
|
|
|
|
} else if (documentChange.type == FIRDocumentChangeTypeRemoved) {
|
|
|
|
[change setValue:@"removed" forKey:@"type"];
|
|
|
|
} else if (documentChange.type == FIRDocumentChangeTypeModified) {
|
|
|
|
[change setValue:@"modified" forKey:@"type"];
|
|
|
|
}
|
2017-10-03 16:23:28 +00:00
|
|
|
|
2017-09-28 12:48:28 +00:00
|
|
|
return change;
|
|
|
|
}
|
|
|
|
|
|
|
|
+ (NSArray *)documentSnapshotsToArray:(NSArray<FIRDocumentSnapshot *> *) documentSnapshots {
|
|
|
|
NSMutableArray *snapshots = [[NSMutableArray alloc] init];
|
|
|
|
for (FIRDocumentSnapshot *snapshot in documentSnapshots) {
|
|
|
|
[snapshots addObject:[RNFirebaseFirestoreDocumentReference snapshotToDictionary:snapshot]];
|
|
|
|
}
|
2017-10-03 16:23:28 +00:00
|
|
|
|
2017-09-28 12:48:28 +00:00
|
|
|
return snapshots;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
@end
|