[android] Add the first raft of Android support
This commit is contained in:
parent
dfd9080281
commit
52b70d58e3
|
@ -33,6 +33,10 @@ android {
|
|||
allprojects {
|
||||
repositories {
|
||||
jcenter()
|
||||
mavenLocal()
|
||||
maven {
|
||||
url "https://maven.google.com"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -78,11 +82,12 @@ dependencies {
|
|||
compile "com.google.android.gms:play-services-base:$firebaseVersion"
|
||||
compile "com.google.firebase:firebase-core:$firebaseVersion"
|
||||
compile "com.google.firebase:firebase-config:$firebaseVersion"
|
||||
compile "com.google.firebase:firebase-auth:$firebaseVersion"
|
||||
compile "com.google.firebase:firebase-auth:11.3.0"
|
||||
compile "com.google.firebase:firebase-database:$firebaseVersion"
|
||||
compile "com.google.firebase:firebase-storage:$firebaseVersion"
|
||||
compile "com.google.firebase:firebase-messaging:$firebaseVersion"
|
||||
compile "com.google.firebase:firebase-crash:$firebaseVersion"
|
||||
compile "com.google.firebase:firebase-perf:$firebaseVersion"
|
||||
compile "com.google.firebase:firebase-ads:$firebaseVersion"
|
||||
compile 'com.google.firebase:firebase-firestore:11.3.0'
|
||||
}
|
||||
|
|
|
@ -78,7 +78,6 @@ public class Utils {
|
|||
|
||||
/**
|
||||
* @param dataSnapshot
|
||||
* @param registration
|
||||
* @param previousChildName
|
||||
* @return
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,195 @@
|
|||
package io.invertase.firebase.firestore;
|
||||
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
import com.facebook.react.bridge.WritableArray;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
import com.google.firebase.firestore.DocumentChange;
|
||||
import com.google.firebase.firestore.DocumentSnapshot;
|
||||
import com.google.firebase.firestore.QuerySnapshot;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class FirestoreSerialize {
|
||||
private static final String KEY_CHANGES = "changes";
|
||||
private static final String KEY_DATA = "data";
|
||||
private static final String KEY_DOC_CHANGE_DOCUMENT = "document";
|
||||
private static final String KEY_DOC_CHANGE_NEW_INDEX = "newIndex";
|
||||
private static final String KEY_DOC_CHANGE_OLD_INDEX = "oldIndex";
|
||||
private static final String KEY_DOC_CHANGE_TYPE = "type";
|
||||
private static final String KEY_DOCUMENTS = "documents";
|
||||
private static final String KEY_PATH = "path";
|
||||
|
||||
/**
|
||||
* Convert a DocumentSnapshot instance into a React Native WritableMap
|
||||
*
|
||||
* @param documentSnapshot DocumentSnapshot
|
||||
* @return WritableMap
|
||||
*/
|
||||
static WritableMap snapshotToWritableMap(DocumentSnapshot documentSnapshot) {
|
||||
WritableMap documentMap = Arguments.createMap();
|
||||
|
||||
documentMap.putString(KEY_PATH, documentSnapshot.getId());
|
||||
documentMap.putMap(KEY_DATA, objectMapToWritable(documentSnapshot.getData()));
|
||||
// Missing fields from web SDK
|
||||
// createTime
|
||||
// readTime
|
||||
// updateTime
|
||||
|
||||
return documentMap;
|
||||
}
|
||||
|
||||
public static WritableMap snapshotToWritableMap(QuerySnapshot querySnapshot) {
|
||||
WritableMap queryMap = Arguments.createMap();
|
||||
|
||||
List<DocumentChange> documentChanges = querySnapshot.getDocumentChanges();
|
||||
if (!documentChanges.isEmpty()) {
|
||||
queryMap.putArray(KEY_CHANGES, documentChangesToWritableArray(documentChanges));
|
||||
}
|
||||
|
||||
// documents
|
||||
WritableArray documents = Arguments.createArray();
|
||||
List<DocumentSnapshot> documentSnapshots = querySnapshot.getDocuments();
|
||||
for (DocumentSnapshot documentSnapshot : documentSnapshots) {
|
||||
documents.pushMap(snapshotToWritableMap(documentSnapshot));
|
||||
}
|
||||
queryMap.putArray(KEY_DOCUMENTS, documents);
|
||||
|
||||
return queryMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a List of DocumentChange instances into a React Native WritableArray
|
||||
*
|
||||
* @param documentChanges List<DocumentChange>
|
||||
* @return WritableArray
|
||||
*/
|
||||
static WritableArray documentChangesToWritableArray(List<DocumentChange> documentChanges) {
|
||||
WritableArray documentChangesWritable = Arguments.createArray();
|
||||
for (DocumentChange documentChange : documentChanges) {
|
||||
documentChangesWritable.pushMap(documentChangeToWritableMap(documentChange));
|
||||
}
|
||||
return documentChangesWritable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a DocumentChange instance into a React Native WritableMap
|
||||
*
|
||||
* @param documentChange DocumentChange
|
||||
* @return WritableMap
|
||||
*/
|
||||
static WritableMap documentChangeToWritableMap(DocumentChange documentChange) {
|
||||
WritableMap documentChangeMap = Arguments.createMap();
|
||||
|
||||
switch (documentChange.getType()) {
|
||||
case ADDED:
|
||||
documentChangeMap.putString(KEY_DOC_CHANGE_TYPE, "added");
|
||||
break;
|
||||
case REMOVED:
|
||||
documentChangeMap.putString(KEY_DOC_CHANGE_TYPE, "removed");
|
||||
break;
|
||||
case MODIFIED:
|
||||
documentChangeMap.putString(KEY_DOC_CHANGE_TYPE, "modified");
|
||||
}
|
||||
|
||||
documentChangeMap.putMap(KEY_DOC_CHANGE_DOCUMENT,
|
||||
snapshotToWritableMap(documentChange.getDocument()));
|
||||
|
||||
return documentChangeMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an Object Map into a React Native WritableMap.
|
||||
*
|
||||
* @param map Map<String, Object>
|
||||
* @return WritableMap
|
||||
*/
|
||||
static WritableMap objectMapToWritable(Map<String, Object> map) {
|
||||
WritableMap writableMap = Arguments.createMap();
|
||||
for (Map.Entry<String, Object> entry : map.entrySet()) {
|
||||
putValue(writableMap, entry.getKey(), entry.getValue());
|
||||
}
|
||||
return writableMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an Object array into a React Native WritableArray.
|
||||
*
|
||||
* @param array Object[]
|
||||
* @return WritableArray
|
||||
*/
|
||||
static WritableArray objectArrayToWritable(Object[] array) {
|
||||
WritableArray writableArray = Arguments.createArray();
|
||||
|
||||
for (Object item : array) {
|
||||
if (item == null) {
|
||||
writableArray.pushNull();
|
||||
continue;
|
||||
}
|
||||
|
||||
Class itemClass = item.getClass();
|
||||
|
||||
if (itemClass == Boolean.class) {
|
||||
writableArray.pushBoolean((Boolean) item);
|
||||
} else if (itemClass == Integer.class) {
|
||||
writableArray.pushDouble(((Integer) item).doubleValue());
|
||||
} else if (itemClass == Double.class) {
|
||||
writableArray.pushDouble((Double) item);
|
||||
} else if (itemClass == Float.class) {
|
||||
writableArray.pushDouble(((Float) item).doubleValue());
|
||||
} else if (itemClass == String.class) {
|
||||
writableArray.pushString(item.toString());
|
||||
} else if (itemClass == Map.class) {
|
||||
writableArray.pushMap((objectMapToWritable((Map<String, Object>) item)));
|
||||
} else if (itemClass == Arrays.class) {
|
||||
writableArray.pushArray(objectArrayToWritable((Object[]) item));
|
||||
} else if (itemClass == List.class) {
|
||||
List<Object> list = (List<Object>) item;
|
||||
Object[] listAsArray = list.toArray(new Object[list.size()]);
|
||||
writableArray.pushArray(objectArrayToWritable(listAsArray));
|
||||
} else {
|
||||
throw new RuntimeException("Cannot convert object of type " + item);
|
||||
}
|
||||
}
|
||||
|
||||
return writableArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects an objects type and calls the relevant WritableMap setter method to add the value.
|
||||
*
|
||||
* @param map WritableMap
|
||||
* @param key String
|
||||
* @param value Object
|
||||
*/
|
||||
static void putValue(WritableMap map, String key, Object value) {
|
||||
if (value == null) {
|
||||
map.putNull(key);
|
||||
} else {
|
||||
Class valueClass = value.getClass();
|
||||
|
||||
if (valueClass == Boolean.class) {
|
||||
map.putBoolean(key, (Boolean) value);
|
||||
} else if (valueClass == Integer.class) {
|
||||
map.putDouble(key, ((Integer) value).doubleValue());
|
||||
} else if (valueClass == Double.class) {
|
||||
map.putDouble(key, (Double) value);
|
||||
} else if (valueClass == Float.class) {
|
||||
map.putDouble(key, ((Float) value).doubleValue());
|
||||
} else if (valueClass == String.class) {
|
||||
map.putString(key, value.toString());
|
||||
} else if (valueClass == Map.class) {
|
||||
map.putMap(key, (objectMapToWritable((Map<String, Object>) value)));
|
||||
} else if (valueClass == Arrays.class) {
|
||||
map.putArray(key, objectArrayToWritable((Object[]) value));
|
||||
} else if (valueClass == List.class) {
|
||||
List<Object> list = (List<Object>) value;
|
||||
Object[] array = list.toArray(new Object[list.size()]);
|
||||
map.putArray(key, objectArrayToWritable(array));
|
||||
} else {
|
||||
throw new RuntimeException("Cannot convert object of type " + value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,136 @@
|
|||
package io.invertase.firebase.firestore;
|
||||
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.util.Log;
|
||||
|
||||
import com.facebook.react.bridge.Promise;
|
||||
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.Query;
|
||||
import com.google.firebase.firestore.QuerySnapshot;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import io.invertase.firebase.Utils;
|
||||
|
||||
public class RNFirebaseCollectionReference {
|
||||
private static final String TAG = "RNFBCollectionReference";
|
||||
private final String appName;
|
||||
private final String path;
|
||||
private final ReadableArray filters;
|
||||
private final ReadableArray orders;
|
||||
private final ReadableMap options;
|
||||
private final Query query;
|
||||
|
||||
RNFirebaseCollectionReference(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();
|
||||
}
|
||||
|
||||
void get(final Promise promise) {
|
||||
query.get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
|
||||
@Override
|
||||
public void onComplete(@NonNull Task<QuerySnapshot> task) {
|
||||
if (task.isSuccessful()) {
|
||||
Log.d(TAG, "get:onComplete:success");
|
||||
WritableMap data = FirestoreSerialize.snapshotToWritableMap(task.getResult());
|
||||
promise.resolve(data);
|
||||
} else {
|
||||
Log.e(TAG, "get:onComplete:failure", task.getException());
|
||||
RNFirebaseFirestore.promiseRejectException(promise, task.getException());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private Query buildQuery() {
|
||||
Query query = RNFirebaseFirestore.getFirestoreForApp(appName).collection(path);
|
||||
query = applyFilters(query, filters);
|
||||
query = applyOrders(query, orders);
|
||||
query = applyOptions(query, options);
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
private Query applyFilters(Query query, ReadableArray filters) {
|
||||
List<Object> filtersList = Utils.recursivelyDeconstructReadableArray(filters);
|
||||
|
||||
for (Object f : filtersList) {
|
||||
Map<String, Object> filter = (Map) f;
|
||||
String fieldPath = (String) filter.get("fieldPath");
|
||||
String operator = (String) filter.get("operator");
|
||||
Object value = filter.get("value");
|
||||
|
||||
switch (operator) {
|
||||
case "EQUAL":
|
||||
query = query.whereEqualTo(fieldPath, value);
|
||||
break;
|
||||
case "GREATER_THAN":
|
||||
query = query.whereGreaterThan(fieldPath, value);
|
||||
break;
|
||||
case "GREATER_THAN_OR_EQUAL":
|
||||
query = query.whereGreaterThanOrEqualTo(fieldPath, value);
|
||||
break;
|
||||
case "LESS_THAN":
|
||||
query = query.whereLessThan(fieldPath, value);
|
||||
break;
|
||||
case "LESS_THAN_OR_EQUAL":
|
||||
query = query.whereLessThanOrEqualTo(fieldPath, value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return query;
|
||||
}
|
||||
|
||||
private Query applyOrders(Query query, ReadableArray orders) {
|
||||
List<Object> ordersList = Utils.recursivelyDeconstructReadableArray(orders);
|
||||
for (Object o : ordersList) {
|
||||
Map<String, Object> order = (Map) o;
|
||||
String direction = (String) order.get("direction");
|
||||
String fieldPath = (String) order.get("fieldPath");
|
||||
|
||||
query = query.orderBy(fieldPath, Query.Direction.valueOf(direction));
|
||||
}
|
||||
return query;
|
||||
}
|
||||
|
||||
private Query applyOptions(Query query, ReadableMap options) {
|
||||
if (options.hasKey("endAt")) {
|
||||
ReadableArray endAtArray = options.getArray("endAt");
|
||||
query = query.endAt(Utils.recursivelyDeconstructReadableArray(endAtArray));
|
||||
}
|
||||
if (options.hasKey("endBefore")) {
|
||||
ReadableArray endBeforeArray = options.getArray("endBefore");
|
||||
query = query.endBefore(Utils.recursivelyDeconstructReadableArray(endBeforeArray));
|
||||
}
|
||||
if (options.hasKey("limit")) {
|
||||
int limit = options.getInt("limit");
|
||||
query = query.limit(limit);
|
||||
}
|
||||
if (options.hasKey("offset")) {
|
||||
// Android doesn't support offset
|
||||
}
|
||||
if (options.hasKey("selectFields")) {
|
||||
// Android doesn't support selectFields
|
||||
}
|
||||
if (options.hasKey("startAfter")) {
|
||||
ReadableArray startAfterArray = options.getArray("startAfter");
|
||||
query = query.startAfter(Utils.recursivelyDeconstructReadableArray(startAfterArray));
|
||||
}
|
||||
if (options.hasKey("startAt")) {
|
||||
ReadableArray startAtArray = options.getArray("startAt");
|
||||
query = query.startAt(Utils.recursivelyDeconstructReadableArray(startAtArray));
|
||||
}
|
||||
return query;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,113 @@
|
|||
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.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.DocumentReference;
|
||||
import com.google.firebase.firestore.DocumentSnapshot;
|
||||
import com.google.firebase.firestore.SetOptions;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import io.invertase.firebase.Utils;
|
||||
|
||||
|
||||
public class RNFirebaseDocumentReference {
|
||||
private static final String TAG = "RNFBDocumentReference";
|
||||
private final String appName;
|
||||
private final String path;
|
||||
private final DocumentReference ref;
|
||||
|
||||
RNFirebaseDocumentReference(String appName, String path) {
|
||||
this.appName = appName;
|
||||
this.path = path;
|
||||
this.ref = RNFirebaseFirestore.getFirestoreForApp(appName).document(path);
|
||||
}
|
||||
|
||||
public void create(ReadableMap data, Promise promise) {
|
||||
// Not supported on Android out of the box
|
||||
}
|
||||
|
||||
public void delete(final ReadableMap options, final Promise promise) {
|
||||
this.ref.delete().addOnCompleteListener(new OnCompleteListener<Void>() {
|
||||
@Override
|
||||
public void onComplete(@NonNull Task<Void> task) {
|
||||
if (task.isSuccessful()) {
|
||||
Log.d(TAG, "delete:onComplete:success");
|
||||
// Missing fields from web SDK
|
||||
// writeTime
|
||||
promise.resolve(Arguments.createMap());
|
||||
} else {
|
||||
Log.e(TAG, "delete:onComplete:failure", task.getException());
|
||||
RNFirebaseFirestore.promiseRejectException(promise, task.getException());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void get(final Promise promise) {
|
||||
this.ref.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
|
||||
@Override
|
||||
public void onComplete(@NonNull Task<DocumentSnapshot> task) {
|
||||
if (task.isSuccessful()) {
|
||||
Log.d(TAG, "get:onComplete:success");
|
||||
WritableMap data = FirestoreSerialize.snapshotToWritableMap(task.getResult());
|
||||
promise.resolve(data);
|
||||
} else {
|
||||
Log.e(TAG, "get:onComplete:failure", task.getException());
|
||||
RNFirebaseFirestore.promiseRejectException(promise, task.getException());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void set(final ReadableMap data, final ReadableMap options, final Promise promise) {
|
||||
Map<String, Object> map = Utils.recursivelyDeconstructReadableMap(data);
|
||||
SetOptions setOptions = null;
|
||||
if (options != null && options.hasKey("merge") && options.getBoolean("merge")) {
|
||||
setOptions = SetOptions.merge();
|
||||
}
|
||||
this.ref.set(map, setOptions).addOnCompleteListener(new OnCompleteListener<Void>() {
|
||||
@Override
|
||||
public void onComplete(@NonNull Task<Void> task) {
|
||||
if (task.isSuccessful()) {
|
||||
Log.d(TAG, "set:onComplete:success");
|
||||
// Missing fields from web SDK
|
||||
// writeTime
|
||||
promise.resolve(Arguments.createMap());
|
||||
} else {
|
||||
Log.e(TAG, "set:onComplete:failure", task.getException());
|
||||
RNFirebaseFirestore.promiseRejectException(promise, task.getException());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void update(final ReadableMap data, final ReadableMap options, final Promise promise) {
|
||||
Map<String, Object> map = Utils.recursivelyDeconstructReadableMap(data);
|
||||
this.ref.update(map).addOnCompleteListener(new OnCompleteListener<Void>() {
|
||||
@Override
|
||||
public void onComplete(@NonNull Task<Void> task) {
|
||||
if (task.isSuccessful()) {
|
||||
Log.d(TAG, "update:onComplete:success");
|
||||
// Missing fields from web SDK
|
||||
// writeTime
|
||||
promise.resolve(Arguments.createMap());
|
||||
} else {
|
||||
Log.e(TAG, "update:onComplete:failure", task.getException());
|
||||
RNFirebaseFirestore.promiseRejectException(promise, task.getException());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void collections(Promise promise) {
|
||||
// Not supported on Android out of the box
|
||||
}
|
||||
}
|
|
@ -0,0 +1,223 @@
|
|||
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.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
import com.facebook.react.bridge.WritableArray;
|
||||
import com.google.android.gms.tasks.OnCompleteListener;
|
||||
import com.google.android.gms.tasks.Task;
|
||||
import com.google.firebase.FirebaseApp;
|
||||
import com.google.firebase.firestore.DocumentReference;
|
||||
import com.google.firebase.firestore.FieldValue;
|
||||
import com.google.firebase.firestore.FirebaseFirestore;
|
||||
import com.google.firebase.firestore.SetOptions;
|
||||
import com.google.firebase.firestore.WriteBatch;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import io.invertase.firebase.Utils;
|
||||
|
||||
|
||||
public class RNFirebaseFirestore extends ReactContextBaseJavaModule {
|
||||
private static final String TAG = "RNFirebaseFirestore";
|
||||
// private HashMap<String, RNFirebaseDatabaseReference> references = new HashMap<>();
|
||||
// private SparseArray<RNFirebaseTransactionHandler> transactionHandlers = new SparseArray<>();
|
||||
|
||||
RNFirebaseFirestore(ReactApplicationContext reactContext) {
|
||||
super(reactContext);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* REACT NATIVE METHODS
|
||||
*/
|
||||
@ReactMethod
|
||||
public void collectionGet(String appName, String path, ReadableArray filters,
|
||||
ReadableArray orders, ReadableMap options, final Promise promise) {
|
||||
RNFirebaseCollectionReference ref = getCollectionForAppPath(appName, path, filters, orders, options);
|
||||
ref.get(promise);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void documentBatch(final String appName, final ReadableArray writes,
|
||||
final ReadableMap commitOptions, final Promise promise) {
|
||||
FirebaseFirestore firestore = getFirestoreForApp(appName);
|
||||
WriteBatch batch = firestore.batch();
|
||||
final List<Object> writesArray = Utils.recursivelyDeconstructReadableArray(writes);
|
||||
|
||||
for (Object w : writesArray) {
|
||||
Map<String, Object> write = (Map) w;
|
||||
String type = (String) write.get("type");
|
||||
String path = (String) write.get("path");
|
||||
Map<String, Object> data = (Map) write.get("data");
|
||||
|
||||
DocumentReference ref = firestore.document(path);
|
||||
switch (type) {
|
||||
case "DELETE":
|
||||
batch = batch.delete(ref);
|
||||
break;
|
||||
case "SET":
|
||||
Map<String, Object> options = (Map) write.get("options");
|
||||
SetOptions setOptions = null;
|
||||
if (options != null && options.containsKey("merge") && (boolean)options.get("merge")) {
|
||||
setOptions = SetOptions.merge();
|
||||
}
|
||||
batch = batch.set(ref, data, setOptions);
|
||||
break;
|
||||
case "UPDATE":
|
||||
batch = batch.update(ref, data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
batch.commit().addOnCompleteListener(new OnCompleteListener<Void>() {
|
||||
@Override
|
||||
public void onComplete(@NonNull Task<Void> task) {
|
||||
if (task.isSuccessful()) {
|
||||
Log.d(TAG, "set:onComplete:success");
|
||||
WritableArray result = Arguments.createArray();
|
||||
for (Object w : writesArray) {
|
||||
// Missing fields from web SDK
|
||||
// writeTime
|
||||
result.pushMap(Arguments.createMap());
|
||||
}
|
||||
promise.resolve(result);
|
||||
} else {
|
||||
Log.e(TAG, "set:onComplete:failure", task.getException());
|
||||
RNFirebaseFirestore.promiseRejectException(promise, task.getException());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void documentCollections(String appName, String path, final Promise promise) {
|
||||
RNFirebaseDocumentReference ref = getDocumentForAppPath(appName, path);
|
||||
ref.collections(promise);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void documentCreate(String appName, String path, ReadableMap data, final Promise promise) {
|
||||
RNFirebaseDocumentReference ref = getDocumentForAppPath(appName, path);
|
||||
ref.create(data, promise);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void documentDelete(String appName, String path, ReadableMap options, final Promise promise) {
|
||||
RNFirebaseDocumentReference ref = getDocumentForAppPath(appName, path);
|
||||
ref.delete(options, promise);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void documentGet(String appName, String path, final Promise promise) {
|
||||
RNFirebaseDocumentReference ref = getDocumentForAppPath(appName, path);
|
||||
ref.get(promise);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void documentGetAll(String appName, ReadableArray documents, final Promise promise) {
|
||||
// Not supported on Android out of the box
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void documentSet(String appName, String path, ReadableMap data, ReadableMap options, final Promise promise) {
|
||||
RNFirebaseDocumentReference ref = getDocumentForAppPath(appName, path);
|
||||
ref.set(data, options, promise);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void documentUpdate(String appName, String path, ReadableMap data, ReadableMap options, final Promise promise) {
|
||||
RNFirebaseDocumentReference ref = getDocumentForAppPath(appName, path);
|
||||
ref.update(data, options, promise);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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, Exception exception) {
|
||||
// TODO
|
||||
// WritableMap jsError = getJSError(exception);
|
||||
promise.reject(
|
||||
"TODO", // jsError.getString("code"),
|
||||
exception.getMessage(), // 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
|
||||
*
|
||||
* @param appName
|
||||
* @param filters
|
||||
* @param orders
|
||||
* @param options
|
||||
* @param path @return
|
||||
*/
|
||||
private RNFirebaseCollectionReference getCollectionForAppPath(String appName, String path,
|
||||
ReadableArray filters,
|
||||
ReadableArray orders,
|
||||
ReadableMap options) {
|
||||
return new RNFirebaseCollectionReference(appName, path, filters, orders, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a document reference for a specific app and path
|
||||
*
|
||||
* @param appName
|
||||
* @param path
|
||||
* @return
|
||||
*/
|
||||
private RNFirebaseDocumentReference getDocumentForAppPath(String appName, String path) {
|
||||
return new RNFirebaseDocumentReference(appName, path);
|
||||
}
|
||||
|
||||
/**
|
||||
* React Method - returns this module name
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public String getName() {
|
||||
return "RNFirebaseFirestore";
|
||||
}
|
||||
|
||||
/**
|
||||
* React Native constants for RNFirebaseFirestore
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public Map<String, Object> getConstants() {
|
||||
final Map<String, Object> constants = new HashMap<>();
|
||||
constants.put("deleteFieldValue", FieldValue.delete());
|
||||
constants.put("serverTimestampFieldValue", FieldValue.serverTimestamp());
|
||||
return constants;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
package io.invertase.firebase.firestore;
|
||||
|
||||
import com.facebook.react.ReactPackage;
|
||||
import com.facebook.react.bridge.JavaScriptModule;
|
||||
import com.facebook.react.bridge.NativeModule;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.uimanager.UIManagerModule;
|
||||
import com.facebook.react.uimanager.ViewManager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class RNFirebaseFirestorePackage implements ReactPackage {
|
||||
public RNFirebaseFirestorePackage() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param reactContext react application context that can be used to create modules
|
||||
* @return list of native modules to register with the newly created catalyst instance
|
||||
*/
|
||||
@Override
|
||||
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
|
||||
List<NativeModule> modules = new ArrayList<>();
|
||||
modules.add(new RNFirebaseFirestore(reactContext));
|
||||
|
||||
return modules;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list of JS modules to register with the newly created catalyst instance.
|
||||
* <p/>
|
||||
* IMPORTANT: Note that only modules that needs to be accessible from the native code should be
|
||||
* listed here. Also listing a native module here doesn't imply that the JS implementation of it
|
||||
* will be automatically included in the JS bundle.
|
||||
*/
|
||||
// TODO: Removed in 0.47.0. Here for backwards compatability
|
||||
public List<Class<? extends JavaScriptModule>> createJSModules() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param reactContext
|
||||
* @return a list of view managers that should be registered with {@link UIManagerModule}
|
||||
*/
|
||||
@Override
|
||||
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
|
@ -5,19 +5,14 @@
|
|||
import CollectionReference from './CollectionReference';
|
||||
import DocumentSnapshot from './DocumentSnapshot';
|
||||
import Path from './Path';
|
||||
import INTERNALS from './../../internals';
|
||||
|
||||
export type DeleteOptions = {
|
||||
lastUpdateTime?: string,
|
||||
}
|
||||
|
||||
export type UpdateOptions = {
|
||||
createIfMissing?: boolean,
|
||||
lastUpdateTime?: string,
|
||||
}
|
||||
|
||||
export type WriteOptions = {
|
||||
createIfMissing?: boolean,
|
||||
lastUpdateTime?: string,
|
||||
merge?: boolean,
|
||||
}
|
||||
|
||||
export type WriteResult = {
|
||||
|
@ -63,24 +58,25 @@ export default class DocumentReference {
|
|||
}
|
||||
|
||||
create(data: { [string]: any }): Promise<WriteResult> {
|
||||
return this._firestore._native
|
||||
.documentCreate(this._documentPath._parts, data);
|
||||
/* return this._firestore._native
|
||||
.documentCreate(this.path, data); */
|
||||
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD('DocumentReference', 'create'));
|
||||
}
|
||||
|
||||
delete(deleteOptions?: DeleteOptions): Promise<WriteResult> {
|
||||
return this._firestore._native
|
||||
.documentDelete(this._documentPath._parts, deleteOptions);
|
||||
.documentDelete(this.path, deleteOptions);
|
||||
}
|
||||
|
||||
get(): Promise<DocumentSnapshot> {
|
||||
return this._firestore._native
|
||||
.documentGet(this._documentPath._parts)
|
||||
.documentGet(this.path)
|
||||
.then(result => new DocumentSnapshot(this._firestore, result));
|
||||
}
|
||||
|
||||
getCollections(): Promise<CollectionReference[]> {
|
||||
return this._firestore._native
|
||||
.documentCollections(this._documentPath._parts)
|
||||
/* return this._firestore._native
|
||||
.documentCollections(this.path)
|
||||
.then((collectionIds) => {
|
||||
const collections = [];
|
||||
|
||||
|
@ -89,7 +85,8 @@ export default class DocumentReference {
|
|||
}
|
||||
|
||||
return collections;
|
||||
});
|
||||
}); */
|
||||
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD('DocumentReference', 'getCollections'));
|
||||
}
|
||||
|
||||
onSnapshot(onNext: () => any, onError?: () => any): () => void {
|
||||
|
@ -98,12 +95,13 @@ export default class DocumentReference {
|
|||
|
||||
set(data: { [string]: any }, writeOptions?: WriteOptions): Promise<WriteResult> {
|
||||
return this._firestore._native
|
||||
.documentSet(this._documentPath._parts, data, writeOptions);
|
||||
.documentSet(this.path, data, writeOptions);
|
||||
}
|
||||
|
||||
update(data: { [string]: any }, updateOptions?: UpdateOptions): Promise<WriteResult> {
|
||||
// TODO: Update to new update method signature
|
||||
update(data: { [string]: any }): Promise<WriteResult> {
|
||||
return this._firestore._native
|
||||
.documentUpdate(this._documentPath._parts, data, updateOptions);
|
||||
.documentUpdate(this.path, data);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -117,6 +115,6 @@ export default class DocumentReference {
|
|||
* @private
|
||||
*/
|
||||
_getDocumentKey() {
|
||||
return `$${this._firestore._appName}$/${this._documentPath._parts.join('/')}`;
|
||||
return `$${this._firestore._appName}$/${this.path}`;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,11 +4,12 @@
|
|||
*/
|
||||
import DocumentReference from './DocumentReference';
|
||||
import Path from './Path';
|
||||
import INTERNALS from './../../internals';
|
||||
|
||||
export type DocumentSnapshotNativeData = {
|
||||
createTime: string,
|
||||
data: Object,
|
||||
name: string,
|
||||
path: string,
|
||||
readTime: string,
|
||||
updateTime: string,
|
||||
}
|
||||
|
@ -26,13 +27,14 @@ export default class DocumentSnapshot {
|
|||
constructor(firestore: Object, nativeData: DocumentSnapshotNativeData) {
|
||||
this._createTime = nativeData.createTime;
|
||||
this._data = nativeData.data;
|
||||
this._ref = new DocumentReference(firestore, Path.fromName(nativeData.name));
|
||||
this._ref = new DocumentReference(firestore, Path.fromName(nativeData.path));
|
||||
this._readTime = nativeData.readTime;
|
||||
this._updateTime = nativeData.updateTime;
|
||||
}
|
||||
|
||||
get createTime(): string {
|
||||
return this._createTime;
|
||||
// return this._createTime;
|
||||
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_PROPERTY('DocumentSnapshot', 'createTime'));
|
||||
}
|
||||
|
||||
get exists(): boolean {
|
||||
|
@ -44,7 +46,8 @@ export default class DocumentSnapshot {
|
|||
}
|
||||
|
||||
get readTime(): string {
|
||||
return this._readTime;
|
||||
// return this._readTime;
|
||||
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_PROPERTY('DocumentSnapshot', 'readTime'));
|
||||
}
|
||||
|
||||
get ref(): DocumentReference {
|
||||
|
@ -52,7 +55,8 @@ export default class DocumentSnapshot {
|
|||
}
|
||||
|
||||
get updateTime(): string {
|
||||
return this._updateTime;
|
||||
// return this._updateTime;
|
||||
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_PROPERTY('DocumentSnapshot', 'updateTime'));
|
||||
}
|
||||
|
||||
data(): Object {
|
||||
|
|
|
@ -5,22 +5,23 @@
|
|||
import DocumentSnapshot from './DocumentSnapshot';
|
||||
import Path from './Path';
|
||||
import QuerySnapshot from './QuerySnapshot';
|
||||
import INTERNALS from './../../internals';
|
||||
|
||||
const DIRECTIONS = {
|
||||
DESC: 'descending',
|
||||
desc: 'descending',
|
||||
ASC: 'ascending',
|
||||
asc: 'ascending'
|
||||
ASC: 'ASCENDING',
|
||||
asc: 'ASCENDING',
|
||||
DESC: 'DESCENDING',
|
||||
desc: 'DESCENDING',
|
||||
};
|
||||
const DOCUMENT_NAME_FIELD = '__name__';
|
||||
|
||||
const OPERATORS = {
|
||||
'<': 'LESS_THAN',
|
||||
'<=': 'LESS_THAN_OR_EQUAL',
|
||||
'=': 'EQUAL',
|
||||
'==': 'EQUAL',
|
||||
'>': 'GREATER_THAN',
|
||||
'>=': 'GREATER_THAN_OR_EQUAL',
|
||||
'<': 'LESS_THAN',
|
||||
'<=': 'LESS_THAN_OR_EQUAL',
|
||||
};
|
||||
|
||||
export type Direction = 'DESC' | 'desc' | 'ASC' | 'asc';
|
||||
|
@ -34,6 +35,8 @@ type FieldOrder = {
|
|||
fieldPath: string,
|
||||
}
|
||||
type QueryOptions = {
|
||||
endAt?: any[],
|
||||
endBefore?: any[],
|
||||
limit?: number,
|
||||
offset?: number,
|
||||
selectFields?: string[],
|
||||
|
@ -92,12 +95,12 @@ export default class Query {
|
|||
get(): Promise<QuerySnapshot> {
|
||||
return this._firestore._native
|
||||
.collectionGet(
|
||||
this._referencePath._parts,
|
||||
this._referencePath.relativeName,
|
||||
this._fieldFilters,
|
||||
this._fieldOrders,
|
||||
this._queryOptions,
|
||||
)
|
||||
.then(nativeData => new QuerySnapshot(nativeData));
|
||||
.then(nativeData => new QuerySnapshot(this._firestore, this, nativeData));
|
||||
}
|
||||
|
||||
limit(n: number): Query {
|
||||
|
@ -116,12 +119,13 @@ export default class Query {
|
|||
// TODO: Validation
|
||||
// validate.isInteger('n', n);
|
||||
|
||||
const options = {
|
||||
/* const options = {
|
||||
...this._queryOptions,
|
||||
offset: n,
|
||||
};
|
||||
return new Query(this.firestore, this._referencePath, this._fieldFilters,
|
||||
this._fieldOrders, options);
|
||||
this._fieldOrders, options); */
|
||||
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD('Query', 'offset'));
|
||||
}
|
||||
|
||||
onSnapshot(onNext: () => any, onError?: () => any): () => void {
|
||||
|
@ -129,9 +133,9 @@ export default class Query {
|
|||
}
|
||||
|
||||
orderBy(fieldPath: string, directionStr?: Direction = 'asc'): Query {
|
||||
//TODO: Validation
|
||||
//validate.isFieldPath('fieldPath', fieldPath);
|
||||
//validate.isOptionalFieldOrder('directionStr', directionStr);
|
||||
// TODO: Validation
|
||||
// validate.isFieldPath('fieldPath', fieldPath);
|
||||
// validate.isOptionalFieldOrder('directionStr', directionStr);
|
||||
|
||||
if (this._queryOptions.startAt || this._queryOptions.endAt) {
|
||||
throw new Error('Cannot specify an orderBy() constraint after calling ' +
|
||||
|
@ -148,6 +152,7 @@ export default class Query {
|
|||
}
|
||||
|
||||
select(varArgs: string[]): Query {
|
||||
/*
|
||||
varArgs = Array.isArray(arguments[0]) ? arguments[0] : [].slice.call(arguments);
|
||||
const fieldReferences = [];
|
||||
|
||||
|
@ -167,7 +172,8 @@ export default class Query {
|
|||
};
|
||||
|
||||
return new Query(this.firestore, this._referencePath, this._fieldFilters,
|
||||
this._fieldOrders, options);
|
||||
this._fieldOrders, options);*/
|
||||
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD('Query', 'select'));
|
||||
}
|
||||
|
||||
startAfter(fieldValues: any): Query {
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
*/
|
||||
import DocumentReference from './DocumentReference';
|
||||
|
||||
import type { DeleteOptions, UpdateOptions, WriteOptions, WriteResult } from './DocumentReference';
|
||||
import type { DeleteOptions, WriteOptions, WriteResult } from './DocumentReference';
|
||||
|
||||
type CommitOptions = {
|
||||
transactionId: string,
|
||||
|
@ -13,8 +13,8 @@ type CommitOptions = {
|
|||
type DocumentWrite = {
|
||||
data?: Object,
|
||||
options?: Object,
|
||||
path: string[],
|
||||
type: 'delete' | 'set' | 'update',
|
||||
path: string,
|
||||
type: 'DELETE' | 'SET' | 'UPDATE',
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -51,8 +51,8 @@ export default class WriteBatch {
|
|||
// validate.isOptionalPrecondition('deleteOptions', deleteOptions);
|
||||
this._writes.push({
|
||||
options: deleteOptions,
|
||||
path: docRef._documentPath._parts,
|
||||
type: 'delete',
|
||||
path: docRef.path,
|
||||
type: 'DELETE',
|
||||
});
|
||||
|
||||
return this;
|
||||
|
@ -67,40 +67,25 @@ export default class WriteBatch {
|
|||
this._writes.push({
|
||||
data,
|
||||
options: writeOptions,
|
||||
path: docRef._documentPath._parts,
|
||||
type: 'set',
|
||||
path: docRef.path,
|
||||
type: 'SET',
|
||||
});
|
||||
|
||||
// TODO: DocumentTransform ?!
|
||||
// let documentTransform = DocumentTransform.fromObject(docRef, data);
|
||||
|
||||
// if (!documentTransform.isEmpty) {
|
||||
// this._writes.push({transform: documentTransform.toProto()});
|
||||
// }
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
update(docRef: DocumentReference, data: Object, updateOptions: UpdateOptions): WriteBatch {
|
||||
// TODO: Update to new method signature
|
||||
update(docRef: DocumentReference, data: { [string]: any }): WriteBatch {
|
||||
// TODO: Validation
|
||||
// validate.isDocumentReference('docRef', docRef);
|
||||
// validate.isDocument('data', data, true);
|
||||
// validate.isOptionalPrecondition('updateOptions', updateOptions);
|
||||
|
||||
this._writes.push({
|
||||
data,
|
||||
options: updateOptions,
|
||||
path: docRef._documentPath._parts,
|
||||
type: 'update',
|
||||
path: docRef.path,
|
||||
type: 'UPDATE',
|
||||
});
|
||||
|
||||
// TODO: DocumentTransform ?!
|
||||
// let documentTransform = DocumentTransform.fromObject(docRef, expandedObject);
|
||||
|
||||
// if (!documentTransform.isEmpty) {
|
||||
// this._writes.push({transform: documentTransform.toProto()});
|
||||
// }
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ import DocumentSnapshot from './DocumentSnapshot';
|
|||
import GeoPoint from './GeoPoint';
|
||||
import Path from './Path';
|
||||
import WriteBatch from './WriteBatch';
|
||||
import INTERNALS from './../../internals';
|
||||
|
||||
const unquotedIdentifier_ = '(?:[A-Za-z_][A-Za-z_0-9]*)';
|
||||
const UNQUOTED_IDENTIFIER_REGEX = new RegExp(`^${unquotedIdentifier_}$`);
|
||||
|
@ -62,17 +63,18 @@ export default class Firestore extends ModuleBase {
|
|||
}
|
||||
|
||||
getAll(varArgs: DocumentReference[]): Promise<DocumentSnapshot[]> {
|
||||
varArgs = Array.isArray(arguments[0]) ? arguments[0] : [].slice.call(arguments);
|
||||
/*varArgs = Array.isArray(arguments[0]) ? arguments[0] : [].slice.call(arguments);
|
||||
|
||||
const documents = [];
|
||||
varArgs.forEach((document) => {
|
||||
// TODO: Validation
|
||||
// validate.isDocumentReference(i, varArgs[i]);
|
||||
documents.push(document._documentPath._parts);
|
||||
documents.push(document.path);
|
||||
});
|
||||
return this._native
|
||||
.documentGetAll(documents)
|
||||
.then(results => results.map(result => new DocumentSnapshot(this, result)));
|
||||
.then(results => results.map(result => new DocumentSnapshot(this, result)));*/
|
||||
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD('Query', 'offset'));
|
||||
}
|
||||
|
||||
getCollections(): Promise<CollectionReference[]> {
|
||||
|
|
|
@ -9,6 +9,14 @@ const AUTO_ID_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234
|
|||
const hasOwnProperty = Object.hasOwnProperty;
|
||||
const DEFAULT_CHUNK_SIZE = 50;
|
||||
|
||||
// Source: https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical
|
||||
const REGEXP_FIELD_NAME = new RegExp(
|
||||
`^(?:\\.?((?:(?:[A-Za-z_][A-Za-z_0-9]*)|(?:[A-Za-z_][A-Za-z_0-9]*))+))$`
|
||||
);
|
||||
const REGEXP_FIELD_PATH = new RegExp(
|
||||
`^((?:(?:[A-Za-z_][A-Za-z_0-9]*)|(?:[A-Za-z_][A-Za-z_0-9]*))+)(?:\\.((?:(?:[A-Za-z_][A-Za-z_0-9]*)|(?:[A-Za-z_][A-Za-z_0-9]*))+))*$`
|
||||
);
|
||||
|
||||
/**
|
||||
* Deep get a value from an object.
|
||||
* @website https://github.com/Salakar/deeps
|
||||
|
@ -88,6 +96,16 @@ export function isString(value): Boolean {
|
|||
return typeof value === 'string';
|
||||
}
|
||||
|
||||
/**
|
||||
* Firestore field name/path validator.
|
||||
* @param field
|
||||
* @param paths
|
||||
* @return {boolean}
|
||||
*/
|
||||
export function isValidFirestoreField(field, paths) {
|
||||
return (paths ? REGEXP_FIELD_PATH : REGEXP_FIELD_NAME).test(field);
|
||||
}
|
||||
|
||||
// platform checks
|
||||
export const isIOS = Platform.OS === 'ios';
|
||||
export const isAndroid = Platform.OS === 'android';
|
||||
|
|
Loading…
Reference in New Issue