merge master into omer_links
This commit is contained in:
commit
af47f7ca03
|
@ -1,22 +1,23 @@
|
|||
package io.invertase.firebase;
|
||||
|
||||
import android.util.Log;
|
||||
import android.app.Activity;
|
||||
import android.content.IntentSender;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.List;
|
||||
import java.util.HashMap;
|
||||
import java.util.ArrayList;
|
||||
|
||||
// react
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
import com.facebook.react.bridge.Callback;
|
||||
import com.facebook.react.bridge.Promise;
|
||||
import com.facebook.react.bridge.Callback;
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
import com.facebook.react.bridge.LifecycleEventListener;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
|
||||
// play services
|
||||
import com.google.android.gms.common.ConnectionResult;
|
||||
|
@ -25,7 +26,7 @@ import com.google.firebase.FirebaseApp;
|
|||
import com.google.firebase.FirebaseOptions;
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public class RNFirebaseModule extends ReactContextBaseJavaModule implements LifecycleEventListener {
|
||||
public class RNFirebaseModule extends ReactContextBaseJavaModule {
|
||||
private static final String TAG = "RNFirebase";
|
||||
|
||||
public RNFirebaseModule(ReactApplicationContext reactContext) {
|
||||
|
@ -37,29 +38,17 @@ public class RNFirebaseModule extends ReactContextBaseJavaModule implements Life
|
|||
return TAG;
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void promptPlayServices() {
|
||||
GoogleApiAvailability gapi = GoogleApiAvailability.getInstance();
|
||||
int status = gapi.isGooglePlayServicesAvailable(getReactApplicationContext());
|
||||
|
||||
if (status != ConnectionResult.SUCCESS && gapi.isUserResolvableError(status)) {
|
||||
Activity activity = getCurrentActivity();
|
||||
if (activity != null) {
|
||||
gapi.getErrorDialog(activity, status, 2404).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void initializeApp(String appName, ReadableMap options, Callback callback) {
|
||||
FirebaseOptions.Builder builder = new FirebaseOptions.Builder();
|
||||
|
||||
builder.setApplicationId(options.getString("appId"));
|
||||
builder.setGcmSenderId(options.getString("messagingSenderId"));
|
||||
builder.setApiKey(options.getString("apiKey"));
|
||||
builder.setApplicationId(options.getString("appId"));
|
||||
builder.setProjectId(options.getString("projectId"));
|
||||
builder.setDatabaseUrl(options.getString("databaseURL"));
|
||||
builder.setStorageBucket(options.getString("storageBucket"));
|
||||
builder.setGcmSenderId(options.getString("messagingSenderId"));
|
||||
// todo firebase sdk has no client id setter
|
||||
|
||||
FirebaseApp.initializeApp(getReactApplicationContext(), builder.build(), appName);
|
||||
|
@ -84,6 +73,9 @@ public class RNFirebaseModule extends ReactContextBaseJavaModule implements Life
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
private WritableMap getPlayServicesStatus() {
|
||||
GoogleApiAvailability gapi = GoogleApiAvailability.getInstance();
|
||||
final int status = gapi.isGooglePlayServicesAvailable(getReactApplicationContext());
|
||||
|
@ -93,38 +85,76 @@ public class RNFirebaseModule extends ReactContextBaseJavaModule implements Life
|
|||
result.putBoolean("isAvailable", true);
|
||||
} else {
|
||||
result.putBoolean("isAvailable", false);
|
||||
result.putBoolean("isUserResolvableError", gapi.isUserResolvableError(status));
|
||||
result.putString("error", gapi.getErrorString(status));
|
||||
result.putBoolean("isUserResolvableError", gapi.isUserResolvableError(status));
|
||||
result.putBoolean("hasResolution", new ConnectionResult(status).hasResolution());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHostResume() {
|
||||
WritableMap params = Arguments.createMap();
|
||||
params.putBoolean("isForeground", true);
|
||||
Utils.sendEvent(getReactApplicationContext(), "RNFirebaseAppState", params);
|
||||
/**
|
||||
* Prompt the device user to update play services
|
||||
*/
|
||||
@ReactMethod
|
||||
public void promptForPlayServices() {
|
||||
GoogleApiAvailability gapi = GoogleApiAvailability.getInstance();
|
||||
int status = gapi.isGooglePlayServicesAvailable(getReactApplicationContext());
|
||||
|
||||
if (status != ConnectionResult.SUCCESS && gapi.isUserResolvableError(status)) {
|
||||
Activity activity = getCurrentActivity();
|
||||
if (activity != null) {
|
||||
gapi.getErrorDialog(activity, status, status).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHostPause() {
|
||||
WritableMap params = Arguments.createMap();
|
||||
params.putBoolean("isForeground", false);
|
||||
Utils.sendEvent(getReactApplicationContext(), "RNFirebaseAppState", params);
|
||||
/**
|
||||
* Prompt the device user to update play services
|
||||
*/
|
||||
@ReactMethod
|
||||
public void resolutionForPlayServices() {
|
||||
int status = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(getReactApplicationContext());
|
||||
ConnectionResult connectionResult = new ConnectionResult(status);
|
||||
|
||||
if (!connectionResult.isSuccess() && connectionResult.hasResolution()) {
|
||||
Activity activity = getCurrentActivity();
|
||||
if (activity != null) {
|
||||
try {
|
||||
connectionResult.startResolutionForResult(activity, status);
|
||||
} catch (IntentSender.SendIntentException error) {
|
||||
Log.d(TAG, "resolutionForPlayServices", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHostDestroy() {
|
||||
|
||||
/**
|
||||
* Prompt the device user to update play services
|
||||
*/
|
||||
@ReactMethod
|
||||
public void makePlayServicesAvailable() {
|
||||
GoogleApiAvailability gapi = GoogleApiAvailability.getInstance();
|
||||
int status = gapi.isGooglePlayServicesAvailable(getReactApplicationContext());
|
||||
|
||||
if (status != ConnectionResult.SUCCESS) {
|
||||
Activity activity = getCurrentActivity();
|
||||
if (activity != null) {
|
||||
gapi.makeGooglePlayServicesAvailable(activity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getConstants() {
|
||||
FirebaseApp firebaseApp;
|
||||
Map<String, Object> constants = new HashMap<>();
|
||||
List<FirebaseApp> firebaseAppList = FirebaseApp.getApps(getReactApplicationContext());
|
||||
List<Map<String, Object>> appMapsList = new ArrayList<Map<String, Object>>();
|
||||
|
||||
Map<String, Object> constants = new HashMap<>();
|
||||
List<Map<String, Object>> appMapsList = new ArrayList<>();
|
||||
List<FirebaseApp> firebaseAppList = FirebaseApp.getApps(getReactApplicationContext());
|
||||
|
||||
// TODO no way to get client id currently from app options - firebase sdk issue
|
||||
for (FirebaseApp app : firebaseAppList) {
|
||||
String appName = app.getName();
|
||||
FirebaseOptions appOptions = app.getOptions();
|
||||
|
@ -133,16 +163,16 @@ public class RNFirebaseModule extends ReactContextBaseJavaModule implements Life
|
|||
appProps.put("name", appName);
|
||||
appProps.put("apiKey", appOptions.getApiKey());
|
||||
appProps.put("appId", appOptions.getApplicationId());
|
||||
appProps.put("projectId", appOptions.getProjectId());
|
||||
appProps.put("databaseURL", appOptions.getDatabaseUrl());
|
||||
appProps.put("messagingSenderId", appOptions.getGcmSenderId());
|
||||
appProps.put("projectId", appOptions.getProjectId());
|
||||
appProps.put("storageBucket", appOptions.getStorageBucket());
|
||||
// TODO no way to get client id currently from app options
|
||||
|
||||
appMapsList.add(appProps);
|
||||
}
|
||||
|
||||
constants.put("apps", appMapsList);
|
||||
constants.put("googleApiAvailability", getPlayServicesStatus());
|
||||
constants.put("playServicesAvailability", getPlayServicesStatus());
|
||||
return constants;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,8 +7,6 @@ import com.google.firebase.firestore.DocumentChange;
|
|||
import com.google.firebase.firestore.DocumentSnapshot;
|
||||
import com.google.firebase.firestore.QuerySnapshot;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
@ -155,16 +153,14 @@ public class FirestoreSerialize {
|
|||
writableArray.pushDouble(((Float) item).doubleValue());
|
||||
} else if (itemClass == String.class) {
|
||||
writableArray.pushString(item.toString());
|
||||
} else if (itemClass == Map.class) {
|
||||
} else if (Map.class.isAssignableFrom(itemClass)) {
|
||||
writableArray.pushMap((objectMapToWritable((Map<String, Object>) item)));
|
||||
} else if (itemClass == Arrays.class) {
|
||||
writableArray.pushArray(objectArrayToWritable((Object[]) item));
|
||||
} else if (itemClass == List.class || itemClass == ArrayList.class) {
|
||||
} else if (List.class.isAssignableFrom(itemClass)) {
|
||||
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);
|
||||
throw new RuntimeException("Cannot convert object of type " + itemClass);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -194,16 +190,14 @@ public class FirestoreSerialize {
|
|||
map.putDouble(key, ((Float) value).doubleValue());
|
||||
} else if (valueClass == String.class) {
|
||||
map.putString(key, value.toString());
|
||||
} else if (valueClass == Map.class) {
|
||||
} else if (Map.class.isAssignableFrom(valueClass)) {
|
||||
map.putMap(key, (objectMapToWritable((Map<String, Object>) value)));
|
||||
} else if (valueClass == Arrays.class) {
|
||||
map.putArray(key, objectArrayToWritable((Object[]) value));
|
||||
} else if (valueClass == List.class || valueClass == ArrayList.class) {
|
||||
} else if (List.class.isAssignableFrom(valueClass)) {
|
||||
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);
|
||||
throw new RuntimeException("Cannot convert object of type " + valueClass);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,9 +57,10 @@ public class RNFirebaseFirestore extends ReactContextBaseJavaModule {
|
|||
|
||||
@ReactMethod
|
||||
public void collectionOnSnapshot(String appName, String path, ReadableArray filters,
|
||||
ReadableArray orders, ReadableMap options, String listenerId) {
|
||||
ReadableArray orders, ReadableMap options, String listenerId,
|
||||
ReadableMap queryListenOptions) {
|
||||
RNFirebaseFirestoreCollectionReference ref = getCollectionForAppPath(appName, path, filters, orders, options);
|
||||
ref.onSnapshot(listenerId);
|
||||
ref.onSnapshot(listenerId, queryListenOptions);
|
||||
}
|
||||
|
||||
|
||||
|
@ -145,9 +146,10 @@ public class RNFirebaseFirestore extends ReactContextBaseJavaModule {
|
|||
}
|
||||
|
||||
@ReactMethod
|
||||
public void documentOnSnapshot(String appName, String path, String listenerId) {
|
||||
public void documentOnSnapshot(String appName, String path, String listenerId,
|
||||
ReadableMap docListenOptions) {
|
||||
RNFirebaseFirestoreDocumentReference ref = getDocumentForAppPath(appName, path);
|
||||
ref.onSnapshot(listenerId);
|
||||
ref.onSnapshot(listenerId, docListenOptions);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
|
|
|
@ -12,10 +12,12 @@ 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.DocumentListenOptions;
|
||||
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.QueryListenOptions;
|
||||
import com.google.firebase.firestore.QuerySnapshot;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
@ -71,7 +73,7 @@ public class RNFirebaseFirestoreCollectionReference {
|
|||
}
|
||||
}
|
||||
|
||||
public void onSnapshot(final String listenerId) {
|
||||
public void onSnapshot(final String listenerId, final ReadableMap queryListenOptions) {
|
||||
if (!collectionSnapshotListeners.containsKey(listenerId)) {
|
||||
final EventListener<QuerySnapshot> listener = new EventListener<QuerySnapshot>() {
|
||||
@Override
|
||||
|
@ -87,7 +89,19 @@ public class RNFirebaseFirestoreCollectionReference {
|
|||
}
|
||||
}
|
||||
};
|
||||
ListenerRegistration listenerRegistration = this.query.addSnapshotListener(listener);
|
||||
QueryListenOptions options = new QueryListenOptions();
|
||||
if (queryListenOptions != null) {
|
||||
if (queryListenOptions.hasKey("includeDocumentMetadataChanges")
|
||||
&& queryListenOptions.getBoolean("includeDocumentMetadataChanges")) {
|
||||
options.includeDocumentMetadataChanges();
|
||||
}
|
||||
if (queryListenOptions.hasKey("includeQueryMetadataChanges")
|
||||
&& queryListenOptions.getBoolean("includeQueryMetadataChanges")) {
|
||||
options.includeQueryMetadataChanges();
|
||||
}
|
||||
}
|
||||
|
||||
ListenerRegistration listenerRegistration = this.query.addSnapshotListener(options, listener);
|
||||
collectionSnapshotListeners.put(listenerId, listenerRegistration);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ 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.DocumentListenOptions;
|
||||
import com.google.firebase.firestore.DocumentReference;
|
||||
import com.google.firebase.firestore.DocumentSnapshot;
|
||||
import com.google.firebase.firestore.EventListener;
|
||||
|
@ -85,7 +86,7 @@ public class RNFirebaseFirestoreDocumentReference {
|
|||
}
|
||||
}
|
||||
|
||||
public void onSnapshot(final String listenerId) {
|
||||
public void onSnapshot(final String listenerId, final ReadableMap docListenOptions) {
|
||||
if (!documentSnapshotListeners.containsKey(listenerId)) {
|
||||
final EventListener<DocumentSnapshot> listener = new EventListener<DocumentSnapshot>() {
|
||||
@Override
|
||||
|
@ -101,7 +102,11 @@ public class RNFirebaseFirestoreDocumentReference {
|
|||
}
|
||||
}
|
||||
};
|
||||
ListenerRegistration listenerRegistration = this.ref.addSnapshotListener(listener);
|
||||
DocumentListenOptions options = new DocumentListenOptions();
|
||||
if (docListenOptions != null && docListenOptions.hasKey("includeMetadataChanges") && docListenOptions.getBoolean("includeMetadataChanges")) {
|
||||
options.includeMetadataChanges();
|
||||
}
|
||||
ListenerRegistration listenerRegistration = this.ref.addSnapshotListener(options, listener);
|
||||
documentSnapshotListeners.put(listenerId, listenerRegistration);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
# Android Installation
|
||||
|
||||
## 1) Setup google-services.json
|
||||
## 1) Link RNFirebase
|
||||
|
||||
Run `react-native link react-native-firebase`
|
||||
|
||||
## 2) Setup google-services.json
|
||||
Download the `google-services.json` file provided by Firebase in the _Add Firebase to Android_ platform menu in your Firebase configuration console. This file should be downloaded to `YOUR_PROJECT/android/app/google-services.json`.
|
||||
|
||||
Next you'll have to add the google-services gradle plugin in order to parse it.
|
||||
|
@ -23,28 +27,19 @@ In your app build.gradle file, add the gradle plugin at the VERY BOTTOM of the f
|
|||
apply plugin: 'com.google.gms.google-services'
|
||||
```
|
||||
|
||||
## 2) Link RNFirebase
|
||||
## 3) Setup Firebase
|
||||
|
||||
RNFirebase is split into separate modules to allow you to only include the Firebase functionality that you need in your application.
|
||||
|
||||
First add the project path to `android/settings.gradle`:
|
||||
|
||||
```groovy
|
||||
include ':react-native-firebase'
|
||||
project(':react-native-firebase').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-firebase/android')
|
||||
```
|
||||
|
||||
Now you need to include RNFirebase and the required Firebase dependencies in our `android/app/build.gradle` so that they are compiled as part of React Native. In the `dependencies` listing, add the appropriate `compile` lines:
|
||||
Now you need to the required Firebase dependencies in our `android/app/build.gradle` so that they are compiled as part of React Native. In the `dependencies` listing, add the appropriate `compile` lines:
|
||||
|
||||
```groovy
|
||||
dependencies {
|
||||
// RNFirebase required dependencies
|
||||
// This should be added already
|
||||
compile(project(':react-native-firebase')) {
|
||||
transitive = false
|
||||
}
|
||||
compile "com.google.firebase:firebase-core:11.4.2"
|
||||
|
||||
// If you are receiving Google Play API availability issues, add the following dependency
|
||||
// RNFirebase required dependencies
|
||||
compile "com.google.firebase:firebase-core:11.4.2"
|
||||
compile "com.google.android.gms:play-services-base:11.4.2"
|
||||
|
||||
// RNFirebase optional dependencies
|
||||
|
@ -61,7 +56,7 @@ dependencies {
|
|||
}
|
||||
```
|
||||
|
||||
Google Play services from 11.4.2 onwards require their dependencies to be downloaded from Google's Maven respository so add the
|
||||
Google Play services from 11.2.0 onwards require their dependencies to be downloaded from Google's Maven respository so add the
|
||||
required reference to the repositories section of the *project* level build.gradle
|
||||
`android/build.gradle`
|
||||
|
||||
|
@ -84,13 +79,17 @@ allprojects {
|
|||
}
|
||||
```
|
||||
|
||||
## 4) Install RNFirebase modules
|
||||
|
||||
RNFirebase is split into separate modules to allow you to only include the Firebase functionality that you need in your application.
|
||||
|
||||
To install `react-native-firebase` in your project, you'll need to import the packages you need from `io.invertase.firebase` in your project's `android/app/src/main/java/com/[app name]/MainApplication.java` and list them as packages for ReactNative in the `getPackages()` function:
|
||||
|
||||
```java
|
||||
package com.youcompany.application;
|
||||
// ...
|
||||
// Required package
|
||||
import io.invertase.firebase.RNFirebasePackage; // <-- Add this line
|
||||
import io.invertase.firebase.RNFirebasePackage; // <-- This should be added already
|
||||
// Optional packages - add as appropriate
|
||||
import io.invertase.firebase.admob.RNFirebaseAdMobPackage; //Firebase AdMob
|
||||
import io.invertase.firebase.analytics.RNFirebaseAnalyticsPackage; // Firebase Analytics
|
||||
|
@ -111,7 +110,7 @@ public class MainApplication extends Application implements ReactApplication {
|
|||
protected List<ReactPackage> getPackages() {
|
||||
return Arrays.<ReactPackage>asList(
|
||||
new MainReactPackage(),
|
||||
new RNFirebasePackage(), // <-- Add this line
|
||||
new RNFirebasePackage(), // <-- This should be added already
|
||||
// Add these packages as appropriate
|
||||
new RNFirebaseAdMobPackage(),
|
||||
new RNFirebaseAnalyticsPackage(),
|
||||
|
@ -131,7 +130,7 @@ public class MainApplication extends Application implements ReactApplication {
|
|||
}
|
||||
```
|
||||
|
||||
## 3) Cloud Messaging (optional)
|
||||
## 5) Cloud Messaging (optional)
|
||||
|
||||
If you plan on using [Firebase Cloud Messaging](https://firebase.google.com/docs/cloud-messaging/), add the following to `android/app/src/main/AndroidManifest.xml`.
|
||||
|
||||
|
@ -182,7 +181,7 @@ If you would like to schedule local notifications then you also need to add the
|
|||
</receiver>
|
||||
```
|
||||
|
||||
## 4) Performance Monitoring (optional)
|
||||
## 6) Performance Monitoring (optional)
|
||||
|
||||
If you'd like to take advantage of Firebase's [Performance Monitoring](https://firebase.google.com/docs/perf-mon/), the following additions
|
||||
to your project setup are required:
|
||||
|
|
|
@ -2,10 +2,14 @@
|
|||
|
||||
Please note that there is a known issue when using Cocoapods with the `use_frameworks!` enabled. This is explained [here](https://github.com/invertase/react-native-firebase/issues/252#issuecomment-316340974). Unfortunately we don't currently have a workaround, but are engaging with Firebase directly to try and resolve the problem.
|
||||
|
||||
## 1) Setup GoogleService-Info.plist
|
||||
## 1) Link RNFirebase
|
||||
|
||||
Run `react-native link react-native-firebase`
|
||||
|
||||
## 2) Setup GoogleService-Info.plist
|
||||
Setup the `GoogleService-Info.plist` file by following the instructions and adding it to the root of your project at `ios/[YOUR APP NAME]/GoogleService-Info.plist` [here](https://firebase.google.com/docs/ios/setup#add_firebase_to_your_app).
|
||||
|
||||
### 1.1) Initialisation
|
||||
### 2.1) Initialisation
|
||||
Make sure you've added the following to the top of your `ios/[YOUR APP NAME]]/AppDelegate.m` file:
|
||||
|
||||
`#import <Firebase.h>`
|
||||
|
@ -14,11 +18,11 @@ and this to the `didFinishLaunchingWithOptions:(NSDictionary *)launchOptions` me
|
|||
|
||||
`[FIRApp configure];`
|
||||
|
||||
## 2) Setup RNFirebase
|
||||
## 3) Setup Firebase Pods
|
||||
|
||||
Unfortunately, due to the fact that Firebase is much easier to setup using Cocoapods, *we do not recommend* `react-native link` as it is not customisable enough for our needs and we have had numerous problems reported.
|
||||
Firebase recommends using Cocoapods to install the Firebase SDK.
|
||||
|
||||
### 2.0) If you don't already have Cocoapods set up
|
||||
### 3.0) If you don't already have Cocoapods set up
|
||||
Follow the instructions to install Cocoapods and create your Podfile [here](https://firebase.google.com/docs/ios/setup#add_the_sdk).
|
||||
|
||||
**NOTE: The Podfile needs to be initialised in the `ios` directory of your project. Make sure to update cocoapods libs first by running `pod update`**
|
||||
|
@ -50,18 +54,17 @@ Follow the instructions to install Cocoapods and create your Podfile [here](http
|
|||
- Uncomment the `# platform :ios, '9.0'` line by removing the `#` character
|
||||
- Change the version as required
|
||||
|
||||
### 2.1) Check the Podfile platform version
|
||||
### 3.1) Check the Podfile platform version
|
||||
We recommend using a minimum platform version of at least 9.0 for your application to ensure that the correct version of the Firebase libraries are used. To do this, you need to uncomment or make sure the following line is present at the top of your `Podfile`:
|
||||
|
||||
`platform :ios, '9.0'`
|
||||
|
||||
### 2.2) Add the required pods
|
||||
### 3.2) Add the required pods
|
||||
Simply add the following to your `Podfile` either at the top level, or within the main project target:
|
||||
|
||||
```ruby
|
||||
# Required by RNFirebase
|
||||
pod 'Firebase/Core'
|
||||
pod 'RNFirebase', :path => '../node_modules/react-native-firebase'
|
||||
|
||||
# [OPTIONAL PODS] - comment out pods for firebase products you won't be using.
|
||||
pod 'Firebase/AdMob'
|
||||
|
@ -75,27 +78,6 @@ pod 'Firebase/RemoteConfig'
|
|||
pod 'Firebase/Storage'
|
||||
```
|
||||
|
||||
If you do not already have React and Yoga installed as pods, then add Yoga and React to your `Podfile` as follows:
|
||||
|
||||
```ruby
|
||||
pod "Yoga", :path => "../node_modules/react-native/ReactCommon/yoga"
|
||||
pod 'React', :path => '../node_modules/react-native', :subspecs => [
|
||||
'BatchedBridge', # Required For React Native 0.45.0+
|
||||
'Core',
|
||||
# Add any other subspecs you want to use in your project
|
||||
]
|
||||
|
||||
#Also add this at the very bottom of your Podfile
|
||||
|
||||
post_install do |installer|
|
||||
installer.pods_project.targets.each do |target|
|
||||
if target.name == "React"
|
||||
target.remove_from_project
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
Run `pod install`.
|
||||
|
||||
**NOTE: You need to use the `ios/[YOUR APP NAME].xcworkspace` instead of the `ios/[YOUR APP NAME].xcproj` file from now on.**
|
||||
|
@ -106,24 +88,24 @@ Run `pod install`.
|
|||
**Resolution**
|
||||
- Run `npm install --save react-native-firebase` from the root of your project
|
||||
|
||||
## 3) Cloud Messaging (optional)
|
||||
## 4) Cloud Messaging (optional)
|
||||
|
||||
If you plan on using [Firebase Cloud Messaging](https://firebase.google.com/docs/cloud-messaging/) then, you need to:
|
||||
|
||||
**NOTE: FCM does not work on the iOS simulator, you must test is using a real device. This is a restriction enforced by Apple for some unknown reason.**
|
||||
|
||||
### 3.1) Set up certificates
|
||||
### 4.1) Set up certificates
|
||||
|
||||
Follow the instructions at https://firebase.google.com/docs/cloud-messaging/ios/certs
|
||||
|
||||
### 3.2) Enable capabilities
|
||||
### 4.2) Enable capabilities
|
||||
|
||||
In Xcode, enable the following capabilities:
|
||||
|
||||
1) Push Notifications
|
||||
2) Background modes > Remote notifications
|
||||
|
||||
### 3.3) Update `AppDelegate.h`
|
||||
### 4.3) Update `AppDelegate.h`
|
||||
|
||||
Add the following import:
|
||||
|
||||
|
@ -133,7 +115,7 @@ Change the interface descriptor to:
|
|||
|
||||
`@interface AppDelegate : UIResponder <UIApplicationDelegate,UNUserNotificationCenterDelegate>`
|
||||
|
||||
### 3.4) Update `AppDelegate.m`
|
||||
### 4.4) Update `AppDelegate.m`
|
||||
|
||||
Add the following import:
|
||||
|
||||
|
@ -172,7 +154,7 @@ didReceiveNotificationResponse:(UNNotificationResponse *)response
|
|||
}
|
||||
```
|
||||
|
||||
### 3.5) Debugging
|
||||
### 4.5) Debugging
|
||||
|
||||
If you're having problems with messages not being received, check out the following blog post for help:
|
||||
|
||||
|
|
|
@ -51,6 +51,8 @@ declare module "react-native-firebase" {
|
|||
|
||||
static initializeApp(options?: any | RNFirebase.configurationOptions, name?: string): FireBase;
|
||||
|
||||
static app(name?: string): FireBase;
|
||||
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
|
|
|
@ -431,6 +431,8 @@
|
|||
"${SRCROOT}/../../../ios/Pods/FirebaseCore/Frameworks",
|
||||
"${SRCROOT}/../../../ios/Pods/FirebaseCrash/Frameworks",
|
||||
"${SRCROOT}/../../../ios/Pods/FirebaseDatabase/Frameworks",
|
||||
"${SRCROOT}/../../../ios/Pods/FirebaseFirestore/Frameworks",
|
||||
"${SRCROOT}/../../../ios/Pods/FirebaseInstanceID/Frameworks",
|
||||
"${SRCROOT}/../../../ios/Pods/FirebaseMessaging/Frameworks",
|
||||
"${SRCROOT}/../../../ios/Pods/FirebasePerformance/Frameworks",
|
||||
"${SRCROOT}/../../../ios/Pods/FirebaseRemoteConfig/Frameworks",
|
||||
|
@ -467,6 +469,8 @@
|
|||
"${SRCROOT}/../../../ios/Pods/FirebaseCore/Frameworks",
|
||||
"${SRCROOT}/../../../ios/Pods/FirebaseCrash/Frameworks",
|
||||
"${SRCROOT}/../../../ios/Pods/FirebaseDatabase/Frameworks",
|
||||
"${SRCROOT}/../../../ios/Pods/FirebaseFirestore/Frameworks",
|
||||
"${SRCROOT}/../../../ios/Pods/FirebaseInstanceID/Frameworks",
|
||||
"${SRCROOT}/../../../ios/Pods/FirebaseMessaging/Frameworks",
|
||||
"${SRCROOT}/../../../ios/Pods/FirebasePerformance/Frameworks",
|
||||
"${SRCROOT}/../../../ios/Pods/FirebaseRemoteConfig/Frameworks",
|
||||
|
|
|
@ -42,9 +42,10 @@ RCT_EXPORT_METHOD(collectionOnSnapshot:(NSString *) appName
|
|||
filters:(NSArray *) filters
|
||||
orders:(NSArray *) orders
|
||||
options:(NSDictionary *) options
|
||||
listenerId:(nonnull NSString *) listenerId) {
|
||||
listenerId:(nonnull NSString *) listenerId
|
||||
queryListenOptions:(NSDictionary *) queryListenOptions) {
|
||||
RNFirebaseFirestoreCollectionReference *ref = [self getCollectionForAppPath:appName path:path filters:filters orders:orders options:options];
|
||||
[ref onSnapshot:listenerId];
|
||||
[ref onSnapshot:listenerId queryListenOptions:queryListenOptions];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(documentBatch:(NSString *) appName
|
||||
|
@ -128,9 +129,10 @@ RCT_EXPORT_METHOD(documentOffSnapshot:(NSString *) appName
|
|||
|
||||
RCT_EXPORT_METHOD(documentOnSnapshot:(NSString *) appName
|
||||
path:(NSString *) path
|
||||
listenerId:(nonnull NSString *) listenerId) {
|
||||
listenerId:(nonnull NSString *) listenerId
|
||||
docListenOptions:(NSDictionary *) docListenOptions) {
|
||||
RNFirebaseFirestoreDocumentReference *ref = [self getDocumentForAppPath:appName path:path];
|
||||
[ref onSnapshot:listenerId];
|
||||
[ref onSnapshot:listenerId docListenOptions:docListenOptions];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(documentSet:(NSString *) appName
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
- (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;
|
||||
- (void)onSnapshot:(NSString *)listenerId queryListenOptions:(NSDictionary *) queryListenOptions;
|
||||
+ (NSDictionary *)snapshotToDictionary:(FIRQuerySnapshot *)querySnapshot;
|
||||
@end
|
||||
|
||||
|
|
|
@ -49,7 +49,8 @@ static NSMutableDictionary *_listeners;
|
|||
}
|
||||
}
|
||||
|
||||
- (void)onSnapshot:(NSString *) listenerId {
|
||||
- (void)onSnapshot:(NSString *) listenerId
|
||||
queryListenOptions:(NSDictionary *) queryListenOptions {
|
||||
if (_listeners[listenerId] == nil) {
|
||||
id listenerBlock = ^(FIRQuerySnapshot * _Nullable snapshot, NSError * _Nullable error) {
|
||||
if (error) {
|
||||
|
@ -63,7 +64,18 @@ static NSMutableDictionary *_listeners;
|
|||
[self handleQuerySnapshotEvent:listenerId querySnapshot:snapshot];
|
||||
}
|
||||
};
|
||||
id<FIRListenerRegistration> listener = [_query addSnapshotListener:listenerBlock];
|
||||
|
||||
FIRQueryListenOptions *options = [[FIRQueryListenOptions alloc] init];
|
||||
if (queryListenOptions) {
|
||||
if (queryListenOptions[@"includeDocumentMetadataChanges"]) {
|
||||
[options includeDocumentMetadataChanges:TRUE];
|
||||
}
|
||||
if (queryListenOptions[@"includeQueryMetadataChanges"]) {
|
||||
[options includeQueryMetadataChanges:TRUE];
|
||||
}
|
||||
}
|
||||
|
||||
id<FIRListenerRegistration> listener = [_query addSnapshotListenerWithOptions:options listener:listenerBlock];
|
||||
_listeners[listenerId] = listener;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
- (void)delete:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject;
|
||||
- (void)get:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject;
|
||||
+ (void)offSnapshot:(NSString *)listenerId;
|
||||
- (void)onSnapshot:(NSString *)listenerId;
|
||||
- (void)onSnapshot:(NSString *)listenerId docListenOptions:(NSDictionary *) docListenOptions;
|
||||
- (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;
|
||||
|
|
|
@ -61,7 +61,8 @@ static NSMutableDictionary *_listeners;
|
|||
}
|
||||
}
|
||||
|
||||
- (void)onSnapshot:(NSString *) listenerId {
|
||||
- (void)onSnapshot:(NSString *) listenerId
|
||||
docListenOptions:(NSDictionary *) docListenOptions {
|
||||
if (_listeners[listenerId] == nil) {
|
||||
id listenerBlock = ^(FIRDocumentSnapshot * _Nullable snapshot, NSError * _Nullable error) {
|
||||
if (error) {
|
||||
|
@ -75,8 +76,11 @@ static NSMutableDictionary *_listeners;
|
|||
[self handleDocumentSnapshotEvent:listenerId documentSnapshot:snapshot];
|
||||
}
|
||||
};
|
||||
|
||||
id<FIRListenerRegistration> listener = [_ref addSnapshotListener:listenerBlock];
|
||||
FIRDocumentListenOptions *options = [[FIRDocumentListenOptions alloc] init];
|
||||
if (docListenOptions && docListenOptions[@"includeMetadataChanges"]) {
|
||||
[options includeMetadataChanges:TRUE];
|
||||
}
|
||||
id<FIRListenerRegistration> listener = [_ref addSnapshotListenerWithOptions:options listener:listenerBlock];
|
||||
_listeners[listenerId] = listener;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { NativeModules } from 'react-native';
|
||||
|
||||
import INTERNALS from './internals';
|
||||
import { isObject } from './utils';
|
||||
import { isObject, isAndroid } from './utils';
|
||||
|
||||
import AdMob, { statics as AdMobStatics } from './modules/admob';
|
||||
import Auth, { statics as AuthStatics } from './modules/auth';
|
||||
|
@ -14,6 +14,7 @@ import Database, { statics as DatabaseStatics } from './modules/database';
|
|||
import Messaging, { statics as MessagingStatics } from './modules/messaging';
|
||||
import Firestore, { statics as FirestoreStatics } from './modules/firestore';
|
||||
import Links, { statics as LinksStatics } from './modules/links';
|
||||
import Utils, { statics as UtilsStatics } from './modules/utils';
|
||||
|
||||
const FirebaseCoreModule = NativeModules.RNFirebase;
|
||||
|
||||
|
@ -35,10 +36,11 @@ export default class FirebaseApp {
|
|||
this.crash = this._staticsOrModuleInstance({}, Crash);
|
||||
this.database = this._staticsOrModuleInstance(DatabaseStatics, Database);
|
||||
this.firestore = this._staticsOrModuleInstance(FirestoreStatics, Firestore);
|
||||
this.links = this._staticsOrModuleInstance(LinksStatics, Links);
|
||||
this.messaging = this._staticsOrModuleInstance(MessagingStatics, Messaging);
|
||||
this.perf = this._staticsOrModuleInstance({}, Performance);
|
||||
this.storage = this._staticsOrModuleInstance(StorageStatics, Storage);
|
||||
this.links = this._staticsOrModuleInstance(LinksStatics, Links);
|
||||
this.utils = this._staticsOrModuleInstance(UtilsStatics, Utils);
|
||||
this._extendedProps = {};
|
||||
}
|
||||
|
||||
|
@ -152,6 +154,11 @@ export default class FirebaseApp {
|
|||
const getInstance = () => {
|
||||
const _name = `_${InstanceClass._NAMESPACE}`;
|
||||
|
||||
if (isAndroid && InstanceClass._NAMESPACE !== Utils._NAMESPACE && !INTERNALS.FLAGS.checkedPlayServices) {
|
||||
INTERNALS.FLAGS.checkedPlayServices = true;
|
||||
this.utils().checkPlayServicesAvailability();
|
||||
}
|
||||
|
||||
if (!this._namespaces[_name]) {
|
||||
this._namespaces[_name] = new InstanceClass(this);
|
||||
}
|
||||
|
|
|
@ -4,11 +4,9 @@
|
|||
*/
|
||||
import { NativeModules, NativeEventEmitter } from 'react-native';
|
||||
|
||||
import { isObject, isString } from './utils';
|
||||
|
||||
import INTERNALS from './internals';
|
||||
import PACKAGE from './../package.json';
|
||||
import FirebaseApp from './firebase-app';
|
||||
import { isObject, isString, isAndroid } from './utils';
|
||||
|
||||
// module imports
|
||||
import AdMob, { statics as AdMobStatics } from './modules/admob';
|
||||
|
@ -22,6 +20,7 @@ import Storage, { statics as StorageStatics } from './modules/storage';
|
|||
import Database, { statics as DatabaseStatics } from './modules/database';
|
||||
import Messaging, { statics as MessagingStatics } from './modules/messaging';
|
||||
import Firestore, { statics as FirestoreStatics } from './modules/firestore';
|
||||
import Utils, { statics as UtilsStatics } from './modules/utils';
|
||||
|
||||
const FirebaseCoreModule = NativeModules.RNFirebase;
|
||||
|
||||
|
@ -34,13 +33,7 @@ class FirebaseCore {
|
|||
throw (new Error(INTERNALS.STRINGS.ERROR_MISSING_CORE));
|
||||
}
|
||||
|
||||
for (let i = 0, len = FirebaseCoreModule.apps.length; i < len; i++) {
|
||||
const app = FirebaseCoreModule.apps[i];
|
||||
const options = Object.assign({}, app);
|
||||
delete options.name;
|
||||
INTERNALS.APPS[app.name] = new FirebaseApp(app.name, options);
|
||||
INTERNALS.APPS[app.name]._initializeApp(true);
|
||||
}
|
||||
this._initializeNativeApps();
|
||||
|
||||
// modules
|
||||
this.admob = this._appNamespaceOrStatics(AdMobStatics, AdMob);
|
||||
|
@ -50,10 +43,25 @@ class FirebaseCore {
|
|||
this.crash = this._appNamespaceOrStatics({}, Crash);
|
||||
this.database = this._appNamespaceOrStatics(DatabaseStatics, Database);
|
||||
this.firestore = this._appNamespaceOrStatics(FirestoreStatics, Firestore);
|
||||
this.links = this._appNamespaceOrStatics(LinksStatics, Links);
|
||||
this.messaging = this._appNamespaceOrStatics(MessagingStatics, Messaging);
|
||||
this.perf = this._appNamespaceOrStatics(DatabaseStatics, Performance);
|
||||
this.storage = this._appNamespaceOrStatics(StorageStatics, Storage);
|
||||
this.links = this._appNamespaceOrStatics(LinksStatics, Links);
|
||||
this.utils = this._appNamespaceOrStatics(UtilsStatics, Utils);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bootstraps all native app instances that were discovered on boot
|
||||
* @private
|
||||
*/
|
||||
_initializeNativeApps() {
|
||||
for (let i = 0, len = FirebaseCoreModule.apps.length; i < len; i++) {
|
||||
const app = FirebaseCoreModule.apps[i];
|
||||
const options = Object.assign({}, app);
|
||||
delete options.name;
|
||||
INTERNALS.APPS[app.name] = new FirebaseApp(app.name, options);
|
||||
INTERNALS.APPS[app.name]._initializeApp(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -141,42 +149,6 @@ class FirebaseCore {
|
|||
return Object.values(INTERNALS.APPS);
|
||||
}
|
||||
|
||||
/**
|
||||
* The current RNFirebase SDK version.
|
||||
*/
|
||||
get SDK_VERSION() {
|
||||
return PACKAGE.version;
|
||||
}
|
||||
|
||||
/**
|
||||
* The platform specific default app name
|
||||
*/
|
||||
get DEFAULT_APP_NAME() {
|
||||
return INTERNALS.STRINGS.DEFAULT_APP_NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns props from the android GoogleApiAvailability sdk
|
||||
* @android
|
||||
* @return {RNFirebase.GoogleApiAvailabilityType|{isAvailable: boolean, status: number}}
|
||||
*/
|
||||
get googleApiAvailability(): GoogleApiAvailabilityType {
|
||||
return FirebaseCoreModule.googleApiAvailability || { isAvailable: true, status: 0 };
|
||||
}
|
||||
|
||||
/*
|
||||
* CONFIG METHODS
|
||||
*/
|
||||
/**
|
||||
* Set the global logging level for all logs.
|
||||
*
|
||||
* @param booleanOrDebugString
|
||||
*/
|
||||
setLogLevel(booleanOrDebugString) {
|
||||
INTERNALS.OPTIONS.logLevel = booleanOrDebugString;
|
||||
Log.setLevel(booleanOrDebugString);
|
||||
}
|
||||
|
||||
/*
|
||||
* INTERNALS
|
||||
*/
|
||||
|
@ -209,7 +181,6 @@ class FirebaseCore {
|
|||
|
||||
/**
|
||||
*
|
||||
* @param namespace
|
||||
* @param statics
|
||||
* @param InstanceClass
|
||||
* @return {function(FirebaseApp=)}
|
||||
|
@ -217,8 +188,10 @@ class FirebaseCore {
|
|||
*/
|
||||
_appNamespaceOrStatics(statics = {}, InstanceClass): Function {
|
||||
const namespace = InstanceClass._NAMESPACE;
|
||||
|
||||
const getNamespace = (app?: FirebaseApp) => {
|
||||
let _app = app;
|
||||
|
||||
// throw an error if it's not a valid app instance
|
||||
if (_app && !(_app instanceof FirebaseApp)) throw new Error(INTERNALS.STRINGS.ERROR_NOT_APP(namespace));
|
||||
|
||||
|
@ -231,6 +204,7 @@ class FirebaseCore {
|
|||
Object.assign(getNamespace, statics, {
|
||||
nativeModuleExists: !!NativeModules[InstanceClass._NATIVE_MODULE],
|
||||
});
|
||||
|
||||
return getNamespace;
|
||||
}
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@ declare type GoogleApiAvailabilityType = {
|
|||
status: number,
|
||||
isAvailable: boolean,
|
||||
isUserResolvableError?: boolean,
|
||||
hasResolution?: boolean,
|
||||
error?: string
|
||||
};
|
||||
|
||||
|
|
|
@ -22,10 +22,43 @@ const GRADLE_DEPS = {
|
|||
admob: 'ads',
|
||||
};
|
||||
|
||||
const PLAY_SERVICES_CODES = {
|
||||
1: {
|
||||
code: 'SERVICE_MISSING',
|
||||
message: 'Google Play services is missing on this device.',
|
||||
},
|
||||
2: {
|
||||
code: 'SERVICE_VERSION_UPDATE_REQUIRED',
|
||||
message: 'The installed version of Google Play services on this device is out of date.',
|
||||
},
|
||||
3: {
|
||||
code: 'SERVICE_DISABLED',
|
||||
message: 'The installed version of Google Play services has been disabled on this device.',
|
||||
},
|
||||
9: {
|
||||
code: 'SERVICE_INVALID',
|
||||
message: 'The version of the Google Play services installed on this device is not authentic.',
|
||||
},
|
||||
18: {
|
||||
code: 'SERVICE_UPDATING',
|
||||
message: 'Google Play services is currently being updated on this device.',
|
||||
},
|
||||
19: {
|
||||
code: 'SERVICE_MISSING_PERMISSION',
|
||||
message: 'Google Play service doesn\'t have one or more required permissions.',
|
||||
},
|
||||
};
|
||||
|
||||
export default {
|
||||
// default options
|
||||
OPTIONS: {
|
||||
logLevel: 'warn',
|
||||
errorOnMissingPlayServices: true,
|
||||
promptOnMissingPlayServices: true,
|
||||
},
|
||||
|
||||
FLAGS: {
|
||||
checkedPlayServices: false,
|
||||
},
|
||||
|
||||
// track all initialized firebase apps
|
||||
|
@ -141,15 +174,15 @@ export default {
|
|||
/**
|
||||
* @return {string}
|
||||
*/
|
||||
ERROR_UNSUPPORTED_CLASS_METHOD(classname, method) {
|
||||
return `${classname}.${method}() is unsupported by the native Firebase SDKs.`;
|
||||
ERROR_UNSUPPORTED_CLASS_METHOD(className, method) {
|
||||
return `${className}.${method}() is unsupported by the native Firebase SDKs.`;
|
||||
},
|
||||
|
||||
/**
|
||||
* @return {string}
|
||||
*/
|
||||
ERROR_UNSUPPORTED_CLASS_PROPERTY(classname, property) {
|
||||
return `${classname}.${property} is unsupported by the native Firebase SDKs.`;
|
||||
ERROR_UNSUPPORTED_CLASS_PROPERTY(className, property) {
|
||||
return `${className}.${property} is unsupported by the native Firebase SDKs.`;
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -159,6 +192,32 @@ export default {
|
|||
return `firebase.${module._NAMESPACE}().${method}() is unsupported by the native Firebase SDKs.`;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* @return {string}
|
||||
*/
|
||||
ERROR_PLAY_SERVICES(statusCode) {
|
||||
const knownError = PLAY_SERVICES_CODES[statusCode];
|
||||
let start = 'Google Play Services is required to run firebase services on android but a valid installation was not found on this device.';
|
||||
|
||||
if (statusCode === 2) {
|
||||
start = 'Google Play Services is out of date and may cause some firebase services like authentication to hang when used. It is recommended that you update it.';
|
||||
}
|
||||
|
||||
// eslint-disable-next-line prefer-template
|
||||
return `${start}\r\n\r\n` +
|
||||
'-------------------------\r\n' +
|
||||
(knownError ?
|
||||
`${knownError.code}: ${knownError.message} (code ${statusCode})` :
|
||||
`A specific play store availability reason reason was not available (unknown code: ${statusCode || null})`
|
||||
) +
|
||||
'\r\n-------------------------' +
|
||||
'\r\n\r\n' +
|
||||
'For more information on how to resolve this issue, configure Play Services checks or for guides on how to validate Play Services on your users devices see the link below:' +
|
||||
'\r\n\r\nhttp://invertase.link/play-services';
|
||||
},
|
||||
|
||||
|
||||
DEFAULT_APP_NAME,
|
||||
},
|
||||
|
||||
|
|
|
@ -5,12 +5,21 @@
|
|||
import CollectionReference from './CollectionReference';
|
||||
import DocumentSnapshot from './DocumentSnapshot';
|
||||
import Path from './Path';
|
||||
import { firestoreAutoId } from '../../utils';
|
||||
import { firestoreAutoId, isFunction, isObject, isString } from '../../utils';
|
||||
|
||||
export type WriteOptions = {
|
||||
merge?: boolean,
|
||||
}
|
||||
|
||||
type DocumentListenOptions = {
|
||||
includeMetadataChanges: boolean,
|
||||
}
|
||||
|
||||
type Observer = {
|
||||
next: (DocumentSnapshot) => void,
|
||||
error?: (Object) => void,
|
||||
}
|
||||
|
||||
/**
|
||||
* @class DocumentReference
|
||||
*/
|
||||
|
@ -60,13 +69,64 @@ export default class DocumentReference {
|
|||
.then(result => new DocumentSnapshot(this._firestore, result));
|
||||
}
|
||||
|
||||
onSnapshot(onNext: Function, onError?: Function): () => void {
|
||||
// TODO: Validation
|
||||
onSnapshot(
|
||||
optionsOrObserverOrOnNext: DocumentListenOptions | Observer | (DocumentSnapshot) => void,
|
||||
observerOrOnNextOrOnError?: Observer | (DocumentSnapshot) => void | (Object) => void,
|
||||
onError?: (Object) => void
|
||||
) {
|
||||
let observer = {};
|
||||
let docListenOptions = {};
|
||||
// Called with: onNext, ?onError
|
||||
if (isFunction(optionsOrObserverOrOnNext)) {
|
||||
observer.next = optionsOrObserverOrOnNext;
|
||||
if (observerOrOnNextOrOnError && !isFunction(observerOrOnNextOrOnError)) {
|
||||
throw new Error('DocumentReference.onSnapshot failed: Second argument must be a valid function.');
|
||||
}
|
||||
observer.error = observerOrOnNextOrOnError;
|
||||
} else if (optionsOrObserverOrOnNext && isObject(optionsOrObserverOrOnNext)) {
|
||||
// Called with: Observer
|
||||
if (optionsOrObserverOrOnNext.next) {
|
||||
if (isFunction(optionsOrObserverOrOnNext.next)) {
|
||||
if (optionsOrObserverOrOnNext.error && !isFunction(optionsOrObserverOrOnNext.error)) {
|
||||
throw new Error('DocumentReference.onSnapshot failed: Observer.error must be a valid function.');
|
||||
}
|
||||
observer = optionsOrObserverOrOnNext;
|
||||
} else {
|
||||
throw new Error('DocumentReference.onSnapshot failed: Observer.next must be a valid function.');
|
||||
}
|
||||
} else if (optionsOrObserverOrOnNext.includeMetadataChanges) {
|
||||
docListenOptions = optionsOrObserverOrOnNext;
|
||||
// Called with: Options, onNext, ?onError
|
||||
if (isFunction(observerOrOnNextOrOnError)) {
|
||||
observer.next = observerOrOnNextOrOnError;
|
||||
if (onError && !isFunction(onError)) {
|
||||
throw new Error('DocumentReference.onSnapshot failed: Third argument must be a valid function.');
|
||||
}
|
||||
observer.error = onError;
|
||||
// Called with Options, Observer
|
||||
} else if (observerOrOnNextOrOnError && isObject(observerOrOnNextOrOnError) && observerOrOnNextOrOnError.next) {
|
||||
if (isFunction(observerOrOnNextOrOnError.next)) {
|
||||
if (observerOrOnNextOrOnError.error && !isFunction(observerOrOnNextOrOnError.error)) {
|
||||
throw new Error('DocumentReference.onSnapshot failed: Observer.error must be a valid function.');
|
||||
}
|
||||
observer = observerOrOnNextOrOnError;
|
||||
} else {
|
||||
throw new Error('DocumentReference.onSnapshot failed: Observer.next must be a valid function.');
|
||||
}
|
||||
} else {
|
||||
throw new Error('DocumentReference.onSnapshot failed: Second argument must be a function or observer.');
|
||||
}
|
||||
} else {
|
||||
throw new Error('DocumentReference.onSnapshot failed: First argument must be a function, observer or options.');
|
||||
}
|
||||
} else {
|
||||
throw new Error('DocumentReference.onSnapshot failed: Called with invalid arguments.');
|
||||
}
|
||||
const listenerId = firestoreAutoId();
|
||||
|
||||
const listener = (nativeDocumentSnapshot) => {
|
||||
const documentSnapshot = new DocumentSnapshot(this, nativeDocumentSnapshot);
|
||||
onNext(documentSnapshot);
|
||||
observer.next(documentSnapshot);
|
||||
};
|
||||
|
||||
// Listen to snapshot events
|
||||
|
@ -76,16 +136,16 @@ export default class DocumentReference {
|
|||
);
|
||||
|
||||
// Listen for snapshot error events
|
||||
if (onError) {
|
||||
if (observer.error) {
|
||||
this._firestore.on(
|
||||
this._firestore._getAppEventName(`onDocumentSnapshotError:${listenerId}`),
|
||||
onError,
|
||||
observer.error,
|
||||
);
|
||||
}
|
||||
|
||||
// Add the native listener
|
||||
this._firestore._native
|
||||
.documentOnSnapshot(this.path, listenerId);
|
||||
.documentOnSnapshot(this.path, listenerId, docListenOptions);
|
||||
|
||||
// Return an unsubscribe method
|
||||
return this._offDocumentSnapshot.bind(this, listenerId, listener);
|
||||
|
@ -96,7 +156,25 @@ export default class DocumentReference {
|
|||
.documentSet(this.path, data, writeOptions);
|
||||
}
|
||||
|
||||
update(data: Object): Promise<void> {
|
||||
update(...args: Object | string[]): Promise<void> {
|
||||
let data = {};
|
||||
if (args.length === 1) {
|
||||
if (!isObject(args[0])) {
|
||||
throw new Error('DocumentReference.update failed: If using a single argument, it must be an object.');
|
||||
}
|
||||
data = args[0];
|
||||
} else if (args.length % 2 === 1) {
|
||||
throw new Error('DocumentReference.update failed: Must have either a single object argument, or equal numbers of key/value pairs.');
|
||||
} else {
|
||||
for (let i = 0; i < args.length; i += 2) {
|
||||
const key = args[i];
|
||||
const value = args[i + 1];
|
||||
if (!isString(key)) {
|
||||
throw new Error(`DocumentReference.update failed: Argument at index ${i} must be a string`);
|
||||
}
|
||||
data[key] = value;
|
||||
}
|
||||
}
|
||||
return this._firestore._native
|
||||
.documentUpdate(this.path, data);
|
||||
}
|
||||
|
|
|
@ -5,8 +5,7 @@
|
|||
import DocumentSnapshot from './DocumentSnapshot';
|
||||
import Path from './Path';
|
||||
import QuerySnapshot from './QuerySnapshot';
|
||||
import INTERNALS from '../../internals';
|
||||
import { firestoreAutoId } from '../../utils';
|
||||
import { firestoreAutoId, isFunction, isObject } from '../../utils';
|
||||
|
||||
const DIRECTIONS = {
|
||||
ASC: 'ASCENDING',
|
||||
|
@ -44,6 +43,15 @@ type QueryOptions = {
|
|||
startAt?: any[],
|
||||
}
|
||||
export type Operator = '<' | '<=' | '=' | '==' | '>' | '>=';
|
||||
type QueryListenOptions = {
|
||||
includeQueryMetadataChanges: boolean,
|
||||
includeQueryMetadataChanges: boolean,
|
||||
}
|
||||
|
||||
type Observer = {
|
||||
next: (DocumentSnapshot) => void,
|
||||
error?: (Object) => void,
|
||||
}
|
||||
|
||||
/**
|
||||
* @class Query
|
||||
|
@ -116,13 +124,65 @@ export default class Query {
|
|||
this._fieldOrders, options);
|
||||
}
|
||||
|
||||
onSnapshot(onNext: () => any, onError?: () => any): () => void {
|
||||
// TODO: Validation
|
||||
onSnapshot(
|
||||
optionsOrObserverOrOnNext: QueryListenOptions | Observer | (DocumentSnapshot) => void,
|
||||
observerOrOnNextOrOnError?: Observer | (DocumentSnapshot) => void | (Object) => void,
|
||||
onError?: (Object) => void,
|
||||
) {
|
||||
let observer = {};
|
||||
let queryListenOptions = {};
|
||||
// Called with: onNext, ?onError
|
||||
if (isFunction(optionsOrObserverOrOnNext)) {
|
||||
observer.next = optionsOrObserverOrOnNext;
|
||||
if (observerOrOnNextOrOnError && !isFunction(observerOrOnNextOrOnError)) {
|
||||
throw new Error('Query.onSnapshot failed: Second argument must be a valid function.');
|
||||
}
|
||||
observer.error = observerOrOnNextOrOnError;
|
||||
} else if (optionsOrObserverOrOnNext && isObject(optionsOrObserverOrOnNext)) {
|
||||
// Called with: Observer
|
||||
if (optionsOrObserverOrOnNext.next) {
|
||||
if (isFunction(optionsOrObserverOrOnNext.next)) {
|
||||
if (optionsOrObserverOrOnNext.error && !isFunction(optionsOrObserverOrOnNext.error)) {
|
||||
throw new Error('Query.onSnapshot failed: Observer.error must be a valid function.');
|
||||
}
|
||||
observer = optionsOrObserverOrOnNext;
|
||||
} else {
|
||||
throw new Error('Query.onSnapshot failed: Observer.next must be a valid function.');
|
||||
}
|
||||
} else if (optionsOrObserverOrOnNext.includeDocumentMetadataChanges || optionsOrObserverOrOnNext.includeQueryMetadataChanges) {
|
||||
queryListenOptions = optionsOrObserverOrOnNext;
|
||||
// Called with: Options, onNext, ?onError
|
||||
if (isFunction(observerOrOnNextOrOnError)) {
|
||||
observer.next = observerOrOnNextOrOnError;
|
||||
if (onError && !isFunction(onError)) {
|
||||
throw new Error('Query.onSnapshot failed: Third argument must be a valid function.');
|
||||
}
|
||||
observer.error = onError;
|
||||
// Called with Options, Observer
|
||||
} else if (observerOrOnNextOrOnError && isObject(observerOrOnNextOrOnError) && observerOrOnNextOrOnError.next) {
|
||||
if (isFunction(observerOrOnNextOrOnError.next)) {
|
||||
if (observerOrOnNextOrOnError.error && !isFunction(observerOrOnNextOrOnError.error)) {
|
||||
throw new Error('Query.onSnapshot failed: Observer.error must be a valid function.');
|
||||
}
|
||||
observer = observerOrOnNextOrOnError;
|
||||
} else {
|
||||
throw new Error('Query.onSnapshot failed: Observer.next must be a valid function.');
|
||||
}
|
||||
} else {
|
||||
throw new Error('Query.onSnapshot failed: Second argument must be a function or observer.');
|
||||
}
|
||||
} else {
|
||||
throw new Error('Query.onSnapshot failed: First argument must be a function, observer or options.');
|
||||
}
|
||||
} else {
|
||||
throw new Error('Query.onSnapshot failed: Called with invalid arguments.');
|
||||
}
|
||||
|
||||
const listenerId = firestoreAutoId();
|
||||
|
||||
const listener = (nativeQuerySnapshot) => {
|
||||
const querySnapshot = new QuerySnapshot(this._firestore, this, nativeQuerySnapshot);
|
||||
onNext(querySnapshot);
|
||||
observer.next(querySnapshot);
|
||||
};
|
||||
|
||||
// Listen to snapshot events
|
||||
|
@ -132,10 +192,10 @@ export default class Query {
|
|||
);
|
||||
|
||||
// Listen for snapshot error events
|
||||
if (onError) {
|
||||
if (observer.error) {
|
||||
this._firestore.on(
|
||||
this._firestore._getAppEventName(`onQuerySnapshotError:${listenerId}`),
|
||||
onError,
|
||||
observer.error,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -146,7 +206,8 @@ export default class Query {
|
|||
this._fieldFilters,
|
||||
this._fieldOrders,
|
||||
this._queryOptions,
|
||||
listenerId
|
||||
listenerId,
|
||||
queryListenOptions,
|
||||
);
|
||||
|
||||
// Return an unsubscribe method
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
* WriteBatch representation wrapper
|
||||
*/
|
||||
import DocumentReference from './DocumentReference';
|
||||
import { isObject, isString } from '../../utils';
|
||||
|
||||
import type { WriteOptions } from './DocumentReference';
|
||||
|
||||
|
@ -58,11 +59,27 @@ export default class WriteBatch {
|
|||
return this;
|
||||
}
|
||||
|
||||
// TODO: Update to new method signature
|
||||
update(docRef: DocumentReference, data: Object): WriteBatch {
|
||||
update(docRef: DocumentReference, ...args: Object | string[]): WriteBatch {
|
||||
// TODO: Validation
|
||||
// validate.isDocumentReference('docRef', docRef);
|
||||
// validate.isDocument('data', data, true);
|
||||
let data = {};
|
||||
if (args.length === 1) {
|
||||
if (!isObject(args[0])) {
|
||||
throw new Error('DocumentReference.update failed: If using two arguments, the second must be an object.');
|
||||
}
|
||||
data = args[0];
|
||||
} else if (args.length % 2 === 1) {
|
||||
throw new Error('DocumentReference.update failed: Must have a document reference, followed by either a single object argument, or equal numbers of key/value pairs.');
|
||||
} else {
|
||||
for (let i = 0; i < args.length; i += 2) {
|
||||
const key = args[i];
|
||||
const value = args[i + 1];
|
||||
if (!isString(key)) {
|
||||
throw new Error(`DocumentReference.update failed: Argument at index ${i + 1} must be a string`);
|
||||
}
|
||||
data[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
this._writes.push({
|
||||
data,
|
||||
|
|
|
@ -25,7 +25,7 @@ const WILL_PRESENT_RESULT = {
|
|||
None: 'UNNotificationPresentationOptionNone',
|
||||
};
|
||||
|
||||
const FirebaseMessaging = NativeModules.FirebaseMessaging;
|
||||
const FirebaseMessaging = NativeModules.RNFirebaseMessaging;
|
||||
|
||||
/**
|
||||
* IOS only finish function
|
||||
|
|
|
@ -0,0 +1,131 @@
|
|||
// @flow
|
||||
import { NativeModules } from 'react-native';
|
||||
// import { version as ReactVersion } from 'react';
|
||||
// import ReactNativeVersion from 'react-native/Libraries/Core/ReactNativeVersion';
|
||||
|
||||
import INTERNALS from './../../internals';
|
||||
import { isIOS } from './../../utils';
|
||||
import PACKAGE from './../../../package.json';
|
||||
|
||||
const FirebaseCoreModule = NativeModules.RNFirebase;
|
||||
|
||||
export default class RNFirebaseUtils {
|
||||
static _NAMESPACE = 'utils';
|
||||
static _NATIVE_DISABLED = true;
|
||||
static _NATIVE_MODULE = 'RNFirebaseUtils';
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
checkPlayServicesAvailability() {
|
||||
if (isIOS) return null;
|
||||
|
||||
const code = this.playServicesAvailability.code;
|
||||
|
||||
if (!this.playServicesAvailability.isAvailable) {
|
||||
if (INTERNALS.OPTIONS.promptOnMissingPlayServices && this.playServicesAvailability.isUserResolvableError) {
|
||||
this.promptForPlayServices();
|
||||
} else {
|
||||
const error = INTERNALS.STRINGS.ERROR_PLAY_SERVICES(code);
|
||||
if (INTERNALS.OPTIONS.errorOnMissingPlayServices) {
|
||||
if (code === 2) console.warn(error); // only warn if it exists but may need an update
|
||||
else throw new Error(error);
|
||||
} else {
|
||||
console.warn(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
promptForPlayServices() {
|
||||
if (isIOS) return null;
|
||||
return FirebaseCoreModule.promptForPlayServices();
|
||||
}
|
||||
|
||||
resolutionForPlayServices() {
|
||||
if (isIOS) return null;
|
||||
return FirebaseCoreModule.resolutionForPlayServices();
|
||||
}
|
||||
|
||||
makePlayServicesAvailable() {
|
||||
if (isIOS) return null;
|
||||
return FirebaseCoreModule.makePlayServicesAvailable();
|
||||
}
|
||||
|
||||
get sharedEventEmitter(): Object {
|
||||
return INTERNALS.SharedEventEmitter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the global logging level for all logs.
|
||||
*
|
||||
* @param booleanOrDebugString
|
||||
*/
|
||||
set logLevel(booleanOrDebugString) {
|
||||
INTERNALS.OPTIONS.logLevel = booleanOrDebugString;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns an array of all current database registrations id strings
|
||||
*/
|
||||
get databaseRegistrations(): Array<string> {
|
||||
return Object.keys(INTERNALS.SyncTree._reverseLookup);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call with a registration id string to get the details off this reg
|
||||
*/
|
||||
get getDatabaseRegistrationDetails(): Function {
|
||||
return INTERNALS.SyncTree.getRegistration.bind(INTERNALS.SyncTree);
|
||||
}
|
||||
|
||||
/**
|
||||
* Accepts an array or a single string of registration ids.
|
||||
* This will remove the refs on both the js and native sides and their listeners.
|
||||
* @return {function(this:T)}
|
||||
*/
|
||||
get removeDatabaseRegistration(): Function {
|
||||
return INTERNALS.SyncTree.removeListenersForRegistrations.bind(INTERNALS.SyncTree);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns props from the android GoogleApiAvailability sdk
|
||||
* @android
|
||||
* @return {RNFirebase.GoogleApiAvailabilityType|{isAvailable: boolean, status: number}}
|
||||
*/
|
||||
get playServicesAvailability(): GoogleApiAvailabilityType {
|
||||
return FirebaseCoreModule.playServicesAvailability || { isAvailable: true, status: 0 };
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable/Disable throwing an error or warning on detecting a play services problem
|
||||
* @android
|
||||
* @param bool
|
||||
*/
|
||||
set errorOnMissingPlayServices(bool: Boolean) {
|
||||
INTERNALS.OPTIONS.errorOnMissingPlayServices = bool;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable/Disable automatic prompting of the play services update dialog
|
||||
* @android
|
||||
* @param bool
|
||||
*/
|
||||
set promptOnMissingPlayServices(bool: Boolean) {
|
||||
INTERNALS.OPTIONS.promptOnMissingPlayServices = bool;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export const statics = {
|
||||
DEFAULT_APP_NAME: INTERNALS.STRINGS.DEFAULT_APP_NAME,
|
||||
// VERSIONS: {
|
||||
// react: ReactVersion,
|
||||
// 'react-native': Object.values(ReactNativeVersion.version).slice(0, 3).join('.'),
|
||||
// 'react-native-firebase': PACKAGE.version,
|
||||
// },
|
||||
};
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "react-native-firebase",
|
||||
"version": "3.0.0-alpha.5",
|
||||
"version": "3.0.2",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "react-native-firebase",
|
||||
"version": "3.0.0",
|
||||
"version": "3.0.2",
|
||||
"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 Admob, Analytics, Auth, Crash Reporting, Cloud Firestore, Database, Messaging (FCM), Remote Config, Storage and Performance.",
|
||||
"main": "index",
|
||||
|
|
|
@ -13,6 +13,7 @@ import io.invertase.firebase.database.RNFirebaseDatabasePackage;
|
|||
import io.invertase.firebase.firestore.RNFirebaseFirestorePackage;
|
||||
import io.invertase.firebase.links.RNFirebaseLinksPackage;
|
||||
import io.invertase.firebase.messaging.RNFirebaseMessagingPackage;
|
||||
import io.invertase.firebase.perf.RNFirebasePerformancePackage;
|
||||
import io.invertase.firebase.storage.RNFirebaseStoragePackage;
|
||||
import com.oblador.vectoricons.VectorIconsPackage;
|
||||
import com.facebook.react.ReactNativeHost;
|
||||
|
@ -46,7 +47,7 @@ public class MainApplication extends Application implements ReactApplication {
|
|||
new RNFirebaseFirestorePackage(),
|
||||
new RNFirebaseLinksPackage(),
|
||||
new RNFirebaseMessagingPackage(),
|
||||
// new RNFirebasePerformancePackage(),
|
||||
new RNFirebasePerformancePackage(),
|
||||
new RNFirebaseStoragePackage()
|
||||
);
|
||||
}
|
||||
|
|
|
@ -129,30 +129,30 @@ PODS:
|
|||
- nanopb/decode (0.3.8)
|
||||
- nanopb/encode (0.3.8)
|
||||
- Protobuf (3.4.0)
|
||||
- React (0.49.0-rc.6):
|
||||
- React/Core (= 0.49.0-rc.6)
|
||||
- React/BatchedBridge (0.49.0-rc.6):
|
||||
- React (0.49.1):
|
||||
- React/Core (= 0.49.1)
|
||||
- React/BatchedBridge (0.49.1):
|
||||
- React/Core
|
||||
- React/cxxreact_legacy
|
||||
- React/Core (0.49.0-rc.6):
|
||||
- yoga (= 0.49.0-rc.6.React)
|
||||
- React/cxxreact_legacy (0.49.0-rc.6):
|
||||
- React/Core (0.49.1):
|
||||
- yoga (= 0.49.1.React)
|
||||
- React/cxxreact_legacy (0.49.1):
|
||||
- React/jschelpers_legacy
|
||||
- React/fishhook (0.49.0-rc.6)
|
||||
- React/jschelpers_legacy (0.49.0-rc.6)
|
||||
- React/RCTBlob (0.49.0-rc.6):
|
||||
- React/fishhook (0.49.1)
|
||||
- React/jschelpers_legacy (0.49.1)
|
||||
- React/RCTBlob (0.49.1):
|
||||
- React/Core
|
||||
- React/RCTNetwork (0.49.0-rc.6):
|
||||
- React/RCTNetwork (0.49.1):
|
||||
- React/Core
|
||||
- React/RCTText (0.49.0-rc.6):
|
||||
- React/RCTText (0.49.1):
|
||||
- React/Core
|
||||
- React/RCTWebSocket (0.49.0-rc.6):
|
||||
- React/RCTWebSocket (0.49.1):
|
||||
- React/Core
|
||||
- React/fishhook
|
||||
- React/RCTBlob
|
||||
- RNFirebase (3.0.0):
|
||||
- React
|
||||
- yoga (0.49.0-rc.6.React)
|
||||
- yoga (0.49.1.React)
|
||||
|
||||
DEPENDENCIES:
|
||||
- Firebase/AdMob
|
||||
|
@ -207,9 +207,9 @@ SPEC CHECKSUMS:
|
|||
leveldb-library: 10fb39c39e243db4af1828441162405bbcec1404
|
||||
nanopb: 5601e6bca2dbf1ed831b519092ec110f66982ca3
|
||||
Protobuf: 03eef2ee0b674770735cf79d9c4d3659cf6908e8
|
||||
React: e6ef6a41ec6dd1b7941417d60ca582bf5e9c739d
|
||||
RNFirebase: 2ceda3aef595ea1379bf5bfcc1cd9ee7d6c05d3b
|
||||
yoga: f9485d2ebf0ca773db2d727ea71b1aa8c9f3e075
|
||||
React: cf892fb84b7d06bf5fea7f328e554c6dcabe85ee
|
||||
RNFirebase: 901a473c68fcbaa28125c56a911923f2fbe5d61b
|
||||
yoga: 3abf02d6d9aeeb139b4c930eb1367feae690a35a
|
||||
|
||||
PODFILE CHECKSUM: b5674be55653f5dda937c8b794d0479900643d45
|
||||
|
||||
|
|
|
@ -48,13 +48,14 @@ function coreTests({ describe, it }) {
|
|||
|
||||
it('it should provide an array of apps', () => {
|
||||
should.equal(!!RNFirebase.apps.length, true);
|
||||
should.equal(RNFirebase.apps[0]._name, RNFirebase.DEFAULT_APP_NAME);
|
||||
should.equal(RNFirebase.apps[0]._name, RNFirebase.utils.DEFAULT_APP_NAME);
|
||||
should.equal(RNFirebase.apps[0].name, '[DEFAULT]');
|
||||
return Promise.resolve();
|
||||
});
|
||||
|
||||
// todo move to UTILS module tests
|
||||
it('it should provide the sdk version', () => {
|
||||
should.equal(!!RNFirebase.SDK_VERSION.length, true);
|
||||
should.equal(!!RNFirebase.utils.VERSIONS['react-native-firebase'].length, true);
|
||||
return Promise.resolve();
|
||||
});
|
||||
|
||||
|
|
|
@ -53,262 +53,360 @@ function collectionReferenceTests({ describe, it, context, firebase }) {
|
|||
});
|
||||
|
||||
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' };
|
||||
it('calls callback with the initial data and then when document changes', async () => {
|
||||
const collectionRef = firebase.native.firestore().collection('document-tests');
|
||||
const currentDocValue = { name: 'doc1' };
|
||||
const newDocValue = { name: 'updated' };
|
||||
|
||||
const callback = sinon.spy();
|
||||
const callback = sinon.spy();
|
||||
|
||||
// Test
|
||||
// Test
|
||||
|
||||
let unsubscribe;
|
||||
await new Promise((resolve2) => {
|
||||
unsubscribe = collectionRef.onSnapshot((snapshot) => {
|
||||
snapshot.forEach(doc => callback(doc.data()));
|
||||
resolve2();
|
||||
});
|
||||
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();
|
||||
});
|
||||
|
||||
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();
|
||||
});
|
||||
});
|
||||
|
||||
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' };
|
||||
it('calls callback with the initial data and then when document is added', async () => {
|
||||
const collectionRef = firebase.native.firestore().collection('document-tests');
|
||||
const currentDocValue = { name: 'doc1' };
|
||||
const newDocValue = { name: 'updated' };
|
||||
|
||||
const callback = sinon.spy();
|
||||
const callback = sinon.spy();
|
||||
|
||||
// Test
|
||||
// Test
|
||||
|
||||
let unsubscribe;
|
||||
await new Promise((resolve2) => {
|
||||
unsubscribe = collectionRef.onSnapshot((snapshot) => {
|
||||
snapshot.forEach(doc => callback(doc.data()));
|
||||
resolve2();
|
||||
});
|
||||
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();
|
||||
});
|
||||
|
||||
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();
|
||||
});
|
||||
});
|
||||
|
||||
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 collectionRef = firebase.native.firestore().collection('document-tests');
|
||||
const currentDocValue = { name: 'doc1' };
|
||||
|
||||
const callback = sinon.spy();
|
||||
const callback = sinon.spy();
|
||||
|
||||
// Test
|
||||
// Test
|
||||
|
||||
let unsubscribe;
|
||||
await new Promise((resolve2) => {
|
||||
unsubscribe = collectionRef.onSnapshot((snapshot) => {
|
||||
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();
|
||||
});
|
||||
});
|
||||
|
||||
context('onSnapshot()', () => {
|
||||
it('allows binding multiple callbacks to the same ref', async () => {
|
||||
// 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();
|
||||
});
|
||||
});
|
||||
|
||||
context('onSnapshot()', () => {
|
||||
it('listener stops listening when unsubscribed', async () => {
|
||||
// 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();
|
||||
});
|
||||
});
|
||||
|
||||
context('onSnapshot()', () => {
|
||||
it('supports options and callback', async () => {
|
||||
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({ includeQueryMetadataChanges: true, includeDocumentMetadataChanges: true }, (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);
|
||||
|
||||
// Tear down
|
||||
|
||||
unsubscribe();
|
||||
});
|
||||
});
|
||||
|
||||
context('onSnapshot()', () => {
|
||||
it('supports observer', async () => {
|
||||
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) => {
|
||||
const observer = {
|
||||
next: (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();
|
||||
},
|
||||
};
|
||||
unsubscribe = collectionRef.onSnapshot(observer);
|
||||
});
|
||||
|
||||
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();
|
||||
});
|
||||
});
|
||||
|
||||
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' };
|
||||
it('supports options and observer', async () => {
|
||||
const collectionRef = firebase.native.firestore().collection('document-tests');
|
||||
const currentDocValue = { name: 'doc1' };
|
||||
const newDocValue = { name: 'updated' };
|
||||
|
||||
const callbackA = sinon.spy();
|
||||
const callbackB = sinon.spy();
|
||||
const callback = sinon.spy();
|
||||
|
||||
// Test
|
||||
let unsubscribeA;
|
||||
let unsubscribeB;
|
||||
await new Promise((resolve2) => {
|
||||
unsubscribeA = collectionRef.onSnapshot((snapshot) => {
|
||||
snapshot.forEach(doc => callbackA(doc.data()));
|
||||
// Test
|
||||
|
||||
let unsubscribe;
|
||||
await new Promise((resolve2) => {
|
||||
const observer = {
|
||||
next: (snapshot) => {
|
||||
snapshot.forEach(doc => callback(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();
|
||||
},
|
||||
};
|
||||
unsubscribe = collectionRef.onSnapshot({ includeQueryMetadataChanges: true, includeDocumentMetadataChanges: true }, observer);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
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' };
|
||||
callback.should.be.calledWith(currentDocValue);
|
||||
|
||||
const callbackA = sinon.spy();
|
||||
const callbackB = sinon.spy();
|
||||
const docRef = firebase.native.firestore().doc('document-tests/doc1');
|
||||
await docRef.set(newDocValue);
|
||||
|
||||
// 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();
|
||||
await new Promise((resolve2) => {
|
||||
setTimeout(() => resolve2(), 5);
|
||||
});
|
||||
|
||||
// Assertions
|
||||
|
||||
callback.should.be.calledWith(newDocValue);
|
||||
|
||||
// Tear down
|
||||
|
||||
unsubscribe();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ import sinon from 'sinon';
|
|||
import 'should-sinon';
|
||||
import should from 'should';
|
||||
|
||||
function collectionReferenceTests({ describe, it, context, firebase }) {
|
||||
function documentReferenceTests({ describe, it, context, firebase }) {
|
||||
describe('DocumentReference', () => {
|
||||
context('class', () => {
|
||||
it('should return instance methods', () => {
|
||||
|
@ -29,219 +29,324 @@ function collectionReferenceTests({ describe, it, context, firebase }) {
|
|||
});
|
||||
|
||||
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' };
|
||||
it('calls callback with the initial data and then when value changes', async () => {
|
||||
const docRef = firebase.native.firestore().doc('document-tests/doc1');
|
||||
const currentDataValue = { name: 'doc1' };
|
||||
const newDataValue = { name: 'updated' };
|
||||
|
||||
const callback = sinon.spy();
|
||||
const callback = sinon.spy();
|
||||
|
||||
// Test
|
||||
// Test
|
||||
|
||||
let unsubscribe;
|
||||
await new Promise((resolve2) => {
|
||||
unsubscribe = docRef.onSnapshot((snapshot) => {
|
||||
callback(snapshot.data());
|
||||
resolve2();
|
||||
});
|
||||
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();
|
||||
});
|
||||
|
||||
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();
|
||||
});
|
||||
});
|
||||
|
||||
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 docRef = firebase.native.firestore().doc('document-tests/doc1');
|
||||
const currentDataValue = { name: 'doc1' };
|
||||
|
||||
const callback = sinon.spy();
|
||||
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();
|
||||
});
|
||||
});
|
||||
|
||||
context('onSnapshot()', () => {
|
||||
it('allows binding multiple callbacks to the same ref', async () => {
|
||||
// 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();
|
||||
});
|
||||
});
|
||||
|
||||
context('onSnapshot()', () => {
|
||||
it('listener stops listening when unsubscribed', async () => {
|
||||
// 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();
|
||||
});
|
||||
});
|
||||
|
||||
context('onSnapshot()', () => {
|
||||
it('supports options and callbacks', async () => {
|
||||
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();
|
||||
});
|
||||
let unsubscribe;
|
||||
await new Promise((resolve2) => {
|
||||
unsubscribe = docRef.onSnapshot({ includeMetadataChanges: true }, (snapshot) => {
|
||||
callback(snapshot.data());
|
||||
resolve2();
|
||||
});
|
||||
});
|
||||
|
||||
callback.should.be.calledWith(currentDataValue);
|
||||
callback.should.be.calledWith(currentDataValue);
|
||||
|
||||
await docRef.set(currentDataValue);
|
||||
// Update the document
|
||||
|
||||
await new Promise((resolve2) => {
|
||||
setTimeout(() => resolve2(), 5);
|
||||
});
|
||||
await docRef.set(newDataValue);
|
||||
|
||||
await new Promise((resolve2) => {
|
||||
setTimeout(() => resolve2(), 5);
|
||||
});
|
||||
|
||||
// Assertions
|
||||
|
||||
callback.should.be.calledOnce(); // Callback is not called again
|
||||
callback.should.be.calledWith(newDataValue);
|
||||
|
||||
// Tear down
|
||||
|
||||
unsubscribe();
|
||||
|
||||
resolve();
|
||||
});
|
||||
unsubscribe();
|
||||
});
|
||||
});
|
||||
|
||||
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' };
|
||||
it('supports observer', async () => {
|
||||
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();
|
||||
const callback = sinon.spy();
|
||||
|
||||
// Test
|
||||
let unsubscribeA;
|
||||
let unsubscribeB;
|
||||
await new Promise((resolve2) => {
|
||||
unsubscribeA = docRef.onSnapshot((snapshot) => {
|
||||
callbackA(snapshot.data());
|
||||
// Test
|
||||
|
||||
let unsubscribe;
|
||||
await new Promise((resolve2) => {
|
||||
const observer = {
|
||||
next: (snapshot) => {
|
||||
callback(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();
|
||||
},
|
||||
};
|
||||
unsubscribe = docRef.onSnapshot(observer);
|
||||
});
|
||||
|
||||
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();
|
||||
});
|
||||
});
|
||||
|
||||
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' };
|
||||
it('supports options and observer', async () => {
|
||||
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();
|
||||
const callback = sinon.spy();
|
||||
|
||||
// Test
|
||||
let unsubscribeA;
|
||||
let unsubscribeB;
|
||||
await new Promise((resolve2) => {
|
||||
unsubscribeA = docRef.onSnapshot((snapshot) => {
|
||||
callbackA(snapshot.data());
|
||||
// Test
|
||||
|
||||
let unsubscribe;
|
||||
await new Promise((resolve2) => {
|
||||
const observer = {
|
||||
next: (snapshot) => {
|
||||
callback(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();
|
||||
},
|
||||
};
|
||||
unsubscribe = docRef.onSnapshot({ includeMetadataChanges: true }, observer);
|
||||
});
|
||||
|
||||
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);
|
||||
|
||||
// Tear down
|
||||
|
||||
unsubscribe();
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -283,10 +388,22 @@ function collectionReferenceTests({ describe, it, context, firebase }) {
|
|||
});
|
||||
|
||||
context('update()', () => {
|
||||
it('should update Document', () => {
|
||||
it('should update Document using object', () => {
|
||||
return firebase.native.firestore()
|
||||
.doc('document-tests/doc1')
|
||||
.set({ name: 'updated' })
|
||||
.update({ name: 'updated' })
|
||||
.then(async () => {
|
||||
const doc = await firebase.native.firestore().doc('document-tests/doc1').get();
|
||||
doc.data().name.should.equal('updated');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
context('update()', () => {
|
||||
it('should update Document using key/value pairs', () => {
|
||||
return firebase.native.firestore()
|
||||
.doc('document-tests/doc1')
|
||||
.update('name', 'updated')
|
||||
.then(async () => {
|
||||
const doc = await firebase.native.firestore().doc('document-tests/doc1').get();
|
||||
doc.data().name.should.equal('updated');
|
||||
|
@ -296,4 +413,4 @@ function collectionReferenceTests({ describe, it, context, firebase }) {
|
|||
});
|
||||
}
|
||||
|
||||
export default collectionReferenceTests;
|
||||
export default documentReferenceTests;
|
||||
|
|
|
@ -36,7 +36,7 @@ function firestoreTests({ describe, it, context, firebase }) {
|
|||
.set(nycRef, { name: 'New York City' })
|
||||
.set(sfRef, { name: 'San Francisco' })
|
||||
.update(nycRef, { population: 1000000 })
|
||||
.update(sfRef, { name: 'San Fran' })
|
||||
.update(sfRef, 'name', 'San Fran')
|
||||
.set(lRef, { population: 3000000 }, { merge: true })
|
||||
.delete(ayRef)
|
||||
.commit()
|
||||
|
|
Loading…
Reference in New Issue