[firestore] Add collection onSnapshot support

This commit is contained in:
Chris Bianca
2017-10-03 10:12:25 +01:00
parent d40f464f1c
commit 22f7d77f54
15 changed files with 559 additions and 107 deletions

View File

@@ -32,8 +32,6 @@ import io.invertase.firebase.Utils;
public class RNFirebaseFirestore extends ReactContextBaseJavaModule {
private static final String TAG = "RNFirebaseFirestore";
private HashMap<String, RNFirebaseFirestoreCollectionReference> collectionReferences = new HashMap<>();
private HashMap<String, RNFirebaseFirestoreDocumentReference> documentReferences = new HashMap<>();
// private SparseArray<RNFirebaseTransactionHandler> transactionHandlers = new SparseArray<>();
RNFirebaseFirestore(ReactApplicationContext reactContext) {
@@ -51,6 +49,20 @@ public class RNFirebaseFirestore extends ReactContextBaseJavaModule {
ref.get(promise);
}
@ReactMethod
public void collectionOffSnapshot(String appName, String path, ReadableArray filters,
ReadableArray orders, ReadableMap options, String listenerId) {
RNFirebaseFirestoreCollectionReference.offSnapshot(listenerId);
}
@ReactMethod
public void collectionOnSnapshot(String appName, String path, ReadableArray filters,
ReadableArray orders, ReadableMap options, String listenerId) {
RNFirebaseFirestoreCollectionReference ref = getCollectionForAppPath(appName, path, filters, orders, options);
ref.onSnapshot(listenerId);
}
@ReactMethod
public void documentBatch(final String appName, final ReadableArray writes,
final ReadableMap commitOptions, final Promise promise) {
@@ -134,18 +146,13 @@ public class RNFirebaseFirestore extends ReactContextBaseJavaModule {
}
@ReactMethod
public void documentOffSnapshot(String appName, String path, int listenerId) {
RNFirebaseFirestoreDocumentReference ref = getCachedDocumentForAppPath(appName, path);
ref.offSnapshot(listenerId);
if (!ref.hasListeners()) {
clearCachedDocumentForAppPath(appName, path);
}
public void documentOffSnapshot(String appName, String path, String listenerId) {
RNFirebaseFirestoreDocumentReference.offSnapshot(listenerId);
}
@ReactMethod
public void documentOnSnapshot(String appName, String path, int listenerId) {
RNFirebaseFirestoreDocumentReference ref = getCachedDocumentForAppPath(appName, path);
public void documentOnSnapshot(String appName, String path, String listenerId) {
RNFirebaseFirestoreDocumentReference ref = getDocumentForAppPath(appName, path);
ref.onSnapshot(listenerId);
}
@@ -204,36 +211,7 @@ public class RNFirebaseFirestore extends ReactContextBaseJavaModule {
ReadableArray filters,
ReadableArray orders,
ReadableMap options) {
return new RNFirebaseFirestoreCollectionReference(appName, path, filters, orders, options);
}
/**
* Get a cached document reference for a specific app and path
*
* @param appName
* @param path
* @return
*/
private RNFirebaseFirestoreDocumentReference getCachedDocumentForAppPath(String appName, String path) {
String key = appName + "/" + path;
RNFirebaseFirestoreDocumentReference ref = documentReferences.get(key);
if (ref == null) {
ref = getDocumentForAppPath(appName, path);
documentReferences.put(key, ref);
}
return ref;
}
/**
* Clear a cached document reference for a specific app and path
*
* @param appName
* @param path
* @return
*/
private void clearCachedDocumentForAppPath(String appName, String path) {
String key = appName + "/" + path;
documentReferences.remove(key);
return new RNFirebaseFirestoreCollectionReference(this.getReactApplicationContext(), appName, path, filters, orders, options);
}
/**

View File

@@ -4,16 +4,21 @@ package io.invertase.firebase.firestore;
import android.support.annotation.NonNull;
import android.util.Log;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import com.google.firebase.firestore.EventListener;
import com.google.firebase.firestore.FirebaseFirestoreException;
import com.google.firebase.firestore.ListenerRegistration;
import com.google.firebase.firestore.Query;
import com.google.firebase.firestore.QuerySnapshot;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -21,21 +26,26 @@ import io.invertase.firebase.Utils;
public class RNFirebaseFirestoreCollectionReference {
private static final String TAG = "RNFSCollectionReference";
private static Map<String, ListenerRegistration> collectionSnapshotListeners = new HashMap<>();
private final String appName;
private final String path;
private final ReadableArray filters;
private final ReadableArray orders;
private final ReadableMap options;
private final Query query;
private ReactContext reactContext;
RNFirebaseFirestoreCollectionReference(String appName, String path, ReadableArray filters,
ReadableArray orders, ReadableMap options) {
RNFirebaseFirestoreCollectionReference(ReactContext reactContext, String appName, String path,
ReadableArray filters, ReadableArray orders,
ReadableMap options) {
this.appName = appName;
this.path = path;
this.filters = filters;
this.orders = orders;
this.options = options;
this.query = buildQuery();
this.reactContext = reactContext;
}
void get(final Promise promise) {
@@ -54,6 +64,42 @@ public class RNFirebaseFirestoreCollectionReference {
});
}
public static void offSnapshot(final String listenerId) {
ListenerRegistration listenerRegistration = collectionSnapshotListeners.remove(listenerId);
if (listenerRegistration != null) {
listenerRegistration.remove();
}
}
public void onSnapshot(final String listenerId) {
if (!collectionSnapshotListeners.containsKey(listenerId)) {
final EventListener<QuerySnapshot> listener = new EventListener<QuerySnapshot>() {
@Override
public void onEvent(QuerySnapshot querySnapshot, FirebaseFirestoreException exception) {
if (exception == null) {
handleQuerySnapshotEvent(listenerId, querySnapshot);
} else {
ListenerRegistration listenerRegistration = collectionSnapshotListeners.remove(listenerId);
if (listenerRegistration != null) {
listenerRegistration.remove();
}
handleQuerySnapshotError(listenerId, exception);
}
}
};
ListenerRegistration listenerRegistration = this.query.addSnapshotListener(listener);
collectionSnapshotListeners.put(listenerId, listenerRegistration);
}
}
/*
* INTERNALS/UTILS
*/
boolean hasListeners() {
return !collectionSnapshotListeners.isEmpty();
}
private Query buildQuery() {
Query query = RNFirebaseFirestore.getFirestoreForApp(appName).collection(path);
query = applyFilters(query);
@@ -134,4 +180,39 @@ public class RNFirebaseFirestoreCollectionReference {
}
return query;
}
/**
* Handles documentSnapshot events.
*
* @param listenerId
* @param querySnapshot
*/
private void handleQuerySnapshotEvent(String listenerId, QuerySnapshot querySnapshot) {
WritableMap event = Arguments.createMap();
WritableMap data = FirestoreSerialize.snapshotToWritableMap(querySnapshot);
event.putString("appName", appName);
event.putString("path", path);
event.putString("listenerId", listenerId);
event.putMap("querySnapshot", data);
Utils.sendEvent(reactContext, "firestore_collection_sync_event", event);
}
/**
* Handles a documentSnapshot error event
*
* @param listenerId
* @param exception
*/
private void handleQuerySnapshotError(String listenerId, FirebaseFirestoreException exception) {
WritableMap event = Arguments.createMap();
event.putString("appName", appName);
event.putString("path", path);
event.putString("listenerId", listenerId);
event.putMap("error", RNFirebaseFirestore.getJSError(exception));
Utils.sendEvent(reactContext, "firestore_collection_sync_event", event);
}
}

View File

@@ -25,11 +25,12 @@ import io.invertase.firebase.Utils;
public class RNFirebaseFirestoreDocumentReference {
private static final String TAG = "RNFBFSDocumentReference";
private static Map<String, ListenerRegistration> documentSnapshotListeners = new HashMap<>();
private final String appName;
private final String path;
private ReactContext reactContext;
private final DocumentReference ref;
private Map<Integer, ListenerRegistration> documentSnapshotListeners = new HashMap<>();
RNFirebaseFirestoreDocumentReference(ReactContext reactContext, String appName, String path) {
this.appName = appName;
@@ -79,14 +80,14 @@ public class RNFirebaseFirestoreDocumentReference {
});
}
public void offSnapshot(final int listenerId) {
public static void offSnapshot(final String listenerId) {
ListenerRegistration listenerRegistration = documentSnapshotListeners.remove(listenerId);
if (listenerRegistration != null) {
listenerRegistration.remove();
}
}
public void onSnapshot(final int listenerId) {
public void onSnapshot(final String listenerId) {
if (!documentSnapshotListeners.containsKey(listenerId)) {
final EventListener<DocumentSnapshot> listener = new EventListener<DocumentSnapshot>() {
@Override
@@ -154,7 +155,7 @@ public class RNFirebaseFirestoreDocumentReference {
* INTERNALS/UTILS
*/
public boolean hasListeners() {
boolean hasListeners() {
return !documentSnapshotListeners.isEmpty();
}
@@ -164,14 +165,14 @@ public class RNFirebaseFirestoreDocumentReference {
* @param listenerId
* @param documentSnapshot
*/
private void handleDocumentSnapshotEvent(int listenerId, DocumentSnapshot documentSnapshot) {
private void handleDocumentSnapshotEvent(String listenerId, DocumentSnapshot documentSnapshot) {
WritableMap event = Arguments.createMap();
WritableMap data = FirestoreSerialize.snapshotToWritableMap(documentSnapshot);
event.putString("appName", appName);
event.putString("path", path);
event.putInt("listenerId", listenerId);
event.putMap("document", data);
event.putString("listenerId", listenerId);
event.putMap("documentSnapshot", data);
Utils.sendEvent(reactContext, "firestore_document_sync_event", event);
}
@@ -182,12 +183,12 @@ public class RNFirebaseFirestoreDocumentReference {
* @param listenerId
* @param exception
*/
private void handleDocumentSnapshotError(int listenerId, FirebaseFirestoreException exception) {
private void handleDocumentSnapshotError(String listenerId, FirebaseFirestoreException exception) {
WritableMap event = Arguments.createMap();
event.putString("appName", appName);
event.putString("path", path);
event.putInt("listenerId", listenerId);
event.putString("listenerId", listenerId);
event.putMap("error", RNFirebaseFirestore.getJSError(exception));
Utils.sendEvent(reactContext, "firestore_document_sync_event", event);