Merge pull request #457 from invertase/firestore
Initial Firestore funtionality
This commit is contained in:
commit
1fe91cd788
19
README.md
19
README.md
|
@ -5,6 +5,7 @@
|
||||||
[![Package Quality](http://npm.packagequality.com/shield/react-native-firebase.svg?style=flat-square)](http://packagequality.com/#?package=react-native-firebase)
|
[![Package Quality](http://npm.packagequality.com/shield/react-native-firebase.svg?style=flat-square)](http://packagequality.com/#?package=react-native-firebase)
|
||||||
[![Chat](https://img.shields.io/badge/chat-on%20discord-7289da.svg?style=flat-square)](https://discord.gg/t6bdqMs)
|
[![Chat](https://img.shields.io/badge/chat-on%20discord-7289da.svg?style=flat-square)](https://discord.gg/t6bdqMs)
|
||||||
[![Donate](https://img.shields.io/badge/Donate-Patreon-green.svg?style=flat-square)](https://www.patreon.com/invertase)
|
[![Donate](https://img.shields.io/badge/Donate-Patreon-green.svg?style=flat-square)](https://www.patreon.com/invertase)
|
||||||
|
[![Twitter Follow](https://img.shields.io/twitter/follow/rnfirebase.svg?style=social&label=Follow)](https://twitter.com/rnfirebase)
|
||||||
|
|
||||||
|
|
||||||
**RNFirebase** makes using [Firebase](http://firebase.com) with React Native simple.
|
**RNFirebase** makes using [Firebase](http://firebase.com) with React Native simple.
|
||||||
|
@ -57,6 +58,7 @@ All in all, RNFirebase provides much faster performance (~2x) over the web SDK a
|
||||||
| **Cloud Messaging (FCM)** | ✅ | ✅ | ✅ |**?**|
|
| **Cloud Messaging (FCM)** | ✅ | ✅ | ✅ |**?**|
|
||||||
| **Crash Reporting** | ✅ | ✅ | ✅ | ❌ |
|
| **Crash Reporting** | ✅ | ✅ | ✅ | ❌ |
|
||||||
| **Dynamic Links** | ❌ | ❌ | ❌ | ❌ |
|
| **Dynamic Links** | ❌ | ❌ | ❌ | ❌ |
|
||||||
|
| **Firestore** | ❌ | ❌ | ✅ | ❌ |
|
||||||
| **Invites** | ❌ | ❌ | ❌ | ❌ |
|
| **Invites** | ❌ | ❌ | ❌ | ❌ |
|
||||||
| **Performance Monitoring** | ✅ | ✅ | ✅ | ❌ |
|
| **Performance Monitoring** | ✅ | ✅ | ✅ | ❌ |
|
||||||
| **Realtime Database** | ✅ | ✅ | ✅ | ✅ |
|
| **Realtime Database** | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
@ -68,18 +70,13 @@ All in all, RNFirebase provides much faster performance (~2x) over the web SDK a
|
||||||
---
|
---
|
||||||
### Supported versions - React Native / Firebase
|
### Supported versions - React Native / Firebase
|
||||||
|
|
||||||
> The table below shows the supported version of `react-native-firebase` for different React Native versions
|
> The table below shows the supported versions of React Native and the Firebase SDKs for different versions of `react-native-firebase`
|
||||||
|
|
||||||
| | v0.36 - v0.39 | v0.40 - v0.46 | v0.47 +
|
| | 1.X.X | 2.0.X | 2.1.X / 2.2.X | 3.0.X |
|
||||||
| ------------------------------- | :---: | :---: | :---: |
|
|------------------------|-------------|-------------|-----------------|----------|
|
||||||
| react-native-firebase | 1.X.X | 2.X.X | 2.1.X |
|
| React Native | 0.36 - 0.39 | 0.40 - 0.46 | 0.47 + | 0.48 + |
|
||||||
|
| Firebase Android SDK | 10.2.0 + | 11.0.0 + | 11.0.0 + | 11.4.2 + |
|
||||||
> The table below shows the minimum supported versions of the Firebase SDKs for each version of `react-native-firebase`
|
| Firebase iOS SDK | 3.15.0 + | 4.0.0 + | 4.0.0 + | 4.3.0 + |
|
||||||
|
|
||||||
| | v1 | v2 | v3 |
|
|
||||||
| ---------------------- | :---: | :---: | :---: |
|
|
||||||
| Firebase Android SDK | 10.2.0+ | 11.0.0 + | 11.2.0 + |
|
|
||||||
| Firebase iOS SDK | 3.15.0+ | 4.0.0 + | 4.0.0 + |
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
buildscript {
|
buildscript {
|
||||||
ext.firebaseVersion = '11.2.0'
|
ext.firebaseVersion = '11.4.2'
|
||||||
repositories {
|
repositories {
|
||||||
jcenter()
|
jcenter()
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,10 @@ android {
|
||||||
allprojects {
|
allprojects {
|
||||||
repositories {
|
repositories {
|
||||||
jcenter()
|
jcenter()
|
||||||
|
mavenLocal()
|
||||||
|
maven {
|
||||||
|
url "https://maven.google.com"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,4 +89,5 @@ dependencies {
|
||||||
compile "com.google.firebase:firebase-crash:$firebaseVersion"
|
compile "com.google.firebase:firebase-crash:$firebaseVersion"
|
||||||
compile "com.google.firebase:firebase-perf:$firebaseVersion"
|
compile "com.google.firebase:firebase-perf:$firebaseVersion"
|
||||||
compile "com.google.firebase:firebase-ads:$firebaseVersion"
|
compile "com.google.firebase:firebase-ads:$firebaseVersion"
|
||||||
|
compile "com.google.firebase:firebase-firestore:$firebaseVersion"
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
package io.invertase.firebase;
|
||||||
|
|
||||||
|
public class ErrorUtils {
|
||||||
|
/**
|
||||||
|
* Wrap a message string with the specified service name e.g. 'Database'
|
||||||
|
*
|
||||||
|
* @param message
|
||||||
|
* @param service
|
||||||
|
* @param fullCode
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static String getMessageWithService(String message, String service, String fullCode) {
|
||||||
|
// Service: Error message (service/code).
|
||||||
|
return service + ": " + message + " (" + fullCode.toLowerCase() + ").";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a service error code string e.g. 'DATABASE/PERMISSION-DENIED'
|
||||||
|
*
|
||||||
|
* @param service
|
||||||
|
* @param code
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static String getCodeWithService(String service, String code) {
|
||||||
|
return service.toLowerCase() + "/" + code.toLowerCase();
|
||||||
|
}
|
||||||
|
}
|
|
@ -31,18 +31,6 @@ public class RNFirebasePackage implements ReactPackage {
|
||||||
return modules;
|
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
|
* @param 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}
|
||||||
|
|
|
@ -78,7 +78,6 @@ public class Utils {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param dataSnapshot
|
* @param dataSnapshot
|
||||||
* @param registration
|
|
||||||
* @param previousChildName
|
* @param previousChildName
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -29,18 +29,6 @@ public class RNFirebaseAdMobPackage implements ReactPackage {
|
||||||
return modules;
|
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
|
* @param 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}
|
||||||
|
|
|
@ -33,18 +33,6 @@ public class RNFirebaseAnalyticsPackage implements ReactPackage {
|
||||||
return modules;
|
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
|
* @param 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}
|
||||||
|
|
|
@ -28,18 +28,6 @@ public class RNFirebaseAuthPackage implements ReactPackage {
|
||||||
return modules;
|
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
|
* @param 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}
|
||||||
|
|
|
@ -28,18 +28,6 @@ public class RNFirebaseRemoteConfigPackage implements ReactPackage {
|
||||||
return modules;
|
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
|
* @param 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}
|
||||||
|
|
|
@ -28,18 +28,6 @@ public class RNFirebaseCrashPackage implements ReactPackage {
|
||||||
return modules;
|
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
|
* @param 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}
|
||||||
|
|
|
@ -26,6 +26,7 @@ import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import io.invertase.firebase.ErrorUtils;
|
||||||
import io.invertase.firebase.Utils;
|
import io.invertase.firebase.Utils;
|
||||||
|
|
||||||
|
|
||||||
|
@ -522,30 +523,6 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
|
||||||
return existingRef;
|
return existingRef;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Wrap a message string with the specified service name e.g. 'Database'
|
|
||||||
*
|
|
||||||
* @param message
|
|
||||||
* @param service
|
|
||||||
* @param fullCode
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
private static String getMessageWithService(String message, String service, String fullCode) {
|
|
||||||
// Service: Error message (service/code).
|
|
||||||
return service + ": " + message + " (" + fullCode.toLowerCase() + ").";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate a service error code string e.g. 'DATABASE/PERMISSION-DENIED'
|
|
||||||
*
|
|
||||||
* @param service
|
|
||||||
* @param code
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
private static String getCodeWithService(String service, String code) {
|
|
||||||
return service.toLowerCase() + "/" + code.toLowerCase();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert as firebase DatabaseError instance into a writable map
|
* Convert as firebase DatabaseError instance into a writable map
|
||||||
* with the correct web-like error codes.
|
* with the correct web-like error codes.
|
||||||
|
@ -564,56 +541,56 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
|
||||||
|
|
||||||
switch (nativeError.getCode()) {
|
switch (nativeError.getCode()) {
|
||||||
case DatabaseError.DATA_STALE:
|
case DatabaseError.DATA_STALE:
|
||||||
code = getCodeWithService(service, "data-stale");
|
code = ErrorUtils.getCodeWithService(service, "data-stale");
|
||||||
message = getMessageWithService("The transaction needs to be run again with current data.", service, code);
|
message = ErrorUtils.getMessageWithService("The transaction needs to be run again with current data.", service, code);
|
||||||
break;
|
break;
|
||||||
case DatabaseError.OPERATION_FAILED:
|
case DatabaseError.OPERATION_FAILED:
|
||||||
code = getCodeWithService(service, "failure");
|
code = ErrorUtils.getCodeWithService(service, "failure");
|
||||||
message = getMessageWithService("The server indicated that this operation failed.", service, code);
|
message = ErrorUtils.getMessageWithService("The server indicated that this operation failed.", service, code);
|
||||||
break;
|
break;
|
||||||
case DatabaseError.PERMISSION_DENIED:
|
case DatabaseError.PERMISSION_DENIED:
|
||||||
code = getCodeWithService(service, "permission-denied");
|
code = ErrorUtils.getCodeWithService(service, "permission-denied");
|
||||||
message = getMessageWithService("Client doesn't have permission to access the desired data.", service, code);
|
message = ErrorUtils.getMessageWithService("Client doesn't have permission to access the desired data.", service, code);
|
||||||
break;
|
break;
|
||||||
case DatabaseError.DISCONNECTED:
|
case DatabaseError.DISCONNECTED:
|
||||||
code = getCodeWithService(service, "disconnected");
|
code = ErrorUtils.getCodeWithService(service, "disconnected");
|
||||||
message = getMessageWithService("The operation had to be aborted due to a network disconnect.", service, code);
|
message = ErrorUtils.getMessageWithService("The operation had to be aborted due to a network disconnect.", service, code);
|
||||||
break;
|
break;
|
||||||
case DatabaseError.EXPIRED_TOKEN:
|
case DatabaseError.EXPIRED_TOKEN:
|
||||||
code = getCodeWithService(service, "expired-token");
|
code = ErrorUtils.getCodeWithService(service, "expired-token");
|
||||||
message = getMessageWithService("The supplied auth token has expired.", service, code);
|
message = ErrorUtils.getMessageWithService("The supplied auth token has expired.", service, code);
|
||||||
break;
|
break;
|
||||||
case DatabaseError.INVALID_TOKEN:
|
case DatabaseError.INVALID_TOKEN:
|
||||||
code = getCodeWithService(service, "invalid-token");
|
code = ErrorUtils.getCodeWithService(service, "invalid-token");
|
||||||
message = getMessageWithService("The supplied auth token was invalid.", service, code);
|
message = ErrorUtils.getMessageWithService("The supplied auth token was invalid.", service, code);
|
||||||
break;
|
break;
|
||||||
case DatabaseError.MAX_RETRIES:
|
case DatabaseError.MAX_RETRIES:
|
||||||
code = getCodeWithService(service, "max-retries");
|
code = ErrorUtils.getCodeWithService(service, "max-retries");
|
||||||
message = getMessageWithService("The transaction had too many retries.", service, code);
|
message = ErrorUtils.getMessageWithService("The transaction had too many retries.", service, code);
|
||||||
break;
|
break;
|
||||||
case DatabaseError.OVERRIDDEN_BY_SET:
|
case DatabaseError.OVERRIDDEN_BY_SET:
|
||||||
code = getCodeWithService(service, "overridden-by-set");
|
code = ErrorUtils.getCodeWithService(service, "overridden-by-set");
|
||||||
message = getMessageWithService("The transaction was overridden by a subsequent set.", service, code);
|
message = ErrorUtils.getMessageWithService("The transaction was overridden by a subsequent set.", service, code);
|
||||||
break;
|
break;
|
||||||
case DatabaseError.UNAVAILABLE:
|
case DatabaseError.UNAVAILABLE:
|
||||||
code = getCodeWithService(service, "unavailable");
|
code = ErrorUtils.getCodeWithService(service, "unavailable");
|
||||||
message = getMessageWithService("The service is unavailable.", service, code);
|
message = ErrorUtils.getMessageWithService("The service is unavailable.", service, code);
|
||||||
break;
|
break;
|
||||||
case DatabaseError.USER_CODE_EXCEPTION:
|
case DatabaseError.USER_CODE_EXCEPTION:
|
||||||
code = getCodeWithService(service, "user-code-exception");
|
code = ErrorUtils.getCodeWithService(service, "user-code-exception");
|
||||||
message = getMessageWithService("User code called from the Firebase Database runloop threw an exception.", service, code);
|
message = ErrorUtils.getMessageWithService("User code called from the Firebase Database runloop threw an exception.", service, code);
|
||||||
break;
|
break;
|
||||||
case DatabaseError.NETWORK_ERROR:
|
case DatabaseError.NETWORK_ERROR:
|
||||||
code = getCodeWithService(service, "network-error");
|
code = ErrorUtils.getCodeWithService(service, "network-error");
|
||||||
message = getMessageWithService("The operation could not be performed due to a network error.", service, code);
|
message = ErrorUtils.getMessageWithService("The operation could not be performed due to a network error.", service, code);
|
||||||
break;
|
break;
|
||||||
case DatabaseError.WRITE_CANCELED:
|
case DatabaseError.WRITE_CANCELED:
|
||||||
code = getCodeWithService(service, "write-cancelled");
|
code = ErrorUtils.getCodeWithService(service, "write-cancelled");
|
||||||
message = getMessageWithService("The write was canceled by the user.", service, code);
|
message = ErrorUtils.getMessageWithService("The write was canceled by the user.", service, code);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
code = getCodeWithService(service, "unknown");
|
code = ErrorUtils.getCodeWithService(service, "unknown");
|
||||||
message = getMessageWithService("An unknown error occurred.", service, code);
|
message = ErrorUtils.getMessageWithService("An unknown error occurred.", service, code);
|
||||||
}
|
}
|
||||||
|
|
||||||
errorMap.putString("code", code);
|
errorMap.putString("code", code);
|
||||||
|
|
|
@ -28,18 +28,6 @@ public class RNFirebaseDatabasePackage implements ReactPackage {
|
||||||
return modules;
|
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
|
* @param 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}
|
||||||
|
|
|
@ -29,8 +29,8 @@ class RNFirebaseDatabaseReference {
|
||||||
private String appName;
|
private String appName;
|
||||||
private ReactContext reactContext;
|
private ReactContext reactContext;
|
||||||
private static final String TAG = "RNFirebaseDBReference";
|
private static final String TAG = "RNFirebaseDBReference";
|
||||||
private HashMap<String, ChildEventListener> childEventListeners;
|
private HashMap<String, ChildEventListener> childEventListeners = new HashMap<>();
|
||||||
private HashMap<String, ValueEventListener> valueEventListeners;
|
private HashMap<String, ValueEventListener> valueEventListeners = new HashMap<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* RNFirebase wrapper around FirebaseDatabaseReference,
|
* RNFirebase wrapper around FirebaseDatabaseReference,
|
||||||
|
@ -47,8 +47,6 @@ class RNFirebaseDatabaseReference {
|
||||||
query = null;
|
query = null;
|
||||||
appName = app;
|
appName = app;
|
||||||
reactContext = context;
|
reactContext = context;
|
||||||
childEventListeners = new HashMap<>();
|
|
||||||
valueEventListeners = new HashMap<>();
|
|
||||||
buildDatabaseQueryAtPathAndModifiers(refPath, modifiersArray);
|
buildDatabaseQueryAtPathAndModifiers(refPath, modifiersArray);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,197 @@
|
||||||
|
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.getReference().getPath());
|
||||||
|
if (documentSnapshot.exists()) {
|
||||||
|
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();
|
||||||
|
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()));
|
||||||
|
documentChangeMap.putInt(KEY_DOC_CHANGE_NEW_INDEX, documentChange.getNewIndex());
|
||||||
|
documentChangeMap.putInt(KEY_DOC_CHANGE_OLD_INDEX, documentChange.getOldIndex());
|
||||||
|
|
||||||
|
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,342 @@
|
||||||
|
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.facebook.react.bridge.WritableMap;
|
||||||
|
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.FirebaseFirestoreException;
|
||||||
|
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.ErrorUtils;
|
||||||
|
import io.invertase.firebase.Utils;
|
||||||
|
|
||||||
|
|
||||||
|
public class RNFirebaseFirestore extends ReactContextBaseJavaModule {
|
||||||
|
private static final String TAG = "RNFirebaseFirestore";
|
||||||
|
// 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) {
|
||||||
|
RNFirebaseFirestoreCollectionReference ref = getCollectionForAppPath(appName, path, filters, orders, options);
|
||||||
|
ref.get(promise);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ReactMethod
|
||||||
|
public void collectionOffSnapshot(String appName, String path, ReadableArray filters,
|
||||||
|
ReadableArray orders, ReadableMap options, String listenerId) {
|
||||||
|
RNFirebaseFirestoreCollectionReference.offSnapshot(listenerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ReactMethod
|
||||||
|
public void collectionOnSnapshot(String appName, String path, ReadableArray filters,
|
||||||
|
ReadableArray orders, ReadableMap options, String listenerId) {
|
||||||
|
RNFirebaseFirestoreCollectionReference ref = getCollectionForAppPath(appName, path, filters, orders, options);
|
||||||
|
ref.onSnapshot(listenerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ReactMethod
|
||||||
|
public void documentBatch(final String appName, final ReadableArray writes,
|
||||||
|
final ReadableMap commitOptions, final Promise promise) {
|
||||||
|
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");
|
||||||
|
if (options != null && options.containsKey("merge") && (boolean)options.get("merge")) {
|
||||||
|
batch = batch.set(ref, data, SetOptions.merge());
|
||||||
|
} else {
|
||||||
|
batch = batch.set(ref, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
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, (FirebaseFirestoreException)task.getException());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@ReactMethod
|
||||||
|
public void documentCollections(String appName, String path, final Promise promise) {
|
||||||
|
RNFirebaseFirestoreDocumentReference ref = getDocumentForAppPath(appName, path);
|
||||||
|
ref.collections(promise);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ReactMethod
|
||||||
|
public void documentCreate(String appName, String path, ReadableMap data, final Promise promise) {
|
||||||
|
RNFirebaseFirestoreDocumentReference ref = getDocumentForAppPath(appName, path);
|
||||||
|
ref.create(data, promise);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ReactMethod
|
||||||
|
public void documentDelete(String appName, String path, ReadableMap options, final Promise promise) {
|
||||||
|
RNFirebaseFirestoreDocumentReference ref = getDocumentForAppPath(appName, path);
|
||||||
|
ref.delete(options, promise);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ReactMethod
|
||||||
|
public void documentGet(String appName, String path, final Promise promise) {
|
||||||
|
RNFirebaseFirestoreDocumentReference 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 documentOffSnapshot(String appName, String path, String listenerId) {
|
||||||
|
RNFirebaseFirestoreDocumentReference.offSnapshot(listenerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ReactMethod
|
||||||
|
public void documentOnSnapshot(String appName, String path, String listenerId) {
|
||||||
|
RNFirebaseFirestoreDocumentReference ref = getDocumentForAppPath(appName, path);
|
||||||
|
ref.onSnapshot(listenerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ReactMethod
|
||||||
|
public void documentSet(String appName, String path, ReadableMap data, ReadableMap options, final Promise promise) {
|
||||||
|
RNFirebaseFirestoreDocumentReference ref = getDocumentForAppPath(appName, path);
|
||||||
|
ref.set(data, options, promise);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ReactMethod
|
||||||
|
public void documentUpdate(String appName, String path, ReadableMap data, final Promise promise) {
|
||||||
|
RNFirebaseFirestoreDocumentReference ref = getDocumentForAppPath(appName, path);
|
||||||
|
ref.update(data, 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, 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
|
||||||
|
*
|
||||||
|
* @param appName
|
||||||
|
* @param filters
|
||||||
|
* @param orders
|
||||||
|
* @param options
|
||||||
|
* @param path @return
|
||||||
|
*/
|
||||||
|
private RNFirebaseFirestoreCollectionReference getCollectionForAppPath(String appName, String path,
|
||||||
|
ReadableArray filters,
|
||||||
|
ReadableArray orders,
|
||||||
|
ReadableMap options) {
|
||||||
|
return new RNFirebaseFirestoreCollectionReference(this.getReactApplicationContext(), appName, path, filters, orders, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a document reference for a specific app and path
|
||||||
|
*
|
||||||
|
* @param appName
|
||||||
|
* @param path
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private RNFirebaseFirestoreDocumentReference getDocumentForAppPath(String appName, String 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("Cancelled.", service, code);
|
||||||
|
break;
|
||||||
|
case UNKNOWN:
|
||||||
|
code = ErrorUtils.getCodeWithService(service, "unknown");
|
||||||
|
message = ErrorUtils.getMessageWithService("An unknown error occurred.", service, code);
|
||||||
|
break;
|
||||||
|
case INVALID_ARGUMENT:
|
||||||
|
code = ErrorUtils.getCodeWithService(service, "invalid-argument");
|
||||||
|
message = ErrorUtils.getMessageWithService("Invalid argument.", service, code);
|
||||||
|
break;
|
||||||
|
case NOT_FOUND:
|
||||||
|
code = ErrorUtils.getCodeWithService(service, "not-found");
|
||||||
|
message = ErrorUtils.getMessageWithService("Not found.", service, code);
|
||||||
|
break;
|
||||||
|
case ALREADY_EXISTS:
|
||||||
|
code = ErrorUtils.getCodeWithService(service, "already-exists");
|
||||||
|
message = ErrorUtils.getMessageWithService("Already exists.", service, code);
|
||||||
|
break;
|
||||||
|
case PERMISSION_DENIED:
|
||||||
|
code = ErrorUtils.getCodeWithService(service, "permission-denied");
|
||||||
|
message = ErrorUtils.getMessageWithService("Permission denied.", service, code);
|
||||||
|
break;
|
||||||
|
case RESOURCE_EXHAUSTED:
|
||||||
|
code = ErrorUtils.getCodeWithService(service, "resource-exhausted");
|
||||||
|
message = ErrorUtils.getMessageWithService("Resource exhausted.", service, code);
|
||||||
|
break;
|
||||||
|
case FAILED_PRECONDITION:
|
||||||
|
code = ErrorUtils.getCodeWithService(service, "failed-precondition");
|
||||||
|
message = ErrorUtils.getMessageWithService("Failed precondition.", service, code);
|
||||||
|
break;
|
||||||
|
case ABORTED:
|
||||||
|
code = ErrorUtils.getCodeWithService(service, "aborted");
|
||||||
|
message = ErrorUtils.getMessageWithService("Aborted.", service, code);
|
||||||
|
break;
|
||||||
|
case OUT_OF_RANGE:
|
||||||
|
code = ErrorUtils.getCodeWithService(service, "out-of-range");
|
||||||
|
message = ErrorUtils.getMessageWithService("Out of range.", service, code);
|
||||||
|
break;
|
||||||
|
case UNIMPLEMENTED:
|
||||||
|
code = ErrorUtils.getCodeWithService(service, "unimplemented");
|
||||||
|
message = ErrorUtils.getMessageWithService("Unimplemented.", service, code);
|
||||||
|
break;
|
||||||
|
case INTERNAL:
|
||||||
|
code = ErrorUtils.getCodeWithService(service, "internal");
|
||||||
|
message = ErrorUtils.getMessageWithService("Internal.", service, code);
|
||||||
|
break;
|
||||||
|
case UNAVAILABLE:
|
||||||
|
code = ErrorUtils.getCodeWithService(service, "unavailable");
|
||||||
|
message = ErrorUtils.getMessageWithService("Unavailable.", service, code);
|
||||||
|
break;
|
||||||
|
case DATA_LOSS:
|
||||||
|
code = ErrorUtils.getCodeWithService(service, "data-loss");
|
||||||
|
message = ErrorUtils.getMessageWithService("Data loss.", service, code);
|
||||||
|
break;
|
||||||
|
case UNAUTHENTICATED:
|
||||||
|
code = ErrorUtils.getCodeWithService(service, "unauthenticated");
|
||||||
|
message = ErrorUtils.getMessageWithService("Unauthenticated.", 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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().toString());
|
||||||
|
constants.put("serverTimestampFieldValue", FieldValue.serverTimestamp().toString());
|
||||||
|
return constants;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,218 @@
|
||||||
|
package io.invertase.firebase.firestore;
|
||||||
|
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.facebook.react.bridge.Arguments;
|
||||||
|
import com.facebook.react.bridge.Promise;
|
||||||
|
import com.facebook.react.bridge.ReactContext;
|
||||||
|
import com.facebook.react.bridge.ReadableArray;
|
||||||
|
import com.facebook.react.bridge.ReadableMap;
|
||||||
|
import com.facebook.react.bridge.WritableMap;
|
||||||
|
import com.google.android.gms.tasks.OnCompleteListener;
|
||||||
|
import com.google.android.gms.tasks.Task;
|
||||||
|
import com.google.firebase.firestore.EventListener;
|
||||||
|
import com.google.firebase.firestore.FirebaseFirestoreException;
|
||||||
|
import com.google.firebase.firestore.ListenerRegistration;
|
||||||
|
import com.google.firebase.firestore.Query;
|
||||||
|
import com.google.firebase.firestore.QuerySnapshot;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import io.invertase.firebase.Utils;
|
||||||
|
|
||||||
|
public class RNFirebaseFirestoreCollectionReference {
|
||||||
|
private static final String TAG = "RNFSCollectionReference";
|
||||||
|
private static Map<String, ListenerRegistration> collectionSnapshotListeners = new HashMap<>();
|
||||||
|
|
||||||
|
private final String appName;
|
||||||
|
private final String path;
|
||||||
|
private final ReadableArray filters;
|
||||||
|
private final ReadableArray orders;
|
||||||
|
private final ReadableMap options;
|
||||||
|
private final Query query;
|
||||||
|
private ReactContext reactContext;
|
||||||
|
|
||||||
|
RNFirebaseFirestoreCollectionReference(ReactContext reactContext, String appName, String path,
|
||||||
|
ReadableArray filters, ReadableArray orders,
|
||||||
|
ReadableMap options) {
|
||||||
|
this.appName = appName;
|
||||||
|
this.path = path;
|
||||||
|
this.filters = filters;
|
||||||
|
this.orders = orders;
|
||||||
|
this.options = options;
|
||||||
|
this.query = buildQuery();
|
||||||
|
this.reactContext = reactContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
void get(final Promise promise) {
|
||||||
|
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, (FirebaseFirestoreException)task.getException());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void offSnapshot(final String listenerId) {
|
||||||
|
ListenerRegistration listenerRegistration = collectionSnapshotListeners.remove(listenerId);
|
||||||
|
if (listenerRegistration != null) {
|
||||||
|
listenerRegistration.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onSnapshot(final String listenerId) {
|
||||||
|
if (!collectionSnapshotListeners.containsKey(listenerId)) {
|
||||||
|
final EventListener<QuerySnapshot> listener = new EventListener<QuerySnapshot>() {
|
||||||
|
@Override
|
||||||
|
public void onEvent(QuerySnapshot querySnapshot, FirebaseFirestoreException exception) {
|
||||||
|
if (exception == null) {
|
||||||
|
handleQuerySnapshotEvent(listenerId, querySnapshot);
|
||||||
|
} else {
|
||||||
|
ListenerRegistration listenerRegistration = collectionSnapshotListeners.remove(listenerId);
|
||||||
|
if (listenerRegistration != null) {
|
||||||
|
listenerRegistration.remove();
|
||||||
|
}
|
||||||
|
handleQuerySnapshotError(listenerId, exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
ListenerRegistration listenerRegistration = this.query.addSnapshotListener(listener);
|
||||||
|
collectionSnapshotListeners.put(listenerId, listenerRegistration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* INTERNALS/UTILS
|
||||||
|
*/
|
||||||
|
|
||||||
|
boolean hasListeners() {
|
||||||
|
return !collectionSnapshotListeners.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Query buildQuery() {
|
||||||
|
Query query = RNFirebaseFirestore.getFirestoreForApp(appName).collection(path);
|
||||||
|
query = applyFilters(query);
|
||||||
|
query = applyOrders(query);
|
||||||
|
query = applyOptions(query);
|
||||||
|
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Query applyFilters(Query query) {
|
||||||
|
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) {
|
||||||
|
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) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles documentSnapshot events.
|
||||||
|
*
|
||||||
|
* @param listenerId
|
||||||
|
* @param querySnapshot
|
||||||
|
*/
|
||||||
|
private void handleQuerySnapshotEvent(String listenerId, QuerySnapshot querySnapshot) {
|
||||||
|
WritableMap event = Arguments.createMap();
|
||||||
|
WritableMap data = FirestoreSerialize.snapshotToWritableMap(querySnapshot);
|
||||||
|
|
||||||
|
event.putString("appName", appName);
|
||||||
|
event.putString("path", path);
|
||||||
|
event.putString("listenerId", listenerId);
|
||||||
|
event.putMap("querySnapshot", data);
|
||||||
|
|
||||||
|
Utils.sendEvent(reactContext, "firestore_collection_sync_event", event);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles a documentSnapshot error event
|
||||||
|
*
|
||||||
|
* @param listenerId
|
||||||
|
* @param exception
|
||||||
|
*/
|
||||||
|
private void handleQuerySnapshotError(String listenerId, FirebaseFirestoreException exception) {
|
||||||
|
WritableMap event = Arguments.createMap();
|
||||||
|
|
||||||
|
event.putString("appName", appName);
|
||||||
|
event.putString("path", path);
|
||||||
|
event.putString("listenerId", listenerId);
|
||||||
|
event.putMap("error", RNFirebaseFirestore.getJSError(exception));
|
||||||
|
|
||||||
|
Utils.sendEvent(reactContext, "firestore_collection_sync_event", event);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,196 @@
|
||||||
|
package io.invertase.firebase.firestore;
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.facebook.react.bridge.Arguments;
|
||||||
|
import com.facebook.react.bridge.Promise;
|
||||||
|
import com.facebook.react.bridge.ReactContext;
|
||||||
|
import com.facebook.react.bridge.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.EventListener;
|
||||||
|
import com.google.firebase.firestore.FirebaseFirestoreException;
|
||||||
|
import com.google.firebase.firestore.ListenerRegistration;
|
||||||
|
import com.google.firebase.firestore.SetOptions;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import io.invertase.firebase.Utils;
|
||||||
|
|
||||||
|
|
||||||
|
public class RNFirebaseFirestoreDocumentReference {
|
||||||
|
private static final String TAG = "RNFBFSDocumentReference";
|
||||||
|
private static Map<String, ListenerRegistration> documentSnapshotListeners = new HashMap<>();
|
||||||
|
|
||||||
|
private final String appName;
|
||||||
|
private final String path;
|
||||||
|
private ReactContext reactContext;
|
||||||
|
private final DocumentReference ref;
|
||||||
|
|
||||||
|
RNFirebaseFirestoreDocumentReference(ReactContext reactContext, String appName, String path) {
|
||||||
|
this.appName = appName;
|
||||||
|
this.path = path;
|
||||||
|
this.reactContext = reactContext;
|
||||||
|
this.ref = RNFirebaseFirestore.getFirestoreForApp(appName).document(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void collections(Promise promise) {
|
||||||
|
// Not supported on Android
|
||||||
|
}
|
||||||
|
|
||||||
|
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, (FirebaseFirestoreException)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, (FirebaseFirestoreException)task.getException());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void offSnapshot(final String listenerId) {
|
||||||
|
ListenerRegistration listenerRegistration = documentSnapshotListeners.remove(listenerId);
|
||||||
|
if (listenerRegistration != null) {
|
||||||
|
listenerRegistration.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onSnapshot(final String listenerId) {
|
||||||
|
if (!documentSnapshotListeners.containsKey(listenerId)) {
|
||||||
|
final EventListener<DocumentSnapshot> listener = new EventListener<DocumentSnapshot>() {
|
||||||
|
@Override
|
||||||
|
public void onEvent(DocumentSnapshot documentSnapshot, FirebaseFirestoreException exception) {
|
||||||
|
if (exception == null) {
|
||||||
|
handleDocumentSnapshotEvent(listenerId, documentSnapshot);
|
||||||
|
} else {
|
||||||
|
ListenerRegistration listenerRegistration = documentSnapshotListeners.remove(listenerId);
|
||||||
|
if (listenerRegistration != null) {
|
||||||
|
listenerRegistration.remove();
|
||||||
|
}
|
||||||
|
handleDocumentSnapshotError(listenerId, exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
ListenerRegistration listenerRegistration = this.ref.addSnapshotListener(listener);
|
||||||
|
documentSnapshotListeners.put(listenerId, listenerRegistration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void set(final ReadableMap data, final ReadableMap options, final Promise promise) {
|
||||||
|
Map<String, Object> map = Utils.recursivelyDeconstructReadableMap(data);
|
||||||
|
Task<Void> task;
|
||||||
|
SetOptions setOptions = null;
|
||||||
|
if (options != null && options.hasKey("merge") && options.getBoolean("merge")) {
|
||||||
|
task = this.ref.set(map, SetOptions.merge());
|
||||||
|
} else {
|
||||||
|
task = this.ref.set(map);
|
||||||
|
}
|
||||||
|
task.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, (FirebaseFirestoreException)task.getException());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update(final ReadableMap data, 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, (FirebaseFirestoreException)task.getException());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* INTERNALS/UTILS
|
||||||
|
*/
|
||||||
|
|
||||||
|
boolean hasListeners() {
|
||||||
|
return !documentSnapshotListeners.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles documentSnapshot events.
|
||||||
|
*
|
||||||
|
* @param listenerId
|
||||||
|
* @param documentSnapshot
|
||||||
|
*/
|
||||||
|
private void handleDocumentSnapshotEvent(String listenerId, DocumentSnapshot documentSnapshot) {
|
||||||
|
WritableMap event = Arguments.createMap();
|
||||||
|
WritableMap data = FirestoreSerialize.snapshotToWritableMap(documentSnapshot);
|
||||||
|
|
||||||
|
event.putString("appName", appName);
|
||||||
|
event.putString("path", path);
|
||||||
|
event.putString("listenerId", listenerId);
|
||||||
|
event.putMap("documentSnapshot", data);
|
||||||
|
|
||||||
|
Utils.sendEvent(reactContext, "firestore_document_sync_event", event);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles a documentSnapshot error event
|
||||||
|
*
|
||||||
|
* @param listenerId
|
||||||
|
* @param exception
|
||||||
|
*/
|
||||||
|
private void handleDocumentSnapshotError(String listenerId, FirebaseFirestoreException exception) {
|
||||||
|
WritableMap event = Arguments.createMap();
|
||||||
|
|
||||||
|
event.putString("appName", appName);
|
||||||
|
event.putString("path", path);
|
||||||
|
event.putString("listenerId", listenerId);
|
||||||
|
event.putMap("error", RNFirebaseFirestore.getJSError(exception));
|
||||||
|
|
||||||
|
Utils.sendEvent(reactContext, "firestore_document_sync_event", event);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -28,18 +28,6 @@ public class RNFirebaseMessagingPackage implements ReactPackage {
|
||||||
return modules;
|
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
|
* @param 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}
|
||||||
|
|
|
@ -28,18 +28,6 @@ public class RNFirebasePerformancePackage implements ReactPackage {
|
||||||
return modules;
|
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
|
* @param 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}
|
||||||
|
|
|
@ -33,18 +33,6 @@ public class RNFirebaseStoragePackage implements ReactPackage {
|
||||||
return modules;
|
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 compatibility
|
|
||||||
public List<Class<? extends JavaScriptModule>> createJSModules() {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param reactContext
|
* @param 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}
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
[![License](https://img.shields.io/npm/l/react-native-firebase.svg?style=flat-square)](/LICENSE)
|
[![License](https://img.shields.io/npm/l/react-native-firebase.svg?style=flat-square)](/LICENSE)
|
||||||
[![Chat](https://img.shields.io/badge/chat-on%20discord-7289da.svg?style=flat-square)](https://discord.gg/t6bdqMs)
|
[![Chat](https://img.shields.io/badge/chat-on%20discord-7289da.svg?style=flat-square)](https://discord.gg/t6bdqMs)
|
||||||
[![Donate](https://img.shields.io/badge/Donate-Patreon-green.svg?style=flat-square)](https://www.patreon.com/invertase)
|
[![Donate](https://img.shields.io/badge/Donate-Patreon-green.svg?style=flat-square)](https://www.patreon.com/invertase)
|
||||||
|
[![Twitter Follow](https://img.shields.io/twitter/follow/rnfirebase.svg?style=social&label=Follow)](https://twitter.com/rnfirebase)
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
---
|
---
|
||||||
|
@ -46,6 +47,7 @@ All in all, RNFirebase provides much faster performance (~2x) over the web SDK a
|
||||||
| **Cloud Messaging (FCM)** | ✅ | ✅ | ✅ |**?**|
|
| **Cloud Messaging (FCM)** | ✅ | ✅ | ✅ |**?**|
|
||||||
| **Crash Reporting** | ✅ | ✅ | ✅ | ❌ |
|
| **Crash Reporting** | ✅ | ✅ | ✅ | ❌ |
|
||||||
| **Dynamic Links** | ❌ | ❌ | ❌ | ❌ |
|
| **Dynamic Links** | ❌ | ❌ | ❌ | ❌ |
|
||||||
|
| **Firestore** | ❌ | ❌ | ✅ | ❌ |
|
||||||
| **Invites** | ❌ | ❌ | ❌ | ❌ |
|
| **Invites** | ❌ | ❌ | ❌ | ❌ |
|
||||||
| **Performance Monitoring** | ✅ | ✅ | ✅ | ❌ |
|
| **Performance Monitoring** | ✅ | ✅ | ✅ | ❌ |
|
||||||
| **Realtime Database** | ✅ | ✅ | ✅ | ✅ |
|
| **Realtime Database** | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
@ -57,15 +59,10 @@ All in all, RNFirebase provides much faster performance (~2x) over the web SDK a
|
||||||
---
|
---
|
||||||
### Supported versions - React Native / Firebase
|
### Supported versions - React Native / Firebase
|
||||||
|
|
||||||
> The table below shows the supported version of `react-native-firebase` for different React Native versions
|
> The table below shows the supported versions of React Native and the Firebase SDKs for different versions of `react-native-firebase`
|
||||||
|
|
||||||
| | v0.36 - v0.39 | v0.40 - v0.46 | v0.47 +
|
| | 1.X.X | 2.0.X | 2.1.X / 2.2.X | 3.0.X |
|
||||||
| ------------------------------- | :---: | :---: | :---: |
|
|------------------------|-------------|-------------|-----------------|----------|
|
||||||
| react-native-firebase | 1.X.X | 2.X.X | 2.1.X |
|
| React Native | 0.36 - 0.39 | 0.40 - 0.46 | 0.47 + | 0.48 + |
|
||||||
|
| Firebase Android SDK | 10.2.0 + | 11.0.0 + | 11.0.0 + | 11.4.2 + |
|
||||||
> The table below shows the minimum supported versions of the Firebase SDKs for each version of `react-native-firebase`
|
| Firebase iOS SDK | 3.15.0 + | 4.0.0 + | 4.0.0 + | 4.3.0 + |
|
||||||
|
|
||||||
| | v1 | v2 | v3 |
|
|
||||||
| ---------------------- | :---: | :---: | :---: |
|
|
||||||
| Firebase Android SDK | 10.2.0+ | 11.0.0 + | 11.2.0 + |
|
|
||||||
| Firebase iOS SDK | 3.15.0+ | 4.0.0 + | 4.0.0 + |
|
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
- [Cloud Messaging](/modules/cloud-messaging)
|
- [Cloud Messaging](/modules/cloud-messaging)
|
||||||
- [Crash Reporting](/modules/crash)
|
- [Crash Reporting](/modules/crash)
|
||||||
- [Database](/modules/database)
|
- [Database](/modules/database)
|
||||||
|
- [Firestore (Beta)](/modules/firestore)
|
||||||
- [Remote Config](/modules/config)
|
- [Remote Config](/modules/config)
|
||||||
- [Storage](/modules/storage)
|
- [Storage](/modules/storage)
|
||||||
- [Transactions](/modules/transactions)
|
- [Transactions](/modules/transactions)
|
||||||
|
|
|
@ -53,6 +53,7 @@ dependencies {
|
||||||
compile "com.google.firebase:firebase-config:11.2.0"
|
compile "com.google.firebase:firebase-config:11.2.0"
|
||||||
compile "com.google.firebase:firebase-crash:11.2.0"
|
compile "com.google.firebase:firebase-crash:11.2.0"
|
||||||
compile "com.google.firebase:firebase-database:11.2.0"
|
compile "com.google.firebase:firebase-database:11.2.0"
|
||||||
|
compile "com.google.firebase:firebase-firestore:11.2.0"
|
||||||
compile "com.google.firebase:firebase-messaging:11.2.0"
|
compile "com.google.firebase:firebase-messaging:11.2.0"
|
||||||
compile "com.google.firebase:firebase-perf:11.2.0"
|
compile "com.google.firebase:firebase-perf:11.2.0"
|
||||||
compile "com.google.firebase:firebase-storage:11.2.0"
|
compile "com.google.firebase:firebase-storage:11.2.0"
|
||||||
|
@ -88,6 +89,7 @@ import io.invertase.firebase.auth.RNFirebaseAuthPackage; // Firebase Auth
|
||||||
import io.invertase.firebase.config.RNFirebaseRemoteConfigPackage; // Firebase Remote Config
|
import io.invertase.firebase.config.RNFirebaseRemoteConfigPackage; // Firebase Remote Config
|
||||||
import io.invertase.firebase.crash.RNFirebaseCrashPackage; // Firebase Crash Reporting
|
import io.invertase.firebase.crash.RNFirebaseCrashPackage; // Firebase Crash Reporting
|
||||||
import io.invertase.firebase.database.RNFirebaseDatabasePackage; // Firebase Realtime Database
|
import io.invertase.firebase.database.RNFirebaseDatabasePackage; // Firebase Realtime Database
|
||||||
|
import io.invertase.firebase.firestore.RNFirebaseFirestorePackage; // Firebase Firestore
|
||||||
import io.invertase.firebase.messaging.RNFirebaseMessagingPackage; // Firebase Cloud Messaging
|
import io.invertase.firebase.messaging.RNFirebaseMessagingPackage; // Firebase Cloud Messaging
|
||||||
import io.invertase.firebase.perf.RNFirebasePerformancePackage; // Firebase Performance
|
import io.invertase.firebase.perf.RNFirebasePerformancePackage; // Firebase Performance
|
||||||
import io.invertase.firebase.storage.RNFirebaseStoragePackage; // Firebase Storage
|
import io.invertase.firebase.storage.RNFirebaseStoragePackage; // Firebase Storage
|
||||||
|
@ -107,6 +109,7 @@ public class MainApplication extends Application implements ReactApplication {
|
||||||
new RNFirebaseRemoteConfigPackage(),
|
new RNFirebaseRemoteConfigPackage(),
|
||||||
new RNFirebaseCrashPackage(),
|
new RNFirebaseCrashPackage(),
|
||||||
new RNFirebaseDatabasePackage(),
|
new RNFirebaseDatabasePackage(),
|
||||||
|
new RNFirebaseFirestorePackage(),
|
||||||
new RNFirebaseMessagingPackage(),
|
new RNFirebaseMessagingPackage(),
|
||||||
new RNFirebasePerformancePackage(),
|
new RNFirebasePerformancePackage(),
|
||||||
new RNFirebaseStoragePackage()
|
new RNFirebaseStoragePackage()
|
||||||
|
|
|
@ -69,6 +69,7 @@ pod 'Firebase/Auth'
|
||||||
pod 'Firebase/Crash'
|
pod 'Firebase/Crash'
|
||||||
pod 'Firebase/Database'
|
pod 'Firebase/Database'
|
||||||
pod 'Firebase/DynamicLinks'
|
pod 'Firebase/DynamicLinks'
|
||||||
|
pod 'Firebase/Firestore'
|
||||||
pod 'Firebase/Messaging'
|
pod 'Firebase/Messaging'
|
||||||
pod 'Firebase/RemoteConfig'
|
pod 'Firebase/RemoteConfig'
|
||||||
pod 'Firebase/Storage'
|
pod 'Firebase/Storage'
|
||||||
|
|
|
@ -2,7 +2,95 @@
|
||||||
|
|
||||||
## From v2 to v3
|
## From v2 to v3
|
||||||
|
|
||||||
<!-- TODO -->
|
The below is a quick summary of steps to take when migrating from v2 to v3 of RNFirebase. Please see the [v3 change log](https://github.com/invertase/react-native-firebase/releases/tag/v3.0.0) for detailed changes.
|
||||||
|
|
||||||
|
** Please note, we're now using `Apache License 2.0` to license this library. **
|
||||||
|
|
||||||
|
##### 1) Install the latest version of RNFirebase:
|
||||||
|
> `npm i react-native-firebase@latest --save`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
##### 2) Upgrade react-native version (only if you're currently lower than v0.48):
|
||||||
|
|
||||||
|
- Follow the instructions [here](https://facebook.github.io/react-native/docs/upgrading.html)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
##### 3) Update your JS code to reflect deprecations/breaking changes:
|
||||||
|
|
||||||
|
- ![#f03c15](https://placehold.it/15/f03c15/000000?text=+) **[breaking]** [app] `new RNFirebase()` is no longer supported. See below for information about app initialisation.
|
||||||
|
- ![#f03c15](https://placehold.it/15/fdfd96/000000?text=+) **[deprecated]** [app] `initializeApp()` for apps that are already initialised natively (i.e. the default app initialised via google-services plist/json) will now log a deprecation warning.
|
||||||
|
- As these apps are already initialised natively there's no need to call `initializeApp` in your JS code. For now, calling it will just return the app that's already internally initialised - in a future version this will throw an `already initialized` exception.
|
||||||
|
- Accessing apps can now be done the same way as the web sdk, simply call `firebase.app()` to get the default app, or with the name of specific app as the first arg, e.g. `const meow = firebase.app('catsApp');` to get a specific app.
|
||||||
|
- ![#f03c15](https://placehold.it/15/f03c15/000000?text=+) **[breaking]** [auth] Third party providers now user `providerId` rather than `provider` as per the Web SDK. If you are manually creating your credentials, you will need to update the field name.
|
||||||
|
- ![#f03c15](https://placehold.it/15/f03c15/000000?text=+) **[breaking]** [database] Error messages and codes internally re-written to match the web sdk
|
||||||
|
- ![#f03c15](https://placehold.it/15/f03c15/000000?text=+) **[breaking]** [database] `ref.isEqual` now checks the query modifiers as well as the ref path (was just path before). With the release of multi apps/core support this check now also includes whether the refs are for the same app.
|
||||||
|
- ![#f03c15](https://placehold.it/15/f03c15/000000?text=+) **[breaking]** [database] on/off behaviour changes. Previous `off` behaviour was incorrect. A `SyncTree/Repo` implementation was added to provide the correct behaviour you'd expect in the web sdk. Whilst this is a breaking change it shouldn't be much of an issue if you've previously setup your on/off handling correctly. See #160 for specifics of this change.
|
||||||
|
- ![#f03c15](https://placehold.it/15/f03c15/000000?text=+) **[breaking]** [storage] UploadTaskSnapshot -> `downloadUrl` renamed to `downloadURL` to match web sdk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
##### 4) Android - Update `android/build.gradle`:
|
||||||
|
|
||||||
|
|
||||||
|
- Check you are using google-services 3.1.0 or greater:
|
||||||
|
- You must add `maven { url 'https://maven.google.com' }` to your `android/build.gradle` as follows:
|
||||||
|
```groovy
|
||||||
|
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||||
|
|
||||||
|
buildscript {
|
||||||
|
repositories {
|
||||||
|
jcenter()
|
||||||
|
}
|
||||||
|
dependencies {
|
||||||
|
classpath 'com.android.tools.build:gradle:2.2.3'
|
||||||
|
classpath 'com.google.gms:google-services:3.1.0' // CHECK VERSION HERE
|
||||||
|
|
||||||
|
// NOTE: Do not place your application dependencies here; they belong
|
||||||
|
// in the individual module build.gradle files
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
allprojects {
|
||||||
|
repositories {
|
||||||
|
mavenLocal()
|
||||||
|
jcenter()
|
||||||
|
maven {
|
||||||
|
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
|
||||||
|
url "$rootDir/../node_modules/react-native/android"
|
||||||
|
}
|
||||||
|
// ADD THIS SECTION HERE
|
||||||
|
maven {
|
||||||
|
url 'https://maven.google.com'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
##### 5) Android - Update `app/build.gradle`:
|
||||||
|
|
||||||
|
|
||||||
|
- You must update all your Firebase dependencies to 11.4.2.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
##### 6) iOS - Update podfile:
|
||||||
|
|
||||||
|
- You need to check that you're running at least version 4.3.0 of the Firebase Pods
|
||||||
|
- Run `pod outdated`
|
||||||
|
- Run `pod update`
|
||||||
|
|
||||||
## From v1 to v2
|
## From v1 to v2
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,171 @@
|
||||||
|
|
||||||
|
# Firestore (Beta)
|
||||||
|
|
||||||
|
RNFirebase mimics the [Firestore Web SDK](https://firebase.google.com/docs/database/web/read-and-write), whilst
|
||||||
|
providing support for devices in low/no data connection state.
|
||||||
|
|
||||||
|
All Firestore operations are accessed via `firestore()`.
|
||||||
|
|
||||||
|
Please note that Persistence (offline support) is enabled by default with Firestore on iOS and Android.
|
||||||
|
|
||||||
|
## Add and Manage Data
|
||||||
|
|
||||||
|
### Collections
|
||||||
|
|
||||||
|
Read information about a collection example:
|
||||||
|
```javascript
|
||||||
|
firebase.firestore()
|
||||||
|
.collection('posts')
|
||||||
|
.get()
|
||||||
|
.then(querySnapshot => {
|
||||||
|
// Access all the documents in the collection
|
||||||
|
const docs = querySnapshot.docs;
|
||||||
|
// Access the list of document changes for the collection
|
||||||
|
const changes = querySnapshot.docChanges;
|
||||||
|
// Loop through the documents
|
||||||
|
querySnapshot.forEach((doc) => {
|
||||||
|
const value = doc.data();
|
||||||
|
})
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
Add to a collection example (generated ID):
|
||||||
|
```javascript
|
||||||
|
firebase.firestore()
|
||||||
|
.collection('posts')
|
||||||
|
.add({
|
||||||
|
title: 'Amazing post',
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
// Document added to collection and ID generated
|
||||||
|
// Will have path: `posts/{generatedId}`
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
Add to a collection example (manual ID):
|
||||||
|
```javascript
|
||||||
|
firebase.firestore()
|
||||||
|
.collection('posts')
|
||||||
|
.doc('post1')
|
||||||
|
.set({
|
||||||
|
title: 'My awesome post',
|
||||||
|
content: 'Some awesome content',
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
// Document added to collection with path: `posts/post1`
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### Documents
|
||||||
|
|
||||||
|
There are multiple ways to read a document. The following are equivalent examples:
|
||||||
|
```javascript
|
||||||
|
firebase.firestore()
|
||||||
|
.doc('posts/posts1')
|
||||||
|
.get((documentSnapshot) => {
|
||||||
|
const value = documentSnapshot.data();
|
||||||
|
});
|
||||||
|
|
||||||
|
firebase.firestore()
|
||||||
|
.collection('posts')
|
||||||
|
.doc('posts1')
|
||||||
|
.get((documentSnapshot) => {
|
||||||
|
const value = documentSnapshot.data();
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
Create a document example:
|
||||||
|
```javascript
|
||||||
|
firebase.firestore()
|
||||||
|
.doc('posts/posts1')
|
||||||
|
.set({
|
||||||
|
title: 'My awesome post',
|
||||||
|
content: 'Some awesome content',
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
// Document created
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
Updating a document example:
|
||||||
|
```javascript
|
||||||
|
firebase.firestore()
|
||||||
|
.doc('posts/posts1')
|
||||||
|
.update({
|
||||||
|
title: 'My awesome post',
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
// Document created
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
Deleting a document example:
|
||||||
|
```javascript
|
||||||
|
firebase.firestore()
|
||||||
|
.doc('posts/posts1')
|
||||||
|
.delete()
|
||||||
|
.then(() => {
|
||||||
|
// Document deleted
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Batching document updates
|
||||||
|
|
||||||
|
Writes, updates and deletes to documents can be batched and committed atomically as follows:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const ayRef = firebase.firestore().doc('places/AY');
|
||||||
|
const lRef = firebase.firestore().doc('places/LON');
|
||||||
|
const nycRef = firebase.firestore().doc('places/NYC');
|
||||||
|
const sfRef = firebase.firestore().doc('places/SF');
|
||||||
|
|
||||||
|
firebase.firestore()
|
||||||
|
.batch()
|
||||||
|
.set(ayRef, { name: 'Aylesbury' })
|
||||||
|
.set(lRef, { name: 'London' })
|
||||||
|
.set(nycRef, { name: 'New York City' })
|
||||||
|
.set(sfRef, { name: 'San Francisco' })
|
||||||
|
.update(nycRef, { population: 1000000 })
|
||||||
|
.update(sfRef, { name: 'San Fran' })
|
||||||
|
.set(lRef, { population: 3000000 }, { merge: true })
|
||||||
|
.delete(ayRef)
|
||||||
|
.commit()
|
||||||
|
.then(() => {
|
||||||
|
// Would end up with three documents in the collection: London, New York City and San Francisco
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Transactions
|
||||||
|
|
||||||
|
Coming soon
|
||||||
|
|
||||||
|
## Realtime Updates
|
||||||
|
|
||||||
|
### Collections
|
||||||
|
|
||||||
|
Listen to collection updates example:
|
||||||
|
```javascript
|
||||||
|
firebase.firestore()
|
||||||
|
.collection('cities')
|
||||||
|
.where('state', '==', 'CA')
|
||||||
|
.onSnapshot((querySnapshot) => {
|
||||||
|
querySnapshot.forEach((doc) => {
|
||||||
|
// DocumentSnapshot available
|
||||||
|
})
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
The snapshot handler will receive a new query snapshot every time the query results change (that is, when a document is added, removed, or modified).
|
||||||
|
|
||||||
|
### Documents
|
||||||
|
|
||||||
|
Listen to document updates example:
|
||||||
|
```javascript
|
||||||
|
firebase.firestore()
|
||||||
|
.doc('posts/post1')
|
||||||
|
.onSnapshot((documentSnapshot) => {
|
||||||
|
// DocumentSnapshot available
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
The snapshot handler will receive the current contents of the document, and any subsequent changes to the document.
|
|
@ -12,6 +12,9 @@
|
||||||
8323CF071F6FBD870071420B /* NativeExpressComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = 8323CF011F6FBD870071420B /* NativeExpressComponent.m */; };
|
8323CF071F6FBD870071420B /* NativeExpressComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = 8323CF011F6FBD870071420B /* NativeExpressComponent.m */; };
|
||||||
8323CF081F6FBD870071420B /* RNFirebaseAdMobBannerManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 8323CF031F6FBD870071420B /* RNFirebaseAdMobBannerManager.m */; };
|
8323CF081F6FBD870071420B /* RNFirebaseAdMobBannerManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 8323CF031F6FBD870071420B /* RNFirebaseAdMobBannerManager.m */; };
|
||||||
8323CF091F6FBD870071420B /* RNFirebaseAdMobNativeExpressManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 8323CF051F6FBD870071420B /* RNFirebaseAdMobNativeExpressManager.m */; };
|
8323CF091F6FBD870071420B /* RNFirebaseAdMobNativeExpressManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 8323CF051F6FBD870071420B /* RNFirebaseAdMobNativeExpressManager.m */; };
|
||||||
|
8376F7141F7C149100D45A85 /* RNFirebaseFirestoreDocumentReference.m in Sources */ = {isa = PBXBuildFile; fileRef = 8376F70E1F7C149000D45A85 /* RNFirebaseFirestoreDocumentReference.m */; };
|
||||||
|
8376F7151F7C149100D45A85 /* RNFirebaseFirestore.m in Sources */ = {isa = PBXBuildFile; fileRef = 8376F7101F7C149000D45A85 /* RNFirebaseFirestore.m */; };
|
||||||
|
8376F7161F7C149100D45A85 /* RNFirebaseFirestoreCollectionReference.m in Sources */ = {isa = PBXBuildFile; fileRef = 8376F7111F7C149000D45A85 /* RNFirebaseFirestoreCollectionReference.m */; };
|
||||||
839D916C1EF3E20B0077C7C8 /* RNFirebaseAdMob.m in Sources */ = {isa = PBXBuildFile; fileRef = 839D914F1EF3E20A0077C7C8 /* RNFirebaseAdMob.m */; };
|
839D916C1EF3E20B0077C7C8 /* RNFirebaseAdMob.m in Sources */ = {isa = PBXBuildFile; fileRef = 839D914F1EF3E20A0077C7C8 /* RNFirebaseAdMob.m */; };
|
||||||
839D916D1EF3E20B0077C7C8 /* RNFirebaseAdMobInterstitial.m in Sources */ = {isa = PBXBuildFile; fileRef = 839D91511EF3E20A0077C7C8 /* RNFirebaseAdMobInterstitial.m */; };
|
839D916D1EF3E20B0077C7C8 /* RNFirebaseAdMobInterstitial.m in Sources */ = {isa = PBXBuildFile; fileRef = 839D91511EF3E20A0077C7C8 /* RNFirebaseAdMobInterstitial.m */; };
|
||||||
839D916E1EF3E20B0077C7C8 /* RNFirebaseAdMobRewardedVideo.m in Sources */ = {isa = PBXBuildFile; fileRef = 839D91531EF3E20A0077C7C8 /* RNFirebaseAdMobRewardedVideo.m */; };
|
839D916E1EF3E20B0077C7C8 /* RNFirebaseAdMobRewardedVideo.m in Sources */ = {isa = PBXBuildFile; fileRef = 839D91531EF3E20A0077C7C8 /* RNFirebaseAdMobRewardedVideo.m */; };
|
||||||
|
@ -50,6 +53,12 @@
|
||||||
8323CF031F6FBD870071420B /* RNFirebaseAdMobBannerManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFirebaseAdMobBannerManager.m; sourceTree = "<group>"; };
|
8323CF031F6FBD870071420B /* RNFirebaseAdMobBannerManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFirebaseAdMobBannerManager.m; sourceTree = "<group>"; };
|
||||||
8323CF041F6FBD870071420B /* RNFirebaseAdMobNativeExpressManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNFirebaseAdMobNativeExpressManager.h; sourceTree = "<group>"; };
|
8323CF041F6FBD870071420B /* RNFirebaseAdMobNativeExpressManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNFirebaseAdMobNativeExpressManager.h; sourceTree = "<group>"; };
|
||||||
8323CF051F6FBD870071420B /* RNFirebaseAdMobNativeExpressManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFirebaseAdMobNativeExpressManager.m; sourceTree = "<group>"; };
|
8323CF051F6FBD870071420B /* RNFirebaseAdMobNativeExpressManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFirebaseAdMobNativeExpressManager.m; sourceTree = "<group>"; };
|
||||||
|
8376F70E1F7C149000D45A85 /* RNFirebaseFirestoreDocumentReference.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFirebaseFirestoreDocumentReference.m; sourceTree = "<group>"; };
|
||||||
|
8376F70F1F7C149000D45A85 /* RNFirebaseFirestore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNFirebaseFirestore.h; sourceTree = "<group>"; };
|
||||||
|
8376F7101F7C149000D45A85 /* RNFirebaseFirestore.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFirebaseFirestore.m; sourceTree = "<group>"; };
|
||||||
|
8376F7111F7C149000D45A85 /* RNFirebaseFirestoreCollectionReference.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFirebaseFirestoreCollectionReference.m; sourceTree = "<group>"; };
|
||||||
|
8376F7121F7C149000D45A85 /* RNFirebaseFirestoreDocumentReference.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNFirebaseFirestoreDocumentReference.h; sourceTree = "<group>"; };
|
||||||
|
8376F7131F7C149000D45A85 /* RNFirebaseFirestoreCollectionReference.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNFirebaseFirestoreCollectionReference.h; sourceTree = "<group>"; };
|
||||||
839D914E1EF3E20A0077C7C8 /* RNFirebaseAdMob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNFirebaseAdMob.h; sourceTree = "<group>"; };
|
839D914E1EF3E20A0077C7C8 /* RNFirebaseAdMob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNFirebaseAdMob.h; sourceTree = "<group>"; };
|
||||||
839D914F1EF3E20A0077C7C8 /* RNFirebaseAdMob.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFirebaseAdMob.m; sourceTree = "<group>"; };
|
839D914F1EF3E20A0077C7C8 /* RNFirebaseAdMob.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFirebaseAdMob.m; sourceTree = "<group>"; };
|
||||||
839D91501EF3E20A0077C7C8 /* RNFirebaseAdMobInterstitial.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNFirebaseAdMobInterstitial.h; sourceTree = "<group>"; };
|
839D91501EF3E20A0077C7C8 /* RNFirebaseAdMobInterstitial.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNFirebaseAdMobInterstitial.h; sourceTree = "<group>"; };
|
||||||
|
@ -105,6 +114,7 @@
|
||||||
839D915A1EF3E20A0077C7C8 /* config */,
|
839D915A1EF3E20A0077C7C8 /* config */,
|
||||||
839D915D1EF3E20A0077C7C8 /* crash */,
|
839D915D1EF3E20A0077C7C8 /* crash */,
|
||||||
839D91601EF3E20A0077C7C8 /* database */,
|
839D91601EF3E20A0077C7C8 /* database */,
|
||||||
|
8376F70D1F7C141500D45A85 /* firestore */,
|
||||||
839D91631EF3E20A0077C7C8 /* messaging */,
|
839D91631EF3E20A0077C7C8 /* messaging */,
|
||||||
839D91661EF3E20A0077C7C8 /* perf */,
|
839D91661EF3E20A0077C7C8 /* perf */,
|
||||||
839D91691EF3E20A0077C7C8 /* storage */,
|
839D91691EF3E20A0077C7C8 /* storage */,
|
||||||
|
@ -115,6 +125,20 @@
|
||||||
);
|
);
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
8376F70D1F7C141500D45A85 /* firestore */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
8376F70F1F7C149000D45A85 /* RNFirebaseFirestore.h */,
|
||||||
|
8376F7101F7C149000D45A85 /* RNFirebaseFirestore.m */,
|
||||||
|
8376F7131F7C149000D45A85 /* RNFirebaseFirestoreCollectionReference.h */,
|
||||||
|
8376F7111F7C149000D45A85 /* RNFirebaseFirestoreCollectionReference.m */,
|
||||||
|
8376F7121F7C149000D45A85 /* RNFirebaseFirestoreDocumentReference.h */,
|
||||||
|
8376F70E1F7C149000D45A85 /* RNFirebaseFirestoreDocumentReference.m */,
|
||||||
|
);
|
||||||
|
name = firestore;
|
||||||
|
path = RNFirebase/firestore;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
839D914D1EF3E20A0077C7C8 /* admob */ = {
|
839D914D1EF3E20A0077C7C8 /* admob */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
@ -277,9 +301,12 @@
|
||||||
files = (
|
files = (
|
||||||
839D916E1EF3E20B0077C7C8 /* RNFirebaseAdMobRewardedVideo.m in Sources */,
|
839D916E1EF3E20B0077C7C8 /* RNFirebaseAdMobRewardedVideo.m in Sources */,
|
||||||
839D916C1EF3E20B0077C7C8 /* RNFirebaseAdMob.m in Sources */,
|
839D916C1EF3E20B0077C7C8 /* RNFirebaseAdMob.m in Sources */,
|
||||||
|
8376F7161F7C149100D45A85 /* RNFirebaseFirestoreCollectionReference.m in Sources */,
|
||||||
839D91761EF3E20B0077C7C8 /* RNFirebaseStorage.m in Sources */,
|
839D91761EF3E20B0077C7C8 /* RNFirebaseStorage.m in Sources */,
|
||||||
|
8376F7151F7C149100D45A85 /* RNFirebaseFirestore.m in Sources */,
|
||||||
839D91701EF3E20B0077C7C8 /* RNFirebaseAuth.m in Sources */,
|
839D91701EF3E20B0077C7C8 /* RNFirebaseAuth.m in Sources */,
|
||||||
8323CF091F6FBD870071420B /* RNFirebaseAdMobNativeExpressManager.m in Sources */,
|
8323CF091F6FBD870071420B /* RNFirebaseAdMobNativeExpressManager.m in Sources */,
|
||||||
|
8376F7141F7C149100D45A85 /* RNFirebaseFirestoreDocumentReference.m in Sources */,
|
||||||
839D916F1EF3E20B0077C7C8 /* RNFirebaseAnalytics.m in Sources */,
|
839D916F1EF3E20B0077C7C8 /* RNFirebaseAnalytics.m in Sources */,
|
||||||
839D91711EF3E20B0077C7C8 /* RNFirebaseRemoteConfig.m in Sources */,
|
839D91711EF3E20B0077C7C8 /* RNFirebaseRemoteConfig.m in Sources */,
|
||||||
D950369E1D19C77400F7094D /* RNFirebase.m in Sources */,
|
D950369E1D19C77400F7094D /* RNFirebase.m in Sources */,
|
||||||
|
|
|
@ -17,6 +17,10 @@ static NSString *const DATABASE_CHILD_MODIFIED_EVENT = @"child_changed";
|
||||||
static NSString *const DATABASE_CHILD_REMOVED_EVENT = @"child_removed";
|
static NSString *const DATABASE_CHILD_REMOVED_EVENT = @"child_removed";
|
||||||
static NSString *const DATABASE_CHILD_MOVED_EVENT = @"child_moved";
|
static NSString *const DATABASE_CHILD_MOVED_EVENT = @"child_moved";
|
||||||
|
|
||||||
|
// Firestore
|
||||||
|
static NSString *const FIRESTORE_COLLECTION_SYNC_EVENT = @"firestore_collection_sync_event";
|
||||||
|
static NSString *const FIRESTORE_DOCUMENT_SYNC_EVENT = @"firestore_document_sync_event";
|
||||||
|
|
||||||
// Storage
|
// Storage
|
||||||
static NSString *const STORAGE_EVENT = @"storage_event";
|
static NSString *const STORAGE_EVENT = @"storage_event";
|
||||||
static NSString *const STORAGE_ERROR = @"storage_error";
|
static NSString *const STORAGE_ERROR = @"storage_error";
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
#ifndef RNFirebaseFirestore_h
|
||||||
|
#define RNFirebaseFirestore_h
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
#if __has_include(<FirebaseFirestore/FirebaseFirestore.h>)
|
||||||
|
|
||||||
|
#import <FirebaseFirestore/FirebaseFirestore.h>
|
||||||
|
#import <React/RCTBridgeModule.h>
|
||||||
|
#import <React/RCTEventEmitter.h>
|
||||||
|
|
||||||
|
@interface RNFirebaseFirestore : RCTEventEmitter <RCTBridgeModule> {}
|
||||||
|
|
||||||
|
+ (void)promiseRejectException:(RCTPromiseRejectBlock)reject error:(NSError *)error;
|
||||||
|
|
||||||
|
+ (FIRFirestore *)getFirestoreForApp:(NSString *)appName;
|
||||||
|
+ (NSDictionary *)getJSError:(NSError *)nativeError;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
#else
|
||||||
|
@interface RNFirebaseFirestore : NSObject
|
||||||
|
@end
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,223 @@
|
||||||
|
#import "RNFirebaseFirestore.h"
|
||||||
|
|
||||||
|
#if __has_include(<FirebaseFirestore/FirebaseFirestore.h>)
|
||||||
|
|
||||||
|
#import <Firebase.h>
|
||||||
|
#import "RNFirebaseEvents.h"
|
||||||
|
#import "RNFirebaseFirestoreCollectionReference.h"
|
||||||
|
#import "RNFirebaseFirestoreDocumentReference.h"
|
||||||
|
|
||||||
|
@implementation RNFirebaseFirestore
|
||||||
|
RCT_EXPORT_MODULE();
|
||||||
|
|
||||||
|
- (id)init {
|
||||||
|
self = [super init];
|
||||||
|
if (self != nil) {
|
||||||
|
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
RCT_EXPORT_METHOD(collectionGet:(NSString *) appName
|
||||||
|
path:(NSString *) path
|
||||||
|
filters:(NSArray *) filters
|
||||||
|
orders:(NSArray *) orders
|
||||||
|
options:(NSDictionary *) options
|
||||||
|
resolver:(RCTPromiseResolveBlock) resolve
|
||||||
|
rejecter:(RCTPromiseRejectBlock) reject) {
|
||||||
|
[[self getCollectionForAppPath:appName path:path filters:filters orders:orders options:options] get:resolve rejecter:reject];
|
||||||
|
}
|
||||||
|
|
||||||
|
RCT_EXPORT_METHOD(collectionOffSnapshot:(NSString *) appName
|
||||||
|
path:(NSString *) path
|
||||||
|
filters:(NSArray *) filters
|
||||||
|
orders:(NSArray *) orders
|
||||||
|
options:(NSDictionary *) options
|
||||||
|
listenerId:(nonnull NSString *) listenerId) {
|
||||||
|
[RNFirebaseFirestoreCollectionReference offSnapshot:listenerId];
|
||||||
|
}
|
||||||
|
|
||||||
|
RCT_EXPORT_METHOD(collectionOnSnapshot:(NSString *) appName
|
||||||
|
path:(NSString *) path
|
||||||
|
filters:(NSArray *) filters
|
||||||
|
orders:(NSArray *) orders
|
||||||
|
options:(NSDictionary *) options
|
||||||
|
listenerId:(nonnull NSString *) listenerId) {
|
||||||
|
RNFirebaseFirestoreCollectionReference *ref = [self getCollectionForAppPath:appName path:path filters:filters orders:orders options:options];
|
||||||
|
[ref onSnapshot:listenerId];
|
||||||
|
}
|
||||||
|
|
||||||
|
RCT_EXPORT_METHOD(documentBatch:(NSString *) appName
|
||||||
|
writes:(NSArray *) writes
|
||||||
|
commitOptions:(NSDictionary *) commitOptions
|
||||||
|
resolver:(RCTPromiseResolveBlock) resolve
|
||||||
|
rejecter:(RCTPromiseRejectBlock) reject) {
|
||||||
|
FIRFirestore *firestore = [RNFirebaseFirestore getFirestoreForApp:appName];
|
||||||
|
FIRWriteBatch *batch = [firestore batch];
|
||||||
|
|
||||||
|
for (NSDictionary *write in writes) {
|
||||||
|
NSString *type = write[@"type"];
|
||||||
|
NSString *path = write[@"path"];
|
||||||
|
NSDictionary *data = write[@"data"];
|
||||||
|
|
||||||
|
FIRDocumentReference *ref = [firestore documentWithPath:path];
|
||||||
|
|
||||||
|
if ([type isEqualToString:@"DELETE"]) {
|
||||||
|
batch = [batch deleteDocument:ref];
|
||||||
|
} else if ([type isEqualToString:@"SET"]) {
|
||||||
|
NSDictionary *options = write[@"options"];
|
||||||
|
if (options && options[@"merge"]) {
|
||||||
|
batch = [batch setData:data forDocument:ref options:[FIRSetOptions merge]];
|
||||||
|
} else {
|
||||||
|
batch = [batch setData:data forDocument:ref];
|
||||||
|
}
|
||||||
|
} else if ([type isEqualToString:@"UPDATE"]) {
|
||||||
|
batch = [batch updateData:data forDocument:ref];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[batch commitWithCompletion:^(NSError * _Nullable error) {
|
||||||
|
if (error) {
|
||||||
|
[RNFirebaseFirestore promiseRejectException:reject error:error];
|
||||||
|
} else {
|
||||||
|
NSMutableArray *result = [[NSMutableArray alloc] init];
|
||||||
|
for (NSDictionary *write in writes) {
|
||||||
|
// Missing fields from web SDK
|
||||||
|
// writeTime
|
||||||
|
[result addObject:@{}];
|
||||||
|
}
|
||||||
|
resolve(result);
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
RCT_EXPORT_METHOD(documentCollections:(NSString *) appName
|
||||||
|
path:(NSString *) path
|
||||||
|
resolver:(RCTPromiseResolveBlock) resolve
|
||||||
|
rejecter:(RCTPromiseRejectBlock) reject) {
|
||||||
|
[[self getDocumentForAppPath:appName path:path] get:resolve rejecter:reject];
|
||||||
|
}
|
||||||
|
|
||||||
|
RCT_EXPORT_METHOD(documentCreate:(NSString *) appName
|
||||||
|
path:(NSString *) path
|
||||||
|
data:(NSDictionary *) data
|
||||||
|
resolver:(RCTPromiseResolveBlock) resolve
|
||||||
|
rejecter:(RCTPromiseRejectBlock) reject) {
|
||||||
|
[[self getDocumentForAppPath:appName path:path] create:data resolver:resolve rejecter:reject];
|
||||||
|
}
|
||||||
|
|
||||||
|
RCT_EXPORT_METHOD(documentDelete:(NSString *) appName
|
||||||
|
path:(NSString *) path
|
||||||
|
options:(NSDictionary *) options
|
||||||
|
resolver:(RCTPromiseResolveBlock) resolve
|
||||||
|
rejecter:(RCTPromiseRejectBlock) reject) {
|
||||||
|
[[self getDocumentForAppPath:appName path:path] delete:options resolver:resolve rejecter:reject];
|
||||||
|
}
|
||||||
|
|
||||||
|
RCT_EXPORT_METHOD(documentGet:(NSString *) appName
|
||||||
|
path:(NSString *) path
|
||||||
|
resolver:(RCTPromiseResolveBlock) resolve
|
||||||
|
rejecter:(RCTPromiseRejectBlock) reject) {
|
||||||
|
[[self getDocumentForAppPath:appName path:path] get:resolve rejecter:reject];
|
||||||
|
}
|
||||||
|
|
||||||
|
RCT_EXPORT_METHOD(documentGetAll:(NSString *) appName
|
||||||
|
documents:(NSString *) documents
|
||||||
|
resolver:(RCTPromiseResolveBlock) resolve
|
||||||
|
rejecter:(RCTPromiseRejectBlock) reject) {
|
||||||
|
// Not supported on iOS out of the box
|
||||||
|
}
|
||||||
|
|
||||||
|
RCT_EXPORT_METHOD(documentOffSnapshot:(NSString *) appName
|
||||||
|
path:(NSString *) path
|
||||||
|
listenerId:(nonnull NSString *) listenerId) {
|
||||||
|
[RNFirebaseFirestoreDocumentReference offSnapshot:listenerId];
|
||||||
|
}
|
||||||
|
|
||||||
|
RCT_EXPORT_METHOD(documentOnSnapshot:(NSString *) appName
|
||||||
|
path:(NSString *) path
|
||||||
|
listenerId:(nonnull NSString *) listenerId) {
|
||||||
|
RNFirebaseFirestoreDocumentReference *ref = [self getDocumentForAppPath:appName path:path];
|
||||||
|
[ref onSnapshot:listenerId];
|
||||||
|
}
|
||||||
|
|
||||||
|
RCT_EXPORT_METHOD(documentSet:(NSString *) appName
|
||||||
|
path:(NSString *) path
|
||||||
|
data:(NSDictionary *) data
|
||||||
|
options:(NSDictionary *) options
|
||||||
|
resolver:(RCTPromiseResolveBlock) resolve
|
||||||
|
rejecter:(RCTPromiseRejectBlock) reject) {
|
||||||
|
[[self getDocumentForAppPath:appName path:path] set:data options:options resolver:resolve rejecter:reject];
|
||||||
|
}
|
||||||
|
|
||||||
|
RCT_EXPORT_METHOD(documentUpdate:(NSString *) appName
|
||||||
|
path:(NSString *) path
|
||||||
|
data:(NSDictionary *) data
|
||||||
|
resolver:(RCTPromiseResolveBlock) resolve
|
||||||
|
rejecter:(RCTPromiseRejectBlock) reject) {
|
||||||
|
[[self getDocumentForAppPath:appName path:path] update:data resolver:resolve rejecter:reject];
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* INTERNALS/UTILS
|
||||||
|
*/
|
||||||
|
+ (void)promiseRejectException:(RCTPromiseRejectBlock)reject error:(NSError *)error {
|
||||||
|
NSDictionary *jsError = [RNFirebaseFirestore getJSError:error];
|
||||||
|
reject([jsError valueForKey:@"code"], [jsError valueForKey:@"message"], error);
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (FIRFirestore *)getFirestoreForApp:(NSString *)appName {
|
||||||
|
FIRApp *app = [FIRApp appNamed:appName];
|
||||||
|
return [FIRFirestore firestoreForApp:app];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (RNFirebaseFirestoreCollectionReference *)getCollectionForAppPath:(NSString *)appName path:(NSString *)path filters:(NSArray *)filters orders:(NSArray *)orders options:(NSDictionary *)options {
|
||||||
|
return [[RNFirebaseFirestoreCollectionReference alloc] initWithPathAndModifiers:self app:appName path:path filters:filters orders:orders options:options];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (RNFirebaseFirestoreDocumentReference *)getDocumentForAppPath:(NSString *)appName path:(NSString *)path {
|
||||||
|
return [[RNFirebaseFirestoreDocumentReference alloc] initWithPath:self app:appName path:path];
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Move to error util for use in other modules
|
||||||
|
+ (NSString *)getMessageWithService:(NSString *)message service:(NSString *)service fullCode:(NSString *)fullCode {
|
||||||
|
return [NSString stringWithFormat:@"%@: %@ (%@).", service, message, [fullCode lowercaseString]];
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (NSString *)getCodeWithService:(NSString *)service code:(NSString *)code {
|
||||||
|
return [NSString stringWithFormat:@"%@/%@", [service lowercaseString], [code lowercaseString]];
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (NSDictionary *)getJSError:(NSError *)nativeError {
|
||||||
|
NSMutableDictionary *errorMap = [[NSMutableDictionary alloc] init];
|
||||||
|
[errorMap setValue:@(nativeError.code) forKey:@"nativeErrorCode"];
|
||||||
|
[errorMap setValue:[nativeError localizedDescription] forKey:@"nativeErrorMessage"];
|
||||||
|
|
||||||
|
NSString *code;
|
||||||
|
NSString *message;
|
||||||
|
NSString *service = @"Firestore";
|
||||||
|
|
||||||
|
// TODO: Proper error codes
|
||||||
|
switch (nativeError.code) {
|
||||||
|
default:
|
||||||
|
code = [RNFirebaseFirestore getCodeWithService:service code:@"unknown"];
|
||||||
|
message = [RNFirebaseFirestore getMessageWithService:@"An unknown error occurred." service:service fullCode:code];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
[errorMap setValue:code forKey:@"code"];
|
||||||
|
[errorMap setValue:message forKey:@"message"];
|
||||||
|
|
||||||
|
return errorMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSArray<NSString *> *)supportedEvents {
|
||||||
|
return @[FIRESTORE_COLLECTION_SYNC_EVENT, FIRESTORE_DOCUMENT_SYNC_EVENT];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
#else
|
||||||
|
@implementation RNFirebaseFirestore
|
||||||
|
@end
|
||||||
|
#endif
|
|
@ -0,0 +1,35 @@
|
||||||
|
#ifndef RNFirebaseFirestoreCollectionReference_h
|
||||||
|
#define RNFirebaseFirestoreCollectionReference_h
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
#if __has_include(<FirebaseFirestore/FirebaseFirestore.h>)
|
||||||
|
|
||||||
|
#import <FirebaseFirestore/FirebaseFirestore.h>
|
||||||
|
#import <React/RCTEventEmitter.h>
|
||||||
|
#import "RNFirebaseEvents.h"
|
||||||
|
#import "RNFirebaseFirestore.h"
|
||||||
|
#import "RNFirebaseFirestoreDocumentReference.h"
|
||||||
|
|
||||||
|
@interface RNFirebaseFirestoreCollectionReference : NSObject
|
||||||
|
@property RCTEventEmitter *emitter;
|
||||||
|
@property NSString *app;
|
||||||
|
@property NSString *path;
|
||||||
|
@property NSArray *filters;
|
||||||
|
@property NSArray *orders;
|
||||||
|
@property NSDictionary *options;
|
||||||
|
@property FIRQuery *query;
|
||||||
|
|
||||||
|
- (id)initWithPathAndModifiers:(RCTEventEmitter *)emitter app:(NSString *)app path:(NSString *)path filters:(NSArray *)filters orders:(NSArray *)orders options:(NSDictionary *)options;
|
||||||
|
- (void)get:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject;
|
||||||
|
+ (void)offSnapshot:(NSString *)listenerId;
|
||||||
|
- (void)onSnapshot:(NSString *)listenerId;
|
||||||
|
+ (NSDictionary *)snapshotToDictionary:(FIRQuerySnapshot *)querySnapshot;
|
||||||
|
@end
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
@interface RNFirebaseFirestoreCollectionReference : NSObject
|
||||||
|
@end
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,201 @@
|
||||||
|
#import "RNFirebaseFirestoreCollectionReference.h"
|
||||||
|
|
||||||
|
@implementation RNFirebaseFirestoreCollectionReference
|
||||||
|
|
||||||
|
#if __has_include(<FirebaseFirestore/FirebaseFirestore.h>)
|
||||||
|
|
||||||
|
static NSMutableDictionary *_listeners;
|
||||||
|
|
||||||
|
- (id)initWithPathAndModifiers:(RCTEventEmitter *) emitter
|
||||||
|
app:(NSString *) app
|
||||||
|
path:(NSString *) path
|
||||||
|
filters:(NSArray *) filters
|
||||||
|
orders:(NSArray *) orders
|
||||||
|
options:(NSDictionary *) options {
|
||||||
|
self = [super init];
|
||||||
|
if (self) {
|
||||||
|
_emitter = emitter;
|
||||||
|
_app = app;
|
||||||
|
_path = path;
|
||||||
|
_filters = filters;
|
||||||
|
_orders = orders;
|
||||||
|
_options = options;
|
||||||
|
_query = [self buildQuery];
|
||||||
|
}
|
||||||
|
// Initialise the static listeners object if required
|
||||||
|
if (!_listeners) {
|
||||||
|
_listeners = [[NSMutableDictionary alloc] init];
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)get:(RCTPromiseResolveBlock) resolve
|
||||||
|
rejecter:(RCTPromiseRejectBlock) reject {
|
||||||
|
[_query getDocumentsWithCompletion:^(FIRQuerySnapshot * _Nullable snapshot, NSError * _Nullable error) {
|
||||||
|
if (error) {
|
||||||
|
[RNFirebaseFirestore promiseRejectException:reject error:error];
|
||||||
|
} else {
|
||||||
|
NSDictionary *data = [RNFirebaseFirestoreCollectionReference snapshotToDictionary:snapshot];
|
||||||
|
resolve(data);
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (void)offSnapshot:(NSString *) listenerId {
|
||||||
|
id<FIRListenerRegistration> listener = _listeners[listenerId];
|
||||||
|
if (listener) {
|
||||||
|
[_listeners removeObjectForKey:listenerId];
|
||||||
|
[listener remove];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)onSnapshot:(NSString *) listenerId {
|
||||||
|
if (_listeners[listenerId] == nil) {
|
||||||
|
id listenerBlock = ^(FIRQuerySnapshot * _Nullable snapshot, NSError * _Nullable error) {
|
||||||
|
if (error) {
|
||||||
|
id<FIRListenerRegistration> listener = _listeners[listenerId];
|
||||||
|
if (listener) {
|
||||||
|
[_listeners removeObjectForKey:listenerId];
|
||||||
|
[listener remove];
|
||||||
|
}
|
||||||
|
[self handleQuerySnapshotError:listenerId error:error];
|
||||||
|
} else {
|
||||||
|
[self handleQuerySnapshotEvent:listenerId querySnapshot:snapshot];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
id<FIRListenerRegistration> listener = [_query addSnapshotListener:listenerBlock];
|
||||||
|
_listeners[listenerId] = listener;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (FIRQuery *)buildQuery {
|
||||||
|
FIRQuery *query = (FIRQuery*)[[RNFirebaseFirestore getFirestoreForApp:_app] collectionWithPath:_path];
|
||||||
|
query = [self applyFilters:query];
|
||||||
|
query = [self applyOrders:query];
|
||||||
|
query = [self applyOptions:query];
|
||||||
|
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (FIRQuery *)applyFilters:(FIRQuery *) query {
|
||||||
|
for (NSDictionary *filter in _filters) {
|
||||||
|
NSString *fieldPath = filter[@"fieldPath"];
|
||||||
|
NSString *operator = filter[@"operator"];
|
||||||
|
// TODO: Validate this works
|
||||||
|
id value = filter[@"value"];
|
||||||
|
|
||||||
|
if ([operator isEqualToString:@"EQUAL"]) {
|
||||||
|
query = [query queryWhereField:fieldPath isEqualTo:value];
|
||||||
|
} else if ([operator isEqualToString:@"GREATER_THAN"]) {
|
||||||
|
query = [query queryWhereField:fieldPath isGreaterThan:value];
|
||||||
|
} else if ([operator isEqualToString:@"GREATER_THAN_OR_EQUAL"]) {
|
||||||
|
query = [query queryWhereField:fieldPath isGreaterThanOrEqualTo:value];
|
||||||
|
} else if ([operator isEqualToString:@"LESS_THAN"]) {
|
||||||
|
query = [query queryWhereField:fieldPath isLessThan:value];
|
||||||
|
} else if ([operator isEqualToString:@"LESS_THAN_OR_EQUAL"]) {
|
||||||
|
query = [query queryWhereField:fieldPath isLessThanOrEqualTo:value];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (FIRQuery *)applyOrders:(FIRQuery *) query {
|
||||||
|
for (NSDictionary *order in _orders) {
|
||||||
|
NSString *direction = order[@"direction"];
|
||||||
|
NSString *fieldPath = order[@"fieldPath"];
|
||||||
|
|
||||||
|
query = [query queryOrderedByField:fieldPath descending:([direction isEqualToString:@"DESCENDING"])];
|
||||||
|
}
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (FIRQuery *)applyOptions:(FIRQuery *) query {
|
||||||
|
if (_options[@"endAt"]) {
|
||||||
|
query = [query queryEndingAtValues:_options[@"endAt"]];
|
||||||
|
}
|
||||||
|
if (_options[@"endBefore"]) {
|
||||||
|
query = [query queryEndingBeforeValues:_options[@"endBefore"]];
|
||||||
|
}
|
||||||
|
if (_options[@"offset"]) {
|
||||||
|
// iOS doesn't support offset
|
||||||
|
}
|
||||||
|
if (_options[@"selectFields"]) {
|
||||||
|
// iOS doesn't support selectFields
|
||||||
|
}
|
||||||
|
if (_options[@"startAfter"]) {
|
||||||
|
query = [query queryStartingAfterValues:_options[@"startAfter"]];
|
||||||
|
}
|
||||||
|
if (_options[@"startAt"]) {
|
||||||
|
query = [query queryStartingAtValues:_options[@"startAt"]];
|
||||||
|
}
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)handleQuerySnapshotError:(NSString *)listenerId
|
||||||
|
error:(NSError *)error {
|
||||||
|
NSMutableDictionary *event = [[NSMutableDictionary alloc] init];
|
||||||
|
[event setValue:_app forKey:@"appName"];
|
||||||
|
[event setValue:_path forKey:@"path"];
|
||||||
|
[event setValue:listenerId forKey:@"listenerId"];
|
||||||
|
[event setValue:[RNFirebaseFirestore getJSError:error] forKey:@"error"];
|
||||||
|
|
||||||
|
[_emitter sendEventWithName:FIRESTORE_COLLECTION_SYNC_EVENT body:event];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)handleQuerySnapshotEvent:(NSString *)listenerId
|
||||||
|
querySnapshot:(FIRQuerySnapshot *)querySnapshot {
|
||||||
|
NSMutableDictionary *event = [[NSMutableDictionary alloc] init];
|
||||||
|
[event setValue:_app forKey:@"appName"];
|
||||||
|
[event setValue:_path forKey:@"path"];
|
||||||
|
[event setValue:listenerId forKey:@"listenerId"];
|
||||||
|
[event setValue:[RNFirebaseFirestoreCollectionReference snapshotToDictionary:querySnapshot] forKey:@"querySnapshot"];
|
||||||
|
|
||||||
|
[_emitter sendEventWithName:FIRESTORE_COLLECTION_SYNC_EVENT body:event];
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (NSDictionary *)snapshotToDictionary:(FIRQuerySnapshot *)querySnapshot {
|
||||||
|
NSMutableDictionary *snapshot = [[NSMutableDictionary alloc] init];
|
||||||
|
[snapshot setValue:[self documentChangesToArray:querySnapshot.documentChanges] forKey:@"changes"];
|
||||||
|
[snapshot setValue:[self documentSnapshotsToArray:querySnapshot.documents] forKey:@"documents"];
|
||||||
|
|
||||||
|
return snapshot;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (NSArray *)documentChangesToArray:(NSArray<FIRDocumentChange *> *) documentChanges {
|
||||||
|
NSMutableArray *changes = [[NSMutableArray alloc] init];
|
||||||
|
for (FIRDocumentChange *change in documentChanges) {
|
||||||
|
[changes addObject:[self documentChangeToDictionary:change]];
|
||||||
|
}
|
||||||
|
|
||||||
|
return changes;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (NSDictionary *)documentChangeToDictionary:(FIRDocumentChange *)documentChange {
|
||||||
|
NSMutableDictionary *change = [[NSMutableDictionary alloc] init];
|
||||||
|
[change setValue:[RNFirebaseFirestoreDocumentReference snapshotToDictionary:documentChange.document] forKey:@"document"];
|
||||||
|
[change setValue:@(documentChange.newIndex) forKey:@"newIndex"];
|
||||||
|
[change setValue:@(documentChange.oldIndex) forKey:@"oldIndex"];
|
||||||
|
|
||||||
|
if (documentChange.type == FIRDocumentChangeTypeAdded) {
|
||||||
|
[change setValue:@"added" forKey:@"type"];
|
||||||
|
} else if (documentChange.type == FIRDocumentChangeTypeRemoved) {
|
||||||
|
[change setValue:@"removed" forKey:@"type"];
|
||||||
|
} else if (documentChange.type == FIRDocumentChangeTypeModified) {
|
||||||
|
[change setValue:@"modified" forKey:@"type"];
|
||||||
|
}
|
||||||
|
|
||||||
|
return change;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (NSArray *)documentSnapshotsToArray:(NSArray<FIRDocumentSnapshot *> *) documentSnapshots {
|
||||||
|
NSMutableArray *snapshots = [[NSMutableArray alloc] init];
|
||||||
|
for (FIRDocumentSnapshot *snapshot in documentSnapshots) {
|
||||||
|
[snapshots addObject:[RNFirebaseFirestoreDocumentReference snapshotToDictionary:snapshot]];
|
||||||
|
}
|
||||||
|
|
||||||
|
return snapshots;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
@end
|
|
@ -0,0 +1,38 @@
|
||||||
|
#ifndef RNFirebaseFirestoreDocumentReference_h
|
||||||
|
#define RNFirebaseFirestoreDocumentReference_h
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
#if __has_include(<FirebaseFirestore/FirebaseFirestore.h>)
|
||||||
|
|
||||||
|
#import <FirebaseFirestore/FirebaseFirestore.h>
|
||||||
|
#import <React/RCTEventEmitter.h>
|
||||||
|
#import "RNFirebaseEvents.h"
|
||||||
|
#import "RNFirebaseFirestore.h"
|
||||||
|
|
||||||
|
@interface RNFirebaseFirestoreDocumentReference : NSObject
|
||||||
|
@property RCTEventEmitter *emitter;
|
||||||
|
@property NSString *app;
|
||||||
|
@property NSString *path;
|
||||||
|
@property FIRDocumentReference *ref;
|
||||||
|
|
||||||
|
- (id)initWithPath:(RCTEventEmitter *)emitter app:(NSString *)app path:(NSString *)path;
|
||||||
|
- (void)collections:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject;
|
||||||
|
- (void)create:(NSDictionary *)data resolver:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject;
|
||||||
|
- (void)delete:(NSDictionary *)options resolver:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject;
|
||||||
|
- (void)get:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject;
|
||||||
|
+ (void)offSnapshot:(NSString *)listenerId;
|
||||||
|
- (void)onSnapshot:(NSString *)listenerId;
|
||||||
|
- (void)set:(NSDictionary *)data options:(NSDictionary *)options resolver:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject;
|
||||||
|
- (void)update:(NSDictionary *)data resolver:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject;
|
||||||
|
- (BOOL)hasListeners;
|
||||||
|
+ (NSDictionary *)snapshotToDictionary:(FIRDocumentSnapshot *)documentSnapshot;
|
||||||
|
@end
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
@interface RNFirebaseFirestoreDocumentReference : NSObject
|
||||||
|
@end
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,162 @@
|
||||||
|
#import "RNFirebaseFirestoreDocumentReference.h"
|
||||||
|
|
||||||
|
@implementation RNFirebaseFirestoreDocumentReference
|
||||||
|
|
||||||
|
#if __has_include(<FirebaseFirestore/FirebaseFirestore.h>)
|
||||||
|
|
||||||
|
static NSMutableDictionary *_listeners;
|
||||||
|
|
||||||
|
- (id)initWithPath:(RCTEventEmitter *)emitter
|
||||||
|
app:(NSString *) app
|
||||||
|
path:(NSString *) path {
|
||||||
|
self = [super init];
|
||||||
|
if (self) {
|
||||||
|
_emitter = emitter;
|
||||||
|
_app = app;
|
||||||
|
_path = path;
|
||||||
|
_ref = [[RNFirebaseFirestore getFirestoreForApp:_app] documentWithPath:_path];
|
||||||
|
}
|
||||||
|
// Initialise the static listeners object if required
|
||||||
|
if (!_listeners) {
|
||||||
|
_listeners = [[NSMutableDictionary alloc] init];
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)collections:(RCTPromiseResolveBlock) resolve
|
||||||
|
rejecter:(RCTPromiseRejectBlock) reject {
|
||||||
|
// Not supported on iOS
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)create:(NSDictionary *) data
|
||||||
|
resolver:(RCTPromiseResolveBlock) resolve
|
||||||
|
rejecter:(RCTPromiseRejectBlock) reject {
|
||||||
|
// Not supported on iOS out of the box
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)delete:(NSDictionary *)options
|
||||||
|
resolver:(RCTPromiseResolveBlock) resolve
|
||||||
|
rejecter:(RCTPromiseRejectBlock) reject {
|
||||||
|
[_ref deleteDocumentWithCompletion:^(NSError * _Nullable error) {
|
||||||
|
[RNFirebaseFirestoreDocumentReference handleWriteResponse:error resolver:resolve rejecter:reject];
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)get:(RCTPromiseResolveBlock) resolve
|
||||||
|
rejecter:(RCTPromiseRejectBlock) reject {
|
||||||
|
[_ref getDocumentWithCompletion:^(FIRDocumentSnapshot * _Nullable snapshot, NSError * _Nullable error) {
|
||||||
|
if (error) {
|
||||||
|
[RNFirebaseFirestore promiseRejectException:reject error:error];
|
||||||
|
} else {
|
||||||
|
NSDictionary *data = [RNFirebaseFirestoreDocumentReference snapshotToDictionary:snapshot];
|
||||||
|
resolve(data);
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (void)offSnapshot:(NSString *) listenerId {
|
||||||
|
id<FIRListenerRegistration> listener = _listeners[listenerId];
|
||||||
|
if (listener) {
|
||||||
|
[_listeners removeObjectForKey:listenerId];
|
||||||
|
[listener remove];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)onSnapshot:(NSString *) listenerId {
|
||||||
|
if (_listeners[listenerId] == nil) {
|
||||||
|
id listenerBlock = ^(FIRDocumentSnapshot * _Nullable snapshot, NSError * _Nullable error) {
|
||||||
|
if (error) {
|
||||||
|
id<FIRListenerRegistration> listener = _listeners[listenerId];
|
||||||
|
if (listener) {
|
||||||
|
[_listeners removeObjectForKey:listenerId];
|
||||||
|
[listener remove];
|
||||||
|
}
|
||||||
|
[self handleDocumentSnapshotError:listenerId error:error];
|
||||||
|
} else {
|
||||||
|
[self handleDocumentSnapshotEvent:listenerId documentSnapshot:snapshot];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
id<FIRListenerRegistration> listener = [_ref addSnapshotListener:listenerBlock];
|
||||||
|
_listeners[listenerId] = listener;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)set:(NSDictionary *) data
|
||||||
|
options:(NSDictionary *) options
|
||||||
|
resolver:(RCTPromiseResolveBlock) resolve
|
||||||
|
rejecter:(RCTPromiseRejectBlock) reject {
|
||||||
|
if (options && options[@"merge"]) {
|
||||||
|
[_ref setData:data options:[FIRSetOptions merge] completion:^(NSError * _Nullable error) {
|
||||||
|
[RNFirebaseFirestoreDocumentReference handleWriteResponse:error resolver:resolve rejecter:reject];
|
||||||
|
}];
|
||||||
|
} else {
|
||||||
|
[_ref setData:data completion:^(NSError * _Nullable error) {
|
||||||
|
[RNFirebaseFirestoreDocumentReference handleWriteResponse:error resolver:resolve rejecter:reject];
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)update:(NSDictionary *) data
|
||||||
|
resolver:(RCTPromiseResolveBlock) resolve
|
||||||
|
rejecter:(RCTPromiseRejectBlock) reject {
|
||||||
|
[_ref updateData:data completion:^(NSError * _Nullable error) {
|
||||||
|
[RNFirebaseFirestoreDocumentReference handleWriteResponse:error resolver:resolve rejecter:reject];
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)hasListeners {
|
||||||
|
return [[_listeners allKeys] count] > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (void)handleWriteResponse:(NSError *) error
|
||||||
|
resolver:(RCTPromiseResolveBlock) resolve
|
||||||
|
rejecter:(RCTPromiseRejectBlock) reject {
|
||||||
|
if (error) {
|
||||||
|
[RNFirebaseFirestore promiseRejectException:reject error:error];
|
||||||
|
} else {
|
||||||
|
// Missing fields from web SDK
|
||||||
|
// writeTime
|
||||||
|
resolve(@{});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (NSDictionary *)snapshotToDictionary:(FIRDocumentSnapshot *)documentSnapshot {
|
||||||
|
NSMutableDictionary *snapshot = [[NSMutableDictionary alloc] init];
|
||||||
|
[snapshot setValue:documentSnapshot.reference.path forKey:@"path"];
|
||||||
|
if (documentSnapshot.exists) {
|
||||||
|
[snapshot setValue:documentSnapshot.data forKey:@"data"];
|
||||||
|
}
|
||||||
|
// Missing fields from web SDK
|
||||||
|
// createTime
|
||||||
|
// readTime
|
||||||
|
// updateTime
|
||||||
|
|
||||||
|
return snapshot;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)handleDocumentSnapshotError:(NSString *)listenerId
|
||||||
|
error:(NSError *)error {
|
||||||
|
NSMutableDictionary *event = [[NSMutableDictionary alloc] init];
|
||||||
|
[event setValue:_app forKey:@"appName"];
|
||||||
|
[event setValue:_path forKey:@"path"];
|
||||||
|
[event setValue:listenerId forKey:@"listenerId"];
|
||||||
|
[event setValue:[RNFirebaseFirestore getJSError:error] forKey:@"error"];
|
||||||
|
|
||||||
|
[_emitter sendEventWithName:FIRESTORE_DOCUMENT_SYNC_EVENT body:event];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)handleDocumentSnapshotEvent:(NSString *)listenerId
|
||||||
|
documentSnapshot:(FIRDocumentSnapshot *)documentSnapshot {
|
||||||
|
NSMutableDictionary *event = [[NSMutableDictionary alloc] init];
|
||||||
|
[event setValue:_app forKey:@"appName"];
|
||||||
|
[event setValue:_path forKey:@"path"];
|
||||||
|
[event setValue:listenerId forKey:@"listenerId"];
|
||||||
|
[event setValue:[RNFirebaseFirestoreDocumentReference snapshotToDictionary:documentSnapshot] forKey:@"documentSnapshot"];
|
||||||
|
|
||||||
|
[_emitter sendEventWithName:FIRESTORE_DOCUMENT_SYNC_EVENT body:event];
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
@end
|
|
@ -12,6 +12,7 @@ import RemoteConfig from './modules/config';
|
||||||
import Storage, { statics as StorageStatics } from './modules/storage';
|
import Storage, { statics as StorageStatics } from './modules/storage';
|
||||||
import Database, { statics as DatabaseStatics } from './modules/database';
|
import Database, { statics as DatabaseStatics } from './modules/database';
|
||||||
import Messaging, { statics as MessagingStatics } from './modules/messaging';
|
import Messaging, { statics as MessagingStatics } from './modules/messaging';
|
||||||
|
import Firestore, { statics as FirestoreStatics } from './modules/firestore';
|
||||||
|
|
||||||
const FirebaseCoreModule = NativeModules.RNFirebase;
|
const FirebaseCoreModule = NativeModules.RNFirebase;
|
||||||
|
|
||||||
|
@ -32,6 +33,7 @@ export default class FirebaseApp {
|
||||||
this.config = this._staticsOrModuleInstance({}, RemoteConfig);
|
this.config = this._staticsOrModuleInstance({}, RemoteConfig);
|
||||||
this.crash = this._staticsOrModuleInstance({}, Crash);
|
this.crash = this._staticsOrModuleInstance({}, Crash);
|
||||||
this.database = this._staticsOrModuleInstance(DatabaseStatics, Database);
|
this.database = this._staticsOrModuleInstance(DatabaseStatics, Database);
|
||||||
|
this.firestore = this._staticsOrModuleInstance(FirestoreStatics, Firestore);
|
||||||
this.messaging = this._staticsOrModuleInstance(MessagingStatics, Messaging);
|
this.messaging = this._staticsOrModuleInstance(MessagingStatics, Messaging);
|
||||||
this.perf = this._staticsOrModuleInstance({}, Performance);
|
this.perf = this._staticsOrModuleInstance({}, Performance);
|
||||||
this.storage = this._staticsOrModuleInstance(StorageStatics, Storage);
|
this.storage = this._staticsOrModuleInstance(StorageStatics, Storage);
|
||||||
|
|
|
@ -20,6 +20,7 @@ import RemoteConfig from './modules/config';
|
||||||
import Storage, { statics as StorageStatics } from './modules/storage';
|
import Storage, { statics as StorageStatics } from './modules/storage';
|
||||||
import Database, { statics as DatabaseStatics } from './modules/database';
|
import Database, { statics as DatabaseStatics } from './modules/database';
|
||||||
import Messaging, { statics as MessagingStatics } from './modules/messaging';
|
import Messaging, { statics as MessagingStatics } from './modules/messaging';
|
||||||
|
import Firestore, { statics as FirestoreStatics } from './modules/firestore';
|
||||||
|
|
||||||
const FirebaseCoreModule = NativeModules.RNFirebase;
|
const FirebaseCoreModule = NativeModules.RNFirebase;
|
||||||
|
|
||||||
|
@ -47,6 +48,7 @@ class FirebaseCore {
|
||||||
this.config = this._appNamespaceOrStatics({}, RemoteConfig);
|
this.config = this._appNamespaceOrStatics({}, RemoteConfig);
|
||||||
this.crash = this._appNamespaceOrStatics({}, Crash);
|
this.crash = this._appNamespaceOrStatics({}, Crash);
|
||||||
this.database = this._appNamespaceOrStatics(DatabaseStatics, Database);
|
this.database = this._appNamespaceOrStatics(DatabaseStatics, Database);
|
||||||
|
this.firestore = this._appNamespaceOrStatics(FirestoreStatics, Firestore);
|
||||||
this.messaging = this._appNamespaceOrStatics(MessagingStatics, Messaging);
|
this.messaging = this._appNamespaceOrStatics(MessagingStatics, Messaging);
|
||||||
this.perf = this._appNamespaceOrStatics(DatabaseStatics, Performance);
|
this.perf = this._appNamespaceOrStatics(DatabaseStatics, Performance);
|
||||||
this.storage = this._appNamespaceOrStatics(StorageStatics, Storage);
|
this.storage = this._appNamespaceOrStatics(StorageStatics, Storage);
|
||||||
|
|
|
@ -0,0 +1,105 @@
|
||||||
|
/**
|
||||||
|
* @flow
|
||||||
|
* CollectionReference representation wrapper
|
||||||
|
*/
|
||||||
|
import DocumentReference from './DocumentReference';
|
||||||
|
import Path from './Path';
|
||||||
|
import Query from './Query';
|
||||||
|
import QuerySnapshot from './QuerySnapshot';
|
||||||
|
import { firestoreAutoId } from '../../utils';
|
||||||
|
|
||||||
|
import type { Direction, Operator } from './Query';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class CollectionReference
|
||||||
|
*/
|
||||||
|
export default class CollectionReference {
|
||||||
|
_collectionPath: Path;
|
||||||
|
_firestore: Object;
|
||||||
|
_query: Query;
|
||||||
|
|
||||||
|
constructor(firestore: Object, collectionPath: Path) {
|
||||||
|
this._collectionPath = collectionPath;
|
||||||
|
this._firestore = firestore;
|
||||||
|
this._query = new Query(firestore, collectionPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
get firestore(): Object {
|
||||||
|
return this._firestore;
|
||||||
|
}
|
||||||
|
|
||||||
|
get id(): string | null {
|
||||||
|
return this._collectionPath.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
get parent(): DocumentReference | null {
|
||||||
|
const parentPath = this._collectionPath.parent();
|
||||||
|
return parentPath ? new DocumentReference(this._firestore, parentPath) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
add(data: { [string]: any }): Promise<DocumentReference> {
|
||||||
|
const documentRef = this.doc();
|
||||||
|
return documentRef.set(data)
|
||||||
|
.then(() => Promise.resolve(documentRef));
|
||||||
|
}
|
||||||
|
|
||||||
|
doc(documentPath?: string): DocumentReference {
|
||||||
|
const newPath = documentPath || firestoreAutoId();
|
||||||
|
|
||||||
|
const path = this._collectionPath.child(newPath);
|
||||||
|
if (!path.isDocument) {
|
||||||
|
throw new Error('Argument "documentPath" must point to a document.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return new DocumentReference(this._firestore, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
// From Query
|
||||||
|
endAt(fieldValues: any): Query {
|
||||||
|
return this._query.endAt(fieldValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
endBefore(fieldValues: any): Query {
|
||||||
|
return this._query.endBefore(fieldValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
get(): Promise<QuerySnapshot> {
|
||||||
|
return this._query.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
limit(n: number): Query {
|
||||||
|
return this._query.limit(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
offset(n: number): Query {
|
||||||
|
return this._query.offset(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
onSnapshot(onNext: () => any, onError?: () => any): () => void {
|
||||||
|
return this._query.onSnapshot(onNext, onError);
|
||||||
|
}
|
||||||
|
|
||||||
|
orderBy(fieldPath: string, directionStr?: Direction): Query {
|
||||||
|
return this._query.orderBy(fieldPath, directionStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
select(varArgs: string[]): Query {
|
||||||
|
return this._query.select(varArgs);
|
||||||
|
}
|
||||||
|
|
||||||
|
startAfter(fieldValues: any): Query {
|
||||||
|
return this._query.startAfter(fieldValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
startAt(fieldValues: any): Query {
|
||||||
|
return this._query.startAt(fieldValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
stream(): Stream<DocumentSnapshot> {
|
||||||
|
return this._query.stream();
|
||||||
|
}
|
||||||
|
|
||||||
|
where(fieldPath: string, opStr: Operator, value: any): Query {
|
||||||
|
return this._query.where(fieldPath, opStr, value);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
/**
|
||||||
|
* @flow
|
||||||
|
* DocumentChange representation wrapper
|
||||||
|
*/
|
||||||
|
import DocumentSnapshot from './DocumentSnapshot';
|
||||||
|
|
||||||
|
|
||||||
|
export type DocumentChangeNativeData = {
|
||||||
|
document: DocumentSnapshot,
|
||||||
|
newIndex: number,
|
||||||
|
oldIndex: number,
|
||||||
|
type: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class DocumentChange
|
||||||
|
*/
|
||||||
|
export default class DocumentChange {
|
||||||
|
_document: DocumentSnapshot;
|
||||||
|
_newIndex: number;
|
||||||
|
_oldIndex: number;
|
||||||
|
_type: string;
|
||||||
|
|
||||||
|
constructor(nativeData: DocumentChangeNativeData) {
|
||||||
|
this._document = nativeData.document;
|
||||||
|
this._newIndex = nativeData.newIndex;
|
||||||
|
this._oldIndex = nativeData.oldIndex;
|
||||||
|
this._type = nativeData.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
get doc(): DocumentSnapshot {
|
||||||
|
return this._document;
|
||||||
|
}
|
||||||
|
|
||||||
|
get newIndex(): number {
|
||||||
|
return this._newIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
get oldIndex(): number {
|
||||||
|
return this._oldIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
get type(): string {
|
||||||
|
return this._type;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,146 @@
|
||||||
|
/**
|
||||||
|
* @flow
|
||||||
|
* DocumentReference representation wrapper
|
||||||
|
*/
|
||||||
|
import CollectionReference from './CollectionReference';
|
||||||
|
import DocumentSnapshot from './DocumentSnapshot';
|
||||||
|
import Path from './Path';
|
||||||
|
import INTERNALS from './../../internals';
|
||||||
|
import { firestoreAutoId } from '../../utils';
|
||||||
|
|
||||||
|
export type DeleteOptions = {
|
||||||
|
lastUpdateTime?: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type WriteOptions = {
|
||||||
|
merge?: boolean,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type WriteResult = {
|
||||||
|
writeTime: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class DocumentReference
|
||||||
|
*/
|
||||||
|
export default class DocumentReference {
|
||||||
|
_documentPath: Path;
|
||||||
|
_firestore: Object;
|
||||||
|
|
||||||
|
constructor(firestore: Object, documentPath: Path) {
|
||||||
|
this._documentPath = documentPath;
|
||||||
|
this._firestore = firestore;
|
||||||
|
}
|
||||||
|
|
||||||
|
get firestore(): Object {
|
||||||
|
return this._firestore;
|
||||||
|
}
|
||||||
|
|
||||||
|
get id(): string | null {
|
||||||
|
return this._documentPath.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
get parent(): CollectionReference | null {
|
||||||
|
const parentPath = this._documentPath.parent();
|
||||||
|
return parentPath ? new CollectionReference(this._firestore, parentPath) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
get path(): string {
|
||||||
|
return this._documentPath.relativeName;
|
||||||
|
}
|
||||||
|
|
||||||
|
collection(collectionPath: string): CollectionReference {
|
||||||
|
const path = this._documentPath.child(collectionPath);
|
||||||
|
if (!path.isCollection) {
|
||||||
|
throw new Error('Argument "collectionPath" must point to a collection.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return new CollectionReference(this._firestore, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
create(data: { [string]: any }): Promise<WriteResult> {
|
||||||
|
/* 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.path, deleteOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
get(): Promise<DocumentSnapshot> {
|
||||||
|
return this._firestore._native
|
||||||
|
.documentGet(this.path)
|
||||||
|
.then(result => new DocumentSnapshot(this._firestore, result));
|
||||||
|
}
|
||||||
|
|
||||||
|
getCollections(): Promise<CollectionReference[]> {
|
||||||
|
/* return this._firestore._native
|
||||||
|
.documentCollections(this.path)
|
||||||
|
.then((collectionIds) => {
|
||||||
|
const collections = [];
|
||||||
|
|
||||||
|
for (const collectionId of collectionIds) {
|
||||||
|
collections.push(this.collection(collectionId));
|
||||||
|
}
|
||||||
|
|
||||||
|
return collections;
|
||||||
|
}); */
|
||||||
|
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD('DocumentReference', 'getCollections'));
|
||||||
|
}
|
||||||
|
|
||||||
|
onSnapshot(onNext: Function, onError?: Function): () => void {
|
||||||
|
// TODO: Validation
|
||||||
|
const listenerId = firestoreAutoId();
|
||||||
|
|
||||||
|
const listener = (nativeDocumentSnapshot) => {
|
||||||
|
const documentSnapshot = new DocumentSnapshot(this, nativeDocumentSnapshot);
|
||||||
|
onNext(documentSnapshot);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Listen to snapshot events
|
||||||
|
this._firestore.on(
|
||||||
|
this._firestore._getAppEventName(`onDocumentSnapshot:${listenerId}`),
|
||||||
|
listener,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Listen for snapshot error events
|
||||||
|
if (onError) {
|
||||||
|
this._firestore.on(
|
||||||
|
this._firestore._getAppEventName(`onDocumentSnapshotError:${listenerId}`),
|
||||||
|
onError,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the native listener
|
||||||
|
this._firestore._native
|
||||||
|
.documentOnSnapshot(this.path, listenerId);
|
||||||
|
|
||||||
|
// Return an unsubscribe method
|
||||||
|
return this._offDocumentSnapshot.bind(this, listenerId, listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
set(data: { [string]: any }, writeOptions?: WriteOptions): Promise<WriteResult> {
|
||||||
|
return this._firestore._native
|
||||||
|
.documentSet(this.path, data, writeOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Update to new update method signature
|
||||||
|
update(data: { [string]: any }): Promise<WriteResult> {
|
||||||
|
return this._firestore._native
|
||||||
|
.documentUpdate(this.path, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove document snapshot listener
|
||||||
|
* @param listener
|
||||||
|
*/
|
||||||
|
_offDocumentSnapshot(listenerId: number, listener: Function) {
|
||||||
|
this._firestore.log.info('Removing onDocumentSnapshot listener');
|
||||||
|
this._firestore.removeListener(this._firestore._getAppEventName(`onDocumentSnapshot:${listenerId}`), listener);
|
||||||
|
this._firestore.removeListener(this._firestore._getAppEventName(`onDocumentSnapshotError:${listenerId}`), listener);
|
||||||
|
this._firestore._native
|
||||||
|
.documentOffSnapshot(this.path, listenerId);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
/**
|
||||||
|
* @flow
|
||||||
|
* DocumentSnapshot representation wrapper
|
||||||
|
*/
|
||||||
|
import DocumentReference from './DocumentReference';
|
||||||
|
import Path from './Path';
|
||||||
|
import INTERNALS from './../../internals';
|
||||||
|
|
||||||
|
export type DocumentSnapshotNativeData = {
|
||||||
|
createTime: string,
|
||||||
|
data: Object,
|
||||||
|
path: string,
|
||||||
|
readTime: string,
|
||||||
|
updateTime: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class DocumentSnapshot
|
||||||
|
*/
|
||||||
|
export default class DocumentSnapshot {
|
||||||
|
_createTime: string;
|
||||||
|
_data: Object;
|
||||||
|
_readTime: string;
|
||||||
|
_ref: DocumentReference;
|
||||||
|
_updateTime: string;
|
||||||
|
|
||||||
|
constructor(firestore: Object, nativeData: DocumentSnapshotNativeData) {
|
||||||
|
this._createTime = nativeData.createTime;
|
||||||
|
this._data = nativeData.data;
|
||||||
|
this._ref = new DocumentReference(firestore, Path.fromName(nativeData.path));
|
||||||
|
this._readTime = nativeData.readTime;
|
||||||
|
this._updateTime = nativeData.updateTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
get createTime(): string {
|
||||||
|
// return this._createTime;
|
||||||
|
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_PROPERTY('DocumentSnapshot', 'createTime'));
|
||||||
|
}
|
||||||
|
|
||||||
|
get exists(): boolean {
|
||||||
|
return this._data !== undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
get id(): string | null {
|
||||||
|
return this._ref.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
get readTime(): string {
|
||||||
|
// return this._readTime;
|
||||||
|
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_PROPERTY('DocumentSnapshot', 'readTime'));
|
||||||
|
}
|
||||||
|
|
||||||
|
get ref(): DocumentReference {
|
||||||
|
return this._ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
get updateTime(): string {
|
||||||
|
// return this._updateTime;
|
||||||
|
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_PROPERTY('DocumentSnapshot', 'updateTime'));
|
||||||
|
}
|
||||||
|
|
||||||
|
data(): Object {
|
||||||
|
return this._data;
|
||||||
|
}
|
||||||
|
|
||||||
|
get(fieldPath: string): any {
|
||||||
|
return this._data[fieldPath];
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
/**
|
||||||
|
* @flow
|
||||||
|
* GeoPoint representation wrapper
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class GeoPoint
|
||||||
|
*/
|
||||||
|
export default class GeoPoint {
|
||||||
|
_latitude: number;
|
||||||
|
_longitude: number;
|
||||||
|
|
||||||
|
constructor(latitude: number, longitude: number) {
|
||||||
|
// TODO: Validation
|
||||||
|
// validate.isNumber('latitude', latitude);
|
||||||
|
// validate.isNumber('longitude', longitude);
|
||||||
|
|
||||||
|
this._latitude = latitude;
|
||||||
|
this._longitude = longitude;
|
||||||
|
}
|
||||||
|
|
||||||
|
get latitude() {
|
||||||
|
return this._latitude;
|
||||||
|
}
|
||||||
|
|
||||||
|
get longitude() {
|
||||||
|
return this._longitude;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
/**
|
||||||
|
* @flow
|
||||||
|
* Path representation wrapper
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class Path
|
||||||
|
*/
|
||||||
|
export default class Path {
|
||||||
|
_parts: string[];
|
||||||
|
|
||||||
|
constructor(pathComponents: string[]) {
|
||||||
|
this._parts = pathComponents;
|
||||||
|
}
|
||||||
|
|
||||||
|
get id(): string | null {
|
||||||
|
if (this._parts.length > 0) {
|
||||||
|
return this._parts[this._parts.length - 1];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
get isDocument(): boolean {
|
||||||
|
return this._parts.length > 0 && this._parts.length % 2 === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
get isCollection(): boolean {
|
||||||
|
return this._parts.length % 2 === 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
get relativeName(): string {
|
||||||
|
return this._parts.join('/');
|
||||||
|
}
|
||||||
|
|
||||||
|
child(relativePath: string): Path {
|
||||||
|
return new Path(this._parts.concat(relativePath.split('/')));
|
||||||
|
}
|
||||||
|
|
||||||
|
parent(): Path | null {
|
||||||
|
if (this._parts.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Path(this._parts.slice(0, this._parts.length - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @package
|
||||||
|
*/
|
||||||
|
static fromName(name): Path {
|
||||||
|
const parts = name.split('/');
|
||||||
|
|
||||||
|
if (parts.length === 0) {
|
||||||
|
return new Path([]);
|
||||||
|
}
|
||||||
|
return new Path(parts);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,272 @@
|
||||||
|
/**
|
||||||
|
* @flow
|
||||||
|
* Query representation wrapper
|
||||||
|
*/
|
||||||
|
import DocumentSnapshot from './DocumentSnapshot';
|
||||||
|
import Path from './Path';
|
||||||
|
import QuerySnapshot from './QuerySnapshot';
|
||||||
|
import INTERNALS from '../../internals';
|
||||||
|
import { firestoreAutoId } from '../../utils';
|
||||||
|
|
||||||
|
const DIRECTIONS = {
|
||||||
|
ASC: 'ASCENDING',
|
||||||
|
asc: 'ASCENDING',
|
||||||
|
DESC: 'DESCENDING',
|
||||||
|
desc: 'DESCENDING',
|
||||||
|
};
|
||||||
|
|
||||||
|
const OPERATORS = {
|
||||||
|
'=': 'EQUAL',
|
||||||
|
'==': 'EQUAL',
|
||||||
|
'>': 'GREATER_THAN',
|
||||||
|
'>=': 'GREATER_THAN_OR_EQUAL',
|
||||||
|
'<': 'LESS_THAN',
|
||||||
|
'<=': 'LESS_THAN_OR_EQUAL',
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Direction = 'DESC' | 'desc' | 'ASC' | 'asc';
|
||||||
|
type FieldFilter = {
|
||||||
|
fieldPath: string,
|
||||||
|
operator: string,
|
||||||
|
value: any,
|
||||||
|
}
|
||||||
|
type FieldOrder = {
|
||||||
|
direction: string,
|
||||||
|
fieldPath: string,
|
||||||
|
}
|
||||||
|
type QueryOptions = {
|
||||||
|
endAt?: any[],
|
||||||
|
endBefore?: any[],
|
||||||
|
limit?: number,
|
||||||
|
offset?: number,
|
||||||
|
selectFields?: string[],
|
||||||
|
startAfter?: any[],
|
||||||
|
startAt?: any[],
|
||||||
|
}
|
||||||
|
export type Operator = '<' | '<=' | '=' | '==' | '>' | '>=';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class Query
|
||||||
|
*/
|
||||||
|
export default class Query {
|
||||||
|
_fieldFilters: FieldFilter[];
|
||||||
|
_fieldOrders: FieldOrder[];
|
||||||
|
_firestore: Object;
|
||||||
|
_iid: number;
|
||||||
|
_queryOptions: QueryOptions;
|
||||||
|
_referencePath: Path;
|
||||||
|
|
||||||
|
constructor(firestore: Object, path: Path, fieldFilters?: FieldFilter[],
|
||||||
|
fieldOrders?: FieldOrder[], queryOptions?: QueryOptions) {
|
||||||
|
this._fieldFilters = fieldFilters || [];
|
||||||
|
this._fieldOrders = fieldOrders || [];
|
||||||
|
this._firestore = firestore;
|
||||||
|
this._queryOptions = queryOptions || {};
|
||||||
|
this._referencePath = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
get firestore(): Object {
|
||||||
|
return this._firestore;
|
||||||
|
}
|
||||||
|
|
||||||
|
endAt(fieldValues: any): Query {
|
||||||
|
fieldValues = [].slice.call(arguments);
|
||||||
|
// TODO: Validation
|
||||||
|
const options = {
|
||||||
|
...this._queryOptions,
|
||||||
|
endAt: fieldValues,
|
||||||
|
};
|
||||||
|
|
||||||
|
return new Query(this.firestore, this._referencePath, this._fieldFilters,
|
||||||
|
this._fieldOrders, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
endBefore(fieldValues: any): Query {
|
||||||
|
fieldValues = [].slice.call(arguments);
|
||||||
|
// TODO: Validation
|
||||||
|
const options = {
|
||||||
|
...this._queryOptions,
|
||||||
|
endBefore: fieldValues,
|
||||||
|
};
|
||||||
|
|
||||||
|
return new Query(this.firestore, this._referencePath, this._fieldFilters,
|
||||||
|
this._fieldOrders, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
get(): Promise<QuerySnapshot> {
|
||||||
|
return this._firestore._native
|
||||||
|
.collectionGet(
|
||||||
|
this._referencePath.relativeName,
|
||||||
|
this._fieldFilters,
|
||||||
|
this._fieldOrders,
|
||||||
|
this._queryOptions,
|
||||||
|
)
|
||||||
|
.then(nativeData => new QuerySnapshot(this._firestore, this, nativeData));
|
||||||
|
}
|
||||||
|
|
||||||
|
limit(n: number): Query {
|
||||||
|
// TODO: Validation
|
||||||
|
// validate.isInteger('n', n);
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
...this._queryOptions,
|
||||||
|
limit: n,
|
||||||
|
};
|
||||||
|
return new Query(this.firestore, this._referencePath, this._fieldFilters,
|
||||||
|
this._fieldOrders, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
offset(n: number): Query {
|
||||||
|
// TODO: Validation
|
||||||
|
// validate.isInteger('n', n);
|
||||||
|
|
||||||
|
/* const options = {
|
||||||
|
...this._queryOptions,
|
||||||
|
offset: n,
|
||||||
|
};
|
||||||
|
return new Query(this.firestore, this._referencePath, this._fieldFilters,
|
||||||
|
this._fieldOrders, options); */
|
||||||
|
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD('Query', 'offset'));
|
||||||
|
}
|
||||||
|
|
||||||
|
onSnapshot(onNext: () => any, onError?: () => any): () => void {
|
||||||
|
// TODO: Validation
|
||||||
|
const listenerId = firestoreAutoId();
|
||||||
|
|
||||||
|
const listener = (nativeQuerySnapshot) => {
|
||||||
|
const querySnapshot = new QuerySnapshot(this._firestore, this, nativeQuerySnapshot);
|
||||||
|
onNext(querySnapshot);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Listen to snapshot events
|
||||||
|
this._firestore.on(
|
||||||
|
this._firestore._getAppEventName(`onQuerySnapshot:${listenerId}`),
|
||||||
|
listener,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Listen for snapshot error events
|
||||||
|
if (onError) {
|
||||||
|
this._firestore.on(
|
||||||
|
this._firestore._getAppEventName(`onQuerySnapshotError:${listenerId}`),
|
||||||
|
onError,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the native listener
|
||||||
|
this._firestore._native
|
||||||
|
.collectionOnSnapshot(
|
||||||
|
this._referencePath.relativeName,
|
||||||
|
this._fieldFilters,
|
||||||
|
this._fieldOrders,
|
||||||
|
this._queryOptions,
|
||||||
|
listenerId
|
||||||
|
);
|
||||||
|
|
||||||
|
// Return an unsubscribe method
|
||||||
|
return this._offCollectionSnapshot.bind(this, listenerId, listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
orderBy(fieldPath: string, directionStr?: Direction = 'asc'): Query {
|
||||||
|
// 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 ' +
|
||||||
|
'startAt(), startAfter(), endBefore() or endAt().');
|
||||||
|
}
|
||||||
|
|
||||||
|
const newOrder = {
|
||||||
|
direction: DIRECTIONS[directionStr],
|
||||||
|
fieldPath,
|
||||||
|
};
|
||||||
|
const combinedOrders = this._fieldOrders.concat(newOrder);
|
||||||
|
return new Query(this.firestore, this._referencePath, this._fieldFilters,
|
||||||
|
combinedOrders, this._queryOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
select(varArgs: string[]): Query {
|
||||||
|
/*
|
||||||
|
varArgs = Array.isArray(arguments[0]) ? arguments[0] : [].slice.call(arguments);
|
||||||
|
const fieldReferences = [];
|
||||||
|
|
||||||
|
if (varArgs.length === 0) {
|
||||||
|
fieldReferences.push(DOCUMENT_NAME_FIELD);
|
||||||
|
} else {
|
||||||
|
for (let i = 0; i < varArgs.length; ++i) {
|
||||||
|
// TODO: Validation
|
||||||
|
// validate.isFieldPath(i, args[i]);
|
||||||
|
fieldReferences.push(varArgs[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
...this._queryOptions,
|
||||||
|
selectFields: fieldReferences,
|
||||||
|
};
|
||||||
|
|
||||||
|
return new Query(this.firestore, this._referencePath, this._fieldFilters,
|
||||||
|
this._fieldOrders, options);*/
|
||||||
|
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD('Query', 'select'));
|
||||||
|
}
|
||||||
|
|
||||||
|
startAfter(fieldValues: any): Query {
|
||||||
|
fieldValues = [].slice.call(arguments);
|
||||||
|
// TODO: Validation
|
||||||
|
const options = {
|
||||||
|
...this._queryOptions,
|
||||||
|
startAfter: fieldValues,
|
||||||
|
};
|
||||||
|
|
||||||
|
return new Query(this.firestore, this._referencePath, this._fieldFilters,
|
||||||
|
this._fieldOrders, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
startAt(fieldValues: any): Query {
|
||||||
|
fieldValues = [].slice.call(arguments);
|
||||||
|
// TODO: Validation
|
||||||
|
const options = {
|
||||||
|
...this._queryOptions,
|
||||||
|
startAt: fieldValues,
|
||||||
|
};
|
||||||
|
|
||||||
|
return new Query(this.firestore, this._referencePath, this._fieldFilters,
|
||||||
|
this._fieldOrders, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
stream(): Stream<DocumentSnapshot> {
|
||||||
|
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD('Query', 'stream'));
|
||||||
|
}
|
||||||
|
|
||||||
|
where(fieldPath: string, opStr: Operator, value: any): Query {
|
||||||
|
// TODO: Validation
|
||||||
|
// validate.isFieldPath('fieldPath', fieldPath);
|
||||||
|
// validate.isFieldFilter('fieldFilter', opStr, value);
|
||||||
|
const newFilter = {
|
||||||
|
fieldPath,
|
||||||
|
operator: OPERATORS[opStr],
|
||||||
|
value,
|
||||||
|
};
|
||||||
|
const combinedFilters = this._fieldFilters.concat(newFilter);
|
||||||
|
return new Query(this.firestore, this._referencePath, combinedFilters,
|
||||||
|
this._fieldOrders, this._queryOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove query snapshot listener
|
||||||
|
* @param listener
|
||||||
|
*/
|
||||||
|
_offCollectionSnapshot(listenerId: number, listener: Function) {
|
||||||
|
this._firestore.log.info('Removing onQuerySnapshot listener');
|
||||||
|
this._firestore.removeListener(this._firestore._getAppEventName(`onQuerySnapshot:${listenerId}`), listener);
|
||||||
|
this._firestore.removeListener(this._firestore._getAppEventName(`onQuerySnapshotError:${listenerId}`), listener);
|
||||||
|
this._firestore._native
|
||||||
|
.collectionOffSnapshot(
|
||||||
|
this._referencePath.relativeName,
|
||||||
|
this._fieldFilters,
|
||||||
|
this._fieldOrders,
|
||||||
|
this._queryOptions,
|
||||||
|
listenerId
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
/**
|
||||||
|
* @flow
|
||||||
|
* QuerySnapshot representation wrapper
|
||||||
|
*/
|
||||||
|
import DocumentChange from './DocumentChange';
|
||||||
|
import DocumentSnapshot from './DocumentSnapshot';
|
||||||
|
import Query from './Query';
|
||||||
|
|
||||||
|
import type { DocumentChangeNativeData } from './DocumentChange';
|
||||||
|
import type { DocumentSnapshotNativeData } from './DocumentSnapshot';
|
||||||
|
|
||||||
|
type QuerySnapshotNativeData = {
|
||||||
|
changes: DocumentChangeNativeData[],
|
||||||
|
documents: DocumentSnapshotNativeData[],
|
||||||
|
readTime: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class QuerySnapshot
|
||||||
|
*/
|
||||||
|
export default class QuerySnapshot {
|
||||||
|
_changes: DocumentChange[];
|
||||||
|
_docs: DocumentSnapshot[];
|
||||||
|
_query: Query;
|
||||||
|
_readTime: string;
|
||||||
|
|
||||||
|
constructor(firestore: Object, query: Query, nativeData: QuerySnapshotNativeData) {
|
||||||
|
this._changes = nativeData.changes.map(change => new DocumentChange(change));
|
||||||
|
this._docs = nativeData.documents.map(doc => new DocumentSnapshot(firestore, doc));
|
||||||
|
this._query = query;
|
||||||
|
this._readTime = nativeData.readTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
get docChanges(): DocumentChange[] {
|
||||||
|
return this._changes;
|
||||||
|
}
|
||||||
|
|
||||||
|
get docs(): DocumentSnapshot[] {
|
||||||
|
return this._docs;
|
||||||
|
}
|
||||||
|
|
||||||
|
get empty(): boolean {
|
||||||
|
return this._docs.length === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
get query(): Query {
|
||||||
|
return this._query;
|
||||||
|
}
|
||||||
|
|
||||||
|
get readTime(): string {
|
||||||
|
return this._readTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
get size(): number {
|
||||||
|
return this._docs.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
forEach(callback: DocumentSnapshot => any) {
|
||||||
|
// TODO: Validation
|
||||||
|
// validate.isFunction('callback', callback);
|
||||||
|
|
||||||
|
for (const doc of this._docs) {
|
||||||
|
callback(doc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,96 @@
|
||||||
|
/**
|
||||||
|
* @flow
|
||||||
|
* WriteBatch representation wrapper
|
||||||
|
*/
|
||||||
|
import DocumentReference from './DocumentReference';
|
||||||
|
|
||||||
|
import type { DeleteOptions, WriteOptions, WriteResult } from './DocumentReference';
|
||||||
|
|
||||||
|
type CommitOptions = {
|
||||||
|
transactionId: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
type DocumentWrite = {
|
||||||
|
data?: Object,
|
||||||
|
options?: Object,
|
||||||
|
path: string,
|
||||||
|
type: 'DELETE' | 'SET' | 'UPDATE',
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class WriteBatch
|
||||||
|
*/
|
||||||
|
export default class WriteBatch {
|
||||||
|
_firestore: Object;
|
||||||
|
_writes: DocumentWrite[];
|
||||||
|
|
||||||
|
constructor(firestore: Object) {
|
||||||
|
this._firestore = firestore;
|
||||||
|
this._writes = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
get firestore(): Object {
|
||||||
|
return this._firestore;
|
||||||
|
}
|
||||||
|
|
||||||
|
get isEmpty(): boolean {
|
||||||
|
return this._writes.length === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
create(docRef: DocumentReference, data: Object): WriteBatch {
|
||||||
|
// TODO: Validation
|
||||||
|
// validate.isDocumentReference('docRef', docRef);
|
||||||
|
// validate.isDocument('data', data);
|
||||||
|
|
||||||
|
return this.set(docRef, data, { exists: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(docRef: DocumentReference, deleteOptions?: DeleteOptions): WriteBatch {
|
||||||
|
// TODO: Validation
|
||||||
|
// validate.isDocumentReference('docRef', docRef);
|
||||||
|
// validate.isOptionalPrecondition('deleteOptions', deleteOptions);
|
||||||
|
this._writes.push({
|
||||||
|
options: deleteOptions,
|
||||||
|
path: docRef.path,
|
||||||
|
type: 'DELETE',
|
||||||
|
});
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
set(docRef: DocumentReference, data: Object, writeOptions?: WriteOptions) {
|
||||||
|
// TODO: Validation
|
||||||
|
// validate.isDocumentReference('docRef', docRef);
|
||||||
|
// validate.isDocument('data', data);
|
||||||
|
// validate.isOptionalPrecondition('writeOptions', writeOptions);
|
||||||
|
|
||||||
|
this._writes.push({
|
||||||
|
data,
|
||||||
|
options: writeOptions,
|
||||||
|
path: docRef.path,
|
||||||
|
type: 'SET',
|
||||||
|
});
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Update to new method signature
|
||||||
|
update(docRef: DocumentReference, data: { [string]: any }): WriteBatch {
|
||||||
|
// TODO: Validation
|
||||||
|
// validate.isDocumentReference('docRef', docRef);
|
||||||
|
// validate.isDocument('data', data, true);
|
||||||
|
|
||||||
|
this._writes.push({
|
||||||
|
data,
|
||||||
|
path: docRef.path,
|
||||||
|
type: 'UPDATE',
|
||||||
|
});
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
commit(commitOptions?: CommitOptions): Promise<WriteResult[]> {
|
||||||
|
return this._firestore._native
|
||||||
|
.documentBatch(this._writes, commitOptions);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,177 @@
|
||||||
|
/**
|
||||||
|
* @flow
|
||||||
|
* Firestore representation wrapper
|
||||||
|
*/
|
||||||
|
import { NativeModules } from 'react-native';
|
||||||
|
|
||||||
|
import ModuleBase from './../../utils/ModuleBase';
|
||||||
|
import CollectionReference from './CollectionReference';
|
||||||
|
import DocumentReference from './DocumentReference';
|
||||||
|
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_}$`);
|
||||||
|
|
||||||
|
type CollectionSyncEvent = {
|
||||||
|
appName: string,
|
||||||
|
querySnapshot?: QuerySnapshot,
|
||||||
|
error?: Object,
|
||||||
|
listenerId: string,
|
||||||
|
path: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
type DocumentSyncEvent = {
|
||||||
|
appName: string,
|
||||||
|
documentSnapshot?: DocumentSnapshot,
|
||||||
|
error?: Object,
|
||||||
|
listenerId: string,
|
||||||
|
path: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class Firestore
|
||||||
|
*/
|
||||||
|
export default class Firestore extends ModuleBase {
|
||||||
|
static _NAMESPACE = 'firestore';
|
||||||
|
static _NATIVE_MODULE = 'RNFirebaseFirestore';
|
||||||
|
|
||||||
|
_referencePath: Path;
|
||||||
|
|
||||||
|
constructor(firebaseApp: Object, options: Object = {}) {
|
||||||
|
super(firebaseApp, options, true);
|
||||||
|
this._referencePath = new Path([]);
|
||||||
|
|
||||||
|
this.addListener(
|
||||||
|
// sub to internal native event - this fans out to
|
||||||
|
// public event name: onCollectionSnapshot
|
||||||
|
this._getAppEventName('firestore_collection_sync_event'),
|
||||||
|
this._onCollectionSyncEvent.bind(this),
|
||||||
|
);
|
||||||
|
|
||||||
|
this.addListener(
|
||||||
|
// sub to internal native event - this fans out to
|
||||||
|
// public event name: onDocumentSnapshot
|
||||||
|
this._getAppEventName('firestore_document_sync_event'),
|
||||||
|
this._onDocumentSyncEvent.bind(this),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
batch(): WriteBatch {
|
||||||
|
return new WriteBatch(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param collectionPath
|
||||||
|
* @returns {CollectionReference}
|
||||||
|
*/
|
||||||
|
collection(collectionPath: string): CollectionReference {
|
||||||
|
const path = this._referencePath.child(collectionPath);
|
||||||
|
if (!path.isCollection) {
|
||||||
|
throw new Error('Argument "collectionPath" must point to a collection.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return new CollectionReference(this, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param documentPath
|
||||||
|
* @returns {DocumentReference}
|
||||||
|
*/
|
||||||
|
doc(documentPath: string): DocumentReference {
|
||||||
|
const path = this._referencePath.child(documentPath);
|
||||||
|
if (!path.isDocument) {
|
||||||
|
throw new Error('Argument "documentPath" must point to a document.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return new DocumentReference(this, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
getAll(varArgs: DocumentReference[]): Promise<DocumentSnapshot[]> {
|
||||||
|
/*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.path);
|
||||||
|
});
|
||||||
|
return this._native
|
||||||
|
.documentGetAll(documents)
|
||||||
|
.then(results => results.map(result => new DocumentSnapshot(this, result)));*/
|
||||||
|
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD('Query', 'offset'));
|
||||||
|
}
|
||||||
|
|
||||||
|
getCollections(): Promise<CollectionReference[]> {
|
||||||
|
const rootDocument = new DocumentReference(this, this._referencePath);
|
||||||
|
return rootDocument.getCollections();
|
||||||
|
}
|
||||||
|
|
||||||
|
runTransaction(updateFunction, transactionOptions?: Object): Promise {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static geoPoint(latitude, longitude): GeoPoint {
|
||||||
|
return new GeoPoint(latitude, longitude);
|
||||||
|
}
|
||||||
|
|
||||||
|
static fieldPath(varArgs: string[]): string {
|
||||||
|
varArgs = Array.isArray(arguments[0]) ? arguments[0] : [].slice.call(arguments);
|
||||||
|
|
||||||
|
let fieldPath = '';
|
||||||
|
|
||||||
|
for (let i = 0; i < varArgs.length; ++i) {
|
||||||
|
let component = varArgs[i];
|
||||||
|
// TODO: Validation
|
||||||
|
// validate.isString(i, component);
|
||||||
|
if (!UNQUOTED_IDENTIFIER_REGEX.test(component)) {
|
||||||
|
component = `\`${component.replace(/[`\\]/g, '\\$&')} \``;
|
||||||
|
}
|
||||||
|
fieldPath += i !== 0 ? `.${component}` : component;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fieldPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* INTERNALS
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal collection sync listener
|
||||||
|
* @param event
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_onCollectionSyncEvent(event: CollectionSyncEvent) {
|
||||||
|
if (event.error) {
|
||||||
|
this.emit(this._getAppEventName(`onQuerySnapshotError:${event.listenerId}`), event.error);
|
||||||
|
} else {
|
||||||
|
this.emit(this._getAppEventName(`onQuerySnapshot:${event.listenerId}`), event.querySnapshot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal document sync listener
|
||||||
|
* @param event
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_onDocumentSyncEvent(event: DocumentSyncEvent) {
|
||||||
|
if (event.error) {
|
||||||
|
this.emit(this._getAppEventName(`onDocumentSnapshotError:${event.listenerId}`), event.error);
|
||||||
|
} else {
|
||||||
|
this.emit(this._getAppEventName(`onDocumentSnapshot:${event.listenerId}`), event.documentSnapshot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const statics = {
|
||||||
|
FieldValue: {
|
||||||
|
delete: () => NativeModules.RNFirebaseFirestore && NativeModules.RNFirebaseFirestore.deleteFieldValue || {},
|
||||||
|
serverTimestamp: () => NativeModules.RNFirebaseFirestore && NativeModules.RNFirebaseFirestore.serverTimestampFieldValue || {}
|
||||||
|
},
|
||||||
|
};
|
|
@ -15,6 +15,7 @@ const logs = {};
|
||||||
const MULTI_APP_MODULES = [
|
const MULTI_APP_MODULES = [
|
||||||
'auth',
|
'auth',
|
||||||
'database',
|
'database',
|
||||||
|
'firestore',
|
||||||
'storage',
|
'storage',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -31,6 +32,10 @@ const NATIVE_MODULE_EVENTS = {
|
||||||
'database_transaction_event',
|
'database_transaction_event',
|
||||||
// 'database_server_offset', // TODO
|
// 'database_server_offset', // TODO
|
||||||
],
|
],
|
||||||
|
Firestore: [
|
||||||
|
'firestore_collection_sync_event',
|
||||||
|
'firestore_document_sync_event',
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
const DEFAULTS = {
|
const DEFAULTS = {
|
||||||
|
|
|
@ -5,10 +5,19 @@ import { Platform } from 'react-native';
|
||||||
|
|
||||||
// modeled after base64 web-safe chars, but ordered by ASCII
|
// modeled after base64 web-safe chars, but ordered by ASCII
|
||||||
const PUSH_CHARS = '-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz';
|
const PUSH_CHARS = '-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz';
|
||||||
|
const AUTO_ID_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||||
const hasOwnProperty = Object.hasOwnProperty;
|
const hasOwnProperty = Object.hasOwnProperty;
|
||||||
|
|
||||||
// const DEFAULT_CHUNK_SIZE = 50;
|
// 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.
|
* Deep get a value from an object.
|
||||||
* @website https://github.com/Salakar/deeps
|
* @website https://github.com/Salakar/deeps
|
||||||
|
@ -88,6 +97,16 @@ export function isString(value: any): boolean {
|
||||||
return typeof value === 'string';
|
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
|
// platform checks
|
||||||
export const isIOS = Platform.OS === 'ios';
|
export const isIOS = Platform.OS === 'ios';
|
||||||
export const isAndroid = Platform.OS === 'android';
|
export const isAndroid = Platform.OS === 'android';
|
||||||
|
@ -374,3 +393,16 @@ export function promiseOrCallback(promise: Promise<*>, optionalCallback?: Functi
|
||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a firestore auto id for use with collection/document .add()
|
||||||
|
* @return {string}
|
||||||
|
*/
|
||||||
|
export function firestoreAutoId(): string {
|
||||||
|
let autoId = '';
|
||||||
|
|
||||||
|
for (let i = 0; i < 20; i++) {
|
||||||
|
autoId += AUTO_ID_CHARS.charAt(Math.floor(Math.random() * AUTO_ID_CHARS.length));
|
||||||
|
}
|
||||||
|
return autoId;
|
||||||
|
}
|
||||||
|
|
|
@ -2624,9 +2624,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"flow-bin": {
|
"flow-bin": {
|
||||||
"version": "0.46.0",
|
"version": "0.55.0",
|
||||||
"resolved": "https://registry.npmjs.org/flow-bin/-/flow-bin-0.46.0.tgz",
|
"resolved": "https://registry.npmjs.org/flow-bin/-/flow-bin-0.55.0.tgz",
|
||||||
"integrity": "sha1-Bq1/4Z3dsQQiZEOAZKKjL+4SuHI=",
|
"integrity": "sha1-kIPakye9jKtrQHbWPYXyJHp+rhs=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"for-in": {
|
"for-in": {
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"name": "react-native-firebase",
|
"name": "react-native-firebase",
|
||||||
"version": "3.0.0-alpha.5",
|
"version": "3.0.0-alpha.5",
|
||||||
"author": "Invertase <contact@invertase.io> (http://invertase.io)",
|
"author": "Invertase <contact@invertase.io> (http://invertase.io)",
|
||||||
"description": "A well tested, feature rich Firebase implementation for React Native, supporting iOS & Android. Individual module support for Auth, Database, Messaging (FCM), Remote Config, Storage, Admob, Analytics, Crash Reporting, and Performance.",
|
"description": "A well tested, feature rich Firebase implementation for React Native, supporting iOS & Android. Individual module support for Auth, Database, Firestore, Messaging (FCM), Remote Config, Storage, Admob, Analytics, Crash Reporting, and Performance.",
|
||||||
"main": "index",
|
"main": "index",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"flow": "flow",
|
"flow": "flow",
|
||||||
|
@ -51,7 +51,8 @@
|
||||||
"ios",
|
"ios",
|
||||||
"crash",
|
"crash",
|
||||||
"firestack",
|
"firestack",
|
||||||
"performance"
|
"performance",
|
||||||
|
"firestore"
|
||||||
],
|
],
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": "*",
|
"react": "*",
|
||||||
|
@ -70,7 +71,7 @@
|
||||||
"eslint-plugin-import": "^2.0.1",
|
"eslint-plugin-import": "^2.0.1",
|
||||||
"eslint-plugin-jsx-a11y": "^2.2.3",
|
"eslint-plugin-jsx-a11y": "^2.2.3",
|
||||||
"eslint-plugin-react": "^6.4.1",
|
"eslint-plugin-react": "^6.4.1",
|
||||||
"flow-bin": "^0.46.0",
|
"flow-bin": "^0.55.0",
|
||||||
"react": "^15.3.0",
|
"react": "^15.3.0",
|
||||||
"react-dom": "^15.3.0",
|
"react-dom": "^15.3.0",
|
||||||
"react-native": "^0.44.0",
|
"react-native": "^0.44.0",
|
||||||
|
|
|
@ -71,7 +71,7 @@ android {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
project.ext.firebaseVersion = '11.2.0'
|
project.ext.firebaseVersion = '11.4.2'
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
// compile(project(':react-native-firebase')) {
|
// compile(project(':react-native-firebase')) {
|
||||||
|
@ -82,7 +82,6 @@ dependencies {
|
||||||
compile fileTree(dir: "libs", include: ["*.jar"])
|
compile fileTree(dir: "libs", include: ["*.jar"])
|
||||||
compile "com.google.android.gms:play-services-base:$firebaseVersion"
|
compile "com.google.android.gms:play-services-base:$firebaseVersion"
|
||||||
compile "com.google.firebase:firebase-ads:$firebaseVersion"
|
compile "com.google.firebase:firebase-ads:$firebaseVersion"
|
||||||
compile "com.google.firebase:firebase-ads:$firebaseVersion"
|
|
||||||
compile "com.google.firebase:firebase-auth:$firebaseVersion"
|
compile "com.google.firebase:firebase-auth:$firebaseVersion"
|
||||||
compile "com.google.firebase:firebase-config:$firebaseVersion"
|
compile "com.google.firebase:firebase-config:$firebaseVersion"
|
||||||
compile "com.google.firebase:firebase-core:$firebaseVersion"
|
compile "com.google.firebase:firebase-core:$firebaseVersion"
|
||||||
|
@ -91,6 +90,7 @@ dependencies {
|
||||||
compile "com.google.firebase:firebase-messaging:$firebaseVersion"
|
compile "com.google.firebase:firebase-messaging:$firebaseVersion"
|
||||||
compile "com.google.firebase:firebase-perf:$firebaseVersion"
|
compile "com.google.firebase:firebase-perf:$firebaseVersion"
|
||||||
compile "com.google.firebase:firebase-storage:$firebaseVersion"
|
compile "com.google.firebase:firebase-storage:$firebaseVersion"
|
||||||
|
compile "com.google.firebase:firebase-firestore:$firebaseVersion"
|
||||||
compile "com.android.support:appcompat-v7:26.0.1"
|
compile "com.android.support:appcompat-v7:26.0.1"
|
||||||
compile "com.facebook.react:react-native:+" // From node_modules
|
compile "com.facebook.react:react-native:+" // From node_modules
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -10,8 +10,8 @@ import io.invertase.firebase.auth.RNFirebaseAuthPackage;
|
||||||
import io.invertase.firebase.config.RNFirebaseRemoteConfigPackage;
|
import io.invertase.firebase.config.RNFirebaseRemoteConfigPackage;
|
||||||
import io.invertase.firebase.crash.RNFirebaseCrashPackage;
|
import io.invertase.firebase.crash.RNFirebaseCrashPackage;
|
||||||
import io.invertase.firebase.database.RNFirebaseDatabasePackage;
|
import io.invertase.firebase.database.RNFirebaseDatabasePackage;
|
||||||
|
import io.invertase.firebase.firestore.RNFirebaseFirestorePackage;
|
||||||
import io.invertase.firebase.messaging.RNFirebaseMessagingPackage;
|
import io.invertase.firebase.messaging.RNFirebaseMessagingPackage;
|
||||||
import io.invertase.firebase.perf.RNFirebasePerformancePackage;
|
|
||||||
import io.invertase.firebase.storage.RNFirebaseStoragePackage;
|
import io.invertase.firebase.storage.RNFirebaseStoragePackage;
|
||||||
import com.oblador.vectoricons.VectorIconsPackage;
|
import com.oblador.vectoricons.VectorIconsPackage;
|
||||||
import com.facebook.react.ReactNativeHost;
|
import com.facebook.react.ReactNativeHost;
|
||||||
|
@ -42,8 +42,9 @@ public class MainApplication extends Application implements ReactApplication {
|
||||||
new RNFirebaseRemoteConfigPackage(),
|
new RNFirebaseRemoteConfigPackage(),
|
||||||
new RNFirebaseCrashPackage(),
|
new RNFirebaseCrashPackage(),
|
||||||
new RNFirebaseDatabasePackage(),
|
new RNFirebaseDatabasePackage(),
|
||||||
|
new RNFirebaseFirestorePackage(),
|
||||||
new RNFirebaseMessagingPackage(),
|
new RNFirebaseMessagingPackage(),
|
||||||
new RNFirebasePerformancePackage(),
|
// new RNFirebasePerformancePackage(),
|
||||||
new RNFirebaseStoragePackage()
|
new RNFirebaseStoragePackage()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,8 +6,8 @@ buildscript {
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:2.3.3'
|
classpath 'com.android.tools.build:gradle:2.3.3'
|
||||||
classpath 'com.google.gms:google-services:3.1.0'
|
classpath 'com.google.gms:google-services:3.1.1'
|
||||||
classpath 'com.google.firebase:firebase-plugins:1.1.0'
|
classpath 'com.google.firebase:firebase-plugins:1.1.1'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@ allprojects {
|
||||||
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
|
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
|
||||||
url "$rootDir/../node_modules/react-native/android"
|
url "$rootDir/../node_modules/react-native/android"
|
||||||
}
|
}
|
||||||
|
mavenLocal()
|
||||||
google()
|
google()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,4 +37,4 @@
|
||||||
<key>DATABASE_URL</key>
|
<key>DATABASE_URL</key>
|
||||||
<string>https://rnfirebase-b9ad4.firebaseio.com</string>
|
<string>https://rnfirebase-b9ad4.firebaseio.com</string>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|
|
@ -25,6 +25,7 @@ target 'ReactNativeFirebaseDemo' do
|
||||||
pod 'Firebase/Crash'
|
pod 'Firebase/Crash'
|
||||||
pod 'Firebase/Database'
|
pod 'Firebase/Database'
|
||||||
pod 'Firebase/DynamicLinks'
|
pod 'Firebase/DynamicLinks'
|
||||||
|
pod 'Firebase/Firestore'
|
||||||
pod 'Firebase/Messaging'
|
pod 'Firebase/Messaging'
|
||||||
pod 'Firebase/RemoteConfig'
|
pod 'Firebase/RemoteConfig'
|
||||||
pod 'Firebase/Storage'
|
pod 'Firebase/Storage'
|
||||||
|
|
|
@ -1,44 +1,53 @@
|
||||||
PODS:
|
PODS:
|
||||||
- Firebase/AdMob (4.2.0):
|
- BoringSSL (9.0):
|
||||||
|
- BoringSSL/Implementation (= 9.0)
|
||||||
|
- BoringSSL/Interface (= 9.0)
|
||||||
|
- BoringSSL/Implementation (9.0):
|
||||||
|
- BoringSSL/Interface (= 9.0)
|
||||||
|
- BoringSSL/Interface (9.0)
|
||||||
|
- Firebase/AdMob (4.3.0):
|
||||||
- Firebase/Core
|
- Firebase/Core
|
||||||
- Google-Mobile-Ads-SDK (= 7.24.0)
|
- Google-Mobile-Ads-SDK (= 7.24.1)
|
||||||
- Firebase/Auth (4.2.0):
|
- Firebase/Auth (4.3.0):
|
||||||
- Firebase/Core
|
- Firebase/Core
|
||||||
- FirebaseAuth (= 4.2.0)
|
- FirebaseAuth (= 4.2.1)
|
||||||
- Firebase/Core (4.2.0):
|
- Firebase/Core (4.3.0):
|
||||||
- FirebaseAnalytics (= 4.0.3)
|
- FirebaseAnalytics (= 4.0.4)
|
||||||
- FirebaseCore (= 4.0.7)
|
- FirebaseCore (= 4.0.8)
|
||||||
- Firebase/Crash (4.2.0):
|
- Firebase/Crash (4.3.0):
|
||||||
- Firebase/Core
|
- Firebase/Core
|
||||||
- FirebaseCrash (= 2.0.2)
|
- FirebaseCrash (= 2.0.2)
|
||||||
- Firebase/Database (4.2.0):
|
- Firebase/Database (4.3.0):
|
||||||
- Firebase/Core
|
- Firebase/Core
|
||||||
- FirebaseDatabase (= 4.0.3)
|
- FirebaseDatabase (= 4.1.0)
|
||||||
- Firebase/DynamicLinks (4.2.0):
|
- Firebase/DynamicLinks (4.3.0):
|
||||||
- Firebase/Core
|
- Firebase/Core
|
||||||
- FirebaseDynamicLinks (= 2.1.0)
|
- FirebaseDynamicLinks (= 2.1.0)
|
||||||
- Firebase/Messaging (4.2.0):
|
- Firebase/Firestore (4.3.0):
|
||||||
- Firebase/Core
|
- Firebase/Core
|
||||||
- FirebaseMessaging (= 2.0.3)
|
- FirebaseFirestore (= 0.8.0)
|
||||||
- Firebase/Performance (4.2.0):
|
- Firebase/Messaging (4.3.0):
|
||||||
- Firebase/Core
|
- Firebase/Core
|
||||||
- FirebasePerformance (= 1.0.5)
|
- FirebaseMessaging (= 2.0.4)
|
||||||
- Firebase/RemoteConfig (4.2.0):
|
- Firebase/Performance (4.3.0):
|
||||||
|
- Firebase/Core
|
||||||
|
- FirebasePerformance (= 1.0.6)
|
||||||
|
- Firebase/RemoteConfig (4.3.0):
|
||||||
- Firebase/Core
|
- Firebase/Core
|
||||||
- FirebaseRemoteConfig (= 2.0.3)
|
- FirebaseRemoteConfig (= 2.0.3)
|
||||||
- Firebase/Storage (4.2.0):
|
- Firebase/Storage (4.3.0):
|
||||||
- Firebase/Core
|
- Firebase/Core
|
||||||
- FirebaseStorage (= 2.0.2)
|
- FirebaseStorage (= 2.0.2)
|
||||||
- FirebaseAnalytics (4.0.3):
|
- FirebaseAnalytics (4.0.4):
|
||||||
- FirebaseCore (~> 4.0)
|
- FirebaseCore (~> 4.0)
|
||||||
- FirebaseInstanceID (~> 2.0)
|
- FirebaseInstanceID (~> 2.0)
|
||||||
- GoogleToolboxForMac/NSData+zlib (~> 2.1)
|
- GoogleToolboxForMac/NSData+zlib (~> 2.1)
|
||||||
- nanopb (~> 0.3)
|
- nanopb (~> 0.3)
|
||||||
- FirebaseAuth (4.2.0):
|
- FirebaseAuth (4.2.1):
|
||||||
- FirebaseAnalytics (~> 4.0)
|
- FirebaseAnalytics (~> 4.0)
|
||||||
- GoogleToolboxForMac/NSDictionary+URLArguments (~> 2.1)
|
- GoogleToolboxForMac/NSDictionary+URLArguments (~> 2.1)
|
||||||
- GTMSessionFetcher/Core (~> 1.1)
|
- GTMSessionFetcher/Core (~> 1.1)
|
||||||
- FirebaseCore (4.0.7):
|
- FirebaseCore (4.0.8):
|
||||||
- GoogleToolboxForMac/NSData+zlib (~> 2.1)
|
- GoogleToolboxForMac/NSData+zlib (~> 2.1)
|
||||||
- nanopb (~> 0.3)
|
- nanopb (~> 0.3)
|
||||||
- FirebaseCrash (2.0.2):
|
- FirebaseCrash (2.0.2):
|
||||||
|
@ -47,20 +56,27 @@ PODS:
|
||||||
- GoogleToolboxForMac/Logger (~> 2.1)
|
- GoogleToolboxForMac/Logger (~> 2.1)
|
||||||
- GoogleToolboxForMac/NSData+zlib (~> 2.1)
|
- GoogleToolboxForMac/NSData+zlib (~> 2.1)
|
||||||
- Protobuf (~> 3.1)
|
- Protobuf (~> 3.1)
|
||||||
- FirebaseDatabase (4.0.3):
|
- FirebaseDatabase (4.1.0):
|
||||||
- FirebaseAnalytics (~> 4.0)
|
- FirebaseAnalytics (~> 4.0)
|
||||||
- FirebaseCore (~> 4.0)
|
- FirebaseCore (~> 4.0)
|
||||||
- leveldb-library (~> 1.18)
|
- leveldb-library (~> 1.18)
|
||||||
- FirebaseDynamicLinks (2.1.0):
|
- FirebaseDynamicLinks (2.1.0):
|
||||||
- FirebaseAnalytics (~> 4.0)
|
- FirebaseAnalytics (~> 4.0)
|
||||||
- FirebaseInstanceID (2.0.3)
|
- FirebaseFirestore (0.8.0):
|
||||||
- FirebaseMessaging (2.0.3):
|
- FirebaseAnalytics (~> 4.0)
|
||||||
|
- FirebaseAuth (~> 4.2)
|
||||||
|
- FirebaseCore (~> 4.0)
|
||||||
|
- gRPC-ProtoRPC (~> 1.0)
|
||||||
|
- leveldb-library (~> 1.18)
|
||||||
|
- Protobuf (~> 3.1)
|
||||||
|
- FirebaseInstanceID (2.0.4)
|
||||||
|
- FirebaseMessaging (2.0.4):
|
||||||
- FirebaseAnalytics (~> 4.0)
|
- FirebaseAnalytics (~> 4.0)
|
||||||
- FirebaseCore (~> 4.0)
|
- FirebaseCore (~> 4.0)
|
||||||
- FirebaseInstanceID (~> 2.0)
|
- FirebaseInstanceID (~> 2.0)
|
||||||
- GoogleToolboxForMac/Logger (~> 2.1)
|
- GoogleToolboxForMac/Logger (~> 2.1)
|
||||||
- Protobuf (~> 3.1)
|
- Protobuf (~> 3.1)
|
||||||
- FirebasePerformance (1.0.5):
|
- FirebasePerformance (1.0.6):
|
||||||
- FirebaseAnalytics (~> 4.0)
|
- FirebaseAnalytics (~> 4.0)
|
||||||
- FirebaseInstanceID (~> 2.0)
|
- FirebaseInstanceID (~> 2.0)
|
||||||
- GoogleToolboxForMac/Logger (~> 2.1)
|
- GoogleToolboxForMac/Logger (~> 2.1)
|
||||||
|
@ -76,7 +92,7 @@ PODS:
|
||||||
- FirebaseAnalytics (~> 4.0)
|
- FirebaseAnalytics (~> 4.0)
|
||||||
- FirebaseCore (~> 4.0)
|
- FirebaseCore (~> 4.0)
|
||||||
- GTMSessionFetcher/Core (~> 1.1)
|
- GTMSessionFetcher/Core (~> 1.1)
|
||||||
- Google-Mobile-Ads-SDK (7.24.0)
|
- Google-Mobile-Ads-SDK (7.24.1)
|
||||||
- GoogleToolboxForMac/DebugUtils (2.1.1):
|
- GoogleToolboxForMac/DebugUtils (2.1.1):
|
||||||
- GoogleToolboxForMac/Defines (= 2.1.1)
|
- GoogleToolboxForMac/Defines (= 2.1.1)
|
||||||
- GoogleToolboxForMac/Defines (2.1.1)
|
- GoogleToolboxForMac/Defines (2.1.1)
|
||||||
|
@ -89,6 +105,22 @@ PODS:
|
||||||
- GoogleToolboxForMac/Defines (= 2.1.1)
|
- GoogleToolboxForMac/Defines (= 2.1.1)
|
||||||
- GoogleToolboxForMac/NSString+URLArguments (= 2.1.1)
|
- GoogleToolboxForMac/NSString+URLArguments (= 2.1.1)
|
||||||
- GoogleToolboxForMac/NSString+URLArguments (2.1.1)
|
- GoogleToolboxForMac/NSString+URLArguments (2.1.1)
|
||||||
|
- gRPC (1.6.0):
|
||||||
|
- gRPC-Core (= 1.6.0)
|
||||||
|
- gRPC-RxLibrary (= 1.6.0)
|
||||||
|
- gRPC-Core (1.6.0):
|
||||||
|
- gRPC-Core/Implementation (= 1.6.0)
|
||||||
|
- gRPC-Core/Interface (= 1.6.0)
|
||||||
|
- gRPC-Core/Implementation (1.6.0):
|
||||||
|
- BoringSSL (~> 9.0)
|
||||||
|
- gRPC-Core/Interface (= 1.6.0)
|
||||||
|
- nanopb (~> 0.3)
|
||||||
|
- gRPC-Core/Interface (1.6.0)
|
||||||
|
- gRPC-ProtoRPC (1.6.0):
|
||||||
|
- gRPC (= 1.6.0)
|
||||||
|
- gRPC-RxLibrary (= 1.6.0)
|
||||||
|
- Protobuf (~> 3.0)
|
||||||
|
- gRPC-RxLibrary (1.6.0)
|
||||||
- GTMSessionFetcher/Core (1.1.12)
|
- GTMSessionFetcher/Core (1.1.12)
|
||||||
- leveldb-library (1.18.3)
|
- leveldb-library (1.18.3)
|
||||||
- nanopb (0.3.8):
|
- nanopb (0.3.8):
|
||||||
|
@ -106,9 +138,6 @@ PODS:
|
||||||
- yoga (= 0.49.0-rc.6.React)
|
- yoga (= 0.49.0-rc.6.React)
|
||||||
- React/cxxreact_legacy (0.49.0-rc.6):
|
- React/cxxreact_legacy (0.49.0-rc.6):
|
||||||
- React/jschelpers_legacy
|
- React/jschelpers_legacy
|
||||||
- React/DevSupport (0.49.0-rc.6):
|
|
||||||
- React/Core
|
|
||||||
- React/RCTWebSocket
|
|
||||||
- React/fishhook (0.49.0-rc.6)
|
- React/fishhook (0.49.0-rc.6)
|
||||||
- React/jschelpers_legacy (0.49.0-rc.6)
|
- React/jschelpers_legacy (0.49.0-rc.6)
|
||||||
- React/RCTBlob (0.49.0-rc.6):
|
- React/RCTBlob (0.49.0-rc.6):
|
||||||
|
@ -132,13 +161,13 @@ DEPENDENCIES:
|
||||||
- Firebase/Crash
|
- Firebase/Crash
|
||||||
- Firebase/Database
|
- Firebase/Database
|
||||||
- Firebase/DynamicLinks
|
- Firebase/DynamicLinks
|
||||||
|
- Firebase/Firestore
|
||||||
- Firebase/Messaging
|
- Firebase/Messaging
|
||||||
- Firebase/Performance
|
- Firebase/Performance
|
||||||
- Firebase/RemoteConfig
|
- Firebase/RemoteConfig
|
||||||
- Firebase/Storage
|
- Firebase/Storage
|
||||||
- React/BatchedBridge (from `../node_modules/react-native`)
|
- React/BatchedBridge (from `../node_modules/react-native`)
|
||||||
- React/Core (from `../node_modules/react-native`)
|
- React/Core (from `../node_modules/react-native`)
|
||||||
- React/DevSupport (from `../node_modules/react-native`)
|
|
||||||
- React/RCTNetwork (from `../node_modules/react-native`)
|
- React/RCTNetwork (from `../node_modules/react-native`)
|
||||||
- React/RCTText (from `../node_modules/react-native`)
|
- React/RCTText (from `../node_modules/react-native`)
|
||||||
- React/RCTWebSocket (from `../node_modules/react-native`)
|
- React/RCTWebSocket (from `../node_modules/react-native`)
|
||||||
|
@ -154,28 +183,34 @@ EXTERNAL SOURCES:
|
||||||
:path: "../node_modules/react-native/ReactCommon/yoga"
|
:path: "../node_modules/react-native/ReactCommon/yoga"
|
||||||
|
|
||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
Firebase: 9548cae14d69718add12d75a5b312893f7ef89c7
|
BoringSSL: 19083b821ef3ae0f758fae15482e183003b1e265
|
||||||
FirebaseAnalytics: 76f754d37ca5b04f36856729b6af3ca0152d1069
|
Firebase: 83283761a1ef6dc9846e03d08059f51421afbd65
|
||||||
FirebaseAuth: 22f8a5170f31d1f111141950590f071f35df3229
|
FirebaseAnalytics: 722b53c7b32bfc7806b06e0093a2f5180d4f2c5a
|
||||||
FirebaseCore: 9a6cc1e3eaf75905390f9220596ad4fd8f92faee
|
FirebaseAuth: d7f047fbeab98062b98ea933b8d934e0fb1190e2
|
||||||
|
FirebaseCore: 69b1a5ac5f857ba6d5fd9d5fe794f4786dd5e579
|
||||||
FirebaseCrash: cded0fc566c03651aea606a101bc156085f333ca
|
FirebaseCrash: cded0fc566c03651aea606a101bc156085f333ca
|
||||||
FirebaseDatabase: '03940adcac54ce30db06f1fc2136f8581734ce2c'
|
FirebaseDatabase: 607284a103e961d7f5863ee603cab5e85f443bd6
|
||||||
FirebaseDynamicLinks: ed4cb6c42705aaa5e841ed2d76e3a4bddbec10c1
|
FirebaseDynamicLinks: ed4cb6c42705aaa5e841ed2d76e3a4bddbec10c1
|
||||||
FirebaseInstanceID: a4fc702b5a026f7322964376047f1a3f1f7cc6ff
|
FirebaseFirestore: 8e2fd99a621ae6fc6acfac3bdea824fe9d9c128d
|
||||||
FirebaseMessaging: eaf1bfff0193170c04ea3ba3bfe983f68f893118
|
FirebaseInstanceID: 70c2b877e9338971b2429ea5a4293df6961aa44e
|
||||||
FirebasePerformance: d0dc2a1d3dc1bca249d154cb40ee4eae25b455ad
|
FirebaseMessaging: 3dd86bfda2acb680b05c97f3f8ac566e9bb87b2a
|
||||||
|
FirebasePerformance: fa032c27e229eb8c1a8638918793fe2e47465205
|
||||||
FirebaseRemoteConfig: 1c982f73af48ec048c8fa8621d5178cfdffac9aa
|
FirebaseRemoteConfig: 1c982f73af48ec048c8fa8621d5178cfdffac9aa
|
||||||
FirebaseStorage: 0cca42d9b889a0227c3a50121f45a4469fc9eb27
|
FirebaseStorage: 0cca42d9b889a0227c3a50121f45a4469fc9eb27
|
||||||
Google-Mobile-Ads-SDK: f405b7acb098fe89e6fcd05fdbf400c1a5bcb935
|
Google-Mobile-Ads-SDK: ed8004a7265b424568dc84f3d2bbe3ea3fff958f
|
||||||
GoogleToolboxForMac: 8e329f1b599f2512c6b10676d45736bcc2cbbeb0
|
GoogleToolboxForMac: 8e329f1b599f2512c6b10676d45736bcc2cbbeb0
|
||||||
|
gRPC: '07788969b862af21491908f82b83d17ac08c94cd'
|
||||||
|
gRPC-Core: f707ade59c559fe718e27713189607d03b15f571
|
||||||
|
gRPC-ProtoRPC: de7505e493a9d1b6b96c8ea8f976c73100fdf53f
|
||||||
|
gRPC-RxLibrary: 17b9699beb0a838b95b57832244f9ead18e66777
|
||||||
GTMSessionFetcher: ebaa1f79a5366922c1735f1566901f50beba23b7
|
GTMSessionFetcher: ebaa1f79a5366922c1735f1566901f50beba23b7
|
||||||
leveldb-library: 10fb39c39e243db4af1828441162405bbcec1404
|
leveldb-library: 10fb39c39e243db4af1828441162405bbcec1404
|
||||||
nanopb: 5601e6bca2dbf1ed831b519092ec110f66982ca3
|
nanopb: 5601e6bca2dbf1ed831b519092ec110f66982ca3
|
||||||
Protobuf: 03eef2ee0b674770735cf79d9c4d3659cf6908e8
|
Protobuf: 03eef2ee0b674770735cf79d9c4d3659cf6908e8
|
||||||
React: e6ef6a41ec6dd1b7941417d60ca582bf5e9c739d
|
React: e6ef6a41ec6dd1b7941417d60ca582bf5e9c739d
|
||||||
RNFirebase: 60be8c01b94551a12e7be5431189e8ee8cefcdd3
|
RNFirebase: 6508ffd6cab78cc3a84305708a250d7d4b74f2dc
|
||||||
yoga: f9485d2ebf0ca773db2d727ea71b1aa8c9f3e075
|
yoga: f9485d2ebf0ca773db2d727ea71b1aa8c9f3e075
|
||||||
|
|
||||||
PODFILE CHECKSUM: de6bb6c2ad60b7ad1f6c01aa01268ec04eb389a9
|
PODFILE CHECKSUM: b5674be55653f5dda937c8b794d0479900643d45
|
||||||
|
|
||||||
COCOAPODS: 1.2.1
|
COCOAPODS: 1.2.1
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
};
|
};
|
||||||
objectVersion = 46;
|
objectVersion = 46;
|
||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */; };
|
00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */; };
|
||||||
00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */; };
|
00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */; };
|
||||||
|
@ -37,6 +36,7 @@
|
||||||
E51DA6317685417F97A59475 /* libRNVectorIcons.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 0C76E33ACF004369AEB318B1 /* libRNVectorIcons.a */; };
|
E51DA6317685417F97A59475 /* libRNVectorIcons.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 0C76E33ACF004369AEB318B1 /* libRNVectorIcons.a */; };
|
||||||
EA30CACE4CB84AC1BAE59432 /* Entypo.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 4935BEDE99B9436581953E77 /* Entypo.ttf */; };
|
EA30CACE4CB84AC1BAE59432 /* Entypo.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 4935BEDE99B9436581953E77 /* Entypo.ttf */; };
|
||||||
F57EC9E3C5414A99821B73F4 /* EvilIcons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = DB5BB32AF70B41678974C6B4 /* EvilIcons.ttf */; };
|
F57EC9E3C5414A99821B73F4 /* EvilIcons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = DB5BB32AF70B41678974C6B4 /* EvilIcons.ttf */; };
|
||||||
|
58EAF3FA628941C98A02BAE4 /* Feather.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 528FC2DCD44F4567A3FE9F59 /* Feather.ttf */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
/* Begin PBXContainerItemProxy section */
|
||||||
|
@ -322,6 +322,7 @@
|
||||||
CC24EB30F0484352BD65FFC1 /* Octicons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Octicons.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Octicons.ttf"; sourceTree = "<group>"; };
|
CC24EB30F0484352BD65FFC1 /* Octicons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Octicons.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Octicons.ttf"; sourceTree = "<group>"; };
|
||||||
DB5BB32AF70B41678974C6B4 /* EvilIcons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = EvilIcons.ttf; path = "../node_modules/react-native-vector-icons/Fonts/EvilIcons.ttf"; sourceTree = "<group>"; };
|
DB5BB32AF70B41678974C6B4 /* EvilIcons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = EvilIcons.ttf; path = "../node_modules/react-native-vector-icons/Fonts/EvilIcons.ttf"; sourceTree = "<group>"; };
|
||||||
FD3DFC8253C74B6298AFD3B7 /* SimpleLineIcons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = SimpleLineIcons.ttf; path = "../node_modules/react-native-vector-icons/Fonts/SimpleLineIcons.ttf"; sourceTree = "<group>"; };
|
FD3DFC8253C74B6298AFD3B7 /* SimpleLineIcons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = SimpleLineIcons.ttf; path = "../node_modules/react-native-vector-icons/Fonts/SimpleLineIcons.ttf"; sourceTree = "<group>"; };
|
||||||
|
528FC2DCD44F4567A3FE9F59 /* Feather.ttf */ = {isa = PBXFileReference; name = "Feather.ttf"; path = "../node_modules/react-native-vector-icons/Fonts/Feather.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
|
@ -597,6 +598,7 @@
|
||||||
CC24EB30F0484352BD65FFC1 /* Octicons.ttf */,
|
CC24EB30F0484352BD65FFC1 /* Octicons.ttf */,
|
||||||
FD3DFC8253C74B6298AFD3B7 /* SimpleLineIcons.ttf */,
|
FD3DFC8253C74B6298AFD3B7 /* SimpleLineIcons.ttf */,
|
||||||
182271FFECD74C3B92960E1D /* Zocial.ttf */,
|
182271FFECD74C3B92960E1D /* Zocial.ttf */,
|
||||||
|
528FC2DCD44F4567A3FE9F59 /* Feather.ttf */,
|
||||||
);
|
);
|
||||||
name = Resources;
|
name = Resources;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -990,6 +992,7 @@
|
||||||
885057F5D1FA461AAAE0B487 /* Octicons.ttf in Resources */,
|
885057F5D1FA461AAAE0B487 /* Octicons.ttf in Resources */,
|
||||||
42A0E8F428A74B23B4C7D95A /* SimpleLineIcons.ttf in Resources */,
|
42A0E8F428A74B23B4C7D95A /* SimpleLineIcons.ttf in Resources */,
|
||||||
D46EBD0604CE40EFB18F8A35 /* Zocial.ttf in Resources */,
|
D46EBD0604CE40EFB18F8A35 /* Zocial.ttf in Resources */,
|
||||||
|
58EAF3FA628941C98A02BAE4 /* Feather.ttf in Resources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
|
|
@ -34,7 +34,7 @@
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
<key>NSLocationWhenInUseUsageDescription</key>
|
<key>NSLocationWhenInUseUsageDescription</key>
|
||||||
<string></string>
|
<string/>
|
||||||
<key>UIAppFonts</key>
|
<key>UIAppFonts</key>
|
||||||
<array>
|
<array>
|
||||||
<string>Entypo.ttf</string>
|
<string>Entypo.ttf</string>
|
||||||
|
@ -47,6 +47,7 @@
|
||||||
<string>Octicons.ttf</string>
|
<string>Octicons.ttf</string>
|
||||||
<string>SimpleLineIcons.ttf</string>
|
<string>SimpleLineIcons.ttf</string>
|
||||||
<string>Zocial.ttf</string>
|
<string>Zocial.ttf</string>
|
||||||
|
<string>Feather.ttf</string>
|
||||||
</array>
|
</array>
|
||||||
<key>UILaunchStoryboardName</key>
|
<key>UILaunchStoryboardName</key>
|
||||||
<string>LaunchScreen</string>
|
<string>LaunchScreen</string>
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -31,7 +31,7 @@ class CoreContainer extends React.Component {
|
||||||
NetInfo.isConnected.fetch().then((isConnected) => {
|
NetInfo.isConnected.fetch().then((isConnected) => {
|
||||||
this.handleAppStateChange('active'); // Force connect (react debugger issue)
|
this.handleAppStateChange('active'); // Force connect (react debugger issue)
|
||||||
this.props.dispatch(setNetworkState(isConnected));
|
this.props.dispatch(setNetworkState(isConnected));
|
||||||
NetInfo.isConnected.addEventListener('change', this.handleNetworkChange);
|
NetInfo.isConnected.addEventListener('connectionChange', this.handleNetworkChange);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ class CoreContainer extends React.Component {
|
||||||
*/
|
*/
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
AppState.removeEventListener('change', this.handleAppStateChange);
|
AppState.removeEventListener('change', this.handleAppStateChange);
|
||||||
NetInfo.isConnected.removeEventListener('change', this.handleNetworkChange);
|
NetInfo.isConnected.removeEventListener('connectionChange', this.handleNetworkChange);
|
||||||
}
|
}
|
||||||
|
|
||||||
props: Props;
|
props: Props;
|
||||||
|
|
|
@ -0,0 +1,385 @@
|
||||||
|
import sinon from 'sinon';
|
||||||
|
import 'should-sinon';
|
||||||
|
import should from 'should';
|
||||||
|
|
||||||
|
function collectionReferenceTests({ describe, it, context, firebase }) {
|
||||||
|
describe('CollectionReference', () => {
|
||||||
|
context('class', () => {
|
||||||
|
it('should return instance methods', () => {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const collection = firebase.native.firestore().collection('collection-tests');
|
||||||
|
collection.should.have.property('firestore');
|
||||||
|
// TODO: Remaining checks
|
||||||
|
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
context('add()', () => {
|
||||||
|
it('should create Document', () => {
|
||||||
|
return firebase.native.firestore()
|
||||||
|
.collection('collection-tests')
|
||||||
|
.add({ first: 'Ada', last: 'Lovelace', born: 1815 })
|
||||||
|
.then(async (docRef) => {
|
||||||
|
const doc = await firebase.native.firestore().doc(docRef.path).get();
|
||||||
|
doc.data().first.should.equal('Ada');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
context('doc()', () => {
|
||||||
|
it('should create DocumentReference with correct path', () => {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const docRef = firebase.native.firestore().collection('collection-tests').doc('doc');
|
||||||
|
should.equal(docRef.path, 'collection-tests/doc');
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
context('get()', () => {
|
||||||
|
it('should retrieve a single document', () => {
|
||||||
|
return firebase.native.firestore()
|
||||||
|
.collection('collection-tests')
|
||||||
|
.get()
|
||||||
|
.then((querySnapshot) => {
|
||||||
|
should.equal(querySnapshot.size, 1);
|
||||||
|
querySnapshot.forEach((documentSnapshot) => {
|
||||||
|
should.equal(documentSnapshot.data().baz, true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
context('onSnapshot()', () => {
|
||||||
|
it('calls callback with the initial data and then when document changes', () => {
|
||||||
|
return new Promise(async (resolve) => {
|
||||||
|
const collectionRef = firebase.native.firestore().collection('document-tests');
|
||||||
|
const currentDocValue = { name: 'doc1' };
|
||||||
|
const newDocValue = { name: 'updated' };
|
||||||
|
|
||||||
|
const callback = sinon.spy();
|
||||||
|
|
||||||
|
// Test
|
||||||
|
|
||||||
|
let unsubscribe;
|
||||||
|
await new Promise((resolve2) => {
|
||||||
|
unsubscribe = collectionRef.onSnapshot((snapshot) => {
|
||||||
|
snapshot.forEach(doc => callback(doc.data()));
|
||||||
|
resolve2();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
callback.should.be.calledWith(currentDocValue);
|
||||||
|
|
||||||
|
const docRef = firebase.native.firestore().doc('document-tests/doc1');
|
||||||
|
await docRef.set(newDocValue);
|
||||||
|
|
||||||
|
await new Promise((resolve2) => {
|
||||||
|
setTimeout(() => resolve2(), 5);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Assertions
|
||||||
|
|
||||||
|
callback.should.be.calledWith(newDocValue);
|
||||||
|
callback.should.be.calledTwice();
|
||||||
|
|
||||||
|
// Tear down
|
||||||
|
|
||||||
|
unsubscribe();
|
||||||
|
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
context('onSnapshot()', () => {
|
||||||
|
it('calls callback with the initial data and then when document is added', () => {
|
||||||
|
return new Promise(async (resolve) => {
|
||||||
|
const collectionRef = firebase.native.firestore().collection('document-tests');
|
||||||
|
const currentDocValue = { name: 'doc1' };
|
||||||
|
const newDocValue = { name: 'updated' };
|
||||||
|
|
||||||
|
const callback = sinon.spy();
|
||||||
|
|
||||||
|
// Test
|
||||||
|
|
||||||
|
let unsubscribe;
|
||||||
|
await new Promise((resolve2) => {
|
||||||
|
unsubscribe = collectionRef.onSnapshot((snapshot) => {
|
||||||
|
snapshot.forEach(doc => callback(doc.data()));
|
||||||
|
resolve2();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
callback.should.be.calledWith(currentDocValue);
|
||||||
|
|
||||||
|
const docRef = firebase.native.firestore().doc('document-tests/doc2');
|
||||||
|
await docRef.set(newDocValue);
|
||||||
|
|
||||||
|
await new Promise((resolve2) => {
|
||||||
|
setTimeout(() => resolve2(), 5);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Assertions
|
||||||
|
|
||||||
|
callback.should.be.calledWith(currentDocValue);
|
||||||
|
callback.should.be.calledWith(newDocValue);
|
||||||
|
callback.should.be.calledThrice();
|
||||||
|
|
||||||
|
// Tear down
|
||||||
|
|
||||||
|
unsubscribe();
|
||||||
|
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
context('onSnapshot()', () => {
|
||||||
|
it('doesn\'t call callback when the ref is updated with the same value', async () => {
|
||||||
|
return new Promise(async (resolve) => {
|
||||||
|
const collectionRef = firebase.native.firestore().collection('document-tests');
|
||||||
|
const currentDocValue = { name: 'doc1' };
|
||||||
|
|
||||||
|
const callback = sinon.spy();
|
||||||
|
|
||||||
|
// Test
|
||||||
|
|
||||||
|
let unsubscribe;
|
||||||
|
await new Promise((resolve2) => {
|
||||||
|
unsubscribe = collectionRef.onSnapshot((snapshot) => {
|
||||||
|
snapshot.forEach(doc => callback(doc.data()));
|
||||||
|
resolve2();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
callback.should.be.calledWith(currentDocValue);
|
||||||
|
|
||||||
|
const docRef = firebase.native.firestore().doc('document-tests/doc1');
|
||||||
|
await docRef.set(currentDocValue);
|
||||||
|
|
||||||
|
await new Promise((resolve2) => {
|
||||||
|
setTimeout(() => resolve2(), 5);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Assertions
|
||||||
|
|
||||||
|
callback.should.be.calledOnce(); // Callback is not called again
|
||||||
|
|
||||||
|
// Tear down
|
||||||
|
|
||||||
|
unsubscribe();
|
||||||
|
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
context('onSnapshot()', () => {
|
||||||
|
it('allows binding multiple callbacks to the same ref', () => {
|
||||||
|
return new Promise(async (resolve) => {
|
||||||
|
// Setup
|
||||||
|
const collectionRef = firebase.native.firestore().collection('document-tests');
|
||||||
|
const currentDocValue = { name: 'doc1' };
|
||||||
|
const newDocValue = { name: 'updated' };
|
||||||
|
|
||||||
|
const callbackA = sinon.spy();
|
||||||
|
const callbackB = sinon.spy();
|
||||||
|
|
||||||
|
// Test
|
||||||
|
let unsubscribeA;
|
||||||
|
let unsubscribeB;
|
||||||
|
await new Promise((resolve2) => {
|
||||||
|
unsubscribeA = collectionRef.onSnapshot((snapshot) => {
|
||||||
|
snapshot.forEach(doc => callbackA(doc.data()));
|
||||||
|
resolve2();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
await new Promise((resolve2) => {
|
||||||
|
unsubscribeB = collectionRef.onSnapshot((snapshot) => {
|
||||||
|
snapshot.forEach(doc => callbackB(doc.data()));
|
||||||
|
resolve2();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
callbackA.should.be.calledWith(currentDocValue);
|
||||||
|
callbackA.should.be.calledOnce();
|
||||||
|
|
||||||
|
callbackB.should.be.calledWith(currentDocValue);
|
||||||
|
callbackB.should.be.calledOnce();
|
||||||
|
|
||||||
|
const docRef = firebase.native.firestore().doc('document-tests/doc1');
|
||||||
|
await docRef.set(newDocValue);
|
||||||
|
|
||||||
|
await new Promise((resolve2) => {
|
||||||
|
setTimeout(() => resolve2(), 5);
|
||||||
|
});
|
||||||
|
|
||||||
|
callbackA.should.be.calledWith(newDocValue);
|
||||||
|
callbackB.should.be.calledWith(newDocValue);
|
||||||
|
|
||||||
|
callbackA.should.be.calledTwice();
|
||||||
|
callbackB.should.be.calledTwice();
|
||||||
|
|
||||||
|
// Tear down
|
||||||
|
|
||||||
|
unsubscribeA();
|
||||||
|
unsubscribeB();
|
||||||
|
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
context('onSnapshot()', () => {
|
||||||
|
it('listener stops listening when unsubscribed', () => {
|
||||||
|
return new Promise(async (resolve) => {
|
||||||
|
// Setup
|
||||||
|
const collectionRef = firebase.native.firestore().collection('document-tests');
|
||||||
|
const currentDocValue = { name: 'doc1' };
|
||||||
|
const newDocValue = { name: 'updated' };
|
||||||
|
|
||||||
|
const callbackA = sinon.spy();
|
||||||
|
const callbackB = sinon.spy();
|
||||||
|
|
||||||
|
// Test
|
||||||
|
let unsubscribeA;
|
||||||
|
let unsubscribeB;
|
||||||
|
await new Promise((resolve2) => {
|
||||||
|
unsubscribeA = collectionRef.onSnapshot((snapshot) => {
|
||||||
|
snapshot.forEach(doc => callbackA(doc.data()));
|
||||||
|
resolve2();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
await new Promise((resolve2) => {
|
||||||
|
unsubscribeB = collectionRef.onSnapshot((snapshot) => {
|
||||||
|
snapshot.forEach(doc => callbackB(doc.data()));
|
||||||
|
resolve2();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
callbackA.should.be.calledWith(currentDocValue);
|
||||||
|
callbackA.should.be.calledOnce();
|
||||||
|
|
||||||
|
callbackB.should.be.calledWith(currentDocValue);
|
||||||
|
callbackB.should.be.calledOnce();
|
||||||
|
|
||||||
|
const docRef = firebase.native.firestore().doc('document-tests/doc1');
|
||||||
|
await docRef.set(newDocValue);
|
||||||
|
|
||||||
|
await new Promise((resolve2) => {
|
||||||
|
setTimeout(() => resolve2(), 5);
|
||||||
|
});
|
||||||
|
|
||||||
|
callbackA.should.be.calledWith(newDocValue);
|
||||||
|
callbackB.should.be.calledWith(newDocValue);
|
||||||
|
|
||||||
|
callbackA.should.be.calledTwice();
|
||||||
|
callbackB.should.be.calledTwice();
|
||||||
|
|
||||||
|
// Unsubscribe A
|
||||||
|
|
||||||
|
unsubscribeA();
|
||||||
|
|
||||||
|
await docRef.set(currentDocValue);
|
||||||
|
|
||||||
|
await new Promise((resolve2) => {
|
||||||
|
setTimeout(() => resolve2(), 5);
|
||||||
|
});
|
||||||
|
|
||||||
|
callbackB.should.be.calledWith(currentDocValue);
|
||||||
|
|
||||||
|
callbackA.should.be.calledTwice();
|
||||||
|
callbackB.should.be.calledThrice();
|
||||||
|
|
||||||
|
// Unsubscribe B
|
||||||
|
|
||||||
|
unsubscribeB();
|
||||||
|
|
||||||
|
await docRef.set(newDocValue);
|
||||||
|
|
||||||
|
await new Promise((resolve2) => {
|
||||||
|
setTimeout(() => resolve2(), 5);
|
||||||
|
});
|
||||||
|
|
||||||
|
callbackA.should.be.calledTwice();
|
||||||
|
callbackB.should.be.calledThrice();
|
||||||
|
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Where
|
||||||
|
context('where()', () => {
|
||||||
|
it('correctly handles == boolean values', () => {
|
||||||
|
return firebase.native.firestore()
|
||||||
|
.collection('collection-tests')
|
||||||
|
.where('baz', '==', true)
|
||||||
|
.get()
|
||||||
|
.then((querySnapshot) => {
|
||||||
|
should.equal(querySnapshot.size, 1);
|
||||||
|
querySnapshot.forEach((documentSnapshot) => {
|
||||||
|
should.equal(documentSnapshot.data().baz, true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('correctly handles == string values', () => {
|
||||||
|
return firebase.native.firestore()
|
||||||
|
.collection('collection-tests')
|
||||||
|
.where('foo', '==', 'bar')
|
||||||
|
.get()
|
||||||
|
.then((querySnapshot) => {
|
||||||
|
should.equal(querySnapshot.size, 1);
|
||||||
|
querySnapshot.forEach((documentSnapshot) => {
|
||||||
|
should.equal(documentSnapshot.data().foo, 'bar');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('correctly handles == null values', () => {
|
||||||
|
return firebase.native.firestore()
|
||||||
|
.collection('collection-tests')
|
||||||
|
.where('naz', '==', null)
|
||||||
|
.get()
|
||||||
|
.then((querySnapshot) => {
|
||||||
|
should.equal(querySnapshot.size, 1);
|
||||||
|
querySnapshot.forEach((documentSnapshot) => {
|
||||||
|
should.equal(documentSnapshot.data().naz, null);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('correctly handles >= number values', () => {
|
||||||
|
return firebase.native.firestore()
|
||||||
|
.collection('collection-tests')
|
||||||
|
.where('daz', '>=', 123)
|
||||||
|
.get()
|
||||||
|
.then((querySnapshot) => {
|
||||||
|
should.equal(querySnapshot.size, 1);
|
||||||
|
querySnapshot.forEach((documentSnapshot) => {
|
||||||
|
should.equal(documentSnapshot.data().daz, 123);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('correctly handles <= float values', () => {
|
||||||
|
return firebase.native.firestore()
|
||||||
|
.collection('collection-tests')
|
||||||
|
.where('gaz', '<=', 12.1234666)
|
||||||
|
.get()
|
||||||
|
.then((querySnapshot) => {
|
||||||
|
should.equal(querySnapshot.size, 1);
|
||||||
|
querySnapshot.forEach((documentSnapshot) => {
|
||||||
|
should.equal(documentSnapshot.data().gaz, 12.1234567);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export default collectionReferenceTests;
|
|
@ -0,0 +1,299 @@
|
||||||
|
import sinon from 'sinon';
|
||||||
|
import 'should-sinon';
|
||||||
|
import should from 'should';
|
||||||
|
|
||||||
|
function collectionReferenceTests({ describe, it, context, firebase }) {
|
||||||
|
describe('DocumentReference', () => {
|
||||||
|
context('class', () => {
|
||||||
|
it('should return instance methods', () => {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const document = firebase.native.firestore().doc('document-tests/doc1');
|
||||||
|
document.should.have.property('firestore');
|
||||||
|
// TODO: Remaining checks
|
||||||
|
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
context('delete()', () => {
|
||||||
|
it('should delete Document', () => {
|
||||||
|
return firebase.native.firestore()
|
||||||
|
.doc('document-tests/doc1')
|
||||||
|
.delete()
|
||||||
|
.then(async () => {
|
||||||
|
const doc = await firebase.native.firestore().doc('document-tests/doc1').get();
|
||||||
|
should.equal(doc.exists, false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
context('onSnapshot()', () => {
|
||||||
|
it('calls callback with the initial data and then when value changes', () => {
|
||||||
|
return new Promise(async (resolve) => {
|
||||||
|
const docRef = firebase.native.firestore().doc('document-tests/doc1');
|
||||||
|
const currentDataValue = { name: 'doc1' };
|
||||||
|
const newDataValue = { name: 'updated' };
|
||||||
|
|
||||||
|
const callback = sinon.spy();
|
||||||
|
|
||||||
|
// Test
|
||||||
|
|
||||||
|
let unsubscribe;
|
||||||
|
await new Promise((resolve2) => {
|
||||||
|
unsubscribe = docRef.onSnapshot((snapshot) => {
|
||||||
|
callback(snapshot.data());
|
||||||
|
resolve2();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
callback.should.be.calledWith(currentDataValue);
|
||||||
|
|
||||||
|
// Update the document
|
||||||
|
|
||||||
|
await docRef.set(newDataValue);
|
||||||
|
|
||||||
|
await new Promise((resolve2) => {
|
||||||
|
setTimeout(() => resolve2(), 5);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Assertions
|
||||||
|
|
||||||
|
callback.should.be.calledWith(newDataValue);
|
||||||
|
callback.should.be.calledTwice();
|
||||||
|
|
||||||
|
// Tear down
|
||||||
|
|
||||||
|
unsubscribe();
|
||||||
|
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
context('onSnapshot()', () => {
|
||||||
|
it('doesn\'t call callback when the ref is updated with the same value', async () => {
|
||||||
|
return new Promise(async (resolve) => {
|
||||||
|
const docRef = firebase.native.firestore().doc('document-tests/doc1');
|
||||||
|
const currentDataValue = { name: 'doc1' };
|
||||||
|
|
||||||
|
const callback = sinon.spy();
|
||||||
|
|
||||||
|
// Test
|
||||||
|
|
||||||
|
let unsubscribe;
|
||||||
|
await new Promise((resolve2) => {
|
||||||
|
unsubscribe = docRef.onSnapshot((snapshot) => {
|
||||||
|
callback(snapshot.data());
|
||||||
|
resolve2();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
callback.should.be.calledWith(currentDataValue);
|
||||||
|
|
||||||
|
await docRef.set(currentDataValue);
|
||||||
|
|
||||||
|
await new Promise((resolve2) => {
|
||||||
|
setTimeout(() => resolve2(), 5);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Assertions
|
||||||
|
|
||||||
|
callback.should.be.calledOnce(); // Callback is not called again
|
||||||
|
|
||||||
|
// Tear down
|
||||||
|
|
||||||
|
unsubscribe();
|
||||||
|
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
context('onSnapshot()', () => {
|
||||||
|
it('allows binding multiple callbacks to the same ref', () => {
|
||||||
|
return new Promise(async (resolve) => {
|
||||||
|
// Setup
|
||||||
|
const docRef = firebase.native.firestore().doc('document-tests/doc1');
|
||||||
|
const currentDataValue = { name: 'doc1' };
|
||||||
|
const newDataValue = { name: 'updated' };
|
||||||
|
|
||||||
|
const callbackA = sinon.spy();
|
||||||
|
const callbackB = sinon.spy();
|
||||||
|
|
||||||
|
// Test
|
||||||
|
let unsubscribeA;
|
||||||
|
let unsubscribeB;
|
||||||
|
await new Promise((resolve2) => {
|
||||||
|
unsubscribeA = docRef.onSnapshot((snapshot) => {
|
||||||
|
callbackA(snapshot.data());
|
||||||
|
resolve2();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await new Promise((resolve2) => {
|
||||||
|
unsubscribeB = docRef.onSnapshot((snapshot) => {
|
||||||
|
callbackB(snapshot.data());
|
||||||
|
resolve2();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
callbackA.should.be.calledWith(currentDataValue);
|
||||||
|
callbackA.should.be.calledOnce();
|
||||||
|
|
||||||
|
callbackB.should.be.calledWith(currentDataValue);
|
||||||
|
callbackB.should.be.calledOnce();
|
||||||
|
|
||||||
|
await docRef.set(newDataValue);
|
||||||
|
|
||||||
|
await new Promise((resolve2) => {
|
||||||
|
setTimeout(() => resolve2(), 5);
|
||||||
|
});
|
||||||
|
|
||||||
|
callbackA.should.be.calledWith(newDataValue);
|
||||||
|
callbackB.should.be.calledWith(newDataValue);
|
||||||
|
|
||||||
|
callbackA.should.be.calledTwice();
|
||||||
|
callbackB.should.be.calledTwice();
|
||||||
|
|
||||||
|
// Tear down
|
||||||
|
|
||||||
|
unsubscribeA();
|
||||||
|
unsubscribeB();
|
||||||
|
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
context('onSnapshot()', () => {
|
||||||
|
it('listener stops listening when unsubscribed', () => {
|
||||||
|
return new Promise(async (resolve) => {
|
||||||
|
// Setup
|
||||||
|
const docRef = firebase.native.firestore().doc('document-tests/doc1');
|
||||||
|
const currentDataValue = { name: 'doc1' };
|
||||||
|
const newDataValue = { name: 'updated' };
|
||||||
|
|
||||||
|
const callbackA = sinon.spy();
|
||||||
|
const callbackB = sinon.spy();
|
||||||
|
|
||||||
|
// Test
|
||||||
|
let unsubscribeA;
|
||||||
|
let unsubscribeB;
|
||||||
|
await new Promise((resolve2) => {
|
||||||
|
unsubscribeA = docRef.onSnapshot((snapshot) => {
|
||||||
|
callbackA(snapshot.data());
|
||||||
|
resolve2();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await new Promise((resolve2) => {
|
||||||
|
unsubscribeB = docRef.onSnapshot((snapshot) => {
|
||||||
|
callbackB(snapshot.data());
|
||||||
|
resolve2();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
callbackA.should.be.calledWith(currentDataValue);
|
||||||
|
callbackA.should.be.calledOnce();
|
||||||
|
|
||||||
|
callbackB.should.be.calledWith(currentDataValue);
|
||||||
|
callbackB.should.be.calledOnce();
|
||||||
|
|
||||||
|
await docRef.set(newDataValue);
|
||||||
|
|
||||||
|
await new Promise((resolve2) => {
|
||||||
|
setTimeout(() => resolve2(), 5);
|
||||||
|
});
|
||||||
|
|
||||||
|
callbackA.should.be.calledWith(newDataValue);
|
||||||
|
callbackB.should.be.calledWith(newDataValue);
|
||||||
|
|
||||||
|
callbackA.should.be.calledTwice();
|
||||||
|
callbackB.should.be.calledTwice();
|
||||||
|
|
||||||
|
// Unsubscribe A
|
||||||
|
|
||||||
|
unsubscribeA();
|
||||||
|
|
||||||
|
await docRef.set(currentDataValue);
|
||||||
|
|
||||||
|
await new Promise((resolve2) => {
|
||||||
|
setTimeout(() => resolve2(), 5);
|
||||||
|
});
|
||||||
|
|
||||||
|
callbackB.should.be.calledWith(currentDataValue);
|
||||||
|
|
||||||
|
callbackA.should.be.calledTwice();
|
||||||
|
callbackB.should.be.calledThrice();
|
||||||
|
|
||||||
|
// Unsubscribe B
|
||||||
|
|
||||||
|
unsubscribeB();
|
||||||
|
|
||||||
|
await docRef.set(newDataValue);
|
||||||
|
|
||||||
|
await new Promise((resolve2) => {
|
||||||
|
setTimeout(() => resolve2(), 5);
|
||||||
|
});
|
||||||
|
|
||||||
|
callbackA.should.be.calledTwice();
|
||||||
|
callbackB.should.be.calledThrice();
|
||||||
|
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
context('set()', () => {
|
||||||
|
it('should create Document', () => {
|
||||||
|
return firebase.native.firestore()
|
||||||
|
.doc('document-tests/doc2')
|
||||||
|
.set({ name: 'doc2' })
|
||||||
|
.then(async () => {
|
||||||
|
const doc = await firebase.native.firestore().doc('document-tests/doc2').get();
|
||||||
|
doc.data().name.should.equal('doc2');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
context('set()', () => {
|
||||||
|
it('should merge Document', () => {
|
||||||
|
return firebase.native.firestore()
|
||||||
|
.doc('document-tests/doc1')
|
||||||
|
.set({ merge: 'merge' }, { merge: true })
|
||||||
|
.then(async () => {
|
||||||
|
const doc = await firebase.native.firestore().doc('document-tests/doc1').get();
|
||||||
|
doc.data().name.should.equal('doc1');
|
||||||
|
doc.data().merge.should.equal('merge');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
context('set()', () => {
|
||||||
|
it('should overwrite Document', () => {
|
||||||
|
return firebase.native.firestore()
|
||||||
|
.doc('document-tests/doc1')
|
||||||
|
.set({ name: 'overwritten' })
|
||||||
|
.then(async () => {
|
||||||
|
const doc = await firebase.native.firestore().doc('document-tests/doc1').get();
|
||||||
|
doc.data().name.should.equal('overwritten');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
context('update()', () => {
|
||||||
|
it('should update Document', () => {
|
||||||
|
return firebase.native.firestore()
|
||||||
|
.doc('document-tests/doc1')
|
||||||
|
.set({ name: 'updated' })
|
||||||
|
.then(async () => {
|
||||||
|
const doc = await firebase.native.firestore().doc('document-tests/doc1').get();
|
||||||
|
doc.data().name.should.equal('updated');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export default collectionReferenceTests;
|
|
@ -0,0 +1,63 @@
|
||||||
|
import should from 'should';
|
||||||
|
|
||||||
|
function firestoreTests({ describe, it, context, firebase }) {
|
||||||
|
describe('firestore()', () => {
|
||||||
|
context('collection()', () => {
|
||||||
|
it('should create CollectionReference with the right id', () => {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const collectionRef = firebase.native.firestore().collection('collection1/doc1/collection2');
|
||||||
|
should.equal(collectionRef.id, 'collection2');
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
context('doc()', () => {
|
||||||
|
it('should create DocumentReference with correct path', () => {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const docRef = firebase.native.firestore().doc('collection1/doc1/collection2/doc2');
|
||||||
|
should.equal(docRef.path, 'collection1/doc1/collection2/doc2');
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
context('batch()', () => {
|
||||||
|
it('should create / update / delete as expected', () => {
|
||||||
|
const ayRef = firebase.native.firestore().collection('firestore-tests').doc('AY');
|
||||||
|
const lRef = firebase.native.firestore().collection('firestore-tests').doc('LON');
|
||||||
|
const nycRef = firebase.native.firestore().collection('firestore-tests').doc('NYC');
|
||||||
|
const sfRef = firebase.native.firestore().collection('firestore-tests').doc('SF');
|
||||||
|
|
||||||
|
return firebase.native.firestore()
|
||||||
|
.batch()
|
||||||
|
.set(ayRef, { name: 'Aylesbury' })
|
||||||
|
.set(lRef, { name: 'London' })
|
||||||
|
.set(nycRef, { name: 'New York City' })
|
||||||
|
.set(sfRef, { name: 'San Francisco' })
|
||||||
|
.update(nycRef, { population: 1000000 })
|
||||||
|
.update(sfRef, { name: 'San Fran' })
|
||||||
|
.set(lRef, { population: 3000000 }, { merge: true })
|
||||||
|
.delete(ayRef)
|
||||||
|
.commit()
|
||||||
|
.then(async () => {
|
||||||
|
const ayDoc = await ayRef.get();
|
||||||
|
should.equal(ayDoc.exists, false);
|
||||||
|
|
||||||
|
const lDoc = await lRef.get();
|
||||||
|
lDoc.data().name.should.equal('London');
|
||||||
|
lDoc.data().population.should.equal(3000000);
|
||||||
|
|
||||||
|
const nycDoc = await nycRef.get();
|
||||||
|
nycDoc.data().name.should.equal('New York City');
|
||||||
|
nycDoc.data().population.should.equal(1000000);
|
||||||
|
|
||||||
|
const sfDoc = await sfRef.get();
|
||||||
|
sfDoc.data().name.should.equal('San Fran');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export default firestoreTests;
|
|
@ -0,0 +1,66 @@
|
||||||
|
import firebase from '../../firebase';
|
||||||
|
import TestSuite from '../../../lib/TestSuite';
|
||||||
|
|
||||||
|
/*
|
||||||
|
Test suite files
|
||||||
|
*/
|
||||||
|
import collectionReferenceTests from './collectionReferenceTests';
|
||||||
|
import documentReferenceTests from './documentReferenceTests';
|
||||||
|
import firestoreTests from './firestoreTests';
|
||||||
|
|
||||||
|
const suite = new TestSuite('Firestore', 'firebase.firestore()', firebase);
|
||||||
|
|
||||||
|
const testGroups = [
|
||||||
|
collectionReferenceTests,
|
||||||
|
documentReferenceTests,
|
||||||
|
firestoreTests,
|
||||||
|
];
|
||||||
|
|
||||||
|
function firestoreTestSuite(testSuite) {
|
||||||
|
testSuite.beforeEach(async () => {
|
||||||
|
this.collectionTestsCollection = testSuite.firebase.native.firestore().collection('collection-tests');
|
||||||
|
this.documentTestsCollection = testSuite.firebase.native.firestore().collection('document-tests');
|
||||||
|
this.firestoreTestsCollection = testSuite.firebase.native.firestore().collection('firestore-tests');
|
||||||
|
// Clean the collections in case the last run failed
|
||||||
|
await cleanCollection(this.collectionTestsCollection);
|
||||||
|
await cleanCollection(this.documentTestsCollection);
|
||||||
|
await cleanCollection(this.firestoreTestsCollection);
|
||||||
|
|
||||||
|
await this.collectionTestsCollection.add({
|
||||||
|
baz: true,
|
||||||
|
daz: 123,
|
||||||
|
foo: 'bar',
|
||||||
|
gaz: 12.1234567,
|
||||||
|
naz: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
await this.documentTestsCollection.doc('doc1').set({
|
||||||
|
name: 'doc1',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
testSuite.afterEach(async () => {
|
||||||
|
await cleanCollection(this.collectionTestsCollection);
|
||||||
|
await cleanCollection(this.documentTestsCollection);
|
||||||
|
await cleanCollection(this.firestoreTestsCollection);
|
||||||
|
});
|
||||||
|
|
||||||
|
testGroups.forEach((testGroup) => {
|
||||||
|
testGroup(testSuite);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Register tests with test suite
|
||||||
|
*/
|
||||||
|
suite.addTests(firestoreTestSuite);
|
||||||
|
|
||||||
|
export default suite;
|
||||||
|
|
||||||
|
/* HELPER FUNCTIONS */
|
||||||
|
async function cleanCollection(collection) {
|
||||||
|
const collectionTestsDocs = await collection.get();
|
||||||
|
const tasks = [];
|
||||||
|
collectionTestsDocs.forEach(doc => tasks.push(doc.ref.delete()));
|
||||||
|
await Promise.all(tasks);
|
||||||
|
}
|
|
@ -1,30 +1,32 @@
|
||||||
import { setSuiteStatus, setTestStatus } from '../actions/TestActions';
|
import { setSuiteStatus, setTestStatus } from '../actions/TestActions';
|
||||||
import analytics from './analytics/index';
|
import analytics from './analytics';
|
||||||
import crash from './crash/index';
|
import crash from './crash';
|
||||||
import core from './core/index';
|
import core from './core';
|
||||||
import database from './database/index';
|
import database from './database';
|
||||||
import messaging from './messaging/index';
|
import messaging from './messaging';
|
||||||
import storage from './storage/index';
|
import storage from './storage';
|
||||||
import auth from './auth/index';
|
import auth from './auth';
|
||||||
import config from './config/index';
|
import config from './config';
|
||||||
import performance from './perf/index';
|
import performance from './perf';
|
||||||
import admob from './admob/index';
|
import admob from './admob';
|
||||||
|
import firestore from './firestore';
|
||||||
|
|
||||||
window.getCoverage = function getCoverage() {
|
window.getCoverage = function getCoverage() {
|
||||||
return (JSON.stringify(global.__coverage__));
|
return (JSON.stringify(global.__coverage__));
|
||||||
};
|
};
|
||||||
|
|
||||||
const testSuiteInstances = [
|
const testSuiteInstances = [
|
||||||
database,
|
|
||||||
auth,
|
|
||||||
analytics,
|
|
||||||
messaging,
|
|
||||||
crash,
|
|
||||||
core,
|
|
||||||
storage,
|
|
||||||
config,
|
|
||||||
performance,
|
|
||||||
admob,
|
admob,
|
||||||
|
analytics,
|
||||||
|
auth,
|
||||||
|
config,
|
||||||
|
core,
|
||||||
|
crash,
|
||||||
|
database,
|
||||||
|
firestore,
|
||||||
|
messaging,
|
||||||
|
performance,
|
||||||
|
storage,
|
||||||
];
|
];
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
Loading…
Reference in New Issue