2
0
mirror of synced 2025-01-18 18:32:00 +00:00

[android][firestore] cleanup

This commit is contained in:
Salakar 2018-08-01 23:00:40 +01:00
parent e7e89732c0
commit 0061ad9888
7 changed files with 566 additions and 318 deletions

View File

@ -1,20 +1,21 @@
package io.invertase.firebase; package io.invertase.firebase;
import android.annotation.SuppressLint;
import android.app.ActivityManager; import android.app.ActivityManager;
import android.content.Context; import android.content.Context;
import android.os.AsyncTask;
import android.util.Log; import android.util.Log;
import com.facebook.react.bridge.Arguments;
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.facebook.react.modules.core.DeviceEventManagerModule;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.facebook.react.bridge.ReadableArray;
import javax.annotation.Nullable; import javax.annotation.Nullable;

View File

@ -52,18 +52,16 @@ public class FirestoreSerialize {
if (documentSnapshot.exists()) { if (documentSnapshot.exists()) {
documentMap.putMap(KEY_DATA, objectMapToWritable(documentSnapshot.getData())); documentMap.putMap(KEY_DATA, objectMapToWritable(documentSnapshot.getData()));
} }
// metadata
if (documentSnapshot.getMetadata() != null) {
WritableMap metadata = Arguments.createMap();
metadata.putBoolean("fromCache", documentSnapshot.getMetadata().isFromCache());
metadata.putBoolean("hasPendingWrites", documentSnapshot.getMetadata().hasPendingWrites());
documentMap.putMap(KEY_METADATA, metadata);
}
// metadata
WritableMap metadata = Arguments.createMap();
metadata.putBoolean("fromCache", documentSnapshot.getMetadata().isFromCache());
metadata.putBoolean("hasPendingWrites", documentSnapshot.getMetadata().hasPendingWrites());
documentMap.putMap(KEY_METADATA, metadata);
return documentMap; return documentMap;
} }
public static WritableMap snapshotToWritableMap(QuerySnapshot querySnapshot) { static WritableMap snapshotToWritableMap(QuerySnapshot querySnapshot) {
WritableMap queryMap = Arguments.createMap(); WritableMap queryMap = Arguments.createMap();
List<DocumentChange> documentChanges = querySnapshot.getDocumentChanges(); List<DocumentChange> documentChanges = querySnapshot.getDocumentChanges();
@ -78,12 +76,10 @@ public class FirestoreSerialize {
queryMap.putArray(KEY_DOCUMENTS, documents); queryMap.putArray(KEY_DOCUMENTS, documents);
// metadata // metadata
if (querySnapshot.getMetadata() != null) { WritableMap metadata = Arguments.createMap();
WritableMap metadata = Arguments.createMap(); metadata.putBoolean("fromCache", querySnapshot.getMetadata().isFromCache());
metadata.putBoolean("fromCache", querySnapshot.getMetadata().isFromCache()); metadata.putBoolean("hasPendingWrites", querySnapshot.getMetadata().hasPendingWrites());
metadata.putBoolean("hasPendingWrites", querySnapshot.getMetadata().hasPendingWrites()); queryMap.putMap(KEY_METADATA, metadata);
queryMap.putMap(KEY_METADATA, metadata);
}
return queryMap; return queryMap;
} }
@ -94,7 +90,7 @@ public class FirestoreSerialize {
* @param documentChanges List<DocumentChange> * @param documentChanges List<DocumentChange>
* @return WritableArray * @return WritableArray
*/ */
static WritableArray documentChangesToWritableArray(List<DocumentChange> documentChanges) { private static WritableArray documentChangesToWritableArray(List<DocumentChange> documentChanges) {
WritableArray documentChangesWritable = Arguments.createArray(); WritableArray documentChangesWritable = Arguments.createArray();
for (DocumentChange documentChange : documentChanges) { for (DocumentChange documentChange : documentChanges) {
documentChangesWritable.pushMap(documentChangeToWritableMap(documentChange)); documentChangesWritable.pushMap(documentChangeToWritableMap(documentChange));
@ -108,7 +104,7 @@ public class FirestoreSerialize {
* @param documentChange DocumentChange * @param documentChange DocumentChange
* @return WritableMap * @return WritableMap
*/ */
static WritableMap documentChangeToWritableMap(DocumentChange documentChange) { private static WritableMap documentChangeToWritableMap(DocumentChange documentChange) {
WritableMap documentChangeMap = Arguments.createMap(); WritableMap documentChangeMap = Arguments.createMap();
switch (documentChange.getType()) { switch (documentChange.getType()) {
@ -122,8 +118,10 @@ public class FirestoreSerialize {
documentChangeMap.putString(KEY_DOC_CHANGE_TYPE, "modified"); documentChangeMap.putString(KEY_DOC_CHANGE_TYPE, "modified");
} }
documentChangeMap.putMap(KEY_DOC_CHANGE_DOCUMENT, documentChangeMap.putMap(
snapshotToWritableMap(documentChange.getDocument())); KEY_DOC_CHANGE_DOCUMENT,
snapshotToWritableMap(documentChange.getDocument())
);
documentChangeMap.putInt(KEY_DOC_CHANGE_NEW_INDEX, documentChange.getNewIndex()); documentChangeMap.putInt(KEY_DOC_CHANGE_NEW_INDEX, documentChange.getNewIndex());
documentChangeMap.putInt(KEY_DOC_CHANGE_OLD_INDEX, documentChange.getOldIndex()); documentChangeMap.putInt(KEY_DOC_CHANGE_OLD_INDEX, documentChange.getOldIndex());
@ -136,7 +134,7 @@ public class FirestoreSerialize {
* @param map Map<String, Object> * @param map Map<String, Object>
* @return WritableMap * @return WritableMap
*/ */
static WritableMap objectMapToWritable(Map<String, Object> map) { private static WritableMap objectMapToWritable(Map<String, Object> map) {
WritableMap writableMap = Arguments.createMap(); WritableMap writableMap = Arguments.createMap();
for (Map.Entry<String, Object> entry : map.entrySet()) { for (Map.Entry<String, Object> entry : map.entrySet()) {
WritableMap typeMap = buildTypeMap(entry.getValue()); WritableMap typeMap = buildTypeMap(entry.getValue());
@ -224,7 +222,10 @@ public class FirestoreSerialize {
return typeMap; return typeMap;
} }
static Map<String, Object> parseReadableMap(FirebaseFirestore firestore, ReadableMap readableMap) { static Map<String, Object> parseReadableMap(
FirebaseFirestore firestore,
ReadableMap readableMap
) {
Map<String, Object> map = new HashMap<>(); Map<String, Object> map = new HashMap<>();
if (readableMap != null) { if (readableMap != null) {
ReadableMapKeySetIterator iterator = readableMap.keySetIterator(); ReadableMapKeySetIterator iterator = readableMap.keySetIterator();
@ -290,7 +291,10 @@ public class FirestoreSerialize {
} }
} }
public static List<Object> parseDocumentBatches(FirebaseFirestore firestore, ReadableArray readableArray) { static List<Object> parseDocumentBatches(
FirebaseFirestore firestore,
ReadableArray readableArray
) {
List<Object> writes = new ArrayList<>(readableArray.size()); List<Object> writes = new ArrayList<>(readableArray.size());
for (int i = 0; i < readableArray.size(); i++) { for (int i = 0; i < readableArray.size(); i++) {
Map<String, Object> write = new HashMap<>(); Map<String, Object> write = new HashMap<>();

View File

@ -18,13 +18,13 @@ import com.google.android.gms.tasks.OnFailureListener;
import com.google.android.gms.tasks.OnSuccessListener; import com.google.android.gms.tasks.OnSuccessListener;
import com.google.android.gms.tasks.Task; import com.google.android.gms.tasks.Task;
import com.google.firebase.FirebaseApp; import com.google.firebase.FirebaseApp;
import com.google.firebase.firestore.FirebaseFirestoreSettings;
import com.google.firebase.firestore.Transaction;
import com.google.firebase.firestore.DocumentReference; import com.google.firebase.firestore.DocumentReference;
import com.google.firebase.firestore.FieldValue; import com.google.firebase.firestore.FieldValue;
import com.google.firebase.firestore.FirebaseFirestore; import com.google.firebase.firestore.FirebaseFirestore;
import com.google.firebase.firestore.FirebaseFirestoreException; import com.google.firebase.firestore.FirebaseFirestoreException;
import com.google.firebase.firestore.FirebaseFirestoreSettings;
import com.google.firebase.firestore.SetOptions; import com.google.firebase.firestore.SetOptions;
import com.google.firebase.firestore.Transaction;
import com.google.firebase.firestore.WriteBatch; import com.google.firebase.firestore.WriteBatch;
import java.util.HashMap; import java.util.HashMap;
@ -48,6 +48,184 @@ public class RNFirebaseFirestore extends ReactContextBaseJavaModule {
* REACT NATIVE METHODS * REACT NATIVE METHODS
*/ */
/**
* Generates a js-like error from an exception and rejects the provided promise with it.
*
* @param exception Exception Exception normally from a task result.
* @param promise Promise react native promise
*/
static void promiseRejectException(Promise promise, FirebaseFirestoreException exception) {
WritableMap jsError = getJSError(exception);
promise.reject(
jsError.getString("code"),
jsError.getString("message"),
exception
);
}
/**
* Get a database instance for a specific firebase app instance
*
* @param appName appName
* @return FirebaseFirestore
*/
static FirebaseFirestore getFirestoreForApp(String appName) {
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
return FirebaseFirestore.getInstance(firebaseApp);
}
/**
* Convert as firebase DatabaseError instance into a writable map
* with the correct web-like error codes.
*
* @param nativeException nativeException
* @return WritableMap
*/
static WritableMap getJSError(FirebaseFirestoreException nativeException) {
WritableMap errorMap = Arguments.createMap();
errorMap.putInt("nativeErrorCode", nativeException.getCode().value());
errorMap.putString("nativeErrorMessage", nativeException.getMessage());
String code;
String message;
String service = "Firestore";
// TODO: Proper error mappings
switch (nativeException.getCode()) {
case OK:
code = ErrorUtils.getCodeWithService(service, "ok");
message = ErrorUtils.getMessageWithService("Ok.", service, code);
break;
case CANCELLED:
code = ErrorUtils.getCodeWithService(service, "cancelled");
message = ErrorUtils.getMessageWithService("The operation was cancelled.", service, code);
break;
case UNKNOWN:
code = ErrorUtils.getCodeWithService(service, "unknown");
message = ErrorUtils.getMessageWithService(
"Unknown error or an error from a different error domain.",
service,
code
);
break;
case INVALID_ARGUMENT:
code = ErrorUtils.getCodeWithService(service, "invalid-argument");
message = ErrorUtils.getMessageWithService(
"Client specified an invalid argument.",
service,
code
);
break;
case DEADLINE_EXCEEDED:
code = ErrorUtils.getCodeWithService(service, "deadline-exceeded");
message = ErrorUtils.getMessageWithService(
"Deadline expired before operation could complete.",
service,
code
);
break;
case NOT_FOUND:
code = ErrorUtils.getCodeWithService(service, "not-found");
message = ErrorUtils.getMessageWithService(
"Some requested document was not found.",
service,
code
);
break;
case ALREADY_EXISTS:
code = ErrorUtils.getCodeWithService(service, "already-exists");
message = ErrorUtils.getMessageWithService(
"Some document that we attempted to create already exists.",
service,
code
);
break;
case PERMISSION_DENIED:
code = ErrorUtils.getCodeWithService(service, "permission-denied");
message = ErrorUtils.getMessageWithService(
"The caller does not have permission to execute the specified operation.",
service,
code
);
break;
case RESOURCE_EXHAUSTED:
code = ErrorUtils.getCodeWithService(service, "resource-exhausted");
message = ErrorUtils.getMessageWithService(
"Some resource has been exhausted, perhaps a per-user quota, or perhaps the entire file system is out of space.",
service,
code
);
break;
case FAILED_PRECONDITION:
code = ErrorUtils.getCodeWithService(service, "failed-precondition");
message = ErrorUtils.getMessageWithService(
"Operation was rejected because the system is not in a state required for the operation`s execution.",
service,
code
);
break;
case ABORTED:
code = ErrorUtils.getCodeWithService(service, "aborted");
message = ErrorUtils.getMessageWithService(
"The operation was aborted, typically due to a concurrency issue like transaction aborts, etc.",
service,
code
);
break;
case OUT_OF_RANGE:
code = ErrorUtils.getCodeWithService(service, "out-of-range");
message = ErrorUtils.getMessageWithService(
"Operation was attempted past the valid range.",
service,
code
);
break;
case UNIMPLEMENTED:
code = ErrorUtils.getCodeWithService(service, "unimplemented");
message = ErrorUtils.getMessageWithService(
"Operation is not implemented or not supported/enabled.",
service,
code
);
break;
case INTERNAL:
code = ErrorUtils.getCodeWithService(service, "internal");
message = ErrorUtils.getMessageWithService("Internal errors.", service, code);
break;
case UNAVAILABLE:
code = ErrorUtils.getCodeWithService(service, "unavailable");
message = ErrorUtils.getMessageWithService(
"The service is currently unavailable.",
service,
code
);
break;
case DATA_LOSS:
code = ErrorUtils.getCodeWithService(service, "data-loss");
message = ErrorUtils.getMessageWithService(
"Unrecoverable data loss or corruption.",
service,
code
);
break;
case UNAUTHENTICATED:
code = ErrorUtils.getCodeWithService(service, "unauthenticated");
message = ErrorUtils.getMessageWithService(
"The request does not have valid authentication credentials for the operation.",
service,
code
);
break;
default:
code = ErrorUtils.getCodeWithService(service, "unknown");
message = ErrorUtils.getMessageWithService("An unknown error occurred.", service, code);
}
errorMap.putString("code", code);
errorMap.putString("message", message);
return errorMap;
}
@ReactMethod @ReactMethod
public void disableNetwork(String appName, final Promise promise) { public void disableNetwork(String appName, final Promise promise) {
getFirestoreForApp(appName).disableNetwork().addOnCompleteListener(new OnCompleteListener<Void>() { getFirestoreForApp(appName).disableNetwork().addOnCompleteListener(new OnCompleteListener<Void>() {
@ -58,7 +236,10 @@ public class RNFirebaseFirestore extends ReactContextBaseJavaModule {
promise.resolve(null); promise.resolve(null);
} else { } else {
Log.e(TAG, "disableNetwork:onComplete:failure", task.getException()); Log.e(TAG, "disableNetwork:onComplete:failure", task.getException());
RNFirebaseFirestore.promiseRejectException(promise, (FirebaseFirestoreException)task.getException()); RNFirebaseFirestore.promiseRejectException(
promise,
(FirebaseFirestoreException) task.getException()
);
} }
} }
}); });
@ -83,35 +264,55 @@ public class RNFirebaseFirestore extends ReactContextBaseJavaModule {
promise.resolve(null); promise.resolve(null);
} else { } else {
Log.e(TAG, "enableNetwork:onComplete:failure", task.getException()); Log.e(TAG, "enableNetwork:onComplete:failure", task.getException());
RNFirebaseFirestore.promiseRejectException(promise, (FirebaseFirestoreException)task.getException()); RNFirebaseFirestore.promiseRejectException(
promise,
(FirebaseFirestoreException) task.getException()
);
} }
} }
}); });
} }
@ReactMethod @ReactMethod
public void collectionGet(String appName, String path, ReadableArray filters, public void collectionGet(
ReadableArray orders, ReadableMap options, ReadableMap getOptions, String appName, String path, ReadableArray filters,
final Promise promise) { ReadableArray orders, ReadableMap options, ReadableMap getOptions,
RNFirebaseFirestoreCollectionReference ref = getCollectionForAppPath(appName, path, filters, orders, options); final Promise promise
) {
RNFirebaseFirestoreCollectionReference ref = getCollectionForAppPath(
appName,
path,
filters,
orders,
options
);
ref.get(getOptions, promise); ref.get(getOptions, promise);
} }
@ReactMethod @ReactMethod
public void collectionOffSnapshot(String appName, String path, ReadableArray filters, public void collectionOffSnapshot(
ReadableArray orders, ReadableMap options, String listenerId) { String appName, String path, ReadableArray filters,
ReadableArray orders, ReadableMap options, String listenerId
) {
RNFirebaseFirestoreCollectionReference.offSnapshot(listenerId); RNFirebaseFirestoreCollectionReference.offSnapshot(listenerId);
} }
@ReactMethod @ReactMethod
public void collectionOnSnapshot(String appName, String path, ReadableArray filters, public void collectionOnSnapshot(
ReadableArray orders, ReadableMap options, String listenerId, String appName, String path, ReadableArray filters,
ReadableMap queryListenOptions) { ReadableArray orders, ReadableMap options, String listenerId,
RNFirebaseFirestoreCollectionReference ref = getCollectionForAppPath(appName, path, filters, orders, options); ReadableMap queryListenOptions
) {
RNFirebaseFirestoreCollectionReference ref = getCollectionForAppPath(
appName,
path,
filters,
orders,
options
);
ref.onSnapshot(listenerId, queryListenOptions); ref.onSnapshot(listenerId, queryListenOptions);
} }
@ReactMethod @ReactMethod
public void documentBatch(final String appName, final ReadableArray writes, public void documentBatch(final String appName, final ReadableArray writes,
final Promise promise) { final Promise promise) {
@ -166,7 +367,12 @@ public class RNFirebaseFirestore extends ReactContextBaseJavaModule {
} }
@ReactMethod @ReactMethod
public void documentGet(String appName, String path, ReadableMap getOptions, final Promise promise) { public void documentGet(
String appName,
String path,
ReadableMap getOptions,
final Promise promise
) {
RNFirebaseFirestoreDocumentReference ref = getDocumentForAppPath(appName, path); RNFirebaseFirestoreDocumentReference ref = getDocumentForAppPath(appName, path);
ref.get(getOptions, promise); ref.get(getOptions, promise);
} }
@ -177,18 +383,31 @@ public class RNFirebaseFirestore extends ReactContextBaseJavaModule {
} }
@ReactMethod @ReactMethod
public void documentOnSnapshot(String appName, String path, String listenerId, public void documentOnSnapshot(
ReadableMap docListenOptions) { String appName, String path, String listenerId,
ReadableMap docListenOptions
) {
RNFirebaseFirestoreDocumentReference ref = getDocumentForAppPath(appName, path); RNFirebaseFirestoreDocumentReference ref = getDocumentForAppPath(appName, path);
ref.onSnapshot(listenerId, docListenOptions); ref.onSnapshot(listenerId, docListenOptions);
} }
@ReactMethod @ReactMethod
public void documentSet(String appName, String path, ReadableMap data, ReadableMap options, final Promise promise) { public void documentSet(
String appName,
String path,
ReadableMap data,
ReadableMap options,
final Promise promise
) {
RNFirebaseFirestoreDocumentReference ref = getDocumentForAppPath(appName, path); RNFirebaseFirestoreDocumentReference ref = getDocumentForAppPath(appName, path);
ref.set(data, options, promise); ref.set(data, options, promise);
} }
/*
* Transaction Methods
*/
@ReactMethod @ReactMethod
public void documentUpdate(String appName, String path, ReadableMap data, final Promise promise) { public void documentUpdate(String appName, String path, ReadableMap data, final Promise promise) {
RNFirebaseFirestoreDocumentReference ref = getDocumentForAppPath(appName, path); RNFirebaseFirestoreDocumentReference ref = getDocumentForAppPath(appName, path);
@ -214,18 +433,17 @@ public class RNFirebaseFirestore extends ReactContextBaseJavaModule {
} else { } else {
firestoreSettings.setSslEnabled(firestore.getFirestoreSettings().isSslEnabled()); firestoreSettings.setSslEnabled(firestore.getFirestoreSettings().isSslEnabled());
} }
if (settings.hasKey("timestampsInSnapshots")) {
// TODO: Not supported on Android yet // if (settings.hasKey("timestampsInSnapshots")) {
} // // TODO: Not supported on Android yet
// }
firestore.setFirestoreSettings(firestoreSettings.build()); firestore.setFirestoreSettings(firestoreSettings.build());
promise.resolve(null); promise.resolve(null);
} }
/** /**
* Try clean up previous transactions on reload * Try clean up previous transactions on reload
*
*/ */
@Override @Override
public void onCatalystInstanceDestroy() { public void onCatalystInstanceDestroy() {
@ -239,23 +457,22 @@ public class RNFirebaseFirestore extends ReactContextBaseJavaModule {
transactionHandlers.clear(); transactionHandlers.clear();
} }
/*
* Transaction Methods
*/
/** /**
* Calls the internal Firestore Transaction classes instance .get(ref) method and resolves with * Calls the internal Firestore Transaction classes instance .get(ref) method and resolves with
* the DocumentSnapshot. * the DocumentSnapshot.
* *
* @param appName * @param appName appName
* @param transactionId * @param transactionId transactionId
* @param path * @param path path
* @param promise * @param promise promise
*/ */
@ReactMethod @ReactMethod
public void transactionGetDocument(String appName, int transactionId, String path, final Promise promise) { public void transactionGetDocument(
String appName,
int transactionId,
String path,
final Promise promise
) {
RNFirebaseFirestoreTransactionHandler handler = transactionHandlers.get(transactionId); RNFirebaseFirestoreTransactionHandler handler = transactionHandlers.get(transactionId);
if (handler == null) { if (handler == null) {
@ -269,11 +486,16 @@ public class RNFirebaseFirestore extends ReactContextBaseJavaModule {
} }
} }
/*
* INTERNALS/UTILS
*/
/** /**
* Aborts any pending signals and deletes the transaction handler. * Aborts any pending signals and deletes the transaction handler.
* *
* @param appName * @param appName appName
* @param transactionId * @param transactionId transactionId
*/ */
@ReactMethod @ReactMethod
public void transactionDispose(String appName, int transactionId) { public void transactionDispose(String appName, int transactionId) {
@ -288,12 +510,16 @@ public class RNFirebaseFirestore extends ReactContextBaseJavaModule {
/** /**
* Signals to transactionHandler that the command buffer is ready. * Signals to transactionHandler that the command buffer is ready.
* *
* @param appName * @param appName appName
* @param transactionId * @param transactionId transactionId
* @param commandBuffer * @param commandBuffer commandBuffer
*/ */
@ReactMethod @ReactMethod
public void transactionApplyBuffer(String appName, int transactionId, ReadableArray commandBuffer) { public void transactionApplyBuffer(
String appName,
int transactionId,
ReadableArray commandBuffer
) {
RNFirebaseFirestoreTransactionHandler handler = transactionHandlers.get(transactionId); RNFirebaseFirestoreTransactionHandler handler = transactionHandlers.get(transactionId);
if (handler != null) { if (handler != null) {
@ -304,12 +530,16 @@ public class RNFirebaseFirestore extends ReactContextBaseJavaModule {
/** /**
* Begin a new transaction via AsyncTask 's * Begin a new transaction via AsyncTask 's
* *
* @param appName * @param appName appName
* @param transactionId * @param transactionId transactionId
*/ */
@ReactMethod @ReactMethod
public void transactionBegin(final String appName, int transactionId) { public void transactionBegin(final String appName, int transactionId) {
final RNFirebaseFirestoreTransactionHandler transactionHandler = new RNFirebaseFirestoreTransactionHandler(appName, transactionId); final RNFirebaseFirestoreTransactionHandler transactionHandler = new RNFirebaseFirestoreTransactionHandler(
appName,
transactionId
);
transactionHandlers.put(transactionId, transactionHandler); transactionHandlers.put(transactionId, transactionHandler);
AsyncTask.execute(new Runnable() { AsyncTask.execute(new Runnable() {
@ -327,7 +557,11 @@ public class RNFirebaseFirestore extends ReactContextBaseJavaModule {
@Override @Override
public void run() { public void run() {
WritableMap eventMap = transactionHandler.createEventMap(null, "update"); WritableMap eventMap = transactionHandler.createEventMap(null, "update");
Utils.sendEvent(getReactApplicationContext(), "firestore_transaction_event", eventMap); Utils.sendEvent(
getReactApplicationContext(),
"firestore_transaction_event",
eventMap
);
} }
}); });
@ -336,12 +570,18 @@ public class RNFirebaseFirestore extends ReactContextBaseJavaModule {
// exit early if aborted - has to throw an exception otherwise will just keep trying ... // exit early if aborted - has to throw an exception otherwise will just keep trying ...
if (transactionHandler.aborted) { if (transactionHandler.aborted) {
throw new FirebaseFirestoreException("abort", FirebaseFirestoreException.Code.ABORTED); throw new FirebaseFirestoreException(
"abort",
FirebaseFirestoreException.Code.ABORTED
);
} }
// exit early if timeout from bridge - has to throw an exception otherwise will just keep trying ... // exit early if timeout from bridge - has to throw an exception otherwise will just keep trying ...
if (transactionHandler.timeout) { if (transactionHandler.timeout) {
throw new FirebaseFirestoreException("timeout", FirebaseFirestoreException.Code.DEADLINE_EXCEEDED); throw new FirebaseFirestoreException(
"timeout",
FirebaseFirestoreException.Code.DEADLINE_EXCEEDED
);
} }
// process any buffered commands from JS land // process any buffered commands from JS land
@ -357,15 +597,19 @@ public class RNFirebaseFirestore extends ReactContextBaseJavaModule {
ReadableMap command = buffer.getMap(i); ReadableMap command = buffer.getMap(i);
String path = command.getString("path"); String path = command.getString("path");
String type = command.getString("type"); String type = command.getString("type");
RNFirebaseFirestoreDocumentReference documentReference = getDocumentForAppPath(appName, path); RNFirebaseFirestoreDocumentReference documentReference = getDocumentForAppPath(
appName,
path
);
switch (type) { switch (type) {
case "set": case "set":
data = command.getMap("data"); data = command.getMap("data");
ReadableMap options = command.getMap("options"); ReadableMap options = command.getMap("options");
Map<String, Object> setData = FirestoreSerialize.parseReadableMap(RNFirebaseFirestore.getFirestoreForApp(appName), data); Map<String, Object> setData = FirestoreSerialize.parseReadableMap(
RNFirebaseFirestore.getFirestoreForApp(appName),
data
);
if (options != null && options.hasKey("merge") && options.getBoolean("merge")) { if (options != null && options.hasKey("merge") && options.getBoolean("merge")) {
transaction.set(documentReference.getRef(), setData, SetOptions.merge()); transaction.set(documentReference.getRef(), setData, SetOptions.merge());
@ -376,7 +620,11 @@ public class RNFirebaseFirestore extends ReactContextBaseJavaModule {
case "update": case "update":
data = command.getMap("data"); data = command.getMap("data");
Map<String, Object> updateData = FirestoreSerialize.parseReadableMap(RNFirebaseFirestore.getFirestoreForApp(appName), data); Map<String, Object> updateData = FirestoreSerialize.parseReadableMap(
RNFirebaseFirestore.getFirestoreForApp(appName),
data
);
transaction.update(documentReference.getRef(), updateData); transaction.update(documentReference.getRef(), updateData);
break; break;
case "delete": case "delete":
@ -396,7 +644,11 @@ public class RNFirebaseFirestore extends ReactContextBaseJavaModule {
if (!transactionHandler.aborted) { if (!transactionHandler.aborted) {
Log.d(TAG, "Transaction onSuccess!"); Log.d(TAG, "Transaction onSuccess!");
WritableMap eventMap = transactionHandler.createEventMap(null, "complete"); WritableMap eventMap = transactionHandler.createEventMap(null, "complete");
Utils.sendEvent(getReactApplicationContext(), "firestore_transaction_event", eventMap); Utils.sendEvent(
getReactApplicationContext(),
"firestore_transaction_event",
eventMap
);
} }
} }
}) })
@ -405,8 +657,15 @@ public class RNFirebaseFirestore extends ReactContextBaseJavaModule {
public void onFailure(@NonNull Exception e) { public void onFailure(@NonNull Exception e) {
if (!transactionHandler.aborted) { if (!transactionHandler.aborted) {
Log.w(TAG, "Transaction onFailure.", e); Log.w(TAG, "Transaction onFailure.", e);
WritableMap eventMap = transactionHandler.createEventMap((FirebaseFirestoreException) e, "error"); WritableMap eventMap = transactionHandler.createEventMap(
Utils.sendEvent(getReactApplicationContext(), "firestore_transaction_event", eventMap); (FirebaseFirestoreException) e,
"error"
);
Utils.sendEvent(
getReactApplicationContext(),
"firestore_transaction_event",
eventMap
);
} }
} }
}); });
@ -414,158 +673,44 @@ public class RNFirebaseFirestore extends ReactContextBaseJavaModule {
}); });
} }
/*
* INTERNALS/UTILS
*/
/**
* Generates a js-like error from an exception and rejects the provided promise with it.
*
* @param exception Exception Exception normally from a task result.
* @param promise Promise react native promise
*/
static void promiseRejectException(Promise promise, FirebaseFirestoreException exception) {
WritableMap jsError = getJSError(exception);
promise.reject(
jsError.getString("code"),
jsError.getString("message"),
exception
);
}
/**
* Get a database instance for a specific firebase app instance
*
* @param appName
* @return
*/
static FirebaseFirestore getFirestoreForApp(String appName) {
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
return FirebaseFirestore.getInstance(firebaseApp);
}
/** /**
* Get a collection reference for a specific app and path * Get a collection reference for a specific app and path
* *
* @param appName * @param appName appName
* @param filters * @param filters filters
* @param orders * @param orders orders
* @param options * @param options options
* @param path @return * @param path @return
*/ */
private RNFirebaseFirestoreCollectionReference getCollectionForAppPath(String appName, String path, private RNFirebaseFirestoreCollectionReference getCollectionForAppPath(
ReadableArray filters, String appName, String path,
ReadableArray orders, ReadableArray filters,
ReadableMap options) { ReadableArray orders,
return new RNFirebaseFirestoreCollectionReference(this.getReactApplicationContext(), appName, path, filters, orders, options); ReadableMap options
) {
return new RNFirebaseFirestoreCollectionReference(
this.getReactApplicationContext(),
appName,
path,
filters,
orders,
options
);
} }
/** /**
* Get a document reference for a specific app and path * Get a document reference for a specific app and path
* *
* @param appName * @param appName appName
* @param path * @param path path
* @return * @return RNFirebaseFirestoreDocumentReference
*/ */
private RNFirebaseFirestoreDocumentReference getDocumentForAppPath(String appName, String path) { private RNFirebaseFirestoreDocumentReference getDocumentForAppPath(String appName, String path) {
return new RNFirebaseFirestoreDocumentReference(this.getReactApplicationContext(), appName, path); return new RNFirebaseFirestoreDocumentReference(
} this.getReactApplicationContext(),
appName,
/** path
* Convert as firebase DatabaseError instance into a writable map );
* with the correct web-like error codes.
*
* @param nativeException
* @return
*/
static WritableMap getJSError(FirebaseFirestoreException nativeException) {
WritableMap errorMap = Arguments.createMap();
errorMap.putInt("nativeErrorCode", nativeException.getCode().value());
errorMap.putString("nativeErrorMessage", nativeException.getMessage());
String code;
String message;
String service = "Firestore";
// TODO: Proper error mappings
switch (nativeException.getCode()) {
case OK:
code = ErrorUtils.getCodeWithService(service, "ok");
message = ErrorUtils.getMessageWithService("Ok.", service, code);
break;
case CANCELLED:
code = ErrorUtils.getCodeWithService(service, "cancelled");
message = ErrorUtils.getMessageWithService("The operation was cancelled.", service, code);
break;
case UNKNOWN:
code = ErrorUtils.getCodeWithService(service, "unknown");
message = ErrorUtils.getMessageWithService("Unknown error or an error from a different error domain.", service, code);
break;
case INVALID_ARGUMENT:
code = ErrorUtils.getCodeWithService(service, "invalid-argument");
message = ErrorUtils.getMessageWithService("Client specified an invalid argument.", service, code);
break;
case DEADLINE_EXCEEDED:
code = ErrorUtils.getCodeWithService(service, "deadline-exceeded");
message = ErrorUtils.getMessageWithService("Deadline expired before operation could complete.", service, code);
break;
case NOT_FOUND:
code = ErrorUtils.getCodeWithService(service, "not-found");
message = ErrorUtils.getMessageWithService("Some requested document was not found.", service, code);
break;
case ALREADY_EXISTS:
code = ErrorUtils.getCodeWithService(service, "already-exists");
message = ErrorUtils.getMessageWithService("Some document that we attempted to create already exists.", service, code);
break;
case PERMISSION_DENIED:
code = ErrorUtils.getCodeWithService(service, "permission-denied");
message = ErrorUtils.getMessageWithService("The caller does not have permission to execute the specified operation.", service, code);
break;
case RESOURCE_EXHAUSTED:
code = ErrorUtils.getCodeWithService(service, "resource-exhausted");
message = ErrorUtils.getMessageWithService("Some resource has been exhausted, perhaps a per-user quota, or perhaps the entire file system is out of space.", service, code);
break;
case FAILED_PRECONDITION:
code = ErrorUtils.getCodeWithService(service, "failed-precondition");
message = ErrorUtils.getMessageWithService("Operation was rejected because the system is not in a state required for the operation`s execution.", service, code);
break;
case ABORTED:
code = ErrorUtils.getCodeWithService(service, "aborted");
message = ErrorUtils.getMessageWithService("The operation was aborted, typically due to a concurrency issue like transaction aborts, etc.", service, code);
break;
case OUT_OF_RANGE:
code = ErrorUtils.getCodeWithService(service, "out-of-range");
message = ErrorUtils.getMessageWithService("Operation was attempted past the valid range.", service, code);
break;
case UNIMPLEMENTED:
code = ErrorUtils.getCodeWithService(service, "unimplemented");
message = ErrorUtils.getMessageWithService("Operation is not implemented or not supported/enabled.", service, code);
break;
case INTERNAL:
code = ErrorUtils.getCodeWithService(service, "internal");
message = ErrorUtils.getMessageWithService("Internal errors.", service, code);
break;
case UNAVAILABLE:
code = ErrorUtils.getCodeWithService(service, "unavailable");
message = ErrorUtils.getMessageWithService("The service is currently unavailable.", service, code);
break;
case DATA_LOSS:
code = ErrorUtils.getCodeWithService(service, "data-loss");
message = ErrorUtils.getMessageWithService("Unrecoverable data loss or corruption.", service, code);
break;
case UNAUTHENTICATED:
code = ErrorUtils.getCodeWithService(service, "unauthenticated");
message = ErrorUtils.getMessageWithService("The request does not have valid authentication credentials for the operation.", service, code);
break;
default:
code = ErrorUtils.getCodeWithService(service, "unknown");
message = ErrorUtils.getMessageWithService("An unknown error occurred.", service, code);
}
errorMap.putString("code", code);
errorMap.putString("message", message);
return errorMap;
} }
/** /**

View File

@ -1,6 +1,7 @@
package io.invertase.firebase.firestore; package io.invertase.firebase.firestore;
import android.annotation.SuppressLint;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.util.Log; import android.util.Log;
@ -22,28 +23,32 @@ import com.google.firebase.firestore.Query;
import com.google.firebase.firestore.QuerySnapshot; import com.google.firebase.firestore.QuerySnapshot;
import com.google.firebase.firestore.Source; import com.google.firebase.firestore.Source;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import io.invertase.firebase.Utils; import io.invertase.firebase.Utils;
public class RNFirebaseFirestoreCollectionReference { class RNFirebaseFirestoreCollectionReference {
private static final String TAG = "RNFSCollectionReference"; private static final String TAG = "RNFSCollectionReference";
private static Map<String, ListenerRegistration> collectionSnapshotListeners = new HashMap<>(); private static Map<String, ListenerRegistration> collectionSnapshotListeners = new HashMap<>();
private final String appName;
private final String path; private final String path;
private final ReadableArray filters;
private final ReadableArray orders;
private final ReadableMap options;
private final Query query; private final Query query;
private final String appName;
private final ReadableMap options;
private final ReadableArray orders;
private final ReadableArray filters;
private ReactContext reactContext; private ReactContext reactContext;
RNFirebaseFirestoreCollectionReference(ReactContext reactContext, String appName, String path, RNFirebaseFirestoreCollectionReference(
ReadableArray filters, ReadableArray orders, ReactContext reactContext,
ReadableMap options) { String appName,
String path,
ReadableArray filters,
ReadableArray orders,
ReadableMap options
) {
this.appName = appName; this.appName = appName;
this.path = path; this.path = path;
this.filters = filters; this.filters = filters;
@ -53,6 +58,13 @@ public class RNFirebaseFirestoreCollectionReference {
this.reactContext = reactContext; this.reactContext = reactContext;
} }
static void offSnapshot(final String listenerId) {
ListenerRegistration listenerRegistration = collectionSnapshotListeners.remove(listenerId);
if (listenerRegistration != null) {
listenerRegistration.remove();
}
}
void get(ReadableMap getOptions, final Promise promise) { void get(ReadableMap getOptions, final Promise promise) {
Source source; Source source;
if (getOptions != null && getOptions.hasKey("source")) { if (getOptions != null && getOptions.hasKey("source")) {
@ -67,29 +79,34 @@ public class RNFirebaseFirestoreCollectionReference {
} else { } else {
source = Source.DEFAULT; source = Source.DEFAULT;
} }
@SuppressLint("StaticFieldLeak") final QuerySnapshotSerializeAsyncTask serializeAsyncTask = new QuerySnapshotSerializeAsyncTask(
reactContext, this
) {
@Override
protected void onPostExecute(WritableMap writableMap) {
promise.resolve(writableMap);
}
};
query.get(source).addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() { query.get(source).addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
@Override @Override
public void onComplete(@NonNull Task<QuerySnapshot> task) { public void onComplete(@NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) { if (task.isSuccessful()) {
Log.d(TAG, "get:onComplete:success"); Log.d(TAG, "get:onComplete:success");
WritableMap data = FirestoreSerialize.snapshotToWritableMap(task.getResult()); serializeAsyncTask.execute(task.getResult());
promise.resolve(data);
} else { } else {
Log.e(TAG, "get:onComplete:failure", task.getException()); Log.e(TAG, "get:onComplete:failure", task.getException());
RNFirebaseFirestore.promiseRejectException(promise, (FirebaseFirestoreException)task.getException()); RNFirebaseFirestore.promiseRejectException(
promise,
(FirebaseFirestoreException) task.getException()
);
} }
} }
}); });
} }
public static void offSnapshot(final String listenerId) { void onSnapshot(final String listenerId, final ReadableMap queryListenOptions) {
ListenerRegistration listenerRegistration = collectionSnapshotListeners.remove(listenerId);
if (listenerRegistration != null) {
listenerRegistration.remove();
}
}
public void onSnapshot(final String listenerId, final ReadableMap queryListenOptions) {
if (!collectionSnapshotListeners.containsKey(listenerId)) { if (!collectionSnapshotListeners.containsKey(listenerId)) {
final EventListener<QuerySnapshot> listener = new EventListener<QuerySnapshot>() { final EventListener<QuerySnapshot> listener = new EventListener<QuerySnapshot>() {
@Override @Override
@ -97,7 +114,8 @@ public class RNFirebaseFirestoreCollectionReference {
if (exception == null) { if (exception == null) {
handleQuerySnapshotEvent(listenerId, querySnapshot); handleQuerySnapshotEvent(listenerId, querySnapshot);
} else { } else {
ListenerRegistration listenerRegistration = collectionSnapshotListeners.remove(listenerId); ListenerRegistration listenerRegistration = collectionSnapshotListeners.remove(
listenerId);
if (listenerRegistration != null) { if (listenerRegistration != null) {
listenerRegistration.remove(); listenerRegistration.remove();
} }
@ -115,7 +133,10 @@ public class RNFirebaseFirestoreCollectionReference {
metadataChanges = MetadataChanges.EXCLUDE; metadataChanges = MetadataChanges.EXCLUDE;
} }
ListenerRegistration listenerRegistration = this.query.addSnapshotListener(metadataChanges, listener); ListenerRegistration listenerRegistration = this.query.addSnapshotListener(
metadataChanges,
listener
);
collectionSnapshotListeners.put(listenerId, listenerRegistration); collectionSnapshotListeners.put(listenerId, listenerRegistration);
} }
} }
@ -170,7 +191,7 @@ public class RNFirebaseFirestoreCollectionReference {
} else { } else {
ReadableArray fieldPathElements = fieldPathMap.getArray("elements"); ReadableArray fieldPathElements = fieldPathMap.getArray("elements");
String[] fieldPathArray = new String[fieldPathElements.size()]; String[] fieldPathArray = new String[fieldPathElements.size()];
for (int j=0; j<fieldPathElements.size(); j++) { for (int j = 0; j < fieldPathElements.size(); j++) {
fieldPathArray[j] = fieldPathElements.getString(j); fieldPathArray[j] = fieldPathElements.getString(j);
} }
FieldPath fieldPath = FieldPath.of(fieldPathArray); FieldPath fieldPath = FieldPath.of(fieldPathArray);
@ -202,13 +223,13 @@ public class RNFirebaseFirestoreCollectionReference {
Map<String, Object> order = (Map) o; Map<String, Object> order = (Map) o;
String direction = (String) order.get("direction"); String direction = (String) order.get("direction");
Map<String, Object> fieldPathMap = (Map) order.get("fieldPath"); Map<String, Object> fieldPathMap = (Map) order.get("fieldPath");
String fieldPathType = (String)fieldPathMap.get("type"); String fieldPathType = (String) fieldPathMap.get("type");
if (fieldPathType.equals("string")) { if (fieldPathType.equals("string")) {
String fieldPath = (String)fieldPathMap.get("string"); String fieldPath = (String) fieldPathMap.get("string");
query = query.orderBy(fieldPath, Query.Direction.valueOf(direction)); query = query.orderBy(fieldPath, Query.Direction.valueOf(direction));
} else { } else {
List<String> fieldPathElements = (List)fieldPathMap.get("elements"); List<String> fieldPathElements = (List) fieldPathMap.get("elements");
FieldPath fieldPath = FieldPath.of(fieldPathElements.toArray(new String[fieldPathElements.size()])); FieldPath fieldPath = FieldPath.of(fieldPathElements.toArray(new String[fieldPathElements.size()]));
query = query.orderBy(fieldPath, Query.Direction.valueOf(direction)); query = query.orderBy(fieldPath, Query.Direction.valueOf(direction));
} }
@ -218,57 +239,79 @@ public class RNFirebaseFirestoreCollectionReference {
private Query applyOptions(FirebaseFirestore firestore, Query query) { private Query applyOptions(FirebaseFirestore firestore, Query query) {
if (options.hasKey("endAt")) { if (options.hasKey("endAt")) {
List<Object> endAtList = FirestoreSerialize.parseReadableArray(firestore, options.getArray("endAt")); List<Object> endAtList = FirestoreSerialize.parseReadableArray(
firestore,
options.getArray("endAt")
);
query = query.endAt(endAtList.toArray()); query = query.endAt(endAtList.toArray());
} }
if (options.hasKey("endBefore")) { if (options.hasKey("endBefore")) {
List<Object> endBeforeList = FirestoreSerialize.parseReadableArray(firestore, options.getArray("endBefore")); List<Object> endBeforeList = FirestoreSerialize.parseReadableArray(
firestore,
options.getArray("endBefore")
);
query = query.endBefore(endBeforeList.toArray()); query = query.endBefore(endBeforeList.toArray());
} }
if (options.hasKey("limit")) { if (options.hasKey("limit")) {
int limit = options.getInt("limit"); int limit = options.getInt("limit");
query = query.limit(limit); query = query.limit(limit);
} }
if (options.hasKey("offset")) { // if (options.hasKey("offset")) {
// Android doesn't support offset // Android doesn't support offset
} // }
if (options.hasKey("selectFields")) { // if (options.hasKey("selectFields")) {
// Android doesn't support selectFields // Android doesn't support selectFields
} // }
if (options.hasKey("startAfter")) { if (options.hasKey("startAfter")) {
List<Object> startAfterList= FirestoreSerialize.parseReadableArray(firestore, options.getArray("startAfter")); List<Object> startAfterList = FirestoreSerialize.parseReadableArray(
firestore,
options.getArray("startAfter")
);
query = query.startAfter(startAfterList.toArray()); query = query.startAfter(startAfterList.toArray());
} }
if (options.hasKey("startAt")) { if (options.hasKey("startAt")) {
List<Object> startAtList= FirestoreSerialize.parseReadableArray(firestore, options.getArray("startAt")); List<Object> startAtList = FirestoreSerialize.parseReadableArray(
firestore,
options.getArray("startAt")
);
query = query.startAt(startAtList.toArray()); query = query.startAt(startAtList.toArray());
} }
return query; return query;
} }
/** /**
* Handles documentSnapshot events. * Handles documentSnapshot events.
* *
* @param listenerId * @param listenerId id
* @param querySnapshot * @param querySnapshot snapshot
*/ */
private void handleQuerySnapshotEvent(String listenerId, QuerySnapshot querySnapshot) { private void handleQuerySnapshotEvent(final String listenerId, QuerySnapshot querySnapshot) {
WritableMap event = Arguments.createMap(); @SuppressLint("StaticFieldLeak") final QuerySnapshotSerializeAsyncTask serializeAsyncTask = new QuerySnapshotSerializeAsyncTask(
WritableMap data = FirestoreSerialize.snapshotToWritableMap(querySnapshot); reactContext, this
) {
@Override
protected void onPostExecute(WritableMap writableMap) {
WritableMap event = Arguments.createMap();
event.putString("path", path);
event.putString("appName", appName);
event.putString("listenerId", listenerId);
event.putMap("querySnapshot", writableMap);
Utils.sendEvent(reactContext, "firestore_collection_sync_event", event);
}
};
event.putString("appName", appName); serializeAsyncTask.execute(querySnapshot);
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 * Handles a documentSnapshot error event
* *
* @param listenerId * @param listenerId id
* @param exception * @param exception exception
*/ */
private void handleQuerySnapshotError(String listenerId, FirebaseFirestoreException exception) { private void handleQuerySnapshotError(String listenerId, FirebaseFirestoreException exception) {
WritableMap event = Arguments.createMap(); WritableMap event = Arguments.createMap();

View File

@ -1,5 +1,6 @@
package io.invertase.firebase.firestore; package io.invertase.firebase.firestore;
import android.annotation.SuppressLint;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.util.Log; import android.util.Log;
@ -31,17 +32,24 @@ public class RNFirebaseFirestoreDocumentReference {
private final String appName; private final String appName;
private final String path; private final String path;
private ReactContext reactContext;
private final DocumentReference ref; private final DocumentReference ref;
private ReactContext reactContext;
RNFirebaseFirestoreDocumentReference(ReactContext reactContext, String appName, String path) { RNFirebaseFirestoreDocumentReference(ReactContext reactContext, String appName, String path) {
this.appName = appName;
this.path = path; this.path = path;
this.appName = appName;
this.reactContext = reactContext; this.reactContext = reactContext;
this.ref = RNFirebaseFirestore.getFirestoreForApp(appName).document(path); this.ref = RNFirebaseFirestore.getFirestoreForApp(appName).document(path);
} }
public void delete(final Promise promise) { static void offSnapshot(final String listenerId) {
ListenerRegistration listenerRegistration = documentSnapshotListeners.remove(listenerId);
if (listenerRegistration != null) {
listenerRegistration.remove();
}
}
void delete(final Promise promise) {
this.ref.delete().addOnCompleteListener(new OnCompleteListener<Void>() { this.ref.delete().addOnCompleteListener(new OnCompleteListener<Void>() {
@Override @Override
public void onComplete(@NonNull Task<Void> task) { public void onComplete(@NonNull Task<Void> task) {
@ -50,7 +58,10 @@ public class RNFirebaseFirestoreDocumentReference {
promise.resolve(null); promise.resolve(null);
} else { } else {
Log.e(TAG, "delete:onComplete:failure", task.getException()); Log.e(TAG, "delete:onComplete:failure", task.getException());
RNFirebaseFirestore.promiseRejectException(promise, (FirebaseFirestoreException)task.getException()); RNFirebaseFirestore.promiseRejectException(
promise,
(FirebaseFirestoreException) task.getException()
);
} }
} }
}); });
@ -58,6 +69,7 @@ public class RNFirebaseFirestoreDocumentReference {
void get(final ReadableMap getOptions, final Promise promise) { void get(final ReadableMap getOptions, final Promise promise) {
Source source; Source source;
if (getOptions != null && getOptions.hasKey("source")) { if (getOptions != null && getOptions.hasKey("source")) {
String optionsSource = getOptions.getString("source"); String optionsSource = getOptions.getString("source");
if ("server".equals(optionsSource)) { if ("server".equals(optionsSource)) {
@ -70,45 +82,57 @@ public class RNFirebaseFirestoreDocumentReference {
} else { } else {
source = Source.DEFAULT; source = Source.DEFAULT;
} }
@SuppressLint("StaticFieldLeak") final DocumentSnapshotSerializeAsyncTask serializeAsyncTask = new DocumentSnapshotSerializeAsyncTask(
reactContext, this
) {
@Override
protected void onPostExecute(WritableMap writableMap) {
promise.resolve(writableMap);
}
};
this.ref.get(source).addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() { this.ref.get(source).addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
@Override @Override
public void onComplete(@NonNull Task<DocumentSnapshot> task) { public void onComplete(@NonNull Task<DocumentSnapshot> task) {
if (task.isSuccessful()) { if (task.isSuccessful()) {
Log.d(TAG, "get:onComplete:success"); Log.d(TAG, "get:onComplete:success");
WritableMap data = FirestoreSerialize.snapshotToWritableMap(task.getResult()); serializeAsyncTask.execute(task.getResult());
promise.resolve(data);
} else { } else {
Log.e(TAG, "get:onComplete:failure", task.getException()); Log.e(TAG, "get:onComplete:failure", task.getException());
RNFirebaseFirestore.promiseRejectException(promise, (FirebaseFirestoreException)task.getException()); RNFirebaseFirestore.promiseRejectException(
promise,
(FirebaseFirestoreException) task.getException()
);
} }
} }
}); });
} }
public static void offSnapshot(final String listenerId) { void onSnapshot(final String listenerId, final ReadableMap docListenOptions) {
ListenerRegistration listenerRegistration = documentSnapshotListeners.remove(listenerId);
if (listenerRegistration != null) {
listenerRegistration.remove();
}
}
public void onSnapshot(final String listenerId, final ReadableMap docListenOptions) {
if (!documentSnapshotListeners.containsKey(listenerId)) { if (!documentSnapshotListeners.containsKey(listenerId)) {
final EventListener<DocumentSnapshot> listener = new EventListener<DocumentSnapshot>() { final EventListener<DocumentSnapshot> listener = new EventListener<DocumentSnapshot>() {
@Override @Override
public void onEvent(DocumentSnapshot documentSnapshot, FirebaseFirestoreException exception) { public void onEvent(
DocumentSnapshot documentSnapshot,
FirebaseFirestoreException exception
) {
if (exception == null) { if (exception == null) {
handleDocumentSnapshotEvent(listenerId, documentSnapshot); handleDocumentSnapshotEvent(listenerId, documentSnapshot);
} else { } else {
ListenerRegistration listenerRegistration = documentSnapshotListeners.remove(listenerId); ListenerRegistration listenerRegistration = documentSnapshotListeners.remove(listenerId);
if (listenerRegistration != null) { if (listenerRegistration != null) {
listenerRegistration.remove(); listenerRegistration.remove();
} }
handleDocumentSnapshotError(listenerId, exception); handleDocumentSnapshotError(listenerId, exception);
} }
} }
}; };
MetadataChanges metadataChanges; MetadataChanges metadataChanges;
if (docListenOptions != null if (docListenOptions != null
&& docListenOptions.hasKey("includeMetadataChanges") && docListenOptions.hasKey("includeMetadataChanges")
&& docListenOptions.getBoolean("includeMetadataChanges")) { && docListenOptions.getBoolean("includeMetadataChanges")) {
@ -116,19 +140,30 @@ public class RNFirebaseFirestoreDocumentReference {
} else { } else {
metadataChanges = MetadataChanges.EXCLUDE; metadataChanges = MetadataChanges.EXCLUDE;
} }
ListenerRegistration listenerRegistration = this.ref.addSnapshotListener(metadataChanges, listener);
ListenerRegistration listenerRegistration = this.ref.addSnapshotListener(
metadataChanges,
listener
);
documentSnapshotListeners.put(listenerId, listenerRegistration); documentSnapshotListeners.put(listenerId, listenerRegistration);
} }
} }
public void set(final ReadableMap data, final ReadableMap options, final Promise promise) { public void set(final ReadableMap data, final ReadableMap options, final Promise promise) {
Map<String, Object> map = FirestoreSerialize.parseReadableMap(RNFirebaseFirestore.getFirestoreForApp(appName), data);
Task<Void> task; Task<Void> task;
Map<String, Object> map = FirestoreSerialize.parseReadableMap(
RNFirebaseFirestore.getFirestoreForApp(appName),
data
);
if (options != null && options.hasKey("merge") && options.getBoolean("merge")) { if (options != null && options.hasKey("merge") && options.getBoolean("merge")) {
task = this.ref.set(map, SetOptions.merge()); task = this.ref.set(map, SetOptions.merge());
} else { } else {
task = this.ref.set(map); task = this.ref.set(map);
} }
task.addOnCompleteListener(new OnCompleteListener<Void>() { task.addOnCompleteListener(new OnCompleteListener<Void>() {
@Override @Override
public void onComplete(@NonNull Task<Void> task) { public void onComplete(@NonNull Task<Void> task) {
@ -137,14 +172,21 @@ public class RNFirebaseFirestoreDocumentReference {
promise.resolve(null); promise.resolve(null);
} else { } else {
Log.e(TAG, "set:onComplete:failure", task.getException()); Log.e(TAG, "set:onComplete:failure", task.getException());
RNFirebaseFirestore.promiseRejectException(promise, (FirebaseFirestoreException)task.getException()); RNFirebaseFirestore.promiseRejectException(
promise,
(FirebaseFirestoreException) task.getException()
);
} }
} }
}); });
} }
public void update(final ReadableMap data, final Promise promise) { void update(final ReadableMap data, final Promise promise) {
Map<String, Object> map = FirestoreSerialize.parseReadableMap(RNFirebaseFirestore.getFirestoreForApp(appName), data); Map<String, Object> map = FirestoreSerialize.parseReadableMap(
RNFirebaseFirestore.getFirestoreForApp(appName),
data
);
this.ref.update(map).addOnCompleteListener(new OnCompleteListener<Void>() { this.ref.update(map).addOnCompleteListener(new OnCompleteListener<Void>() {
@Override @Override
public void onComplete(@NonNull Task<Void> task) { public void onComplete(@NonNull Task<Void> task) {
@ -153,7 +195,10 @@ public class RNFirebaseFirestoreDocumentReference {
promise.resolve(null); promise.resolve(null);
} else { } else {
Log.e(TAG, "update:onComplete:failure", task.getException()); Log.e(TAG, "update:onComplete:failure", task.getException());
RNFirebaseFirestore.promiseRejectException(promise, (FirebaseFirestoreException)task.getException()); RNFirebaseFirestore.promiseRejectException(
promise,
(FirebaseFirestoreException) task.getException()
);
} }
} }
}); });
@ -163,7 +208,7 @@ public class RNFirebaseFirestoreDocumentReference {
* INTERNALS/UTILS * INTERNALS/UTILS
*/ */
public DocumentReference getRef() { DocumentReference getRef() {
return ref; return ref;
} }
@ -174,32 +219,44 @@ public class RNFirebaseFirestoreDocumentReference {
/** /**
* Handles documentSnapshot events. * Handles documentSnapshot events.
* *
* @param listenerId * @param listenerId id
* @param documentSnapshot * @param documentSnapshot snapshot
*/ */
private void handleDocumentSnapshotEvent(String listenerId, DocumentSnapshot documentSnapshot) { private void handleDocumentSnapshotEvent(
WritableMap event = Arguments.createMap(); final String listenerId,
WritableMap data = FirestoreSerialize.snapshotToWritableMap(documentSnapshot); DocumentSnapshot documentSnapshot
) {
@SuppressLint("StaticFieldLeak") final DocumentSnapshotSerializeAsyncTask serializeAsyncTask = new DocumentSnapshotSerializeAsyncTask(
reactContext, this
) {
@Override
protected void onPostExecute(WritableMap writableMap) {
WritableMap event = Arguments.createMap();
event.putString("path", path);
event.putString("appName", appName);
event.putString("listenerId", listenerId);
event.putMap("documentSnapshot", writableMap);
Utils.sendEvent(reactContext, "firestore_document_sync_event", event);
}
};
event.putString("appName", appName); serializeAsyncTask.execute(documentSnapshot);
event.putString("path", path);
event.putString("listenerId", listenerId);
event.putMap("documentSnapshot", data);
Utils.sendEvent(reactContext, "firestore_document_sync_event", event);
} }
/** /**
* Handles a documentSnapshot error event * Handles a documentSnapshot error event
* *
* @param listenerId * @param listenerId id
* @param exception * @param exception exception
*/ */
private void handleDocumentSnapshotError(String listenerId, FirebaseFirestoreException exception) { private void handleDocumentSnapshotError(
String listenerId,
FirebaseFirestoreException exception
) {
WritableMap event = Arguments.createMap(); WritableMap event = Arguments.createMap();
event.putString("appName", appName);
event.putString("path", path); event.putString("path", path);
event.putString("appName", appName);
event.putString("listenerId", listenerId); event.putString("listenerId", listenerId);
event.putMap("error", RNFirebaseFirestore.getJSError(exception)); event.putMap("error", RNFirebaseFirestore.getJSError(exception));

View File

@ -1,7 +1,6 @@
package io.invertase.firebase.firestore; package io.invertase.firebase.firestore;
import com.facebook.react.ReactPackage; import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.bridge.NativeModule; import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.UIManagerModule; import com.facebook.react.uimanager.UIManagerModule;
@ -29,7 +28,7 @@ public class RNFirebaseFirestorePackage implements ReactPackage {
} }
/** /**
* @param reactContext * @param reactContext reactContext
* @return a list of view managers that should be registered with {@link UIManagerModule} * @return a list of view managers that should be registered with {@link UIManagerModule}
*/ */
@Override @Override

View File

@ -17,17 +17,16 @@ import javax.annotation.Nullable;
class RNFirebaseFirestoreTransactionHandler { class RNFirebaseFirestoreTransactionHandler {
private final ReentrantLock lock;
private final Condition condition;
boolean aborted = false;
boolean timeout = false;
private String appName; private String appName;
private long timeoutAt; private long timeoutAt;
private int transactionId; private int transactionId;
private final ReentrantLock lock;
private final Condition condition;
private ReadableArray commandBuffer; private ReadableArray commandBuffer;
private Transaction firestoreTransaction; private Transaction firestoreTransaction;
boolean aborted = false;
boolean timeout = false;
RNFirebaseFirestoreTransactionHandler(String app, int id) { RNFirebaseFirestoreTransactionHandler(String app, int id) {
appName = app; appName = app;
transactionId = id; transactionId = id;