Files

Return to Package Diff Home.
Brought to you by Intrinsic.

Package Diff: react-native-firebase @ 5.0.0-rc0 .. 5.4.0

android/build.gradle

@@ -7,21 +7,18 @@
}
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.1.2'
- classpath 'io.fabric.tools:gradle:1.25.4'
+ classpath 'com.android.tools.build:gradle:3.3.2'
}
}
apply plugin: 'com.android.library'
-def DEFAULT_COMPILE_SDK_VERSION = 27
-def DEFAULT_BUILD_TOOLS_VERSION = "27.0.3"
-def DEFAULT_TARGET_SDK_VERSION = 26
-def DEFAULT_SUPPORT_LIB_VERSION = "27.0.2"
+def DEFAULT_COMPILE_SDK_VERSION = 28
+def DEFAULT_TARGET_SDK_VERSION = 28
+def DEFAULT_SUPPORT_LIB_VERSION = "28.0.0"
android {
compileSdkVersion rootProject.hasProperty('compileSdkVersion') ? rootProject.compileSdkVersion : DEFAULT_COMPILE_SDK_VERSION
- buildToolsVersion rootProject.hasProperty('buildToolsVersion') ? rootProject.buildToolsVersion : DEFAULT_BUILD_TOOLS_VERSION
defaultConfig {
minSdkVersion 16
targetSdkVersion rootProject.hasProperty('targetSdkVersion') ? rootProject.targetSdkVersion : DEFAULT_TARGET_SDK_VERSION
@@ -39,10 +36,15 @@
lintOptions {
disable 'GradleCompatible'
}
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
}
rootProject.gradle.buildFinished { buildResult ->
if (buildResult.getFailure() != null) {
+ //noinspection GroovyUnusedCatchParameter
try {
String message = buildResult.getFailure().properties.get("reportableCauses").toString()
if (message.contains("com.android.dex.DexException: Multiple dex files define Lcom/google/") ||
@@ -79,35 +81,118 @@
repositories {
google()
jcenter()
+
+ def found = false
+ def parentDir = rootProject.projectDir
+ def androidSourcesName = 'React Native sources'
+ def androidPrebuiltBinaryName = 'React Native prebuilt binary'
+
+ 1.upto(4, {
+ if (found) return true
+ parentDir = parentDir.parentFile
+
+ // Running React Native from sources locally or for ExpoKit
+ def androidSourcesDir = new File(
+ parentDir,
+ 'node_modules/react-native'
+ )
+
+ // Official releases of React Native come with a prebuilt version of Android sources
+ // in ./android, e.g. react-native/android/**/react-native-0.57.1.aar
+ def androidPrebuiltBinaryDir = new File(
+ parentDir,
+ 'node_modules/react-native/android'
+ )
+
+ if (androidPrebuiltBinaryDir.exists()) {
+ maven {
+ url androidPrebuiltBinaryDir.toString()
+ name androidPrebuiltBinaryName
+ }
+
+ println "${project.name}: using ${androidPrebuiltBinaryName} from ${androidPrebuiltBinaryDir.toString()}"
+ found = true
+ } else if (androidSourcesDir.exists()) {
maven {
- url "$rootDir/../node_modules/react-native/android"
- name 'React Native (local)'
+ url androidSourcesDir.toString()
+ name androidSourcesName
+ }
+
+ println "${project.name}: using ${androidSourcesName} from ${androidSourcesDir.toString()}"
+ found = true
+ }
+ })
+
+ if (!found) {
+ throw new GradleException(
+ "${project.name}: unable to locate React Native android sources or prebuilt binary. " +
+ "Ensure you have you installed React Native as a dependency in your project and try again."
+ )
}
}
def supportVersion = rootProject.hasProperty('supportLibVersion') ? rootProject.supportLibVersion : DEFAULT_SUPPORT_LIB_VERSION
dependencies {
- // compile fileTree(include: ['*.jar'], dir: 'libs')
- api "com.facebook.react:react-native:+" // From node_modules
- api "com.android.support:support-v4:$supportVersion"
-
- // Firebase SDKs
- compileOnly('com.crashlytics.sdk.android:crashlytics:2.9.3@aar') {
+ //noinspection GradleDynamicVersion
+ api "com.facebook.react:react-native:+"
+ // implementation "com.google.firebase:firebase-common:16.1.0"
+
+ /* ----------------------------
+ * REACT NATIVE FIREBASE
+ * ---------------------------- */
+
+ // Required dependencies
+ //noinspection GradleCompatible
+ compileOnly "com.google.firebase:firebase-core:16.0.9"
+ compileOnly "com.google.android.gms:play-services-base:16.1.0"
+
+ /* -------------------------
+ * OPTIONAL FIREBASE SDKS
+ * ------------------------- */
+
+ // Ads
+ compileOnly('com.google.firebase:firebase-ads:15.0.1') {
+ // exclude `customtabs` as the support lib version is out of date
+ // we manually add it as a dependency below with a custom version
+ exclude group: 'com.android.support', module: 'customtabs'
+ }
+ // Authentication
+ compileOnly "com.google.firebase:firebase-auth:17.0.0"
+ // Analytics
+ compileOnly "com.google.firebase:firebase-analytics:16.5.0"
+ // Performance Monitoring
+ compileOnly "com.google.firebase:firebase-perf:16.2.4"
+ // Remote Config
+ compileOnly "com.google.firebase:firebase-config:17.0.0"
+ // Cloud Storage
+ compileOnly "com.google.firebase:firebase-storage:17.0.0"
+ // Invites
+ compileOnly "com.google.firebase:firebase-invites:17.0.0"
+ // Dynamic Links
+ compileOnly "com.google.firebase:firebase-dynamic-links:17.0.0"
+ // Real-time Database
+ compileOnly "com.google.firebase:firebase-database:17.0.0"
+ // Cloud Functions
+ compileOnly "com.google.firebase:firebase-functions:17.0.0"
+ // Cloud Firestore
+ compileOnly "com.google.firebase:firebase-firestore:19.0.0"
+ // Cloud Messaging / FCM
+ compileOnly "com.google.firebase:firebase-messaging:18.0.0"
+ // Crashlytics
+ compileOnly('com.crashlytics.sdk.android:crashlytics:2.9.5@aar') {
transitive = true
}
- compileOnly "com.google.android.gms:play-services-base:15.0.1"
- compileOnly "com.google.firebase:firebase-ads:15.0.1"
- compileOnly "com.google.firebase:firebase-auth:16.0.2"
- compileOnly "com.google.firebase:firebase-config:16.0.0"
- compileOnly "com.google.firebase:firebase-core:16.0.1"
- compileOnly "com.google.firebase:firebase-crash:16.0.1"
- compileOnly "com.google.firebase:firebase-database:16.0.1"
- compileOnly "com.google.firebase:firebase-firestore:17.0.2"
- compileOnly "com.google.firebase:firebase-functions:16.0.1"
- compileOnly "com.google.firebase:firebase-invites:16.0.1"
- compileOnly "com.google.firebase:firebase-storage:16.0.1"
- compileOnly "com.google.firebase:firebase-messaging:17.1.0"
- compileOnly "com.google.firebase:firebase-perf:16.0.0"
+ /* --------------------------------
+ * OPTIONAL SUPPORT LIBS
+ * -------------------------------- */
+
+ // For Firebase Ads
+ compileOnly "com.android.support:customtabs:$supportVersion"
+
+ // For React Native Firebase Notifications
+ api "com.android.support:support-v4:$supportVersion"
+
+ // For React Native Firebase Notifications
compileOnly 'me.leolin:ShortcutBadger:1.1.21@aar'
}

android/src/main/AndroidManifest.xml

@@ -1,7 +1,13 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="io.invertase.firebase">
- <uses-permission android:name="android.permission.INTERNET" />
- <uses-permission android:name="android.permission.WAKE_LOCK" />
- <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+ <uses-permission android:name="android.permission.INTERNET"/>
+ <uses-permission android:name="android.permission.WAKE_LOCK"/>
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+<!-- <application>-->
+<!-- <service android:name="com.google.firebase.components.ComponentDiscoveryService">-->
+<!-- <meta-data android:name="com.google.firebase.components:io.invertase.firebase.ReactNativeFirebaseAppRegistrar"-->
+<!-- android:value="com.google.firebase.components.ComponentRegistrar"/>-->
+<!-- </service>-->
+<!-- </application>-->
</manifest>

android/src/main/java/io/invertase/firebase/admob/RNFirebaseAdMobBanner.java

@@ -1,7 +1,5 @@
package io.invertase.firebase.admob;
-import android.support.annotation.Nullable;
-
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap;
@@ -19,10 +17,11 @@
import java.util.Map;
-public class RNFirebaseAdMobBanner extends SimpleViewManager<ReactViewGroup> {
+import javax.annotation.Nullable;
- public static final String REACT_CLASS = "RNFirebaseAdMobBanner";
- public static final String BANNER_EVENT = "onBannerEvent";
+public class RNFirebaseAdMobBanner extends SimpleViewManager<ReactViewGroup> {
+ private static final String REACT_CLASS = "RNFirebaseAdMobBanner";
+ private static final String BANNER_EVENT = "onBannerEvent";
private ThemedReactContext context;
private ReactViewGroup viewGroup;
private RCTEventEmitter emitter;
@@ -56,7 +55,7 @@
return viewGroup;
}
- AdView getAdView() {
+ private AdView getAdView() {
return (AdView) viewGroup.getChildAt(0);
}
@@ -142,7 +141,7 @@
/**
* Loads a new ad into a viewGroup
*/
- void requestAd() {
+ private void requestAd() {
// If the props have not yet been set
if (size == null || unitId == null || request == null) {
return;
@@ -165,7 +164,7 @@
/**
* Listen to Ad events
*/
- void setAdListener() {
+ private void setAdListener() {
final AdView adView = getAdView();
adView.setAdListener(new AdListener() {
@@ -225,7 +224,7 @@
* @param type
* @param payload
*/
- void sendEvent(String type, final @Nullable WritableMap payload) {
+ private void sendEvent(String type, final @Nullable WritableMap payload) {
WritableMap event = Arguments.createMap();
event.putString("type", type);

android/src/main/java/io/invertase/firebase/admob/RNFirebaseAdmobInterstitial.java

@@ -2,7 +2,6 @@
import android.app.Activity;
-import android.support.annotation.Nullable;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.WritableMap;
@@ -10,13 +9,14 @@
import com.google.android.gms.ads.AdRequest;
import com.google.android.gms.ads.InterstitialAd;
+import javax.annotation.Nullable;
+
import io.invertase.firebase.Utils;
class RNFirebaseAdmobInterstitial {
private InterstitialAd interstitialAd;
private RNFirebaseAdMob adMob;
- private AdListener adListener;
private String adUnit;
RNFirebaseAdmobInterstitial(final String adUnitString, final RNFirebaseAdMob adMobInstance) {
@@ -25,7 +25,7 @@
interstitialAd = new InterstitialAd(adMob.getContext());
interstitialAd.setAdUnitId(adUnit);
- adListener = new AdListener() {
+ AdListener adListener = new AdListener() {
@Override
public void onAdLoaded() {
sendEvent("onAdLoaded", null);
@@ -96,7 +96,7 @@
* @param type
* @param payload
*/
- void sendEvent(String type, final @Nullable WritableMap payload) {
+ private void sendEvent(String type, final @Nullable WritableMap payload) {
WritableMap map = Arguments.createMap();
map.putString("type", type);
map.putString("adUnit", adUnit);

android/src/main/java/io/invertase/firebase/admob/RNFirebaseAdMob.java

@@ -21,7 +21,7 @@
private HashMap<String, RNFirebaseAdmobInterstitial> interstitials = new HashMap<>();
private HashMap<String, RNFirebaseAdMobRewardedVideo> rewardedVideos = new HashMap<>();
- public RNFirebaseAdMob(ReactApplicationContext reactContext) {
+ RNFirebaseAdMob(ReactApplicationContext reactContext) {
super(reactContext);
Log.d(TAG, "New instance");
}

android/src/main/java/io/invertase/firebase/admob/RNFirebaseAdMobNativeExpress.java

@@ -1,7 +1,5 @@
package io.invertase.firebase.admob;
-import android.support.annotation.Nullable;
-
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap;
@@ -21,10 +19,12 @@
import java.util.Map;
+import javax.annotation.Nullable;
+
public class RNFirebaseAdMobNativeExpress extends SimpleViewManager<ReactViewGroup> {
- public static final String REACT_CLASS = "RNFirebaseAdMobNativeExpress";
- public static final String BANNER_EVENT = "onBannerEvent";
+ private static final String REACT_CLASS = "RNFirebaseAdMobNativeExpress";
+ private static final String BANNER_EVENT = "onBannerEvent";
private ThemedReactContext context;
private ReactViewGroup viewGroup;
private RCTEventEmitter emitter;
@@ -59,7 +59,7 @@
return viewGroup;
}
- NativeExpressAdView getAdView() {
+ private NativeExpressAdView getAdView() {
return (NativeExpressAdView) viewGroup.getChildAt(0);
}
@@ -157,7 +157,7 @@
/**
* Loads a new ad into a viewGroup
*/
- void requestAd() {
+ private void requestAd() {
if (size == null || unitId == null || request == null || videoOptions == null) {
return;
}
@@ -179,7 +179,7 @@
/**
* Listen to Ad events
*/
- void setAdListener() {
+ private void setAdListener() {
final NativeExpressAdView adView = getAdView();
adView.setAdListener(new AdListener() {
@@ -263,7 +263,7 @@
* @param type
* @param payload
*/
- void sendEvent(String type, final @Nullable WritableMap payload) {
+ private void sendEvent(String type, final @Nullable WritableMap payload) {
WritableMap event = Arguments.createMap();
event.putString("type", type);
@@ -271,7 +271,6 @@
event.putMap("payload", payload);
}
- int id = viewGroup.getId();
emitter.receiveEvent(viewGroup.getId(), BANNER_EVENT, event);
}

android/src/main/java/io/invertase/firebase/admob/RNFirebaseAdMobRewardedVideo.java

@@ -2,7 +2,6 @@
import android.app.Activity;
-import android.support.annotation.Nullable;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.WritableMap;
@@ -12,11 +11,12 @@
import com.google.android.gms.ads.reward.RewardedVideoAd;
import com.google.android.gms.ads.reward.RewardedVideoAdListener;
+import javax.annotation.Nullable;
+
import io.invertase.firebase.Utils;
public class RNFirebaseAdMobRewardedVideo implements RewardedVideoAdListener {
- private RewardedVideoAd mAd;
private String adUnit;
private RNFirebaseAdMob adMob;
private RewardedVideoAd rewardedVideo;
@@ -126,7 +126,7 @@
* @param type
* @param payload
*/
- void sendEvent(String type, final @Nullable WritableMap payload) {
+ private void sendEvent(String type, final @Nullable WritableMap payload) {
WritableMap map = Arguments.createMap();
map.putString("type", type);
map.putString("adUnit", adUnit);

android/src/main/java/io/invertase/firebase/analytics/RNFirebaseAnalytics.java

@@ -1,7 +1,6 @@
package io.invertase.firebase.analytics;
import android.app.Activity;
-import android.support.annotation.Nullable;
import android.util.Log;
import com.facebook.react.bridge.Arguments;
@@ -11,12 +10,14 @@
import com.facebook.react.bridge.ReadableMap;
import com.google.firebase.analytics.FirebaseAnalytics;
+import javax.annotation.Nullable;
+
public class RNFirebaseAnalytics extends ReactContextBaseJavaModule {
private static final String TAG = "RNFirebaseAnalytics";
- public RNFirebaseAnalytics(ReactApplicationContext reactContext) {
+ RNFirebaseAnalytics(ReactApplicationContext reactContext) {
super(reactContext);
Log.d(TAG, "New instance");
}

android/src/main/java/io/invertase/firebase/auth/RNFirebaseAuth.java

@@ -3,7 +3,6 @@
import android.app.Activity;
import android.net.Uri;
import android.os.Parcel;
-import android.support.annotation.NonNull;
import android.util.Log;
import com.facebook.react.bridge.Arguments;
@@ -31,6 +30,7 @@
import com.google.firebase.auth.FirebaseAuthException;
import com.google.firebase.auth.FirebaseAuthInvalidCredentialsException;
import com.google.firebase.auth.FirebaseAuthProvider;
+import com.google.firebase.auth.FirebaseAuthSettings;
import com.google.firebase.auth.FirebaseUser;
import com.google.firebase.auth.FirebaseUserMetadata;
import com.google.firebase.auth.GetTokenResult;
@@ -45,30 +45,33 @@
import com.google.firebase.auth.UserProfileChangeRequest;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import javax.annotation.Nonnull;
+
import io.invertase.firebase.Utils;
@SuppressWarnings({"ThrowableResultOfMethodCallIgnored", "JavaDoc"})
class RNFirebaseAuth extends ReactContextBaseJavaModule {
private static final String TAG = "RNFirebaseAuth";
+ private static HashMap<String, FirebaseAuth.AuthStateListener> mAuthListeners = new HashMap<>();
+ private static HashMap<String, FirebaseAuth.IdTokenListener> mIdTokenListeners = new HashMap<>();
private String mVerificationId;
private String mLastPhoneNumber;
private PhoneAuthProvider.ForceResendingToken mForceResendingToken;
private PhoneAuthCredential mCredential;
private ReactContext mReactContext;
- private HashMap<String, FirebaseAuth.AuthStateListener> mAuthListeners = new HashMap<>();
- private HashMap<String, FirebaseAuth.IdTokenListener> mIdTokenListeners = new HashMap<>();
RNFirebaseAuth(ReactApplicationContext reactContext) {
super(reactContext);
mReactContext = reactContext;
- Log.d(TAG, "RNFirebaseAuth:initialized");
+ Log.d(TAG, "instance-created");
}
@Override
@@ -76,6 +79,46 @@
return TAG;
}
+ @Override
+ public void initialize() {
+ super.initialize();
+ Log.d(TAG, "instance-initialized");
+ }
+
+ @Override
+ public void onCatalystInstanceDestroy() {
+ super.onCatalystInstanceDestroy();
+ Log.d(TAG, "instance-destroyed");
+
+ Iterator authListenerIterator = mAuthListeners
+ .entrySet()
+ .iterator();
+
+ while (authListenerIterator.hasNext()) {
+ Map.Entry pair = (Map.Entry) authListenerIterator.next();
+ String appName = (String) pair.getKey();
+ FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
+ FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp);
+ FirebaseAuth.AuthStateListener mAuthListener = (FirebaseAuth.AuthStateListener) pair.getValue();
+ firebaseAuth.removeAuthStateListener(mAuthListener);
+ authListenerIterator.remove();
+ }
+
+ Iterator idTokenListenerIterator = mIdTokenListeners
+ .entrySet()
+ .iterator();
+
+ while (idTokenListenerIterator.hasNext()) {
+ Map.Entry pair = (Map.Entry) idTokenListenerIterator.next();
+ String appName = (String) pair.getKey();
+ FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
+ FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp);
+ FirebaseAuth.IdTokenListener mAuthListener = (FirebaseAuth.IdTokenListener) pair.getValue();
+ firebaseAuth.removeIdTokenListener(mAuthListener);
+ idTokenListenerIterator.remove();
+ }
+ }
+
/**
* Add a new auth state listener - if one doesn't exist already
*/
@@ -90,7 +133,7 @@
if (mAuthListener == null) {
FirebaseAuth.AuthStateListener newAuthListener = new FirebaseAuth.AuthStateListener() {
@Override
- public void onAuthStateChanged(@NonNull FirebaseAuth firebaseAuth) {
+ public void onAuthStateChanged(@Nonnull FirebaseAuth firebaseAuth) {
FirebaseUser user = firebaseAuth.getCurrentUser();
WritableMap msgMap = Arguments.createMap();
if (user != null) {
@@ -140,7 +183,7 @@
if (!mIdTokenListeners.containsKey(appName)) {
FirebaseAuth.IdTokenListener newIdTokenListener = new FirebaseAuth.IdTokenListener() {
@Override
- public void onIdTokenChanged(@NonNull FirebaseAuth firebaseAuth) {
+ public void onIdTokenChanged(@Nonnull FirebaseAuth firebaseAuth) {
FirebaseUser user = firebaseAuth.getCurrentUser();
WritableMap msgMap = Arguments.createMap();
if (user != null) {
@@ -179,6 +222,33 @@
}
}
+ /**
+ * The phone number and SMS code here must have been configured in the
+ * Firebase Console (Authentication > Sign In Method > Phone).
+ * <p>
+ * Calling this method a second time will overwrite the previously passed parameters.
+ * Only one number can be configured at a given time.
+ *
+ * @param appName
+ * @param phoneNumber
+ * @param smsCode
+ * @param promise
+ */
+ @ReactMethod
+ public void setAutoRetrievedSmsCodeForPhoneNumber(
+ String appName,
+ String phoneNumber,
+ String smsCode,
+ Promise promise
+ ) {
+ Log.d(TAG, "setAutoRetrievedSmsCodeForPhoneNumber");
+ FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
+ FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp);
+ FirebaseAuthSettings firebaseAuthSettings = firebaseAuth.getFirebaseAuthSettings();
+ firebaseAuthSettings.setAutoRetrievedSmsCodeForPhoneNumber(phoneNumber, smsCode);
+ promise.resolve(null);
+ }
+
@ReactMethod
public void signOut(String appName, Promise promise) {
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
@@ -194,16 +264,7 @@
}
@ReactMethod
- public void signInAnonymously(String appName, final Promise promise) {
- signInAnonymously(appName, promise, false);
- }
-
- @ReactMethod
- public void signInAnonymouslyAndRetrieveData(String appName, final Promise promise) {
- signInAnonymously(appName, promise, true);
- }
-
- private void signInAnonymously(String appName, final Promise promise, final boolean withData) {
+ private void signInAnonymously(String appName, final Promise promise) {
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp);
@@ -214,16 +275,12 @@
@Override
public void onSuccess(AuthResult authResult) {
Log.d(TAG, "signInAnonymously:onComplete:success");
- if (withData) {
promiseWithAuthResult(authResult, promise);
- } else {
- promiseWithUser(authResult.getUser(), promise);
- }
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
- public void onFailure(@NonNull Exception exception) {
+ public void onFailure(@Nonnull Exception exception) {
Log.e(TAG, "signInAnonymously:onComplete:failure", exception);
promiseRejectAuthException(promise, exception);
}
@@ -238,31 +295,11 @@
* @param promise
*/
@ReactMethod
- public void createUserWithEmailAndPassword(
- String appName,
- final String email,
- final String password,
- final Promise promise
- ) {
- createUserWithEmailAndPassword(appName, email, password, promise, false);
- }
-
- @ReactMethod
- public void createUserAndRetrieveDataWithEmailAndPassword(
- String appName,
- final String email,
- final String password,
- final Promise promise
- ) {
- createUserWithEmailAndPassword(appName, email, password, promise, true);
- }
-
private void createUserWithEmailAndPassword(
String appName,
final String email,
final String password,
- final Promise promise,
- final boolean withData
+ final Promise promise
) {
Log.d(TAG, "createUserWithEmailAndPassword");
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
@@ -274,16 +311,12 @@
@Override
public void onSuccess(AuthResult authResult) {
Log.d(TAG, "createUserWithEmailAndPassword:onComplete:success");
- if (withData) {
promiseWithAuthResult(authResult, promise);
- } else {
- promiseWithUser(authResult.getUser(), promise);
- }
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
- public void onFailure(@NonNull Exception exception) {
+ public void onFailure(@Nonnull Exception exception) {
Log.e(TAG, "createUserWithEmailAndPassword:onComplete:failure", exception);
promiseRejectAuthException(promise, exception);
}
@@ -298,31 +331,11 @@
* @param promise
*/
@ReactMethod
- public void signInWithEmailAndPassword(
- String appName,
- final String email,
- final String password,
- final Promise promise
- ) {
- signInWithEmailAndPassword(appName, email, password, promise, false);
- }
-
- @ReactMethod
- public void signInAndRetrieveDataWithEmailAndPassword(
- String appName,
- final String email,
- final String password,
- final Promise promise
- ) {
- signInWithEmailAndPassword(appName, email, password, promise, true);
- }
-
private void signInWithEmailAndPassword(
String appName,
final String email,
final String password,
- final Promise promise,
- final boolean withData
+ final Promise promise
) {
Log.d(TAG, "signInWithEmailAndPassword");
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
@@ -334,16 +347,12 @@
@Override
public void onSuccess(AuthResult authResult) {
Log.d(TAG, "signInWithEmailAndPassword:onComplete:success");
- if (withData) {
promiseWithAuthResult(authResult, promise);
- } else {
- promiseWithUser(authResult.getUser(), promise);
- }
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
- public void onFailure(@NonNull Exception exception) {
+ public void onFailure(@Nonnull Exception exception) {
Log.e(TAG, "signInWithEmailAndPassword:onComplete:failure", exception);
promiseRejectAuthException(promise, exception);
}
@@ -380,34 +389,18 @@
})
.addOnFailureListener(new OnFailureListener() {
@Override
- public void onFailure(@NonNull Exception exception) {
+ public void onFailure(@Nonnull Exception exception) {
Log.e(TAG, "signInWithEmailLink:onComplete:failure", exception);
promiseRejectAuthException(promise, exception);
}
});
}
-
- @ReactMethod
- public void signInWithCustomToken(String appName, final String token, final Promise promise) {
- signInWithCustomToken(appName, token, promise, false);
- }
-
-
@ReactMethod
- public void signInAndRetrieveDataWithCustomToken(
- String appName,
- final String token,
- final Promise promise
- ) {
- signInWithCustomToken(appName, token, promise, true);
- }
-
private void signInWithCustomToken(
String appName,
final String token,
- final Promise promise,
- final boolean withData
+ final Promise promise
) {
Log.d(TAG, "signInWithCustomToken");
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
@@ -419,16 +412,12 @@
@Override
public void onSuccess(AuthResult authResult) {
Log.d(TAG, "signInWithCustomToken:onComplete:success");
- if (withData) {
promiseWithAuthResult(authResult, promise);
- } else {
- promiseWithUser(authResult.getUser(), promise);
- }
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
- public void onFailure(@NonNull Exception exception) {
+ public void onFailure(@Nonnull Exception exception) {
Log.e(TAG, "signInWithCustomToken:onComplete:failure", exception);
promiseRejectAuthException(promise, exception);
}
@@ -454,7 +443,7 @@
OnCompleteListener<Void> listener = new OnCompleteListener<Void>() {
@Override
- public void onComplete(@NonNull Task<Void> task) {
+ public void onComplete(@Nonnull Task<Void> task) {
if (task.isSuccessful()) {
Log.d(TAG, "sendPasswordResetEmail:onComplete:success");
promiseNoUser(promise, false);
@@ -497,7 +486,7 @@
OnCompleteListener<Void> listener = new OnCompleteListener<Void>() {
@Override
- public void onComplete(@NonNull Task<Void> task) {
+ public void onComplete(@Nonnull Task<Void> task) {
if (task.isSuccessful()) {
Log.d(TAG, "sendSignInLinkToEmail:onComplete:success");
promiseNoUser(promise, false);
@@ -539,7 +528,7 @@
.delete()
.addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
- public void onComplete(@NonNull Task<Void> task) {
+ public void onComplete(@Nonnull Task<Void> task) {
if (task.isSuccessful()) {
Log.d(TAG, "delete:onComplete:success");
promiseNoUser(promise, false);
@@ -577,7 +566,7 @@
.reload()
.addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
- public void onComplete(@NonNull Task<Void> task) {
+ public void onComplete(@Nonnull Task<Void> task) {
if (task.isSuccessful()) {
Log.d(TAG, "reload:onComplete:success");
promiseWithUser(firebaseAuth.getCurrentUser(), promise);
@@ -614,7 +603,7 @@
} else {
OnCompleteListener<Void> listener = new OnCompleteListener<Void>() {
@Override
- public void onComplete(@NonNull Task<Void> task) {
+ public void onComplete(@Nonnull Task<Void> task) {
if (task.isSuccessful()) {
Log.d(TAG, "sendEmailVerification:onComplete:success");
promiseWithUser(firebaseAuth.getCurrentUser(), promise);
@@ -661,7 +650,7 @@
.updateEmail(email)
.addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
- public void onComplete(@NonNull Task<Void> task) {
+ public void onComplete(@Nonnull Task<Void> task) {
if (task.isSuccessful()) {
Log.d(TAG, "updateEmail:onComplete:success");
promiseWithUser(firebaseAuth.getCurrentUser(), promise);
@@ -697,7 +686,7 @@
.updatePassword(password)
.addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
- public void onComplete(@NonNull Task<Void> task) {
+ public void onComplete(@Nonnull Task<Void> task) {
if (task.isSuccessful()) {
Log.d(TAG, "updatePassword:onComplete:success");
promiseWithUser(firebaseAuth.getCurrentUser(), promise);
@@ -712,6 +701,63 @@
}
/**
+ * updatePhoneNumber
+ *
+ * @param provider
+ * @param authToken
+ * @param authSecret
+ * @param promise
+ */
+ @ReactMethod
+ private void updatePhoneNumber(
+ String appName,
+ String provider,
+ String authToken,
+ String authSecret,
+ final Promise promise
+ ) {
+ FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
+ final FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp);
+ FirebaseUser user = firebaseAuth.getCurrentUser();
+
+ if (!provider.equals("phone")) {
+ promise.reject(
+ "auth/invalid-credential",
+ "The supplied auth credential does not have a phone provider."
+ );
+ }
+
+ PhoneAuthCredential credential = getPhoneAuthCredential(authToken, authSecret);
+
+ if (credential == null) {
+ promise.reject(
+ "auth/invalid-credential",
+ "The supplied auth credential is malformed, has expired or is not currently supported."
+ );
+ } else if (user == null) {
+ promiseNoUser(promise, false);
+ Log.e(TAG, "updatePhoneNumber:failure:noCurrentUser");
+ } else {
+ Log.d(TAG, "updatePhoneNumber");
+ user
+ .updatePhoneNumber(credential)
+ .addOnCompleteListener(new OnCompleteListener<Void>() {
+ @Override
+ public void onComplete(@Nonnull Task<Void> task) {
+ if (task.isSuccessful()) {
+ Log.d(TAG, "updatePhoneNumber:onComplete:success");
+ promiseWithUser(firebaseAuth.getCurrentUser(), promise);
+ } else {
+ Exception exception = task.getException();
+ Log.e(TAG, "updatePhoneNumber:onComplete:failure", exception);
+ promiseRejectAuthException(promise, exception);
+ }
+ }
+ });
+ }
+ }
+
+ /**
* updateProfile
*
* @param props
@@ -747,7 +793,7 @@
.updateProfile(profileUpdates)
.addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
- public void onComplete(@NonNull Task<Void> task) {
+ public void onComplete(@Nonnull Task<Void> task) {
if (task.isSuccessful()) {
Log.d(TAG, "updateProfile:onComplete:success");
promiseWithUser(firebaseAuth.getCurrentUser(), promise);
@@ -762,34 +808,12 @@
}
@ReactMethod
- public void signInWithCredential(
- String appName,
- String provider,
- String authToken,
- String authSecret,
- final Promise promise
- ) {
- signInWithCredential(appName, provider, authToken, authSecret, promise, false);
- }
-
- @ReactMethod
- public void signInAndRetrieveDataWithCredential(
- String appName,
- String provider,
- String authToken,
- String authSecret,
- final Promise promise
- ) {
- signInWithCredential(appName, provider, authToken, authSecret, promise, true);
- }
-
private void signInWithCredential(
String appName,
String provider,
String authToken,
String authSecret,
- final Promise promise,
- final boolean withData
+ final Promise promise
) {
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp);
@@ -807,17 +831,11 @@
.signInWithCredential(credential)
.addOnCompleteListener(new OnCompleteListener<AuthResult>() {
@Override
- public void onComplete(@NonNull Task<AuthResult> task) {
+ public void onComplete(@Nonnull Task<AuthResult> task) {
if (task.isSuccessful()) {
Log.d(TAG, "signInWithCredential:onComplete:success");
- if (withData) {
promiseWithAuthResult(task.getResult(), promise);
} else {
- promiseWithUser(task
- .getResult()
- .getUser(), promise);
- }
- } else {
Exception exception = task.getException();
Log.e(TAG, "signInWithCredential:onComplete:failure", exception);
promiseRejectAuthException(promise, exception);
@@ -864,7 +882,7 @@
.signInWithCredential(phoneAuthCredential)
.addOnCompleteListener(new OnCompleteListener<AuthResult>() {
@Override
- public void onComplete(@NonNull Task<AuthResult> task) {
+ public void onComplete(@Nonnull Task<AuthResult> task) {
if (task.isSuccessful()) {
// onAuthStateChanged will pick up the user change
Log.d(
@@ -875,7 +893,15 @@
// as calling ConfirmationResult.confirm(code) is invalid in this case anyway
if (!promiseResolved) {
WritableMap verificationMap = Arguments.createMap();
- verificationMap.putNull("verificationId");
+
+ Parcel parcel = Parcel.obtain();
+ phoneAuthCredential.writeToParcel(parcel, 0);
+ parcel.setDataPosition(16); // verificationId
+ String verificationId = parcel.readString();
+ mVerificationId = verificationId;
+ parcel.recycle();
+
+ verificationMap.putString("verificationId", verificationId);
promise.resolve(verificationMap);
}
} else {
@@ -971,15 +997,18 @@
.signInWithCredential(credential)
.addOnCompleteListener(new OnCompleteListener<AuthResult>() {
@Override
- public void onComplete(@NonNull Task<AuthResult> task) {
+ public void onComplete(@Nonnull Task<AuthResult> task) {
if (task.isSuccessful()) {
Log.d(
TAG,
"_confirmVerificationCode:signInWithCredential:onComplete:success"
);
- promiseWithUser(task
+ promiseWithUser(
+ task
.getResult()
- .getUser(), promise);
+ .getUser(),
+ promise
+ );
} else {
Exception exception = task.getException();
Log.e(
@@ -1147,7 +1176,7 @@
.confirmPasswordReset(code, newPassword)
.addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
- public void onComplete(@NonNull Task<Void> task) {
+ public void onComplete(@Nonnull Task<Void> task) {
if (task.isSuccessful()) {
Log.d(TAG, "confirmPasswordReset:onComplete:success");
promiseNoUser(promise, false);
@@ -1177,10 +1206,10 @@
.applyActionCode(code)
.addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
- public void onComplete(@NonNull Task<Void> task) {
+ public void onComplete(@Nonnull Task<Void> task) {
if (task.isSuccessful()) {
Log.d(TAG, "applyActionCode:onComplete:success");
- promiseNoUser(promise, false);
+ promiseWithUser(firebaseAuth.getCurrentUser(), promise);
} else {
Exception exception = task.getException();
Log.e(TAG, "applyActionCode:onComplete:failure", exception);
@@ -1205,7 +1234,7 @@
.checkActionCode(code)
.addOnCompleteListener(new OnCompleteListener<ActionCodeResult>() {
@Override
- public void onComplete(@NonNull Task<ActionCodeResult> task) {
+ public void onComplete(@Nonnull Task<ActionCodeResult> task) {
if (task.isSuccessful()) {
Log.d(TAG, "checkActionCode:onComplete:success");
ActionCodeResult result = task.getResult();
@@ -1238,7 +1267,7 @@
break;
}
- writableMap.putString("actionType", actionType);
+ writableMap.putString("operation", actionType);
promise.resolve(writableMap);
} else {
@@ -1259,35 +1288,13 @@
* @param promise
*/
@ReactMethod
- public void linkWithCredential(
+ private void linkWithCredential(
String appName,
String provider,
String authToken,
String authSecret,
final Promise promise
) {
- link(appName, provider, authToken, authSecret, promise, false);
- }
-
- @ReactMethod
- public void linkAndRetrieveDataWithCredential(
- String appName,
- String provider,
- String authToken,
- String authSecret,
- final Promise promise
- ) {
- link(appName, provider, authToken, authSecret, promise, true);
- }
-
- private void link(
- String appName,
- String provider,
- String authToken,
- String authSecret,
- final Promise promise,
- final boolean withData
- ) {
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp);
@@ -1307,17 +1314,11 @@
.linkWithCredential(credential)
.addOnCompleteListener(new OnCompleteListener<AuthResult>() {
@Override
- public void onComplete(@NonNull Task<AuthResult> task) {
+ public void onComplete(@Nonnull Task<AuthResult> task) {
if (task.isSuccessful()) {
Log.d(TAG, "link:onComplete:success");
- if (withData) {
promiseWithAuthResult(task.getResult(), promise);
} else {
- promiseWithUser(task
- .getResult()
- .getUser(), promise);
- }
- } else {
Exception exception = task.getException();
Log.e(TAG, "link:onComplete:failure", exception);
promiseRejectAuthException(promise, exception);
@@ -1342,7 +1343,7 @@
.unlink(providerId)
.addOnCompleteListener(new OnCompleteListener<AuthResult>() {
@Override
- public void onComplete(@NonNull Task<AuthResult> task) {
+ public void onComplete(@Nonnull Task<AuthResult> task) {
if (task.isSuccessful()) {
Log.d(TAG, "unlink:onComplete:success");
promiseWithUser(task
@@ -1361,35 +1362,13 @@
}
@ReactMethod
- public void reauthenticateWithCredential(
+ private void reauthenticateWithCredential(
String appName,
String provider,
String authToken,
String authSecret,
final Promise promise
) {
- reauthenticate(appName, provider, authToken, authSecret, promise, false);
- }
-
- @ReactMethod
- public void reauthenticateAndRetrieveDataWithCredential(
- String appName,
- String provider,
- String authToken,
- String authSecret,
- final Promise promise
- ) {
- reauthenticate(appName, provider, authToken, authSecret, promise, true);
- }
-
- private void reauthenticate(
- String appName,
- String provider,
- String authToken,
- String authSecret,
- final Promise promise,
- final boolean withData
- ) {
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
final FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp);
@@ -1409,17 +1388,11 @@
.reauthenticateAndRetrieveData(credential)
.addOnCompleteListener(new OnCompleteListener<AuthResult>() {
@Override
- public void onComplete(@NonNull Task<AuthResult> task) {
+ public void onComplete(@Nonnull Task<AuthResult> task) {
if (task.isSuccessful()) {
Log.d(TAG, "reauthenticate:onComplete:success");
- if (withData) {
promiseWithAuthResult(task.getResult(), promise);
} else {
- promiseWithUser(task
- .getResult()
- .getUser(), promise);
- }
- } else {
Exception exception = task.getException();
Log.e(TAG, "reauthenticate:onComplete:failure", exception);
promiseRejectAuthException(promise, exception);
@@ -1452,16 +1425,7 @@
case "oauth":
return OAuthProvider.getCredential(provider, authToken, authSecret);
case "phone":
- // If the phone number is auto-verified quickly, then the verificationId can be null
- // We cached the credential as part of the verifyPhoneNumber request to be re-used here
- // if possible
- if (authToken == null && mCredential != null) {
- PhoneAuthCredential credential = mCredential;
- // Reset the cached credential
- mCredential = null;
- return credential;
- }
- return PhoneAuthProvider.getCredential(authToken, authSecret);
+ return getPhoneAuthCredential(authToken, authSecret);
case "password":
// authToken = email
// authSecret = password
@@ -1475,37 +1439,152 @@
}
}
+ /**
+ * Returns an instance of PhoneAuthCredential, potentially cached
+ */
+ private PhoneAuthCredential getPhoneAuthCredential(
+ String authToken,
+ String authSecret
+ ) {
+ // If the phone number is auto-verified quickly, then the verificationId can be null
+ // We cached the credential as part of the verifyPhoneNumber request to be re-used here
+ // if possible
+ if (authToken == null && mCredential != null) {
+ PhoneAuthCredential credential = mCredential;
+ // Reset the cached credential
+ mCredential = null;
+ return credential;
+ }
+
+ if (authToken != null) {
+ return PhoneAuthProvider.getCredential(authToken, authSecret);
+ }
+
+ return null;
+ }
+
+ /**
+ * getIdToken
+ *
+ * @param appName
+ * @param forceRefresh
+ * @param promise
+ */
@ReactMethod
- public void getToken(String appName, Boolean forceRefresh, final Promise promise) {
+ public void getIdToken(String appName, Boolean forceRefresh, final Promise promise) {
+ Log.d(TAG, "getIdToken");
+
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp);
-
FirebaseUser user = firebaseAuth.getCurrentUser();
- Log.d(TAG, "getToken/getIdToken");
- if (user != null) {
+ if (user == null) {
+ promiseNoUser(promise, true);
+ return;
+ }
+
user
.getIdToken(forceRefresh)
.addOnCompleteListener(new OnCompleteListener<GetTokenResult>() {
@Override
- public void onComplete(@NonNull Task<GetTokenResult> task) {
+ public void onComplete(@Nonnull Task<GetTokenResult> task) {
if (task.isSuccessful()) {
- Log.d(TAG, "getToken:onComplete:success");
- promise.resolve(task
- .getResult()
- .getToken());
+ Log.d(TAG, "getIdToken:onComplete:success");
+ GetTokenResult tokenResult = task.getResult();
+ promise.resolve(tokenResult.getToken());
} else {
Exception exception = task.getException();
- Log.e(TAG, "getToken:onComplete:failure", exception);
+ Log.e(TAG, "getIdToken:onComplete:failure", exception);
promiseRejectAuthException(promise, exception);
}
}
});
- } else {
+ }
+
+ /**
+ * getIdTokenResult
+ *
+ * @param appName
+ * @param forceRefresh
+ * @param promise
+ */
+ @ReactMethod
+ public void getIdTokenResult(String appName, Boolean forceRefresh, final Promise promise) {
+ Log.d(TAG, "getIdTokenResult");
+
+ FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
+ FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp);
+ FirebaseUser user = firebaseAuth.getCurrentUser();
+
+ if (user == null) {
promiseNoUser(promise, true);
+ return;
+ }
+
+ user
+ .getIdToken(forceRefresh)
+ .addOnCompleteListener(new OnCompleteListener<GetTokenResult>() {
+ @Override
+ public void onComplete(@Nonnull Task<GetTokenResult> task) {
+ if (task.isSuccessful()) {
+ Log.d(TAG, "getIdTokenResult:onComplete:success");
+ GetTokenResult tokenResult = task.getResult();
+ WritableMap tokenResultMap = Arguments.createMap();
+
+ Utils.mapPutValue(
+ "authTime",
+ Utils.timestampToUTC(tokenResult.getAuthTimestamp()),
+ tokenResultMap
+ );
+
+ Utils.mapPutValue(
+ "expirationTime",
+ Utils.timestampToUTC(tokenResult.getExpirationTimestamp()),
+ tokenResultMap
+ );
+
+ Utils.mapPutValue(
+ "issuedAtTime",
+ Utils.timestampToUTC(tokenResult.getIssuedAtTimestamp()),
+ tokenResultMap
+ );
+
+ Utils.mapPutValue(
+ "claims",
+ tokenResult.getClaims(),
+ tokenResultMap
+ );
+
+ Utils.mapPutValue(
+ "signInProvider",
+ tokenResult.getSignInProvider(),
+ tokenResultMap
+ );
+
+ Utils.mapPutValue(
+ "token",
+ tokenResult.getToken(),
+ tokenResultMap
+ );
+
+ promise.resolve(tokenResultMap);
+ } else {
+ Exception exception = task.getException();
+ Log.e(TAG, "getIdTokenResult:onComplete:failure", exception);
+ promiseRejectAuthException(promise, exception);
}
}
+ });
+ }
+
+ /**
+ * fetchSignInMethodsForEmail
+ *
+ * @param appName
+ * @param email
+ * @param promise
+ */
@ReactMethod
public void fetchSignInMethodsForEmail(String appName, String email, final Promise promise) {
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
@@ -1517,7 +1596,7 @@
.fetchSignInMethodsForEmail(email)
.addOnCompleteListener(new OnCompleteListener<SignInMethodQueryResult>() {
@Override
- public void onComplete(@NonNull Task<SignInMethodQueryResult> task) {
+ public void onComplete(@Nonnull Task<SignInMethodQueryResult> task) {
if (task.isSuccessful()) {
Log.d(TAG, "fetchProvidersForEmail:onComplete:success");
List<String> providers = task
@@ -1541,6 +1620,12 @@
});
}
+ /**
+ * setLanguageCode
+ *
+ * @param appName
+ * @param code
+ */
@ReactMethod
public void setLanguageCode(String appName, String code) {
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
@@ -1549,6 +1634,11 @@
firebaseAuth.setLanguageCode(code);
}
+ /**
+ * useDeviceLanguage
+ *
+ * @param appName
+ */
@ReactMethod
public void useDeviceLanguage(String appName) {
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
@@ -1568,7 +1658,7 @@
.verifyPasswordResetCode(code)
.addOnCompleteListener(new OnCompleteListener<String>() {
@Override
- public void onComplete(@NonNull Task<String> task) {
+ public void onComplete(@Nonnull Task<String> task) {
if (task.isSuccessful()) {
Log.d(TAG, "verifyPasswordResetCode:onComplete:success");
promise.resolve(task.getResult());
@@ -1622,10 +1712,12 @@
*/
private void promiseWithAuthResult(AuthResult authResult, Promise promise) {
if (authResult != null && authResult.getUser() != null) {
- WritableMap userMap = firebaseUserToMap(authResult.getUser());
WritableMap authResultMap = Arguments.createMap();
+ WritableMap userMap = firebaseUserToMap(authResult.getUser());
+
if (authResult.getAdditionalUserInfo() != null) {
WritableMap additionalUserInfoMap = Arguments.createMap();
+
additionalUserInfoMap.putBoolean(
"isNewUser",
authResult
@@ -1802,7 +1898,7 @@
final Uri photoUrl = userInfo.getPhotoUrl();
- if (photoUrl != null && !"".equals(photoUrl)) {
+ if (photoUrl != null && !"".equals(photoUrl.toString())) {
userInfoMap.putString("photoURL", photoUrl.toString());
} else {
userInfoMap.putNull("photoURL");
@@ -1872,7 +1968,7 @@
userMap.putNull("displayName");
}
- if (photoUrl != null && !"".equals(photoUrl)) {
+ if (photoUrl != null && !"".equals(photoUrl.toString())) {
userMap.putString("photoURL", photoUrl.toString());
} else {
userMap.putNull("photoURL");
@@ -1952,17 +2048,25 @@
List<FirebaseApp> firebaseAppList = FirebaseApp.getApps(getReactApplicationContext());
final Map<String, Object> appLanguage = new HashMap<>();
+ final Map<String, Object> appUser = new HashMap<>();
for (FirebaseApp app : firebaseAppList) {
String appName = app.getName();
FirebaseApp instance = FirebaseApp.getInstance(appName);
FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(instance);
+ FirebaseUser user = firebaseAuth.getCurrentUser();
appLanguage.put(appName, firebaseAuth.getLanguageCode());
+
+ if (user != null) {
+ appUser.put(appName, firebaseUserToMap(user));
+ }
}
constants.put("APP_LANGUAGE", appLanguage);
+ constants.put("APP_USER", appUser);
+
return constants;
}
}

android/src/main/java/io/invertase/firebase/config/RNFirebaseRemoteConfig.java

@@ -1,6 +1,5 @@
package io.invertase.firebase.config;
-import android.support.annotation.NonNull;
import android.util.Log;
import com.facebook.react.bridge.Arguments;
@@ -23,6 +22,8 @@
import java.util.Map;
import java.util.Set;
+import javax.annotation.Nonnull;
+
import io.invertase.firebase.Utils;
class RNFirebaseRemoteConfig extends ReactContextBaseJavaModule {
@@ -142,7 +143,7 @@
.fetch(withExpiration ? expirationDuration : 43200) // 12 hours default
.addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
- public void onComplete(@NonNull Task<Void> task) {
+ public void onComplete(@Nonnull Task<Void> task) {
if (task.isSuccessful()) {
promise.resolve(null);
} else {

android/src/main/java/io/invertase/firebase/database/RNFirebaseDatabase.java

@@ -25,27 +25,30 @@
import com.google.firebase.database.Transaction;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import javax.annotation.Nonnull;
+
import io.invertase.firebase.ErrorUtils;
import io.invertase.firebase.Utils;
public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
private static final String TAG = "RNFirebaseDatabase";
private static boolean enableLogging = false;
+ private static ReactApplicationContext reactApplicationContext = null;
private static HashMap<String, Boolean> loggingLevelSet = new HashMap<>();
- private HashMap<String, RNFirebaseDatabaseReference> references = new HashMap<>();
- private SparseArray<RNFirebaseTransactionHandler> transactionHandlers = new SparseArray<>();
+ private static HashMap<String, RNFirebaseDatabaseReference> references = new HashMap<>();
+ private static SparseArray<RNFirebaseTransactionHandler> transactionHandlers = new SparseArray<>();
RNFirebaseDatabase(ReactApplicationContext reactContext) {
super(reactContext);
}
-
- /*
- * REACT NATIVE METHODS
- */
+ static ReactApplicationContext getReactApplicationContextInstance() {
+ return reactApplicationContext;
+ }
/**
* Resolve null or reject with a js like error if databaseError exists
@@ -66,6 +69,11 @@
}
}
+
+ /*
+ * REACT NATIVE METHODS
+ */
+
/**
* Get a database instance for a specific firebase app instance
*
@@ -73,7 +81,7 @@
* @param dbURL
* @return
*/
- public static FirebaseDatabase getDatabaseForApp(String appName, String dbURL) {
+ static FirebaseDatabase getDatabaseForApp(String appName, String dbURL) {
FirebaseDatabase firebaseDatabase;
if (dbURL != null && dbURL.length() > 0) {
if (appName != null && appName.length() > 0) {
@@ -251,6 +259,26 @@
return errorMap;
}
+ @Override
+ public void initialize() {
+ super.initialize();
+ Log.d(TAG, "RNFirebaseDatabase:initialized");
+ reactApplicationContext = getReactApplicationContext();
+ }
+
+ @Override
+ public void onCatalystInstanceDestroy() {
+ super.onCatalystInstanceDestroy();
+
+ Iterator refIterator = references.entrySet().iterator();
+ while (refIterator.hasNext()) {
+ Map.Entry pair = (Map.Entry) refIterator.next();
+ RNFirebaseDatabaseReference nativeRef = (RNFirebaseDatabaseReference) pair.getValue();
+ nativeRef.removeAllEventListeners();
+ refIterator.remove(); // avoids a ConcurrentModificationException
+ }
+ }
+
/**
* @param appName
*/
@@ -386,8 +414,9 @@
DatabaseReference reference = getReferenceForAppPath(appName, dbURL, path);
reference.runTransaction(new Transaction.Handler() {
+ @Nonnull
@Override
- public Transaction.Result doTransaction(MutableData mutableData) {
+ public Transaction.Result doTransaction(@Nonnull MutableData mutableData) {
final RNFirebaseTransactionHandler transactionHandler = new RNFirebaseTransactionHandler(
transactionId,
appName,
@@ -421,6 +450,10 @@
return Transaction.abort();
}
+ if (transactionHandler.timeout) {
+ return Transaction.abort();
+ }
+
mutableData.setValue(transactionHandler.value);
return Transaction.success(mutableData);
}
@@ -785,7 +818,6 @@
ReadableArray modifiers
) {
return new RNFirebaseDatabaseReference(
- getReactApplicationContext(),
appName,
dbURL,
key,

android/src/main/java/io/invertase/firebase/database/RNFirebaseDatabaseReference.java

@@ -2,8 +2,6 @@
import android.annotation.SuppressLint;
import android.os.AsyncTask;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
import android.util.Log;
import com.facebook.react.bridge.Arguments;
@@ -21,9 +19,13 @@
import java.lang.ref.WeakReference;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
import io.invertase.firebase.Utils;
class RNFirebaseDatabaseReference {
@@ -32,7 +34,6 @@
private Query query;
private String appName;
private String dbURL;
- private ReactContext reactContext;
private HashMap<String, ChildEventListener> childEventListeners = new HashMap<>();
private HashMap<String, ValueEventListener> valueEventListeners = new HashMap<>();
@@ -40,14 +41,12 @@
* RNFirebase wrapper around FirebaseDatabaseReference,
* handles Query generation and event listeners.
*
- * @param context
* @param app
* @param refKey
* @param refPath
* @param modifiersArray
*/
RNFirebaseDatabaseReference(
- ReactContext context,
String app,
String url,
String refKey,
@@ -58,10 +57,32 @@
query = null;
appName = app;
dbURL = url;
- reactContext = context;
buildDatabaseQueryAtPathAndModifiers(refPath, modifiersArray);
}
+ void removeAllEventListeners() {
+ if (hasListeners()) {
+ Iterator valueIterator = valueEventListeners.entrySet().iterator();
+
+ while (valueIterator.hasNext()) {
+ Map.Entry pair = (Map.Entry) valueIterator.next();
+ ValueEventListener valueEventListener = (ValueEventListener) pair.getValue();
+ query.removeEventListener(valueEventListener);
+ valueIterator.remove();
+ }
+
+ Iterator childIterator = childEventListeners.entrySet().iterator();
+
+ while (childIterator.hasNext()) {
+ Map.Entry pair = (Map.Entry) childIterator.next();
+ ChildEventListener childEventListener = (ChildEventListener) pair.getValue();
+ query.removeEventListener(childEventListener);
+ childIterator.remove();
+ }
+ }
+ }
+
+
/**
* Used outside of class for keepSynced etc.
*
@@ -140,7 +161,6 @@
*/
private void addOnceValueEventListener(final Promise promise) {
@SuppressLint("StaticFieldLeak") final DataSnapshotToMapAsyncTask asyncTask = new DataSnapshotToMapAsyncTask(
- reactContext,
this
) {
@Override
@@ -151,12 +171,12 @@
ValueEventListener onceValueEventListener = new ValueEventListener() {
@Override
- public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
+ public void onDataChange(@Nonnull DataSnapshot dataSnapshot) {
asyncTask.execute(dataSnapshot, null);
}
@Override
- public void onCancelled(@NonNull DatabaseError error) {
+ public void onCancelled(@Nonnull DatabaseError error) {
RNFirebaseDatabase.handlePromise(promise, error);
}
};
@@ -174,7 +194,7 @@
private void addChildOnceEventListener(final String eventName, final Promise promise) {
ChildEventListener childEventListener = new ChildEventListener() {
@Override
- public void onChildAdded(@NonNull DataSnapshot dataSnapshot, String previousChildName) {
+ public void onChildAdded(@Nonnull DataSnapshot dataSnapshot, String previousChildName) {
if ("child_added".equals(eventName)) {
query.removeEventListener(this);
WritableMap data = RNFirebaseDatabaseUtils.snapshotToMap(dataSnapshot, previousChildName);
@@ -183,7 +203,7 @@
}
@Override
- public void onChildChanged(@NonNull DataSnapshot dataSnapshot, String previousChildName) {
+ public void onChildChanged(@Nonnull DataSnapshot dataSnapshot, String previousChildName) {
if ("child_changed".equals(eventName)) {
query.removeEventListener(this);
WritableMap data = RNFirebaseDatabaseUtils.snapshotToMap(dataSnapshot, previousChildName);
@@ -192,7 +212,7 @@
}
@Override
- public void onChildRemoved(@NonNull DataSnapshot dataSnapshot) {
+ public void onChildRemoved(@Nonnull DataSnapshot dataSnapshot) {
if ("child_removed".equals(eventName)) {
query.removeEventListener(this);
WritableMap data = RNFirebaseDatabaseUtils.snapshotToMap(dataSnapshot, null);
@@ -201,7 +221,7 @@
}
@Override
- public void onChildMoved(@NonNull DataSnapshot dataSnapshot, String previousChildName) {
+ public void onChildMoved(@Nonnull DataSnapshot dataSnapshot, String previousChildName) {
if ("child_moved".equals(eventName)) {
query.removeEventListener(this);
WritableMap data = RNFirebaseDatabaseUtils.snapshotToMap(dataSnapshot, previousChildName);
@@ -210,7 +230,7 @@
}
@Override
- public void onCancelled(@NonNull DatabaseError error) {
+ public void onCancelled(@Nonnull DatabaseError error) {
query.removeEventListener(this);
RNFirebaseDatabase.handlePromise(promise, error);
}
@@ -259,35 +279,35 @@
if (!hasEventListener(eventRegistrationKey)) {
ChildEventListener childEventListener = new ChildEventListener() {
@Override
- public void onChildAdded(@NonNull DataSnapshot dataSnapshot, String previousChildName) {
+ public void onChildAdded(@Nonnull DataSnapshot dataSnapshot, String previousChildName) {
if ("child_added".equals(eventType)) {
handleDatabaseEvent("child_added", registration, dataSnapshot, previousChildName);
}
}
@Override
- public void onChildChanged(@NonNull DataSnapshot dataSnapshot, String previousChildName) {
+ public void onChildChanged(@Nonnull DataSnapshot dataSnapshot, String previousChildName) {
if ("child_changed".equals(eventType)) {
handleDatabaseEvent("child_changed", registration, dataSnapshot, previousChildName);
}
}
@Override
- public void onChildRemoved(@NonNull DataSnapshot dataSnapshot) {
+ public void onChildRemoved(@Nonnull DataSnapshot dataSnapshot) {
if ("child_removed".equals(eventType)) {
handleDatabaseEvent("child_removed", registration, dataSnapshot, null);
}
}
@Override
- public void onChildMoved(@NonNull DataSnapshot dataSnapshot, String previousChildName) {
+ public void onChildMoved(@Nonnull DataSnapshot dataSnapshot, String previousChildName) {
if ("child_moved".equals(eventType)) {
handleDatabaseEvent("child_moved", registration, dataSnapshot, previousChildName);
}
}
@Override
- public void onCancelled(@NonNull DatabaseError error) {
+ public void onCancelled(@Nonnull DatabaseError error) {
removeEventListener(eventRegistrationKey);
handleDatabaseError(registration, error);
}
@@ -308,12 +328,12 @@
if (!hasEventListener(eventRegistrationKey)) {
ValueEventListener valueEventListener = new ValueEventListener() {
@Override
- public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
+ public void onDataChange(@Nonnull DataSnapshot dataSnapshot) {
handleDatabaseEvent("value", registration, dataSnapshot, null);
}
@Override
- public void onCancelled(@NonNull DatabaseError error) {
+ public void onCancelled(@Nonnull DatabaseError error) {
removeEventListener(eventRegistrationKey);
handleDatabaseError(registration, error);
}
@@ -337,7 +357,7 @@
@Nullable String previousChildName
) {
@SuppressLint("StaticFieldLeak")
- DataSnapshotToMapAsyncTask asyncTask = new DataSnapshotToMapAsyncTask(reactContext, this) {
+ DataSnapshotToMapAsyncTask asyncTask = new DataSnapshotToMapAsyncTask(this) {
@Override
protected void onPostExecute(WritableMap data) {
if (this.isAvailable()) {
@@ -346,7 +366,11 @@
event.putString("key", key);
event.putString("eventType", eventType);
event.putMap("registration", Utils.readableMapToWritableMap(registration));
- Utils.sendEvent(reactContext, "database_sync_event", event);
+ Utils.sendEvent(
+ RNFirebaseDatabase.getReactApplicationContextInstance(),
+ "database_sync_event",
+ event
+ );
}
}
};
@@ -366,7 +390,11 @@
event.putMap("error", RNFirebaseDatabase.getJSError(error));
event.putMap("registration", Utils.readableMapToWritableMap(registration));
- Utils.sendEvent(reactContext, "database_sync_event", event);
+ Utils.sendEvent(
+ RNFirebaseDatabase.getReactApplicationContextInstance(),
+ "database_sync_event",
+ event
+ );
}
/**
@@ -553,13 +581,10 @@
* Introduced due to https://github.com/invertase/react-native-firebase/issues/1284
*/
private static class DataSnapshotToMapAsyncTask extends AsyncTask<Object, Void, WritableMap> {
-
- private WeakReference<ReactContext> reactContextWeakReference;
private WeakReference<RNFirebaseDatabaseReference> referenceWeakReference;
- DataSnapshotToMapAsyncTask(ReactContext context, RNFirebaseDatabaseReference reference) {
+ DataSnapshotToMapAsyncTask(RNFirebaseDatabaseReference reference) {
referenceWeakReference = new WeakReference<>(reference);
- reactContextWeakReference = new WeakReference<>(context);
}
@Override
@@ -571,8 +596,7 @@
return RNFirebaseDatabaseUtils.snapshotToMap(dataSnapshot, previousChildName);
} catch (RuntimeException e) {
if (isAvailable()) {
- reactContextWeakReference
- .get()
+ RNFirebaseDatabase.getReactApplicationContextInstance()
.handleException(e);
}
throw e;
@@ -585,7 +609,7 @@
}
Boolean isAvailable() {
- return reactContextWeakReference.get() != null && referenceWeakReference.get() != null;
+ return RNFirebaseDatabase.getReactApplicationContextInstance() != null && referenceWeakReference.get() != null;
}
}
}

android/src/main/java/io/invertase/firebase/database/RNFirebaseDatabaseUtils.java

@@ -1,6 +1,5 @@
package io.invertase.firebase.database;
-import android.support.annotation.Nullable;
import android.util.Log;
import com.facebook.react.bridge.Arguments;
@@ -10,6 +9,8 @@
import com.google.firebase.database.DataSnapshot;
import com.google.firebase.database.MutableData;
+import javax.annotation.Nullable;
+
import io.invertase.firebase.Utils;
public class RNFirebaseDatabaseUtils {

android/src/main/java/io/invertase/firebase/database/RNFirebaseTransactionHandler.java

@@ -71,6 +71,7 @@
*/
void await() throws InterruptedException {
lock.lock();
+ signalled = false;
long timeoutExpired = System.currentTimeMillis() + 5000;

android/src/main/java/io/invertase/firebase/fabric/crashlytics/RNFirebaseCrashlytics.java

@@ -7,6 +7,7 @@
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
+import io.fabric.sdk.android.Fabric;
public class RNFirebaseCrashlytics extends ReactContextBaseJavaModule {
@@ -63,4 +64,10 @@
public void setUserIdentifier(String userId) {
Crashlytics.setUserIdentifier(userId);
}
+
+ @ReactMethod
+ public void enableCrashlyticsCollection() {
+ Fabric.with(getReactApplicationContext(), new Crashlytics());
+ }
+
}

android/src/main/java/io/invertase/firebase/firestore/FirestoreSerialize.java

@@ -9,6 +9,7 @@
import com.facebook.react.bridge.ReadableMapKeySetIterator;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap;
+import com.google.firebase.Timestamp;
import com.google.firebase.firestore.Blob;
import com.google.firebase.firestore.DocumentChange;
import com.google.firebase.firestore.DocumentReference;
@@ -18,6 +19,7 @@
import com.google.firebase.firestore.FirebaseFirestore;
import com.google.firebase.firestore.GeoPoint;
import com.google.firebase.firestore.QuerySnapshot;
+import com.google.firebase.firestore.SnapshotMetadata;
import java.util.ArrayList;
import java.util.Date;
@@ -25,19 +27,61 @@
import java.util.List;
import java.util.Map;
+import javax.annotation.Nullable;
+
import io.invertase.firebase.Utils;
-public class FirestoreSerialize {
+class FirestoreSerialize {
private static final String TAG = "FirestoreSerialize";
- private static final String KEY_CHANGES = "changes";
+
+ // Keys
+ private static final String TYPE = "type";
+ private static final String VALUE = "value";
private static final String KEY_DATA = "data";
+ private static final String KEY_PATH = "path";
+ private static final String KEY_META = "metadata";
+ private static final String KEY_CHANGES = "changes";
+ private static final String KEY_OPTIONS = "options";
+ private static final String KEY_SECONDS = "seconds";
+ private static final String KEY_NANOSECONDS = "nanoseconds";
+ private static final String KEY_LATITUDE = "latitude";
+ private static final String KEY_LONGITUDE = "longitude";
+ private static final String KEY_DOCUMENTS = "documents";
+ private static final String KEY_DOC_CHANGE_TYPE = "type";
+ private static final String KEY_META_FROM_CACHE = "fromCache";
private static final String KEY_DOC_CHANGE_DOCUMENT = "document";
private static final String KEY_DOC_CHANGE_NEW_INDEX = "newIndex";
private static final String KEY_DOC_CHANGE_OLD_INDEX = "oldIndex";
- private static final String KEY_DOC_CHANGE_TYPE = "type";
- private static final String KEY_DOCUMENTS = "documents";
- private static final String KEY_METADATA = "metadata";
- private static final String KEY_PATH = "path";
+ private static final String KEY_META_HAS_PENDING_WRITES = "hasPendingWrites";
+
+ // Types
+ private static final String TYPE_NAN = "nan";
+ private static final String TYPE_NULL = "null";
+ private static final String TYPE_BLOB = "blob";
+ private static final String TYPE_DATE = "date";
+ private static final String TYPE_ARRAY = "array";
+ private static final String TYPE_STRING = "string";
+ private static final String TYPE_NUMBER = "number";
+ private static final String TYPE_OBJECT = "object";
+ private static final String TYPE_BOOLEAN = "boolean";
+ private static final String TYPE_GEOPOINT = "geopoint";
+ private static final String TYPE_TIMESTAMP = "timestamp";
+ private static final String TYPE_INFINITY = "infinity";
+ private static final String TYPE_REFERENCE = "reference";
+ private static final String TYPE_DOCUMENTID = "documentid";
+ private static final String TYPE_FIELDVALUE = "fieldvalue";
+ private static final String TYPE_FIELDVALUE_DELETE = "delete";
+ private static final String TYPE_FIELDVALUE_TIMESTAMP = "timestamp";
+ private static final String TYPE_FIELDVALUE_INCREMENT = "increment";
+ private static final String TYPE_FIELDVALUE_UNION = "union";
+ private static final String TYPE_FIELDVALUE_REMOVE = "remove";
+ private static final String TYPE_FIELDVALUE_TYPE = "type";
+ private static final String TYPE_FIELDVALUE_ELEMENTS = "elements";
+
+ // Document Change Types
+ private static final String CHANGE_ADDED = "added";
+ private static final String CHANGE_MODIFIED = "modified";
+ private static final String CHANGE_REMOVED = "removed";
/**
* Convert a DocumentSnapshot instance into a React Native WritableMap
@@ -46,67 +90,60 @@
* @return WritableMap
*/
static WritableMap snapshotToWritableMap(DocumentSnapshot documentSnapshot) {
+ WritableMap metadata = Arguments.createMap();
WritableMap documentMap = Arguments.createMap();
+ SnapshotMetadata snapshotMetadata = documentSnapshot.getMetadata();
- documentMap.putString(
- KEY_PATH,
- documentSnapshot
- .getReference()
- .getPath()
- );
+ // build metadata
+ metadata.putBoolean(KEY_META_FROM_CACHE, snapshotMetadata.isFromCache());
+ metadata.putBoolean(KEY_META_HAS_PENDING_WRITES, snapshotMetadata.hasPendingWrites());
+
+ documentMap.putMap(KEY_META, metadata);
+ documentMap.putString(KEY_PATH, documentSnapshot.getReference().getPath());
if (documentSnapshot.exists()) {
documentMap.putMap(KEY_DATA, objectMapToWritable(documentSnapshot.getData()));
}
- // metadata
- WritableMap metadata = Arguments.createMap();
- metadata.putBoolean(
- "fromCache",
- documentSnapshot
- .getMetadata()
- .isFromCache()
- );
- metadata.putBoolean(
- "hasPendingWrites",
- documentSnapshot
- .getMetadata()
- .hasPendingWrites()
- );
- documentMap.putMap(KEY_METADATA, metadata);
return documentMap;
}
+ /**
+ * Convert a Firestore QuerySnapshot instance to a RN serializable WritableMap type map
+ *
+ * @param querySnapshot QuerySnapshot
+ * @return WritableMap
+ */
static WritableMap snapshotToWritableMap(QuerySnapshot querySnapshot) {
- WritableMap queryMap = Arguments.createMap();
+ WritableMap metadata = Arguments.createMap();
+ WritableMap writableMap = Arguments.createMap();
+ WritableArray documents = Arguments.createArray();
+ SnapshotMetadata snapshotMetadata = querySnapshot.getMetadata();
+ List<DocumentSnapshot> documentSnapshots = querySnapshot.getDocuments();
List<DocumentChange> documentChanges = querySnapshot.getDocumentChanges();
- queryMap.putArray(KEY_CHANGES, documentChangesToWritableArray(documentChanges));
- // documents
- WritableArray documents = Arguments.createArray();
- List<DocumentSnapshot> documentSnapshots = querySnapshot.getDocuments();
+ // convert documents documents
for (DocumentSnapshot documentSnapshot : documentSnapshots) {
documents.pushMap(snapshotToWritableMap(documentSnapshot));
}
- queryMap.putArray(KEY_DOCUMENTS, documents);
- // metadata
- WritableMap metadata = Arguments.createMap();
- metadata.putBoolean(
- "fromCache",
- querySnapshot
- .getMetadata()
- .isFromCache()
+ // build metadata
+ metadata.putBoolean(KEY_META_FROM_CACHE, snapshotMetadata.isFromCache());
+ metadata.putBoolean(KEY_META_HAS_PENDING_WRITES, snapshotMetadata.hasPendingWrites());
+
+ // set metadata
+ writableMap.putMap(KEY_META, metadata);
+
+ // set documents
+ writableMap.putArray(KEY_DOCUMENTS, documents);
+
+ // set document changes
+ writableMap.putArray(
+ KEY_CHANGES,
+ documentChangesToWritableArray(documentChanges)
);
- metadata.putBoolean(
- "hasPendingWrites",
- querySnapshot
- .getMetadata()
- .hasPendingWrites()
- );
- queryMap.putMap(KEY_METADATA, metadata);
- return queryMap;
+ return writableMap;
}
/**
@@ -134,13 +173,14 @@
switch (documentChange.getType()) {
case ADDED:
- documentChangeMap.putString(KEY_DOC_CHANGE_TYPE, "added");
+ documentChangeMap.putString(KEY_DOC_CHANGE_TYPE, CHANGE_ADDED);
+ break;
+ case MODIFIED:
+ documentChangeMap.putString(KEY_DOC_CHANGE_TYPE, CHANGE_MODIFIED);
break;
case REMOVED:
- documentChangeMap.putString(KEY_DOC_CHANGE_TYPE, "removed");
+ documentChangeMap.putString(KEY_DOC_CHANGE_TYPE, CHANGE_REMOVED);
break;
- case MODIFIED:
- documentChangeMap.putString(KEY_DOC_CHANGE_TYPE, "modified");
}
documentChangeMap.putMap(
@@ -165,11 +207,12 @@
WritableMap typeMap = buildTypeMap(entry.getValue());
writableMap.putMap(entry.getKey(), typeMap);
}
+
return writableMap;
}
/**
- * Converts an Object array into a React Native WritableArray.
+ * Converts a Object array into a React Native WritableArray.
*
* @param array Object[]
* @return WritableArray
@@ -186,9 +229,10 @@
}
/**
- * Detects an objects type and creates a Map to represent the type and value.
+ * Convert an Object to a type map for use in JS land to convert to JS equivalent.
*
* @param value Object
+ * @return WritableMap
*/
private static WritableMap buildTypeMap(Object value) {
WritableMap typeMap = Arguments.createMap();
@@ -192,130 +236,287 @@
*/
private static WritableMap buildTypeMap(Object value) {
WritableMap typeMap = Arguments.createMap();
+
if (value == null) {
- typeMap.putString("type", "null");
- typeMap.putNull("value");
- } else {
+ typeMap.putString(TYPE, TYPE_NULL);
+ typeMap.putNull(VALUE);
+ return typeMap;
+ }
+
if (value instanceof Boolean) {
- typeMap.putString("type", "boolean");
- typeMap.putBoolean("value", (Boolean) value);
- } else if (value instanceof Integer) {
- typeMap.putString("type", "number");
- typeMap.putDouble("value", ((Integer) value).doubleValue());
- } else if (value instanceof Long) {
- typeMap.putString("type", "number");
- typeMap.putDouble("value", ((Long) value).doubleValue());
- } else if (value instanceof Double) {
- typeMap.putString("type", "number");
- typeMap.putDouble("value", (Double) value);
- } else if (value instanceof Float) {
- typeMap.putString("type", "number");
- typeMap.putDouble("value", ((Float) value).doubleValue());
- } else if (value instanceof String) {
- typeMap.putString("type", "string");
- typeMap.putString("value", (String) value);
- } else if (Map.class.isAssignableFrom(value.getClass())) {
- typeMap.putString("type", "object");
- typeMap.putMap("value", objectMapToWritable((Map<String, Object>) value));
- } else if (List.class.isAssignableFrom(value.getClass())) {
- typeMap.putString("type", "array");
+ typeMap.putString(TYPE, TYPE_BOOLEAN);
+ typeMap.putBoolean(VALUE, (Boolean) value);
+ return typeMap;
+ }
+
+ if (value instanceof Integer) {
+ typeMap.putString(TYPE, TYPE_NUMBER);
+ typeMap.putDouble(VALUE, ((Integer) value).doubleValue());
+ return typeMap;
+ }
+
+ if (value instanceof Double) {
+ Double doubleValue = (Double) value;
+
+ if (Double.isInfinite(doubleValue)) {
+ typeMap.putString(TYPE, TYPE_INFINITY);
+ return typeMap;
+ }
+
+ if (Double.isNaN(doubleValue)) {
+ typeMap.putString(TYPE, TYPE_NAN);
+ return typeMap;
+ }
+
+ typeMap.putString(TYPE, TYPE_NUMBER);
+ typeMap.putDouble(VALUE, doubleValue);
+ return typeMap;
+ }
+
+ if (value instanceof Float) {
+ typeMap.putString(TYPE, TYPE_NUMBER);
+ typeMap.putDouble(VALUE, ((Float) value).doubleValue());
+ return typeMap;
+ }
+
+ if (value instanceof Long) {
+ typeMap.putString(TYPE, TYPE_NUMBER);
+ typeMap.putDouble(VALUE, ((Long) value).doubleValue());
+ return typeMap;
+ }
+
+ if (value instanceof String) {
+ typeMap.putString(TYPE, TYPE_STRING);
+ typeMap.putString(VALUE, (String) value);
+ return typeMap;
+ }
+
+ if (value instanceof Date) {
+ typeMap.putString(TYPE, TYPE_DATE);
+ typeMap.putDouble(VALUE, ((Date) value).getTime());
+ return typeMap;
+ }
+
+ if (Map.class.isAssignableFrom(value.getClass())) {
+ typeMap.putString(TYPE, TYPE_OBJECT);
+ typeMap.putMap(VALUE, objectMapToWritable((Map<String, Object>) value));
+ return typeMap;
+ }
+
+ if (List.class.isAssignableFrom(value.getClass())) {
+ typeMap.putString(TYPE, TYPE_ARRAY);
List<Object> list = (List<Object>) value;
Object[] array = list.toArray(new Object[list.size()]);
- typeMap.putArray("value", objectArrayToWritable(array));
- } else if (value instanceof DocumentReference) {
- typeMap.putString("type", "reference");
- typeMap.putString("value", ((DocumentReference) value).getPath());
- } else if (value instanceof GeoPoint) {
- typeMap.putString("type", "geopoint");
+ typeMap.putArray(VALUE, objectArrayToWritable(array));
+ return typeMap;
+ }
+
+ if (value instanceof DocumentReference) {
+ typeMap.putString(TYPE, TYPE_REFERENCE);
+ typeMap.putString(VALUE, ((DocumentReference) value).getPath());
+ return typeMap;
+ }
+
+
+ if (value instanceof Timestamp) {
+ WritableMap timestampMap = Arguments.createMap();
+
+ timestampMap.putDouble(KEY_SECONDS, ((Timestamp) value).getSeconds());
+ timestampMap.putInt(KEY_NANOSECONDS, ((Timestamp) value).getNanoseconds());
+
+ typeMap.putString(TYPE, TYPE_TIMESTAMP);
+ typeMap.putMap(VALUE, timestampMap);
+ return typeMap;
+ }
+
+ if (value instanceof GeoPoint) {
WritableMap geoPoint = Arguments.createMap();
- geoPoint.putDouble("latitude", ((GeoPoint) value).getLatitude());
- geoPoint.putDouble("longitude", ((GeoPoint) value).getLongitude());
- typeMap.putMap("value", geoPoint);
- } else if (value instanceof Date) {
- typeMap.putString("type", "date");
- typeMap.putDouble("value", ((Date) value).getTime());
- } else if (value instanceof Blob) {
- typeMap.putString("type", "blob");
- typeMap.putString("value", Base64.encodeToString(((Blob) value).toBytes(), Base64.NO_WRAP));
- } else {
- Log.e(TAG, "buildTypeMap: Cannot convert object of type " + value.getClass());
- typeMap.putString("type", "null");
- typeMap.putNull("value");
+
+ geoPoint.putDouble(KEY_LATITUDE, ((GeoPoint) value).getLatitude());
+ geoPoint.putDouble(KEY_LONGITUDE, ((GeoPoint) value).getLongitude());
+
+ typeMap.putMap(VALUE, geoPoint);
+ typeMap.putString(TYPE, TYPE_GEOPOINT);
+
+ return typeMap;
}
+
+ if (value instanceof Blob) {
+ typeMap.putString(TYPE, TYPE_BLOB);
+ typeMap.putString(VALUE, Base64.encodeToString(((Blob) value).toBytes(), Base64.NO_WRAP));
+ return typeMap;
}
+ Log.w(TAG, "Unknown object of type " + value.getClass());
+ typeMap.putString(TYPE, TYPE_NULL);
+ typeMap.putNull(VALUE);
return typeMap;
}
+ /**
+ * Converts a ReadableMap to a usable format for Firestore
+ *
+ * @param firestore FirebaseFirestore
+ * @param readableMap ReadableMap
+ * @return Map<>
+ */
static Map<String, Object> parseReadableMap(
FirebaseFirestore firestore,
- ReadableMap readableMap
+ @Nullable ReadableMap readableMap
) {
Map<String, Object> map = new HashMap<>();
- if (readableMap != null) {
+ if (readableMap == null) return map;
+
ReadableMapKeySetIterator iterator = readableMap.keySetIterator();
while (iterator.hasNextKey()) {
String key = iterator.nextKey();
map.put(key, parseTypeMap(firestore, readableMap.getMap(key)));
}
- }
+
return map;
}
- static List<Object> parseReadableArray(FirebaseFirestore firestore, ReadableArray readableArray) {
+ /**
+ * Convert a RN array to a valid Firestore array
+ *
+ * @param firestore FirebaseFirestore
+ * @param readableArray ReadableArray
+ * @return List<Object>
+ */
+ static List<Object> parseReadableArray(
+ FirebaseFirestore firestore,
+ @Nullable ReadableArray readableArray
+ ) {
List<Object> list = new ArrayList<>();
- if (readableArray != null) {
+ if (readableArray == null) return list;
+
for (int i = 0; i < readableArray.size(); i++) {
list.add(parseTypeMap(firestore, readableArray.getMap(i)));
}
- }
+
return list;
}
+ /**
+ * Convert a JS type to a Firestore type
+ *
+ * @param firestore FirebaseFirestore
+ * @param typeMap ReadableMap
+ * @return Object
+ */
static Object parseTypeMap(FirebaseFirestore firestore, ReadableMap typeMap) {
- String type = typeMap.getString("type");
- if ("boolean".equals(type)) {
- return typeMap.getBoolean("value");
- } else if ("number".equals(type)) {
- return typeMap.getDouble("value");
- } else if ("string".equals(type)) {
- return typeMap.getString("value");
- } else if ("null".equals(type)) {
+ String type = typeMap.getString(TYPE);
+
+ if (TYPE_NULL.equals(type)) {
return null;
- } else if ("array".equals(type)) {
- return parseReadableArray(firestore, typeMap.getArray("value"));
- } else if ("object".equals(type)) {
- return parseReadableMap(firestore, typeMap.getMap("value"));
- } else if ("reference".equals(type)) {
- String path = typeMap.getString("value");
- return firestore.document(path);
- } else if ("geopoint".equals(type)) {
- ReadableMap geoPoint = typeMap.getMap("value");
- return new GeoPoint(geoPoint.getDouble("latitude"), geoPoint.getDouble("longitude"));
- } else if ("blob".equals(type)) {
- String base64String = typeMap.getString("value");
- return Blob.fromBytes(Base64.decode(base64String, Base64.NO_WRAP));
- } else if ("date".equals(type)) {
- Double time = typeMap.getDouble("value");
+ }
+
+ if (TYPE_BOOLEAN.equals(type)) {
+ return typeMap.getBoolean(VALUE);
+ }
+
+ if (TYPE_NAN.equals(type)) {
+ return Double.NaN;
+ }
+
+ if (TYPE_NUMBER.equals(type)) {
+ return typeMap.getDouble(VALUE);
+ }
+
+ if (TYPE_INFINITY.equals(type)) {
+ return Double.POSITIVE_INFINITY;
+ }
+
+ if (TYPE_STRING.equals(type)) {
+ return typeMap.getString(VALUE);
+ }
+
+ if (TYPE_ARRAY.equals(type)) {
+ return parseReadableArray(firestore, typeMap.getArray(VALUE));
+ }
+
+ if (TYPE_OBJECT.equals(type)) {
+ return parseReadableMap(firestore, typeMap.getMap(VALUE));
+ }
+
+ if (TYPE_DATE.equals(type)) {
+ Double time = typeMap.getDouble(VALUE);
return new Date(time.longValue());
- } else if ("documentid".equals(type)) {
+ }
+
+ /* --------------------------
+ * Firestore Specific Types
+ * -------------------------- */
+
+ if (TYPE_DOCUMENTID.equals(type)) {
return FieldPath.documentId();
- } else if ("fieldvalue".equals(type)) {
- String value = typeMap.getString("value");
- if ("delete".equals(value)) {
- return FieldValue.delete();
- } else if ("timestamp".equals(value)) {
+ }
+
+ if (TYPE_GEOPOINT.equals(type)) {
+ ReadableMap geoPoint = typeMap.getMap(VALUE);
+ return new GeoPoint(geoPoint.getDouble(KEY_LATITUDE), geoPoint.getDouble(KEY_LONGITUDE));
+ }
+
+ if (TYPE_BLOB.equals(type)) {
+ String base64String = typeMap.getString(VALUE);
+ return Blob.fromBytes(Base64.decode(base64String, Base64.NO_WRAP));
+ }
+
+ if (TYPE_REFERENCE.equals(type)) {
+ String path = typeMap.getString(VALUE);
+ return firestore.document(path);
+ }
+
+ if (TYPE_TIMESTAMP.equals(type)) {
+ ReadableMap timestampMap = typeMap.getMap(VALUE);
+
+ return new Timestamp(
+ (long) timestampMap.getDouble(KEY_SECONDS),
+ timestampMap.getInt(KEY_NANOSECONDS)
+ );
+ }
+
+ if (TYPE_FIELDVALUE.equals(type)) {
+ ReadableMap fieldValueMap = typeMap.getMap(VALUE);
+ String fieldValueType = fieldValueMap.getString(TYPE_FIELDVALUE_TYPE);
+
+ if (TYPE_FIELDVALUE_TIMESTAMP.equals(fieldValueType)) {
return FieldValue.serverTimestamp();
- } else {
- Log.e(TAG, "parseTypeMap: Invalid fieldvalue: " + value);
- return null;
}
- } else {
- Log.e(TAG, "parseTypeMap: Cannot convert object of type " + type);
+
+ if (TYPE_FIELDVALUE_INCREMENT.equals(fieldValueType)) {
+ return FieldValue.increment(fieldValueMap.getDouble(TYPE_FIELDVALUE_ELEMENTS));
+ }
+
+ if (TYPE_FIELDVALUE_DELETE.equals(fieldValueType)) {
+ return FieldValue.delete();
+ }
+
+ if (TYPE_FIELDVALUE_UNION.equals(fieldValueType)) {
+ ReadableArray elements = fieldValueMap.getArray(TYPE_FIELDVALUE_ELEMENTS);
+ return FieldValue.arrayUnion(parseReadableArray(firestore, elements).toArray());
+ }
+
+ if (TYPE_FIELDVALUE_REMOVE.equals(fieldValueType)) {
+ ReadableArray elements = fieldValueMap.getArray(TYPE_FIELDVALUE_ELEMENTS);
+ return FieldValue.arrayRemove(parseReadableArray(firestore, elements).toArray());
+ }
+
+ Log.w(TAG, "Unknown FieldValue type: " + fieldValueType);
return null;
}
+
+ Log.w(TAG, "Unknown object of type " + type);
+ return null;
}
+ /**
+ * Parse JS batches array
+ *
+ * @param firestore FirebaseFirestore
+ * @param readableArray ReadableArray
+ * @return List<Object>
+ */
static List<Object> parseDocumentBatches(
FirebaseFirestore firestore,
ReadableArray readableArray
@@ -321,17 +522,24 @@
ReadableArray readableArray
) {
List<Object> writes = new ArrayList<>(readableArray.size());
+
for (int i = 0; i < readableArray.size(); i++) {
Map<String, Object> write = new HashMap<>();
ReadableMap map = readableArray.getMap(i);
- if (map.hasKey("data")) {
- write.put("data", parseReadableMap(firestore, map.getMap("data")));
+
+ write.put(TYPE, map.getString(TYPE));
+ write.put(KEY_PATH, map.getString(KEY_PATH));
+
+ if (map.hasKey(KEY_DATA)) {
+ write.put(KEY_DATA, parseReadableMap(firestore, map.getMap(KEY_DATA)));
}
- if (map.hasKey("options")) {
- write.put("options", Utils.recursivelyDeconstructReadableMap(map.getMap("options")));
+
+ if (map.hasKey(KEY_OPTIONS)) {
+ write.put(
+ KEY_OPTIONS,
+ Utils.recursivelyDeconstructReadableMap(map.getMap(KEY_OPTIONS))
+ );
}
- write.put("path", map.getString("path"));
- write.put("type", map.getString("type"));
writes.add(write);
}

android/src/main/java/io/invertase/firebase/firestore/RNFirebaseFirestoreCollectionReference.java

@@ -2,7 +2,6 @@
import android.annotation.SuppressLint;
-import android.support.annotation.NonNull;
import android.util.Log;
import com.facebook.react.bridge.Arguments;
@@ -27,6 +26,8 @@
import java.util.List;
import java.util.Map;
+import javax.annotation.Nonnull;
+
import io.invertase.firebase.Utils;
class RNFirebaseFirestoreCollectionReference {
@@ -93,7 +94,7 @@
.get(source)
.addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
@Override
- public void onComplete(@NonNull Task<QuerySnapshot> task) {
+ public void onComplete(@Nonnull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
Log.d(TAG, "get:onComplete:success");
serializeAsyncTask.execute(task.getResult());
@@ -189,6 +190,9 @@
case "LESS_THAN_OR_EQUAL":
query = query.whereLessThanOrEqualTo(fieldPath, value);
break;
+ case "ARRAY_CONTAINS":
+ query = query.whereArrayContains(fieldPath, value);
+ break;
}
} else {
ReadableArray fieldPathElements = fieldPathMap.getArray("elements");
@@ -213,6 +217,9 @@
case "LESS_THAN_OR_EQUAL":
query = query.whereLessThanOrEqualTo(fieldPath, value);
break;
+ case "ARRAY_CONTAINS":
+ query = query.whereArrayContains(fieldPath, value);
+ break;
}
}
}

android/src/main/java/io/invertase/firebase/firestore/RNFirebaseFirestoreDocumentReference.java

@@ -1,7 +1,6 @@
package io.invertase.firebase.firestore;
import android.annotation.SuppressLint;
-import android.support.annotation.NonNull;
import android.util.Log;
import com.facebook.react.bridge.Arguments;
@@ -23,8 +22,9 @@
import java.util.HashMap;
import java.util.Map;
-import io.invertase.firebase.Utils;
+import javax.annotation.Nonnull;
+import io.invertase.firebase.Utils;
public class RNFirebaseFirestoreDocumentReference {
private static final String TAG = "RNFBFSDocumentReference";
@@ -56,7 +56,7 @@
.delete()
.addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
- public void onComplete(@NonNull Task<Void> task) {
+ public void onComplete(@Nonnull Task<Void> task) {
if (task.isSuccessful()) {
Log.d(TAG, "delete:onComplete:success");
promise.resolve(null);
@@ -100,7 +100,7 @@
.get(source)
.addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
@Override
- public void onComplete(@NonNull Task<DocumentSnapshot> task) {
+ public void onComplete(@Nonnull Task<DocumentSnapshot> task) {
if (task.isSuccessful()) {
Log.d(TAG, "get:onComplete:success");
serializeAsyncTask.execute(task.getResult());
@@ -172,7 +172,7 @@
task.addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
- public void onComplete(@NonNull Task<Void> task) {
+ public void onComplete(@Nonnull Task<Void> task) {
if (task.isSuccessful()) {
Log.d(TAG, "set:onComplete:success");
promise.resolve(null);
@@ -197,7 +197,7 @@
.update(map)
.addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
- public void onComplete(@NonNull Task<Void> task) {
+ public void onComplete(@Nonnull Task<Void> task) {
if (task.isSuccessful()) {
Log.d(TAG, "update:onComplete:success");
promise.resolve(null);

android/src/main/java/io/invertase/firebase/firestore/RNFirebaseFirestore.java

@@ -1,7 +1,6 @@
package io.invertase.firebase.firestore;
import android.os.AsyncTask;
-import android.support.annotation.NonNull;
import android.util.Log;
import android.util.SparseArray;
@@ -31,6 +30,8 @@
import java.util.List;
import java.util.Map;
+import javax.annotation.Nonnull;
+
import io.invertase.firebase.ErrorUtils;
import io.invertase.firebase.Utils;
@@ -236,7 +237,7 @@
.disableNetwork()
.addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
- public void onComplete(@NonNull Task<Void> task) {
+ public void onComplete(@Nonnull Task<Void> task) {
if (task.isSuccessful()) {
Log.d(TAG, "disableNetwork:onComplete:success");
promise.resolve(null);
@@ -266,7 +267,7 @@
.enableNetwork()
.addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
- public void onComplete(@NonNull Task<Void> task) {
+ public void onComplete(@Nonnull Task<Void> task) {
if (task.isSuccessful()) {
Log.d(TAG, "enableNetwork:onComplete:success");
promise.resolve(null);
@@ -360,7 +361,7 @@
.commit()
.addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
- public void onComplete(@NonNull Task<Void> task) {
+ public void onComplete(@Nonnull Task<Void> task) {
if (task.isSuccessful()) {
Log.d(TAG, "documentBatch:onComplete:success");
promise.resolve(null);
@@ -455,9 +456,9 @@
.isSslEnabled());
}
-// if (settings.hasKey("timestampsInSnapshots")) {
-// // TODO: Not supported on Android yet
-// }
+ if (settings.hasKey("timestampsInSnapshots")) {
+ firestoreSettings.setTimestampsInSnapshotsEnabled(settings.getBoolean("timestampsInSnapshots"));
+ }
firestore.setFirestoreSettings(firestoreSettings.build());
promise.resolve(null);
@@ -569,7 +570,7 @@
getFirestoreForApp(appName)
.runTransaction(new Transaction.Function<Void>() {
@Override
- public Void apply(@NonNull Transaction transaction) throws FirebaseFirestoreException {
+ public Void apply(@Nonnull Transaction transaction) throws FirebaseFirestoreException {
transactionHandler.resetState(transaction);
// emit the update cycle to JS land using an async task
@@ -675,7 +676,7 @@
})
.addOnFailureListener(new OnFailureListener() {
@Override
- public void onFailure(@NonNull Exception e) {
+ public void onFailure(@Nonnull Exception e) {
if (!transactionHandler.aborted) {
Log.w(TAG, "Transaction onFailure.", e);
WritableMap eventMap = transactionHandler.createEventMap(

android/src/main/java/io/invertase/firebase/functions/RNFirebaseFunctions.java

@@ -1,23 +1,24 @@
package io.invertase.firebase.functions;
-import android.support.annotation.NonNull;
import android.util.Log;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Promise;
-import com.facebook.react.bridge.ReactMethod;
-import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
-
+import com.facebook.react.bridge.ReactMethod;
+import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap;
import com.google.android.gms.tasks.OnFailureListener;
import com.google.android.gms.tasks.OnSuccessListener;
+import com.google.firebase.FirebaseApp;
import com.google.firebase.functions.FirebaseFunctions;
import com.google.firebase.functions.FirebaseFunctionsException;
import com.google.firebase.functions.HttpsCallableReference;
import com.google.firebase.functions.HttpsCallableResult;
+import javax.annotation.Nonnull;
+
import io.invertase.firebase.Utils;
public class RNFirebaseFunctions extends ReactContextBaseJavaModule {
@@ -39,17 +40,56 @@
return TAG;
}
+ /**
+ * Changes this instance to point to a Cloud Functions emulator running
+ * locally.
+ * <p>
+ * See https://firebase.google.com/docs/functions/local-emulator
+ *
+ * @param origin the origin string of the local emulator started via firebase tools
+ * "http://10.0.0.8:1337".
+ * @param appName
+ * @param region
+ * @param origin
+ * @param promise
+ */
+ @ReactMethod
+ public void useFunctionsEmulator(
+ String appName,
+ String region,
+ String origin,
+ Promise promise
+ ) {
+ FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
+ FirebaseFunctions functionsInstance = FirebaseFunctions.getInstance(firebaseApp, region);
+ functionsInstance.useFunctionsEmulator(origin);
+ promise.resolve(null);
+ }
+
+ /**
+ * @param appName
+ * @param region
+ * @param name
+ * @param wrapper
+ * @param promise
+ */
@ReactMethod
- public void httpsCallable(final String name, ReadableMap wrapper, final Promise promise) {
+ public void httpsCallable(
+ String appName,
+ String region,
+ final String name,
+ ReadableMap wrapper,
+ final Promise promise
+ ) {
Object input = wrapper
.toHashMap()
.get(DATA_KEY);
Log.d(TAG, "function:call:input:" + name + ":" + (input != null ? input.toString() : "null"));
- HttpsCallableReference httpsCallableReference = FirebaseFunctions
- .getInstance()
- .getHttpsCallable(name);
+ FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
+ FirebaseFunctions functionsInstance = FirebaseFunctions.getInstance(firebaseApp, region);
+ HttpsCallableReference httpsCallableReference = functionsInstance.getHttpsCallable(name);
httpsCallableReference
.call(input)
@@ -82,7 +122,7 @@
})
.addOnFailureListener(new OnFailureListener() {
@Override
- public void onFailure(@NonNull Exception exception) {
+ public void onFailure(@Nonnull Exception exception) {
Log.d(TAG, "function:call:onFailure:" + name, exception);
String message;
@@ -96,9 +136,9 @@
code = ffe
.getCode()
.name();
- message = ffe.getLocalizedMessage();
+ message = ffe.getMessage();
} else {
- message = exception.getLocalizedMessage();
+ message = exception.getMessage();
}
Utils.mapPutValue(CODE_KEY, code, map);

android/src/main/java/io/invertase/firebase/functions/RNFirebaseFunctionsPackage.java

@@ -2,13 +2,13 @@
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
-import com.facebook.react.uimanager.ViewManager;
-import com.facebook.react.uimanager.UIManagerModule;
import com.facebook.react.bridge.ReactApplicationContext;
+import com.facebook.react.uimanager.UIManagerModule;
+import com.facebook.react.uimanager.ViewManager;
-import java.util.List;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.List;
@SuppressWarnings("unused")
public class RNFirebaseFunctionsPackage implements ReactPackage {

android/src/main/java/io/invertase/firebase/invites/RNFirebaseInvites.java

@@ -3,7 +3,6 @@
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
-import android.support.annotation.NonNull;
import android.util.Log;
import com.facebook.react.bridge.ActivityEventListener;
@@ -27,11 +26,13 @@
import java.util.HashMap;
import java.util.Map;
+import javax.annotation.Nonnull;
+
import io.invertase.firebase.Utils;
public class RNFirebaseInvites extends ReactContextBaseJavaModule implements ActivityEventListener, LifecycleEventListener {
private static final String TAG = "RNFirebaseInvites";
- private static final int REQUEST_INVITE = 81283;
+ private static final int REQUEST_INVITE = 17517;
private boolean mInitialInvitationInitialized = false;
private String mInitialDeepLink = null;
private String mInitialInvitationId = null;
@@ -87,7 +88,7 @@
})
.addOnFailureListener(new OnFailureListener() {
@Override
- public void onFailure(@NonNull Exception e) {
+ public void onFailure(@Nonnull Exception e) {
Log.e(TAG, "getInitialInvitation: failed to resolve invitation", e);
promise.reject(
"invites/initial-invitation-error",
@@ -3,7 +3,6 @@
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
-import android.support.annotation.NonNull;
import android.util.Log;
import com.facebook.react.bridge.ActivityEventListener;
@@ -23,6 +22,8 @@
import com.google.firebase.dynamiclinks.PendingDynamicLinkData;
import com.google.firebase.dynamiclinks.ShortDynamicLink;
+import javax.annotation.Nonnull;
+
import io.invertase.firebase.Utils;
public class RNFirebaseLinks extends ReactContextBaseJavaModule implements ActivityEventListener, LifecycleEventListener {
@@ -76,7 +77,7 @@
shortLinkTask.addOnCompleteListener(new OnCompleteListener<ShortDynamicLink>() {
@Override
- public void onComplete(@NonNull Task<ShortDynamicLink> task) {
+ public void onComplete(@Nonnull Task<ShortDynamicLink> task) {
if (task.isSuccessful()) {
String shortLink = task
.getResult()
@@ -133,7 +134,7 @@
})
.addOnFailureListener(new OnFailureListener() {
@Override
- public void onFailure(@NonNull Exception e) {
+ public void onFailure(@Nonnull Exception e) {
Log.e(TAG, "getInitialLink: failed to resolve link", e);
promise.reject("link/initial-link-error", e.getMessage(), e);
}

android/src/main/java/io/invertase/firebase/messaging/RNFirebaseInstanceIdService.java

@@ -1,25 +0,0 @@
-package io.invertase.firebase.messaging;
-
-import android.content.Intent;
-import android.support.v4.content.LocalBroadcastManager;
-import android.util.Log;
-
-import com.google.firebase.iid.FirebaseInstanceIdService;
-
-public class RNFirebaseInstanceIdService extends FirebaseInstanceIdService {
- public static final String TOKEN_REFRESH_EVENT = "messaging-token-refresh";
- private static final String TAG = "RNFInstanceIdService";
-
- @Override
- public void onTokenRefresh() {
- Log.d(TAG, "onTokenRefresh event received");
-
- // Build an Intent to pass the token to the RN Application
- Intent tokenRefreshEvent = new Intent(TOKEN_REFRESH_EVENT);
-
- // Broadcast it so it is only available to the RN Application
- LocalBroadcastManager
- .getInstance(this)
- .sendBroadcast(tokenRefreshEvent);
- }
-}

android/src/main/java/io/invertase/firebase/messaging/RNFirebaseMessaging.java

@@ -4,7 +4,6 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.support.annotation.NonNull;
import android.support.v4.app.NotificationManagerCompat;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
@@ -18,16 +17,21 @@
import com.facebook.react.bridge.WritableMap;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
+import com.google.firebase.FirebaseApp;
import com.google.firebase.iid.FirebaseInstanceId;
import com.google.firebase.messaging.FirebaseMessaging;
import com.google.firebase.messaging.RemoteMessage;
+import java.io.IOException;
+
+import javax.annotation.Nonnull;
+
import io.invertase.firebase.Utils;
public class RNFirebaseMessaging extends ReactContextBaseJavaModule {
private static final String TAG = "RNFirebaseMessaging";
- public RNFirebaseMessaging(ReactApplicationContext context) {
+ RNFirebaseMessaging(ReactApplicationContext context) {
super(context);
LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(context);
@@ -37,10 +41,10 @@
new IntentFilter(RNFirebaseMessagingService.MESSAGE_EVENT)
);
- // Subscribe to token refresh events
+ // Subscribe to new token events
localBroadcastManager.registerReceiver(
new RefreshTokenReceiver(),
- new IntentFilter(RNFirebaseInstanceIdService.TOKEN_REFRESH_EVENT)
+ new IntentFilter(RNFirebaseMessagingService.NEW_TOKEN_EVENT)
);
}
@@ -51,11 +55,28 @@
@ReactMethod
public void getToken(Promise promise) {
+ try {
+ String senderId = FirebaseApp.getInstance().getOptions().getGcmSenderId();
String token = FirebaseInstanceId
.getInstance()
- .getToken();
- Log.d(TAG, "Firebase token: " + token);
+ .getToken(senderId, FirebaseMessaging.INSTANCE_ID_SCOPE);
promise.resolve(token);
+ } catch (Throwable e) {
+ e.printStackTrace();
+ promise.reject("messaging/fcm-token-error", e.getMessage());
+ }
+ }
+
+ @ReactMethod
+ public void deleteToken(Promise promise) {
+ try {
+ String senderId = FirebaseApp.getInstance().getOptions().getGcmSenderId();
+ FirebaseInstanceId.getInstance().deleteToken(senderId, FirebaseMessaging.INSTANCE_ID_SCOPE);
+ promise.resolve(null);
+ } catch (Throwable e) {
+ e.printStackTrace();
+ promise.reject("messaging/fcm-token-error", e.getMessage());
+ }
}
@ReactMethod
@@ -102,9 +123,7 @@
}
}
- FirebaseMessaging
- .getInstance()
- .send(mb.build());
+ FirebaseMessaging.getInstance().send(mb.build());
// TODO: Listen to onMessageSent and onSendError for better feedback?
promise.resolve(null);
@@ -117,7 +136,7 @@
.subscribeToTopic(topic)
.addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
- public void onComplete(@NonNull Task<Void> task) {
+ public void onComplete(@Nonnull Task<Void> task) {
if (task.isSuccessful()) {
Log.d(TAG, "subscribeToTopic:onComplete:success");
promise.resolve(null);
@@ -137,7 +156,7 @@
.unsubscribeFromTopic(topic)
.addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
- public void onComplete(@NonNull Task<Void> task) {
+ public void onComplete(@Nonnull Task<Void> task) {
if (task.isSuccessful()) {
Log.d(TAG, "unsubscribeFromTopic:onComplete:success");
promise.resolve(null);
@@ -168,13 +187,30 @@
@Override
public void onReceive(Context context, Intent intent) {
if (getReactApplicationContext().hasActiveCatalystInstance()) {
- String token = FirebaseInstanceId
+ Log.d(TAG, "Received new messaging token.");
+ Thread thread = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ String token = null;
+ String senderId = FirebaseApp.getInstance().getOptions().getGcmSenderId();
+
+ try {
+ token = FirebaseInstanceId
.getInstance()
- .getToken();
- Log.d(TAG, "Received new FCM token: " + token);
+ .getToken(senderId, FirebaseMessaging.INSTANCE_ID_SCOPE);
+ } catch (IOException e) {
+ Log.d(TAG, "onNewToken error", e);
+ }
+ if (token != null) {
+ Log.d(TAG, "Sending new messaging token event.");
Utils.sendEvent(getReactApplicationContext(), "messaging_token_refreshed", token);
}
}
+ });
+
+ thread.start();
+ }
+ }
}
}

android/src/main/java/io/invertase/firebase/messaging/RNFirebaseMessagingService.java

@@ -1,6 +1,7 @@
package io.invertase.firebase.messaging;
import android.content.Intent;
+import android.content.ComponentName;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
@@ -11,9 +12,21 @@
import io.invertase.firebase.Utils;
public class RNFirebaseMessagingService extends FirebaseMessagingService {
+ private static final String TAG = "RNFMessagingService";
+
public static final String MESSAGE_EVENT = "messaging-message";
+ public static final String NEW_TOKEN_EVENT = "messaging-token-refresh";
public static final String REMOTE_NOTIFICATION_EVENT = "notifications-remote-notification";
- private static final String TAG = "RNFMessagingService";
+
+ @Override
+ public void onNewToken(String token) {
+ Log.d(TAG, "onNewToken event received");
+
+ Intent newTokenEvent = new Intent(NEW_TOKEN_EVENT);
+ LocalBroadcastManager
+ .getInstance(this)
+ .sendBroadcast(newTokenEvent);
+ }
@Override
public void onMessageReceived(RemoteMessage message) {
@@ -46,10 +59,10 @@
RNFirebaseBackgroundMessagingService.class
);
headlessIntent.putExtra("message", message);
- this
- .getApplicationContext()
- .startService(headlessIntent);
+ ComponentName name = this.getApplicationContext().startService(headlessIntent);
+ if (name != null) {
HeadlessJsTaskService.acquireWakeLockNow(this.getApplicationContext());
+ }
} catch (IllegalStateException ex) {
Log.e(
TAG,

android/src/main/java/io/invertase/firebase/notifications/DisplayNotificationTask.java

@@ -21,6 +21,7 @@
import com.facebook.react.bridge.ReactApplicationContext;
import java.io.IOException;
+import java.lang.ref.WeakReference;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
@@ -30,29 +31,40 @@
public class DisplayNotificationTask extends AsyncTask<Void, Void, Void> {
private static final String TAG = "DisplayNotificationTask";
+ private final WeakReference<Context> contextWeakReference;
+ private final WeakReference<ReactApplicationContext> reactContextWeakReference;
- private final Context context;
+ private final Promise promise;
private final Bundle notification;
private final NotificationManager notificationManager;
- private final Promise promise;
- private ReactApplicationContext reactContext;
- public DisplayNotificationTask(
+ DisplayNotificationTask(
Context context, ReactApplicationContext reactContext,
NotificationManager notificationManager,
Bundle notification, Promise promise
) {
- this.context = context;
+ this.contextWeakReference = new WeakReference<>(context);
+ this.reactContextWeakReference = new WeakReference<>(reactContext);
+
+ this.promise = promise;
this.notification = notification;
this.notificationManager = notificationManager;
- this.promise = promise;
- this.reactContext = reactContext;
+ }
+
+ @Override
+ protected void onPostExecute(Void result) {
+ contextWeakReference.clear();
+ reactContextWeakReference.clear();
}
@Override
protected Void doInBackground(Void... voids) {
+ Context context = contextWeakReference.get();
+ if (context == null) return null;
+
try {
- Class intentClass = getMainActivityClass();
+ Class intentClass = getMainActivityClass(context);
+
if (intentClass == null) {
if (promise != null) {
promise.reject(
@@ -64,14 +76,14 @@
}
Bundle android = notification.getBundle("android");
-
- String channelId = android.getString("channelId");
String notificationId = notification.getString("notificationId");
NotificationCompat.Builder nb;
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ try {
+ String channelId = android.getString("channelId");
nb = new NotificationCompat.Builder(context, channelId);
- } else {
+ } catch (Throwable t) {
+ // thrown if v4 android support library < 26
nb = new NotificationCompat.Builder(context);
}
@@ -98,15 +114,22 @@
if (android.containsKey("autoCancel")) {
nb = nb.setAutoCancel(android.getBoolean("autoCancel"));
}
- if (android.containsKey("badgeIconType")) {
+
+ if (android.containsKey("badgeIconType") && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Double badgeIconType = android.getDouble("badgeIconType");
+ try {
nb = nb.setBadgeIconType(badgeIconType.intValue());
+ } catch (Throwable t) {
+ // thrown if v4 android support library < 26
+ // do nothing
+ }
}
+
if (android.containsKey("bigPicture")) {
Bundle bigPicture = android.getBundle("bigPicture");
-
NotificationCompat.BigPictureStyle bp = new NotificationCompat.BigPictureStyle();
Bitmap picture = getBitmap(bigPicture.getString("picture"));
+
if (picture != null) {
bp = bp.bigPicture(picture);
}
@@ -140,13 +169,21 @@
if (android.containsKey("category")) {
nb = nb.setCategory(android.getString("category"));
}
+
if (android.containsKey("color")) {
String color = android.getString("color");
nb = nb.setColor(Color.parseColor(color));
}
- if (android.containsKey("colorized")) {
+
+ if (android.containsKey("colorized") && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ try {
nb = nb.setColorized(android.getBoolean("colorized"));
+ } catch (Throwable t) {
+ // thrown if v4 android support library < 26
+ // do nothing
+ }
}
+
if (android.containsKey("contentInfo")) {
nb = nb.setContentInfo(android.getString("contentInfo"));
}
@@ -165,13 +203,21 @@
nb = nb.setDefaults(defaults);
}
+
if (android.containsKey("group")) {
nb = nb.setGroup(android.getString("group"));
}
- if (android.containsKey("groupAlertBehaviour")) {
+
+ if (android.containsKey("groupAlertBehaviour") && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Double groupAlertBehaviour = android.getDouble("groupAlertBehaviour");
+ try {
nb = nb.setGroupAlertBehavior(groupAlertBehaviour.intValue());
+ } catch (Throwable t) {
+ // thrown if v4 android support library < 26
+ // do nothing
+ }
}
+
if (android.containsKey("groupSummary")) {
nb = nb.setGroupSummary(android.getBoolean("groupSummary"));
}
@@ -228,12 +283,20 @@
/* if (android.containsKey("publicVersion")) {
nb = nb.setPublicVersion();
} */
+
if (android.containsKey("remoteInputHistory")) {
nb = nb.setRemoteInputHistory(android.getStringArray("remoteInputHistory"));
}
- if (android.containsKey("shortcutId")) {
+
+ if (android.containsKey("shortcutId") && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ try {
nb = nb.setShortcutId(android.getString("shortcutId"));
+ } catch (Throwable t) {
+ // thrown if v4 android support library < 26
+ // do nothing
}
+ }
+
if (android.containsKey("showWhen")) {
nb = nb.setShowWhen(android.getBoolean("showWhen"));
}
@@ -288,7 +358,7 @@
if (android.containsKey("actions")) {
List<Bundle> actions = (List) android.getSerializable("actions");
for (Bundle a : actions) {
- NotificationCompat.Action action = createAction(a, intentClass, notification);
+ NotificationCompat.Action action = createAction(context, a, intentClass, notification);
nb = nb.addAction(action);
}
}
@@ -300,6 +370,7 @@
// Create the notification intent
PendingIntent contentIntent = createIntent(
+ context,
intentClass,
notification,
android.getString("clickAction")
@@ -304,15 +375,16 @@
notification,
android.getString("clickAction")
);
+
nb = nb.setContentIntent(contentIntent);
// Build the notification and send it
Notification builtNotification = nb.build();
notificationManager.notify(tag, notificationId.hashCode(), builtNotification);
- if (reactContext != null) {
+ if (reactContextWeakReference.get() != null) {
Utils.sendEvent(
- reactContext,
+ reactContextWeakReference.get(),
"notifications_notification_displayed",
Arguments.fromBundle(notification)
);
@@ -333,16 +405,19 @@
}
private NotificationCompat.Action createAction(
+ Context context,
Bundle action,
Class intentClass,
Bundle notification
) {
+ String actionKey = action.getString("action");
boolean showUserInterface = action.containsKey("showUserInterface") && action.getBoolean(
"showUserInterface");
- String actionKey = action.getString("action");
+
PendingIntent actionIntent = showUserInterface ?
- createIntent(intentClass, notification, actionKey) :
- createBroadcastIntent(notification, actionKey);
+ createIntent(context, intentClass, notification, actionKey) :
+ createBroadcastIntent(context, notification, actionKey);
+
int icon = getIcon(action.getString("icon"));
String title = action.getString("title");
@@ -374,7 +451,12 @@
return ab.build();
}
- private PendingIntent createIntent(Class intentClass, Bundle notification, String action) {
+ private PendingIntent createIntent(
+ Context context,
+ Class intentClass,
+ Bundle notification,
+ String action
+ ) {
Intent intent = new Intent(context, intentClass);
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.putExtras(notification);
@@ -392,15 +474,15 @@
);
}
- private PendingIntent createBroadcastIntent(Bundle notification, String action) {
- Intent intent = new Intent(context, RNFirebaseBackgroundNotificationActionReceiver.class);
- intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
-
+ private PendingIntent createBroadcastIntent(Context context, Bundle notification, String action) {
String notificationId = notification.getString("notificationId") + action;
+ Intent intent = new Intent(context, RNFirebaseBackgroundNotificationActionReceiver.class);
- intent.setAction("io.invertase.firebase.notifications.BackgroundAction");
intent.putExtra("action", action);
+ intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.putExtra("notification", notification);
+ intent.setAction("io.invertase.firebase.notifications.BackgroundAction");
+
return PendingIntent.getBroadcast(
context,
notificationId.hashCode(),
@@ -437,12 +521,17 @@
private Bitmap getBitmap(String image) {
if (image.startsWith("http://") || image.startsWith("https://")) {
return getBitmapFromUrl(image);
- } else if (image.startsWith("file://")) {
+ }
+
+ if (image.startsWith("file://")) {
return BitmapFactory.decodeFile(image.replace("file://", ""));
- } else {
- int largeIconResId = getIcon(image);
- return BitmapFactory.decodeResource(context.getResources(), largeIconResId);
}
+
+ int largeIconResId = getIcon(image);
+ return BitmapFactory.decodeResource(
+ contextWeakReference.get().getResources(),
+ largeIconResId
+ );
}
private Bitmap getBitmapFromUrl(String imageUrl) {
@@ -458,14 +547,24 @@
}
private int getIcon(String icon) {
- int resourceId = RNFirebaseNotificationManager.getResourceId(context, "mipmap", icon);
+ int resourceId = RNFirebaseNotificationManager.getResourceId(
+ contextWeakReference.get(),
+ "mipmap",
+ icon
+ );
+
if (resourceId == 0) {
- resourceId = RNFirebaseNotificationManager.getResourceId(context, "drawable", icon);
+ resourceId = RNFirebaseNotificationManager.getResourceId(
+ contextWeakReference.get(),
+ "drawable",
+ icon
+ );
}
+
return resourceId;
}
- private Class getMainActivityClass() {
+ private Class getMainActivityClass(Context context) {
String packageName = context.getPackageName();
Intent launchIntent = context
.getPackageManager()
@@ -470,10 +569,9 @@
Intent launchIntent = context
.getPackageManager()
.getLaunchIntentForPackage(packageName);
+
try {
- return Class.forName(launchIntent
- .getComponent()
- .getClassName());
+ return Class.forName(launchIntent.getComponent().getClassName());
} catch (ClassNotFoundException e) {
Log.e(TAG, "Failed to get main activity class", e);
return null;

android/src/main/java/io/invertase/firebase/notifications/RNFirebaseBackgroundNotificationActionReceiver.java

@@ -3,7 +3,9 @@
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.content.ComponentName;
import android.os.Bundle;
+import android.support.v4.app.RemoteInput;
import com.facebook.react.HeadlessJsTaskService;
import com.facebook.react.ReactApplication;
@@ -24,6 +26,13 @@
WritableMap notificationOpenMap = Arguments.createMap();
notificationOpenMap.putString("action", extras.getString("action"));
notificationOpenMap.putMap("notification", notificationMap);
+
+ Bundle extrasBundle = extras.getBundle("results");
+ if (extrasBundle != null) {
+ WritableMap results = Arguments.makeNativeMap(extrasBundle);
+ notificationOpenMap.putMap("results", results);
+ }
+
return notificationOpenMap;
}
@@ -49,8 +58,15 @@
RNFirebaseBackgroundNotificationActionsService.class
);
serviceIntent.putExtras(intent.getExtras());
- context.startService(serviceIntent);
+
+ Bundle remoteInput = RemoteInput.getResultsFromIntent(intent);
+ if (remoteInput != null) {
+ serviceIntent.putExtra("results", remoteInput);
+ }
+ ComponentName name = context.startService(serviceIntent);
+ if (name != null) {
HeadlessJsTaskService.acquireWakeLockNow(context);
}
}
+ }
}

android/src/main/java/io/invertase/firebase/notifications/RNFirebaseNotificationManager.java

@@ -1,6 +1,6 @@
package io.invertase.firebase.notifications;
-
+import android.annotation.SuppressLint;
import android.app.AlarmManager;
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
@@ -9,13 +9,15 @@
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
+import android.database.Cursor;
import android.graphics.Color;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
+import android.provider.OpenableColumns;
import android.service.notification.StatusBarNotification;
-import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
@@ -24,6 +26,8 @@
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
+import com.facebook.react.bridge.WritableArray;
+import com.facebook.react.bridge.WritableMap;
import org.json.JSONException;
import org.json.JSONObject;
@@ -33,11 +37,13 @@
import java.util.List;
import java.util.Map;
+import javax.annotation.Nullable;
+
import io.invertase.firebase.Utils;
import io.invertase.firebase.messaging.BundleJSONConverter;
-public class RNFirebaseNotificationManager {
- public static final String SCHEDULED_NOTIFICATION_EVENT = "notifications-scheduled-notification";
+class RNFirebaseNotificationManager {
+ static final String SCHEDULED_NOTIFICATION_EVENT = "notifications-scheduled-notification";
private static final String PREFERENCES_KEY = "RNFNotifications";
private static final String TAG = "RNFNotificationManager";
private AlarmManager alarmManager;
@@ -46,25 +52,25 @@
private NotificationManager notificationManager;
private SharedPreferences preferences;
- public RNFirebaseNotificationManager(ReactApplicationContext reactContext) {
+ RNFirebaseNotificationManager(ReactApplicationContext reactContext) {
this(reactContext.getApplicationContext());
this.reactContext = reactContext;
}
- public RNFirebaseNotificationManager(Context context) {
+ RNFirebaseNotificationManager(Context context) {
this.alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
this.context = context;
this.notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
this.preferences = context.getSharedPreferences(PREFERENCES_KEY, Context.MODE_PRIVATE);
}
- public static int getResourceId(Context context, String type, String image) {
+ static int getResourceId(Context context, String type, String image) {
return context
.getResources()
.getIdentifier(image, type, context.getPackageName());
}
- public static Uri getSound(Context context, String sound) {
+ static Uri getSound(Context context, String sound) {
if (sound == null) {
return null;
} else if (sound.contains("://")) {
@@ -80,7 +86,7 @@
}
}
- public void cancelAllNotifications(Promise promise) {
+ void cancelAllNotifications(Promise promise) {
try {
Map<String, ?> notifications = preferences.getAll();
@@ -104,7 +112,7 @@
}
}
- public void cancelNotification(String notificationId, Promise promise) {
+ void cancelNotification(String notificationId, Promise promise) {
try {
cancelAlarm(notificationId);
preferences
@@ -120,22 +128,22 @@
}
}
- public void createChannel(ReadableMap channelMap) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ void createChannel(ReadableMap channelMap) {
+ if (Build.VERSION.SDK_INT >= 26) {
NotificationChannel channel = parseChannelMap(channelMap);
notificationManager.createNotificationChannel(channel);
}
}
- public void createChannelGroup(ReadableMap channelGroupMap) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ void createChannelGroup(ReadableMap channelGroupMap) {
+ if (Build.VERSION.SDK_INT >= 26) {
NotificationChannelGroup channelGroup = parseChannelGroupMap(channelGroupMap);
notificationManager.createNotificationChannelGroup(channelGroup);
}
}
- public void createChannelGroups(ReadableArray channelGroupsArray) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ void createChannelGroups(ReadableArray channelGroupsArray) {
+ if (Build.VERSION.SDK_INT >= 26) {
List<NotificationChannelGroup> channelGroups = new ArrayList<>();
for (int i = 0; i < channelGroupsArray.size(); i++) {
NotificationChannelGroup channelGroup = parseChannelGroupMap(channelGroupsArray.getMap(i));
@@ -145,8 +153,8 @@
}
}
- public void createChannels(ReadableArray channelsArray) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ void createChannels(ReadableArray channelsArray) {
+ if (Build.VERSION.SDK_INT >= 26) {
List<NotificationChannel> channels = new ArrayList<>();
for (int i = 0; i < channelsArray.size(); i++) {
NotificationChannel channel = parseChannelMap(channelsArray.getMap(i));
@@ -156,24 +164,24 @@
}
}
- public void deleteChannelGroup(String groupId) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ void deleteChannelGroup(String groupId) {
+ if (Build.VERSION.SDK_INT >= 26) {
notificationManager.deleteNotificationChannelGroup(groupId);
}
}
- public void deleteChannel(String channelId) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ void deleteChannel(String channelId) {
+ if (Build.VERSION.SDK_INT >= 26) {
notificationManager.deleteNotificationChannel(channelId);
}
}
- public void displayNotification(ReadableMap notification, Promise promise) {
+ void displayNotification(ReadableMap notification, Promise promise) {
Bundle notificationBundle = Arguments.toBundle(notification);
displayNotification(notificationBundle, promise);
}
- public void displayScheduledNotification(Bundle notification) {
+ void displayScheduledNotification(Bundle notification) {
// If this isn't a repeated notification, clear it from the scheduled notifications list
if (!notification
.getBundle("schedule")
@@ -189,7 +197,7 @@
}
if (Utils.isAppInForeground(context)) {
- // If the app is in the foregound, broadcast the notification to the RN Application
+ // If the app is in the foreground, broadcast the notification to the RN Application
// It is up to the JS to decide whether to display the notification
Intent scheduledNotificationEvent = new Intent(SCHEDULED_NOTIFICATION_EVENT);
scheduledNotificationEvent.putExtra("notification", notification);
@@ -202,7 +210,39 @@
}
}
- public ArrayList<Bundle> getScheduledNotifications() {
+ WritableMap getChannel(String channelId) {
+ if (Build.VERSION.SDK_INT >= 26) {
+ return createChannelMap(notificationManager.getNotificationChannel(channelId));
+ }
+
+ return null;
+ }
+
+ WritableArray getChannels() {
+ if (Build.VERSION.SDK_INT >= 26) {
+ return createChannelsArray(notificationManager.getNotificationChannels());
+ }
+
+ return null;
+ }
+
+ WritableMap getChannelGroup(String channelGroupId) {
+ if (Build.VERSION.SDK_INT >= 28) {
+ return createChannelGroupMap(notificationManager.getNotificationChannelGroup(channelGroupId));
+ }
+
+ return null;
+ }
+
+ WritableArray getChannelGroups() {
+ if (Build.VERSION.SDK_INT >= 26) {
+ return createChannelGroupsArray(notificationManager.getNotificationChannelGroups());
+ }
+
+ return null;
+ }
+
+ ArrayList<Bundle> getScheduledNotifications() {
ArrayList<Bundle> array = new ArrayList<>();
Map<String, ?> notifications = preferences.getAll();
@@ -219,34 +259,37 @@
return array;
}
- public void removeAllDeliveredNotifications(Promise promise) {
+ void removeAllDeliveredNotifications(Promise promise) {
notificationManager.cancelAll();
promise.resolve(null);
}
- public void removeDeliveredNotification(String notificationId, Promise promise) {
+ void removeDeliveredNotification(String notificationId, Promise promise) {
notificationManager.cancel(notificationId.hashCode());
promise.resolve(null);
}
- public void removeDeliveredNotificationsByTag(String tag, Promise promise) {
+ void removeDeliveredNotificationsByTag(String tag, Promise promise) {
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
StatusBarNotification[] statusBarNotifications = notificationManager.getActiveNotifications();
for (StatusBarNotification statusBarNotification : statusBarNotifications) {
- if (statusBarNotification.getTag() == tag) {
+ if (tag.equals(statusBarNotification.getTag())) {
notificationManager.cancel(statusBarNotification.getTag(), statusBarNotification.getId());
}
}
+ }
+
promise.resolve(null);
}
- public void rescheduleNotifications() {
+ void rescheduleNotifications() {
ArrayList<Bundle> bundles = getScheduledNotifications();
for (Bundle bundle : bundles) {
scheduleNotification(bundle, null);
}
}
- public void scheduleNotification(ReadableMap notification, Promise promise) {
+ void scheduleNotification(ReadableMap notification, Promise promise) {
Bundle notificationBundle = Arguments.toBundle(notification);
scheduleNotification(notificationBundle, promise);
@@ -274,17 +317,146 @@
}
private NotificationChannelGroup parseChannelGroupMap(ReadableMap channelGroupMap) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ if (Build.VERSION.SDK_INT >= 26) {
String groupId = channelGroupMap.getString("groupId");
String name = channelGroupMap.getString("name");
- return new NotificationChannelGroup(groupId, name);
+ NotificationChannelGroup notificationChannelGroup = new NotificationChannelGroup(
+ groupId,
+ name
+ );
+
+ if (Build.VERSION.SDK_INT >= 28 && channelGroupMap.hasKey("description")) {
+ String description = channelGroupMap.getString("description");
+ notificationChannelGroup.setDescription(description);
+ }
+
+ return notificationChannelGroup;
}
+
return null;
}
+ private String getFileName(Uri uri) {
+ String result = null;
+ if (uri.getScheme() == "content") {
+ Cursor cursor = reactContext.getContentResolver().query(uri, null, null, null, null);
+ try {
+ if (cursor != null && cursor.moveToFirst()) {
+ result = cursor.getString(cursor.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME));
+ }
+ } finally {
+ if (cursor != null) cursor.close();
+ }
+ }
+
+ if (result == null) {
+ result = uri.getPath();
+ if (result != null) {
+ int cut = result.lastIndexOf('/');
+ if (cut != -1) {
+ result = result.substring(cut + 1);
+ } else {
+ result = "default";
+ }
+ }
+ }
+
+ if (result.equals("notification_sound")) result = "default";
+
+ return result;
+ }
+
+ @RequiresApi(api = 26)
+ private WritableArray createChannelsArray(List<NotificationChannel> notificationChannels) {
+ WritableArray writableArray = Arguments.createArray();
+
+ if (Build.VERSION.SDK_INT >= 26) {
+ int size = notificationChannels.size();
+ for (int i = 0; i < size; i++) {
+ writableArray.pushMap(createChannelMap(notificationChannels.get(i)));
+ }
+ }
+
+ return writableArray;
+ }
+
+ @RequiresApi(api = 26)
+ private WritableArray createChannelGroupsArray(List<NotificationChannelGroup> notificationChannelGroups) {
+ WritableArray writableArray = Arguments.createArray();
+
+ if (Build.VERSION.SDK_INT >= 26) {
+ int size = notificationChannelGroups.size();
+ for (int i = 0; i < size; i++) {
+ writableArray.pushMap(createChannelGroupMap(notificationChannelGroups.get(i)));
+ }
+ }
+
+ return writableArray;
+ }
+
+ @RequiresApi(api = 26)
+ private WritableMap createChannelGroupMap(NotificationChannelGroup notificationChannelGroup) {
+ WritableMap writableMap = Arguments.createMap();
+
+ if (Build.VERSION.SDK_INT >= 26) {
+ writableMap.putString("groupId", notificationChannelGroup.getId());
+ writableMap.putString("name", notificationChannelGroup.getName().toString());
+ writableMap.putArray("channels", createChannelsArray(notificationChannelGroup.getChannels()));
+ if (Build.VERSION.SDK_INT >= 28) {
+ writableMap.putString("description", notificationChannelGroup.getDescription());
+ }
+ }
+
+ return writableMap;
+ }
+
+ @RequiresApi(api = 26)
+ private WritableMap createChannelMap(NotificationChannel notificationChannel) {
+ if (notificationChannel == null) return null;
+ WritableMap writableMap = Arguments.createMap();
+
+ if (Build.VERSION.SDK_INT >= 26) {
+ writableMap.putString("channelId", notificationChannel.getId());
+ writableMap.putString("name", notificationChannel.getName().toString());
+ writableMap.putInt("importance", notificationChannel.getImportance());
+ writableMap.putString("description", notificationChannel.getDescription());
+
+ writableMap.putBoolean("bypassDnd", notificationChannel.canBypassDnd());
+ writableMap.putString("group", notificationChannel.getGroup());
+ writableMap.putString(
+ "lightColor",
+ String.format("#%06X", (0xFFFFFF & notificationChannel.getLightColor()))
+ );
+ writableMap.putBoolean("lightsEnabled", notificationChannel.shouldShowLights());
+
+ int visibility = notificationChannel.getLockscreenVisibility();
+ if (visibility == -1000) { // -1000 = not set
+ writableMap.putNull("lockScreenVisibility");
+ } else {
+ writableMap.putInt("lockScreenVisibility", visibility);
+ }
+
+ writableMap.putBoolean("showBadge", notificationChannel.canShowBadge());
+ writableMap.putString("sound", getFileName(notificationChannel.getSound()));
+ writableMap.putBoolean("vibrationEnabled", notificationChannel.shouldVibrate());
+
+ long[] vibration = notificationChannel.getVibrationPattern();
+ WritableArray vibrationArray = Arguments.createArray();
+ if (vibration != null) {
+ for (long aVibration : vibration) {
+ vibrationArray.pushDouble(aVibration);
+ }
+ }
+ writableMap.putArray("vibrationPattern", vibrationArray);
+ }
+
+ return writableMap;
+ }
+
+ @RequiresApi(api = 26)
private NotificationChannel parseChannelMap(ReadableMap channelMap) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ if (Build.VERSION.SDK_INT >= 26) {
String channelId = channelMap.getString("channelId");
String name = channelMap.getString("name");
int importance = channelMap.getInt("importance");
@@ -321,7 +493,7 @@
}
if (channelMap.hasKey("vibrationPattern")) {
ReadableArray vibrationArray = channelMap.getArray("vibrationPattern");
- long[] vibration = new long[]{};
+ long[] vibration = new long[vibrationArray.size()];
for (int i = 0; i < vibrationArray.size(); i++) {
vibration[i] = (long) vibrationArray.getDouble(i);
}
@@ -329,9 +501,11 @@
}
return channel;
}
+
return null;
}
+ @SuppressLint("ShortAlarm")
private void scheduleNotification(Bundle notification, @Nullable Promise promise) {
if (!notification.containsKey("notificationId")) {
if (promise == null) {
@@ -356,7 +530,7 @@
// fireDate may be stored in the Bundle as 2 different types that we need to handle:
// 1. Double - when a call comes directly from React
- // 2. Long - when notifications are rescheduled from boot service (Bundle is loaded from prefences).
+ // 2. Long - when notifications are rescheduled from boot service (Bundle is loaded from preferences).
// At the end we need Long value (timestamp) for the scheduler
Long fireDate = -1L;
Object fireDateObject = schedule.get("fireDate");

android/src/main/java/io/invertase/firebase/notifications/RNFirebaseNotifications.java

@@ -7,7 +7,6 @@
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.os.Bundle;
-import android.support.annotation.Nullable;
import android.support.v4.app.RemoteInput;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
@@ -25,8 +24,11 @@
import com.google.firebase.messaging.RemoteMessage;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.Map;
+import javax.annotation.Nullable;
+
import io.invertase.firebase.Utils;
import io.invertase.firebase.messaging.RNFirebaseMessagingService;
import me.leolin.shortcutbadger.ShortcutBadger;
@@ -42,7 +44,7 @@
private RNFirebaseNotificationManager notificationManager;
- public RNFirebaseNotifications(ReactApplicationContext context) {
+ RNFirebaseNotifications(ReactApplicationContext context) {
super(context);
context.addActivityEventListener(this);
@@ -152,39 +154,110 @@
//////////////////////////////////////////////////////////////////////
@ReactMethod
public void createChannel(ReadableMap channelMap, Promise promise) {
+ try {
notificationManager.createChannel(channelMap);
+ } catch (Throwable t) {
+ // do nothing - most likely a NoSuchMethodError for < v4 support lib
+ }
promise.resolve(null);
}
@ReactMethod
public void createChannelGroup(ReadableMap channelGroupMap, Promise promise) {
+ try {
notificationManager.createChannelGroup(channelGroupMap);
+ } catch (Throwable t) {
+ // do nothing - most likely a NoSuchMethodError for < v4 support lib
+ }
promise.resolve(null);
}
@ReactMethod
- public void createChannelGroup(ReadableArray channelGroupsArray, Promise promise) {
+ public void createChannelGroups(ReadableArray channelGroupsArray, Promise promise) {
+ try {
notificationManager.createChannelGroups(channelGroupsArray);
+ } catch (Throwable t) {
+ // do nothing - most likely a NoSuchMethodError for < v4 support lib
+ }
promise.resolve(null);
}
@ReactMethod
public void createChannels(ReadableArray channelsArray, Promise promise) {
+ try {
notificationManager.createChannels(channelsArray);
+ } catch (Throwable t) {
+ // do nothing - most likely a NoSuchMethodError for < v4 support lib
+ }
promise.resolve(null);
}
@ReactMethod
public void deleteChannelGroup(String channelId, Promise promise) {
+ try {
notificationManager.deleteChannelGroup(channelId);
promise.resolve(null);
+ } catch (NullPointerException e) {
+ promise.reject(
+ "notifications/channel-group-not-found",
+ "The requested NotificationChannelGroup does not exist, have you created it?"
+ );
+ }
}
@ReactMethod
public void deleteChannel(String channelId, Promise promise) {
+ try {
notificationManager.deleteChannel(channelId);
+ } catch (Throwable t) {
+ // do nothing - most likely a NoSuchMethodError for < v4 support lib
+ }
+ promise.resolve(null);
+ }
+
+ @ReactMethod
+ public void getChannel(String channelId, Promise promise) {
+ try {
+ promise.resolve(notificationManager.getChannel(channelId));
+ return;
+ } catch (Throwable t) {
+ // do nothing - most likely a NoSuchMethodError for < v4 support lib
+ }
promise.resolve(null);
}
+
+ @ReactMethod
+ public void getChannels(Promise promise) {
+ try {
+ promise.resolve(notificationManager.getChannels());
+ return;
+ } catch (Throwable t) {
+ // do nothing - most likely a NoSuchMethodError for < v4 support lib
+ }
+ promise.resolve(Collections.emptyList());
+ }
+
+ @ReactMethod
+ public void getChannelGroup(String channelGroupId, Promise promise) {
+ try {
+ promise.resolve(notificationManager.getChannelGroup(channelGroupId));
+ return;
+ } catch (Throwable t) {
+ // do nothing - most likely a NoSuchMethodError for < v4 support lib
+ }
+ promise.resolve(null);
+ }
+
+ @ReactMethod
+ public void getChannelGroups(Promise promise) {
+ try {
+ promise.resolve(notificationManager.getChannelGroups());
+ return;
+ } catch (Throwable t) {
+ // do nothing - most likely a NoSuchMethodError for < v4 support lib
+ }
+ promise.resolve(Collections.emptyList());
+ }
//////////////////////////////////////////////////////////////////////
// End Android specific methods
//////////////////////////////////////////////////////////////////////

android/src/main/java/io/invertase/firebase/perf/RNFirebasePerformance.java

@@ -10,8 +10,8 @@
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.WritableMap;
import com.google.firebase.perf.FirebasePerformance;
-import com.google.firebase.perf.metrics.Trace;
import com.google.firebase.perf.metrics.HttpMetric;
+import com.google.firebase.perf.metrics.Trace;
import java.util.HashMap;
import java.util.Map;
@@ -69,13 +69,23 @@
}
@ReactMethod
- public void incrementTraceMetric(String identifier, String metricName, Integer incrementBy, Promise promise) {
+ public void incrementTraceMetric(
+ String identifier,
+ String metricName,
+ Integer incrementBy,
+ Promise promise
+ ) {
getOrCreateTrace(identifier).incrementMetric(metricName, incrementBy.longValue());
promise.resolve(null);
}
@ReactMethod
- public void putTraceAttribute(String identifier, String attribute, String value, Promise promise) {
+ public void putTraceAttribute(
+ String identifier,
+ String attribute,
+ String value,
+ Promise promise
+ ) {
getOrCreateTrace(identifier).putAttribute(attribute, value);
// Docs say it returns a bool, actually void so we internally check attributes
Map<String, String> attributes = getOrCreateTrace(identifier).getAttributes();
@@ -121,7 +131,12 @@
*/
@ReactMethod
- public void getHttpMetricAttribute(String url, String httpMethod, String attribute, Promise promise) {
+ public void getHttpMetricAttribute(
+ String url,
+ String httpMethod,
+ String attribute,
+ Promise promise
+ ) {
promise.resolve(getOrCreateHttpMetric(url, httpMethod).getAttribute(attribute));
}
@@ -138,43 +153,74 @@
}
@ReactMethod
- public void putHttpMetricAttribute(String url, String httpMethod, String attribute, String value, Promise promise) {
+ public void putHttpMetricAttribute(
+ String url,
+ String httpMethod,
+ String attribute,
+ String value,
+ Promise promise
+ ) {
getOrCreateHttpMetric(url, httpMethod).putAttribute(attribute, value);
// Docs say it returns a bool, actually void so we internally check attributes
Map<String, String> attributes = getOrCreateHttpMetric(url, httpMethod).getAttributes();
if (attributes.containsKey(attribute)) {
- promise.resolve( true);
+ promise.resolve(true);
} else {
promise.resolve(false);
}
}
@ReactMethod
- public void removeHttpMetricAttribute(String url, String httpMethod, String attribute, Promise promise) {
+ public void removeHttpMetricAttribute(
+ String url,
+ String httpMethod,
+ String attribute,
+ Promise promise
+ ) {
getOrCreateHttpMetric(url, httpMethod).removeAttribute(attribute);
promise.resolve(null);
}
@ReactMethod
- public void setHttpMetricResponseCode(String url, String httpMethod, Integer code, Promise promise) {
+ public void setHttpMetricResponseCode(
+ String url,
+ String httpMethod,
+ Integer code,
+ Promise promise
+ ) {
getOrCreateHttpMetric(url, httpMethod).setHttpResponseCode(code);
promise.resolve(null);
}
@ReactMethod
- public void setHttpMetricRequestPayloadSize(String url, String httpMethod, Integer bytes, Promise promise) {
+ public void setHttpMetricRequestPayloadSize(
+ String url,
+ String httpMethod,
+ Integer bytes,
+ Promise promise
+ ) {
getOrCreateHttpMetric(url, httpMethod).setRequestPayloadSize(bytes.longValue());
promise.resolve(null);
}
@ReactMethod
- public void setHttpMetricResponseContentType(String url, String httpMethod, String type, Promise promise) {
+ public void setHttpMetricResponseContentType(
+ String url,
+ String httpMethod,
+ String type,
+ Promise promise
+ ) {
getOrCreateHttpMetric(url, httpMethod).setResponseContentType(type);
promise.resolve(null);
}
@ReactMethod
- public void setHttpMetricResponsePayloadSize(String url, String httpMethod, Integer bytes, Promise promise) {
+ public void setHttpMetricResponsePayloadSize(
+ String url,
+ String httpMethod,
+ Integer bytes,
+ Promise promise
+ ) {
getOrCreateHttpMetric(url, httpMethod).setResponsePayloadSize(bytes.longValue());
promise.resolve(null);
}
@@ -210,7 +256,10 @@
if (httpMetrics.containsKey(identifier)) {
return httpMetrics.get(identifier);
}
- HttpMetric httpMetric = FirebasePerformance.getInstance().newHttpMetric(url, this.mapStringToMethod(httpMethod));
+ HttpMetric httpMetric = FirebasePerformance.getInstance().newHttpMetric(
+ url,
+ this.mapStringToMethod(httpMethod)
+ );
httpMetrics.put(identifier, httpMetric);
return httpMetric;
}

android/src/main/java/io/invertase/firebase/perf/RNFirebasePerformancePackage.java

@@ -1,7 +1,6 @@
package io.invertase.firebase.perf;
import com.facebook.react.ReactPackage;
-import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.UIManagerModule;

android/src/main/java/io/invertase/firebase/ReactNativeFirebaseAppRegistrar.java

@@ -0,0 +1,40 @@
+package io.invertase.firebase;
+
+/*
+ * Copyright (c) 2016-present Invertase Limited & Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this library except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+import android.support.annotation.Keep;
+
+import com.google.firebase.components.Component;
+import com.google.firebase.components.ComponentRegistrar;
+import com.google.firebase.platforminfo.LibraryVersionComponent;
+
+import java.util.Collections;
+import java.util.List;
+
+@Keep
+public class ReactNativeFirebaseAppRegistrar implements ComponentRegistrar {
+ @Override
+ public List<Component<?>> getComponents() {
+ return Collections.singletonList(
+ LibraryVersionComponent.create(
+ "react-native-firebase",
+ "5.3.0"
+ )
+ );
+ }
+}

android/src/main/java/io/invertase/firebase/RNFirebaseModule.java

@@ -62,21 +62,22 @@
public void deleteApp(String appName, Promise promise) {
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
- if (firebaseApp == null) {
+ if (firebaseApp != null) {
+ firebaseApp.delete();
+ }
+
promise.resolve(null);
- } else {
- // todo ? not implemented on firebase sdk
- promise.reject(
- "app/delete-app-failed",
- "Failed to delete app. The android Firebase SDK currently does not support this functionality"
- );
}
+
+ @ReactMethod
+ public void getPlayServicesStatus(Promise promise) {
+ promise.resolve(getPlayServicesStatusMap());
}
/**
* @return
*/
- private WritableMap getPlayServicesStatus() {
+ private WritableMap getPlayServicesStatusMap() {
GoogleApiAvailability gapi = GoogleApiAvailability.getInstance();
final int status = gapi.isGooglePlayServicesAvailable(getReactApplicationContext());
WritableMap result = Arguments.createMap();
@@ -152,8 +153,6 @@
@Override
public Map<String, Object> getConstants() {
- FirebaseApp firebaseApp;
-
Map<String, Object> constants = new HashMap<>();
List<Map<String, Object>> appMapsList = new ArrayList<>();
List<FirebaseApp> firebaseAppList = FirebaseApp.getApps(getReactApplicationContext());
@@ -168,6 +167,7 @@
appProps.put("apiKey", appOptions.getApiKey());
appProps.put("appId", appOptions.getApplicationId());
appProps.put("projectId", appOptions.getProjectId());
+ appProps.put("projectId", appOptions.getProjectId());
appProps.put("databaseURL", appOptions.getDatabaseUrl());
appProps.put("messagingSenderId", appOptions.getGcmSenderId());
appProps.put("storageBucket", appOptions.getStorageBucket());
@@ -176,7 +176,7 @@
}
constants.put("apps", appMapsList);
- constants.put("playServicesAvailability", getPlayServicesStatus());
+ constants.put("playServicesAvailability", getPlayServicesStatusMap());
return constants;
}
}

android/src/main/java/io/invertase/firebase/storage/RNFirebaseStorage.java

@@ -1,43 +1,41 @@
package io.invertase.firebase.storage;
import android.content.ContentResolver;
-import android.support.annotation.Nullable;
-import android.util.Log;
-import android.os.Environment;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Map;
-import java.util.HashMap;
-
import android.net.Uri;
-import android.support.annotation.NonNull;
+import android.os.Environment;
+import android.util.Log;
import android.webkit.MimeTypeMap;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Promise;
-import com.facebook.react.bridge.WritableMap;
-import com.facebook.react.bridge.ReactMethod;
-import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
-
-import com.google.android.gms.tasks.Task;
+import com.facebook.react.bridge.ReactMethod;
+import com.facebook.react.bridge.ReadableMap;
+import com.facebook.react.bridge.WritableMap;
import com.google.android.gms.tasks.OnFailureListener;
import com.google.android.gms.tasks.OnSuccessListener;
-
+import com.google.android.gms.tasks.Task;
import com.google.firebase.FirebaseApp;
-import com.google.firebase.storage.UploadTask;
-import com.google.firebase.storage.StorageTask;
import com.google.firebase.storage.FirebaseStorage;
-import com.google.firebase.storage.StorageMetadata;
+import com.google.firebase.storage.OnPausedListener;
+import com.google.firebase.storage.OnProgressListener;
import com.google.firebase.storage.StorageException;
+import com.google.firebase.storage.StorageMetadata;
import com.google.firebase.storage.StorageReference;
-import com.google.firebase.storage.OnPausedListener;
+import com.google.firebase.storage.StorageTask;
import com.google.firebase.storage.StreamDownloadTask;
-import com.google.firebase.storage.OnProgressListener;
+import com.google.firebase.storage.UploadTask;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
import io.invertase.firebase.Utils;
@@ -120,7 +118,7 @@
})
.addOnFailureListener(new OnFailureListener() {
@Override
- public void onFailure(@NonNull Exception exception) {
+ public void onFailure(@Nonnull Exception exception) {
promiseRejectStorageException(promise, exception);
}
});
@@ -148,7 +146,7 @@
})
.addOnFailureListener(new OnFailureListener() {
@Override
- public void onFailure(@NonNull Exception exception) {
+ public void onFailure(@Nonnull Exception exception) {
promiseRejectStorageException(promise, exception);
}
});
@@ -174,7 +172,7 @@
})
.addOnFailureListener(new OnFailureListener() {
@Override
- public void onFailure(@NonNull Exception exception) {
+ public void onFailure(@Nonnull Exception exception) {
promiseRejectStorageException(promise, exception);
}
});
@@ -209,7 +207,7 @@
})
.addOnFailureListener(new OnFailureListener() {
@Override
- public void onFailure(@NonNull Exception exception) {
+ public void onFailure(@Nonnull Exception exception) {
promiseRejectStorageException(promise, exception);
}
});
@@ -301,7 +299,7 @@
})
.addOnFailureListener(new OnFailureListener() {
@Override
- public void onFailure(@NonNull Exception exception) {
+ public void onFailure(@Nonnull Exception exception) {
Log.e(TAG, "downloadFile failure " + exception.getMessage());
// TODO sendJS error event
promiseRejectStorageException(promise, exception);
@@ -381,7 +379,7 @@
uploadTask
.addOnFailureListener(new OnFailureListener() {
@Override
- public void onFailure(@NonNull Exception exception) {
+ public void onFailure(@Nonnull Exception exception) {
// handle unsuccessful uploads
Log.e(TAG, "putFile failure " + exception.getMessage());
// TODO sendJS error event
@@ -566,13 +564,34 @@
taskSnapshot
.getStorage()
.getDownloadUrl()
+ .addOnFailureListener(new OnFailureListener() {
+ @Override
+ public void onFailure(@Nonnull Exception e) {
+ int errorCode = ((StorageException) e).getErrorCode();
+ if (errorCode == StorageException.ERROR_NOT_AUTHORIZED) {
+ WritableMap resp = getRespAsMap(taskSnapshot, null);
+ listener.onSuccess(resp);
+ }
+ }
+ })
.addOnSuccessListener(new OnSuccessListener<Uri>() {
@Override
public void onSuccess(Uri downloadUrl) {
+ WritableMap resp = getRespAsMap(taskSnapshot, downloadUrl.toString());
+ listener.onSuccess(resp);
+ }
+ });
+ } else {
+ listener.onSuccess(Arguments.createMap());
+ }
+ }
+
+
+ private WritableMap getRespAsMap(final UploadTask.TaskSnapshot taskSnapshot, final String downloadUrl) {
WritableMap resp = Arguments.createMap();
resp.putDouble("bytesTransferred", taskSnapshot.getBytesTransferred());
- resp.putString("downloadURL", downloadUrl.toString());
+ resp.putString("downloadURL", downloadUrl);
StorageMetadata d = taskSnapshot.getMetadata();
if (d != null) {
@@ -588,15 +607,8 @@
);
resp.putString("state", RNFirebaseStorage.this.getTaskStatus(taskSnapshot.getTask()));
resp.putDouble("totalBytes", taskSnapshot.getTotalByteCount());
-
- listener.onSuccess(resp);
- }
- });
- } else {
- listener.onSuccess(Arguments.createMap());
- }
+ return resp;
}
-
/**
* Converts storageMetadata into a map
*

android/src/main/java/io/invertase/firebase/Utils.java

@@ -8,11 +8,23 @@
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
+import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap;
+import com.facebook.react.common.LifecycleState;
import com.facebook.react.modules.core.DeviceEventManagerModule;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.Iterator;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
+import java.util.TimeZone;
import javax.annotation.Nullable;
@@ -21,6 +33,14 @@
public class Utils {
private static final String TAG = "Utils";
+ public static String timestampToUTC(long timestamp) {
+ Calendar calendar = Calendar.getInstance();
+ Date date = new Date((timestamp + calendar.getTimeZone().getOffset(timestamp)) * 1000);
+ SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);
+ format.setTimeZone(TimeZone.getTimeZone("UTC"));
+ return format.format(date);
+ }
+
/**
* send a JS event
**/
@@ -34,21 +54,134 @@
}
}
- /**
- * Takes a value and calls the appropriate setter for its type on the target map + key
- *
- * @param key String key to set on target map
- * @param value Object value to set on target map
- * @param map WritableMap target map to write the value to
- */
+ public static WritableMap jsonObjectToWritableMap(JSONObject jsonObject) throws JSONException {
+ Iterator<String> iterator = jsonObject.keys();
+ WritableMap writableMap = Arguments.createMap();
+
+ while (iterator.hasNext()) {
+ String key = iterator.next();
+ Object value = jsonObject.get(key);
+ if (value instanceof Float || value instanceof Double) {
+ writableMap.putDouble(key, jsonObject.getDouble(key));
+ } else if (value instanceof Number) {
+ writableMap.putInt(key, jsonObject.getInt(key));
+ } else if (value instanceof String) {
+ writableMap.putString(key, jsonObject.getString(key));
+ } else if (value instanceof JSONObject) {
+ writableMap.putMap(key, jsonObjectToWritableMap(jsonObject.getJSONObject(key)));
+ } else if (value instanceof JSONArray) {
+ writableMap.putArray(key, jsonArrayToWritableArray(jsonObject.getJSONArray(key)));
+ } else if (value == JSONObject.NULL) {
+ writableMap.putNull(key);
+ }
+ }
+
+ return writableMap;
+ }
+
+ public static WritableArray jsonArrayToWritableArray(JSONArray jsonArray) throws JSONException {
+ WritableArray writableArray = Arguments.createArray();
+
+ for (int i = 0; i < jsonArray.length(); i++) {
+ Object value = jsonArray.get(i);
+ if (value instanceof Float || value instanceof Double) {
+ writableArray.pushDouble(jsonArray.getDouble(i));
+ } else if (value instanceof Number) {
+ writableArray.pushInt(jsonArray.getInt(i));
+ } else if (value instanceof String) {
+ writableArray.pushString(jsonArray.getString(i));
+ } else if (value instanceof JSONObject) {
+ writableArray.pushMap(jsonObjectToWritableMap(jsonArray.getJSONObject(i)));
+ } else if (value instanceof JSONArray) {
+ writableArray.pushArray(jsonArrayToWritableArray(jsonArray.getJSONArray(i)));
+ } else if (value == JSONObject.NULL) {
+ writableArray.pushNull();
+ }
+ }
+ return writableArray;
+ }
+
+ public static WritableMap mapToWritableMap(Map<String, Object> value) {
+ WritableMap writableMap = Arguments.createMap();
+
+ for (Map.Entry<String, Object> entry : value.entrySet()) {
+ mapPutValue(entry.getKey(), entry.getValue(), writableMap);
+ }
+
+ return writableMap;
+ }
+
+ private static WritableArray listToWritableArray(List<Object> objects) {
+ WritableArray writableArray = Arguments.createArray();
+ for (Object object : objects) {
+ arrayPushValue(object, writableArray);
+ }
+ return writableArray;
+ }
+
+ @SuppressWarnings("unchecked")
+ public static void arrayPushValue(@Nullable Object value, WritableArray array) {
+ if (value == null || value == JSONObject.NULL) {
+ array.pushNull();
+ return;
+ }
+
+ String type = value.getClass().getName();
+ switch (type) {
+ case "java.lang.Boolean":
+ array.pushBoolean((Boolean) value);
+ break;
+ case "java.lang.Long":
+ Long longVal = (Long) value;
+ array.pushDouble((double) longVal);
+ break;
+ case "java.lang.Float":
+ float floatVal = (float) value;
+ array.pushDouble((double) floatVal);
+ break;
+ case "java.lang.Double":
+ array.pushDouble((double) value);
+ break;
+ case "java.lang.Integer":
+ array.pushInt((int) value);
+ break;
+ case "java.lang.String":
+ array.pushString((String) value);
+ break;
+ case "org.json.JSONObject$1":
+ try {
+ array.pushMap(jsonObjectToWritableMap((JSONObject) value));
+ } catch (JSONException e) {
+ array.pushNull();
+ }
+ break;
+ case "org.json.JSONArray$1":
+ try {
+ array.pushArray(jsonArrayToWritableArray((JSONArray) value));
+ } catch (JSONException e) {
+ array.pushNull();
+ }
+ break;
+ default:
+ if (List.class.isAssignableFrom(value.getClass())) {
+ array.pushArray(listToWritableArray((List<Object>) value));
+ } else if (Map.class.isAssignableFrom(value.getClass())) {
+ array.pushMap(mapToWritableMap((Map<String, Object>) value));
+ } else {
+ Log.d(TAG, "utils:arrayPushValue:unknownType:" + type);
+ array.pushNull();
+ }
+ }
+ }
+
@SuppressWarnings("unchecked")
public static void mapPutValue(String key, @Nullable Object value, WritableMap map) {
- if (value == null) {
+ if (value == null || value == JSONObject.NULL) {
map.putNull(key);
- } else {
- String type = value
- .getClass()
- .getName();
+ return;
+ }
+
+ String type = value.getClass().getName();
switch (type) {
case "java.lang.Boolean":
map.putBoolean(key, (Boolean) value);
@@ -62,7 +195,7 @@
map.putDouble(key, (double) floatVal);
break;
case "java.lang.Double":
- map.putDouble(key, (Double) value);
+ map.putDouble(key, (double) value);
break;
case "java.lang.Integer":
map.putInt(key, (int) value);
@@ -71,27 +204,30 @@
map.putString(key, (String) value);
break;
case "org.json.JSONObject$1":
- map.putString(key, value.toString());
+ try {
+ map.putMap(key, jsonObjectToWritableMap((JSONObject) value));
+ } catch (JSONException e) {
+ map.putNull(key);
+ }
+ break;
+ case "org.json.JSONArray$1":
+ try {
+ map.putArray(key, jsonArrayToWritableArray((JSONArray) value));
+ } catch (JSONException e) {
+ map.putNull(key);
+ }
break;
default:
if (List.class.isAssignableFrom(value.getClass())) {
- map.putArray(key, Arguments.makeNativeArray((List<Object>) value));
+ map.putArray(key, listToWritableArray((List<Object>) value));
} else if (Map.class.isAssignableFrom(value.getClass())) {
- WritableMap childMap = Arguments.createMap();
- Map<String, Object> valueMap = (Map<String, Object>) value;
-
- for (Map.Entry<String, Object> entry : valueMap.entrySet()) {
- mapPutValue(entry.getKey(), entry.getValue(), childMap);
- }
-
- map.putMap(key, childMap);
+ map.putMap(key, mapToWritableMap((Map<String, Object>) value));
} else {
Log.d(TAG, "utils:mapPutValue:unknownType:" + type);
map.putNull(key);
}
}
}
- }
/**
* Convert a ReadableMap to a WritableMap for the purposes of re-sending back to JS
@@ -151,8 +287,17 @@
appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND
&& appProcess.processName.equals(packageName)
) {
+ ReactContext reactContext;
+
+ try {
+ reactContext = (ReactContext) context;
+ } catch (ClassCastException exception) {
+ // Not react context so default to true
return true;
}
+
+ return reactContext.getLifecycleState() == LifecycleState.RESUMED;
+ }
}
return false;

babel.config.js

@@ -0,0 +1,24 @@
+module.exports = {
+ env: {
+ development: {
+ presets: [
+ [
+ '@invertase/react-native-syntax',
+ {
+ flow: 'comment',
+ },
+ ],
+ ],
+ },
+ publish: {
+ presets: [
+ [
+ '@invertase/react-native-syntax',
+ {
+ flow: 'strip',
+ },
+ ],
+ ],
+ },
+ },
+};

.babelrc

@@ -1,16 +0,0 @@
-{
- "env": {
- "development": {
- "presets": ["react-native"],
- "plugins": [
- "transform-flow-strip-types"
- ]
- },
- "publish": {
- "presets": ["@invertase/react-native-syntax"],
- "plugins": [
- "transform-flow-strip-types"
- ]
- }
- }
-}

dist/common/commonTypes.flow.js.flow

@@ -0,0 +1,19 @@
+export type NativeErrorObject = {
+ code: string,
+ message: string,
+ nativeErrorCode: string | number,
+ nativeErrorMessage: string,
+};
+
+export type NativeErrorResponse = {
+ error: NativeErrorObject,
+ // everything else
+ [key: string]: ?any,
+};
+
+export interface NativeErrorInterface extends Error {
+ +code: string;
+ +message: string;
+ +nativeErrorCode: string | number;
+ +nativeErrorMessage: string;
+}

dist/common/NativeError.js

@@ -0,0 +1,10 @@
+export default class NativeError extends Error {
+ constructor(nativeError) {
+ super(nativeError.message);
+ this.code = nativeError.code;
+ this.message = nativeError.message;
+ this.nativeErrorCode = nativeError.nativeErrorCode;
+ this.nativeErrorMessage = nativeError.nativeErrorMessage;
+ }
+
+}
\ No newline at end of file

dist/common/NativeError.js.flow

@@ -0,0 +1,14 @@
+import type {
+ NativeErrorObject,
+ NativeErrorInterface,
+} from './commonTypes.flow';
+
+export default class NativeError extends Error implements NativeErrorInterface {
+ constructor(nativeError: NativeErrorObject) {
+ super(nativeError.message);
+ this.code = nativeError.code;
+ this.message = nativeError.message;
+ this.nativeErrorCode = nativeError.nativeErrorCode;
+ this.nativeErrorMessage = nativeError.nativeErrorMessage;
+ }
+}

dist/index.d.ts

@@ -1,11 +1,11 @@
-// Type definitions for React Native Firebase v4.2.0
+// Type definitions for React Native Firebase v5.0.0
// Project: https://github.com/invertase/react-native-firebase
// Definitions by: React Native Firebase Contributors
// TypeScript Version: 2.1
declare module 'react-native-firebase' {
/** 3rd party provider Credentials */
- type AuthCredential = {
+ export type AuthCredential = {
providerId: string;
token: string;
secret: string;
@@ -16,48 +16,74 @@
nativeModuleExists: boolean;
} & S;
- // Modules commented-out do not currently have type definitions
- export class Firebase {
- private constructor();
- // admob: FirebaseModuleAndStatics<RNFirebase.admob.AdMob>;
- analytics: FirebaseModuleAndStatics<RNFirebase.Analytics>;
- auth: FirebaseModuleAndStatics<
+ // type AdmobModule = FirebaseModuleAndStatics<RNFirebase.admob.AdMob>;
+ type AnalyticsModule = FirebaseModuleAndStatics<RNFirebase.Analytics>;
+ type AuthModule = FirebaseModuleAndStatics<
RNFirebase.auth.Auth,
RNFirebase.auth.AuthStatics
>;
- config: FirebaseModuleAndStatics<RNFirebase.config.Config>;
- crashlytics: FirebaseModuleAndStatics<RNFirebase.crashlytics.Crashlytics>;
- database: FirebaseModuleAndStatics<
+ type ConfigModule = FirebaseModuleAndStatics<RNFirebase.config.Config>;
+ type CrashlyticsModule = FirebaseModuleAndStatics<
+ RNFirebase.crashlytics.Crashlytics
+ >;
+ type DatabaseModule = FirebaseModuleAndStatics<
RNFirebase.database.Database,
RNFirebase.database.DatabaseStatics
>;
- firestore: FirebaseModuleAndStatics<
+ type FirestoreModule = FirebaseModuleAndStatics<
RNFirebase.firestore.Firestore,
RNFirebase.firestore.FirestoreStatics
>;
- functions: FirebaseModuleAndStatics<
+ type FunctionsModule = FirebaseModuleAndStatics<
RNFirebase.functions.Functions,
RNFirebase.functions.FunctionsStatics
>;
- iid: FirebaseModuleAndStatics<RNFirebase.iid.InstanceId>;
- // invites: FirebaseModuleAndStatics<RNFirebase.invites.Invites>
- links: FirebaseModuleAndStatics<
+ type IidModule = FirebaseModuleAndStatics<RNFirebase.iid.InstanceId>;
+ // type InvitesModule = FirebaseModuleAndStatics<RNFirebase.invites.Invites>;
+ type LinksModule = FirebaseModuleAndStatics<
RNFirebase.links.Links,
RNFirebase.links.LinksStatics
>;
- messaging: FirebaseModuleAndStatics<
+ type MessagingModule = FirebaseModuleAndStatics<
RNFirebase.messaging.Messaging,
RNFirebase.messaging.MessagingStatics
>;
- notifications: FirebaseModuleAndStatics<
+ type NotificationsModule = FirebaseModuleAndStatics<
RNFirebase.notifications.Notifications,
RNFirebase.notifications.NotificationsStatics
>;
- perf: FirebaseModuleAndStatics<RNFirebase.perf.Perf>;
- storage: FirebaseModuleAndStatics<RNFirebase.storage.Storage>;
- // utils: FirebaseModuleAndStatics<RNFirebase.utils.Utils>;
+ type PerfModule = FirebaseModuleAndStatics<RNFirebase.perf.Perf>;
+ type StorageModule = FirebaseModuleAndStatics<
+ RNFirebase.storage.Storage,
+ RNFirebase.storage.StorageStatics
+ >;
+ // type UtilsModule: FirebaseModuleAndStatics<RNFirebase.utils.Utils>;
+
+ // Modules commented-out do not currently have type definitions
+ export class Firebase {
+ private constructor();
+
+ // admob: AdmobModule;
+ analytics: AnalyticsModule;
+ auth: AuthModule;
+ config: ConfigModule;
+ crashlytics: CrashlyticsModule;
+ database: DatabaseModule;
+ firestore: FirestoreModule;
+ functions: FunctionsModule;
+ iid: IidModule;
+ // invites: InvitesModule;
+ links: LinksModule;
+ messaging: MessagingModule;
+ notifications: NotificationsModule;
+ perf: PerfModule;
+ storage: StorageModule;
+
+ // utils: UtilsModule;
initializeApp(options: Firebase.Options, name: string): App;
+
app(name?: string): App;
+
readonly apps: App[];
readonly SDK_VERSION: string;
}
@@ -73,6 +100,20 @@
}
const firebase: Firebase;
export default firebase;
+ // export const admob: AdmobModule;
+ export const analytics: AnalyticsModule;
+ export const auth: AuthModule;
+ export const config: ConfigModule;
+ export const crashlytics: CrashlyticsModule;
+ export const database: DatabaseModule;
+ export const firestore: FirestoreModule;
+ export const functions: FunctionsModule;
+ export const iid: IidModule;
+ // export const invites: InvitesModule;
+ export const links: LinksModule;
+ export const messaging: MessagingModule;
+ export const notifications: NotificationsModule;
+ export const storage: StorageModule;
// Modules commented-out do not currently have type definitions
export class App {
@@ -100,7 +155,10 @@
}
export namespace RNFirebase {
- interface RnError extends Error {
+ type Handler<T> = (value: T) => void;
+ type ErrorHandler = Handler<RnError>;
+
+ export interface RnError extends Error {
code?: string;
}
@@ -134,22 +192,22 @@
*/
bundleID?: string;
/**
- * defualt ""
+ * default ""
* The Google App ID that is used to uniquely identify an instance of an app.
*/
googleAppID?: string;
/**
- * deufalt ""
+ * default ""
* The database root (i.e. https://my-app.firebaseio.com)
*/
databaseURL?: string;
/**
- * defualt ""
+ * default ""
* URL scheme to set up durable deep link service
*/
deepLinkURLScheme?: string;
/**
- * defualt ""
+ * default ""
* The Google Cloud storage bucket name
*/
storageBucket?: string;
@@ -174,105 +232,148 @@
*/
clientID?: string;
/**
- * defualt ""
+ * default ""
* The secret iOS API key used for authenticating requests from our app
*/
APIKey?: string;
}
namespace storage {
- interface StorageTask<T> extends Promise<T> {
- on(
- event: TaskEvent,
- nextOrObserver: (snapshot: any) => any,
- error: (error: RnError) => any,
- complete: (complete: any) => any
- ): any;
+ interface StorageStatics {
+ TaskState: TaskState;
+ TaskEvent: TaskState;
+ Native?: {
+ MAIN_BUNDLE_PATH: string;
+ CACHES_DIRECTORY_PATH: string;
+ DOCUMENT_DIRECTORY_PATH: string;
+ EXTERNAL_DIRECTORY_PATH: string;
+ EXTERNAL_STORAGE_DIRECTORY_PATH: string;
+ TEMP_DIRECTORY_PATH: string;
+ LIBRARY_DIRECTORY_PATH: string;
+ FILETYPE_REGULAR: string;
+ FILETYPE_DIRECTORY: string;
+ };
+ }
/**
- * is not currently supported by react-native-firebase
+ * The Firebase Storage service interface.
+ *
+ * An instance can be accessed using `firebase.storage()`.
*/
- pause(): void;
+ class Storage {
+ /**
+ * The app associated with the Storage service instance.
+ */
+ app: App;
/**
- * is not currently supported by react-native-firebase
+ * Returns a reference for the given path in the default bucket.
+ *
+ * @param path A relative path to initialize the reference with, for
+ * example path/to/image.jpg. If not passed, the returned
+ * reference points to the bucket root.
*/
- resume(): void;
+ ref(path?: string): Reference;
/**
- * is not currently supported by react-native-firebase
+ * Returns a reference for the given absolute URL.
+ *
+ * @param url URL must be in the form of either
+ * - a Cloud Storage URL, for example gs://bucket/files/image.png; or
+ * - download URL taken from object metadata.
*/
- cancel(): void;
- }
+ refFromURL(url: string): Reference;
- interface RNStorage extends Reference {
/**
- * Downloads a reference to the device
- * @param {String} filePath Where to store the file
- * @return {Promise}
- * */
- downloadFile(filePath: string): StorageTask<any>;
+ * @param time The new maximum operation retry time in milliseconds.
+ */
+ setMaxOperationRetryTime(time: number): void;
/**
- * Upload a file path
- * @returns {Promise}
+ * @param time The new maximum upload retry time in milliseconds.
*/
- putFile(filePath: string, metadata?: any): StorageTask<any>;
+ setMaxUploadRetryTime(time: number): void;
+ /**
+ * @param time The new maximum download retry time in milliseconds.
+ */
setMaxDownloadRetryTime(time: number): void;
-
- [key: string]: any;
- }
-
- interface Storage {
- maxOperationRetryTime: number;
- maxUploadRetryTime: number;
-
- ref(path?: string): storage.RNStorage;
-
- refFromURL(url: string): storage.RNStorage;
-
- setMaxOperationRetryTime(time: number): any;
-
- setMaxUploadRetryTime(time: number): any;
}
+ /**
+ * A reference represents a reference to a Google Cloud Storage object.
+ *
+ * You can upload, download, and delete objects, as well as get/set object
+ * metadata for a file via this reference.
+ */
interface Reference {
- bucket: string;
-
- child(path: string): storage.Reference;
-
- delete(): Promise<any>;
-
fullPath: string;
- getDownloadURL(): Promise<any>;
+ toString(): string;
- getMetadata(): Promise<any>;
+ /**
+ * Returns a reference to a relative path from this reference.
+ *
+ * @param path The relative path
+ */
+ child(path: string): Reference;
- name: string;
- parent: storage.Reference | null;
+ /**
+ * Deletes the object at this reference's location.
+ */
+ delete(): Promise<void>;
- put(
- data: any | Uint8Array | ArrayBuffer,
- metadata?: storage.UploadMetadata
- ): storage.UploadTask;
-
- putString(
- data: string,
- format?: storage.StringFormat,
- metadata?: storage.UploadMetadata
- ): storage.UploadTask;
+ /**
+ * Fetches a long lived download URL for this object.
+ */
+ getDownloadURL(): Promise<string>;
+
+ /**
+ * Fetches metadata for the object at this location, if one exists.
+ *
+ * @returns A promise that is resolved with the metadata; or rejected on
+ * failure, including if the object does not exist.
+ */
+ getMetadata(): Promise<FullMetadata>;
- root: storage.Reference;
- storage: storage.Storage;
+ /**
+ * Updates the metadata for the object at this location, if one exists.
+ *
+ * @param metadata
+ */
+ updateMetadata(metadata: SettableMetadata): Promise<FullMetadata>;
- toString(): string;
+ /**
+ * Downloads the storage object for this reference to the device file
+ * path specified.
+ *
+ * @param filePath The destination path of the downloaded file.
+ */
+ downloadFile(filePath: string): StorageTask<DownloadTaskSnapshot>;
- updateMetadata(metadata: storage.SettableMetadata): Promise<any>;
+ /**
+ * Uploads the file path specified from the device into a storage object
+ * for this reference.
+ *
+ * @param filePath The path to the file on the device. It must be a full
+ * file path.
+ * @param metadata The metadata to associate with this file.
+ */
+ putFile(
+ filePath: string,
+ metadata?: SettableMetadata
+ ): StorageTask<UploadTaskSnapshot>;
}
- interface UploadMetadata extends storage.SettableMetadata {
+ interface FullMetadata extends SettableMetadata {
+ bucket: string;
+ fullPath: string;
+ generation: string;
+ metageneration: string;
+ name: string;
+ size: number;
+ timeCreated: string;
+ updated: string;
md5Hash?: string | null;
}
@@ -282,77 +383,70 @@
contentEncoding?: string | null;
contentLanguage?: string | null;
contentType?: string | null;
- customMetadata?: {
- [/* warning: coerced from ? */ key: string]: string;
- } | null;
+ customMetadata?: Partial<Record<string, string>>;
}
- type StringFormat = string;
- var StringFormat: {
- BASE64: StringFormat;
- BASE64URL: StringFormat;
- DATA_URL: StringFormat;
- RAW: StringFormat;
- };
-
- interface UploadTask {
- cancel(): boolean;
-
- catch(onRejected: (a: RnError) => any): Promise<any>;
-
+ interface StorageTask<T> extends Promise<T> {
on(
- event: storage.TaskEvent,
- nextOrObserver?: null | Object,
- error?: ((a: RnError) => any) | null,
- complete?: (() => any) | null
- ): Function;
+ event: TaskEvent,
+ next: Handler<T>,
+ error?: ErrorHandler,
+ complete?: Handler<T>
+ ): () => void;
- pause(): boolean;
+ on(
+ event: TaskEvent,
+ observer: {
+ next?: Handler<T>;
+ error?: ErrorHandler;
+ complete?: Handler<T>;
+ }
+ ): () => void;
- resume(): boolean;
+ /**
+ * Not supported by react-native-firebase
+ */
+ pause(): void;
- snapshot: storage.UploadTaskSnapshot;
+ /**
+ * Not supported by react-native-firebase
+ */
+ resume(): void;
- then(
- onFulfilled?: ((a: storage.UploadTaskSnapshot) => any) | null,
- onRejected?: ((a: RnError) => any) | null
- ): Promise<any>;
+ /**
+ * Not supported by react-native-firebase
+ */
+ cancel(): void;
}
interface UploadTaskSnapshot {
bytesTransferred: number;
downloadURL: string | null;
- metadata: storage.FullMetadata;
- ref: storage.Reference;
- state: storage.TaskState;
- task: storage.UploadTask;
+ metadata: FullMetadata;
+ ref: Reference;
+ state: TaskState;
+ task: StorageTask<UploadTaskSnapshot>;
totalBytes: number;
}
- interface FullMetadata extends storage.UploadMetadata {
- bucket: string;
- fullPath: string;
- generation: string;
- metageneration: string;
- name: string;
- size: number;
- timeCreated: string;
- updated: string;
+ interface DownloadTaskSnapshot {
+ bytesTransferred: number;
+ ref: Reference;
+ state: TaskState;
+ totalBytes: number;
}
- type TaskEvent = string;
- var TaskEvent: {
- STATE_CHANGED: TaskEvent;
- };
+ enum TaskEvent {
+ STATE_CHANGED = 'state_changed',
+ }
- type TaskState = string;
- var TaskState: {
- CANCELED: TaskState;
- ERROR: TaskState;
- PAUSED: TaskState;
- RUNNING: TaskState;
- SUCCESS: TaskState;
- };
+ enum TaskState {
+ CANCELLED = 'cancelled',
+ ERROR = 'error',
+ PAUSED = 'paused',
+ RUNNING = 'running',
+ SUCCESS = 'success',
+ }
}
namespace database {
@@ -460,7 +554,7 @@
successCallback?: QuerySuccessCallback,
failureCallbackOrContext?: QueryErrorCallback,
context?: Object
- ): Promise<DataSnapshot>;
+ ): Promise<database.DataSnapshot>;
orderByChild(path: string): database.Query;
@@ -487,7 +581,10 @@
exists(): boolean;
- exportVal(): any;
+ exportVal(): {
+ '.value': any;
+ '.priority': string | number | null;
+ };
forEach(action: (a: database.DataSnapshot) => boolean): boolean;
@@ -594,16 +691,16 @@
* Sets the minimum engagement time required before starting a session.
* The default value is 10000 (10 seconds)
*/
- setMinimumSessionDuration(miliseconds: number): void;
+ setMinimumSessionDuration(milliseconds: number): void;
/**
* Sets the duration of inactivity that terminates the current session.
* The default value is 1800000 (30 minutes).
*/
- setSessionTimeoutDuration(miliseconds: number): void;
+ setSessionTimeoutDuration(milliseconds: number): void;
/**
- * Gives a user a uniqiue identificaition.
+ * Gives a user a unique identification.
* @example
* const id = firebase.auth().currentUser.uid;
*
@@ -616,6 +713,11 @@
*/
setUserProperty(name: string, value: string | null): void;
+ /**
+ * Sets multiple user properties to the supplied values.
+ */
+ setUserProperties(fieldMapping: { [key: string]: string | null }): void;
+
[key: string]: any;
}
@@ -650,6 +752,17 @@
lastSignInTime?: string;
};
+ type IdTokenResult = {
+ token: string;
+ authTime: string;
+ issuedAtTime: string;
+ expirationTime: string;
+ signInProvider: null | string;
+ claims: {
+ [key: string]: any;
+ };
+ };
+
interface User {
/**
* The user's display name (if available).
@@ -701,8 +814,19 @@
*/
getIdToken(forceRefresh?: boolean): Promise<string>;
- getToken(forceRefresh?: boolean): Promise<string>;
+ /**
+ * Returns a firebase.auth.IdTokenResult object which contains the ID token JWT string and
+ * other helper properties for getting different data associated with the token as well as
+ * all the decoded payload claims.
+ *
+ * @param forceRefresh boolean Force refresh regardless of token expiration.
+ */
+ getIdTokenResult(forceRefresh?: boolean): Promise<IdTokenResult>;
+ /**
+ * @deprecated
+ * @param credential
+ */
linkAndRetrieveDataWithCredential(
credential: AuthCredential
): Promise<UserCredential>;
@@ -710,8 +834,12 @@
/**
* Link the user with a 3rd party credential provider.
*/
- linkWithCredential(credential: AuthCredential): Promise<User>;
+ linkWithCredential(credential: AuthCredential): Promise<UserCredential>;
+ /**
+ * @deprecated
+ * @param credential
+ */
reauthenticateAndRetrieveDataWithCredential(
credential: AuthCredential
): Promise<UserCredential>;
@@ -719,7 +847,9 @@
/**
* Re-authenticate a user with a third-party authentication provider
*/
- reauthenticateWithCredential(credential: AuthCredential): Promise<void>;
+ reauthenticateWithCredential(
+ credential: AuthCredential
+ ): Promise<UserCredential>;
/**
* Refreshes the current user.
@@ -753,6 +883,13 @@
updatePassword(password: string): Promise<void>;
/**
+ * Updates the user's phone number.
+ * See Firebase docs for more information on security & email validation.
+ * This will Promise reject is the user is anonymous.
+ */
+ updatePhoneNumber(credential: AuthCredential): Promise<void>;
+
+ /**
* Updates a user's profile data.
* Profile data should be an object of fields to update:
*/
@@ -777,7 +914,12 @@
email?: string;
fromEmail?: string;
};
- operation: 'PASSWORD_RESET' | 'VERIFY_EMAIL' | 'RECOVER_EMAIL';
+ operation:
+ | 'PASSWORD_RESET'
+ | 'VERIFY_EMAIL'
+ | 'RECOVER_EMAIL'
+ | 'EMAIL_SIGNIN'
+ | 'ERROR';
}
interface ConfirmationResult {
@@ -786,11 +928,18 @@
verificationId: string | null;
}
+ interface NativeError extends Error {
+ code: string;
+ message: string;
+ nativeErrorCode?: string;
+ nativeErrorMessage?: string;
+ }
+
type PhoneAuthSnapshot = {
state: 'sent' | 'timeout' | 'verified' | 'error';
verificationId: string;
code: string | null;
- error: Error | null;
+ error: NativeError | null;
};
type PhoneAuthError = {
@@ -821,7 +970,7 @@
type AuthProvider = {
PROVIDER_ID: string;
- credential: (token: string, secret?: string) => AuthCredential;
+ credential: (token: string | null, secret?: string) => AuthCredential;
};
type EmailAuthProvider = {
@@ -835,53 +984,93 @@
) => AuthCredential;
};
+ interface AuthSettings {
+ /**
+ * Flag to determine whether app verification should be disabled for testing or not.
+ *
+ * @platform iOS
+ * @param disabled
+ */
+ appVerificationDisabledForTesting: boolean;
+
+ /**
+ * The phone number and SMS code here must have been configured in the
+ * Firebase Console (Authentication > Sign In Method > Phone).
+ *
+ * Calling this method a second time will overwrite the previously passed parameters.
+ * Only one number can be configured at a given time.
+ *
+ * @platform Android
+ * @param phoneNumber
+ * @param smsCode
+ * @return {*}
+ */
+ setAutoRetrievedSmsCodeForPhoneNumber(
+ phoneNumber: string,
+ smsCode: string
+ ): Promise<null>;
+ }
+
+ type OrNull<T> = T | null;
+ type AuthListenerCallback = (user: OrNull<User>) => void;
+
interface Auth {
readonly app: App;
/**
* Returns the current Firebase authentication state.
*/
- authResult: AuthResult | null;
+ authResult: OrNull<AuthResult>;
/**
* Returns the currently signed-in user (or null). See the User class documentation for further usage.
*/
- currentUser: User | null;
+ currentUser: OrNull<User>;
/**
* Gets/Sets the language for the app instance
*/
- languageCode: string | null;
+ languageCode: OrNull<string>;
+
+ settings: AuthSettings;
/**
* Listen for changes in the users auth state (logging in and out).
* This method returns a unsubscribe function to stop listening to events.
* Always ensure you unsubscribe from the listener when no longer needed to prevent updates to components no longer in use.
*/
- onAuthStateChanged(listener: Function): () => void;
+ onAuthStateChanged(listener: AuthListenerCallback): () => void;
/**
* Listen for changes in id token.
* This method returns a unsubscribe function to stop listening to events.
* Always ensure you unsubscribe from the listener when no longer needed to prevent updates to components no longer in use.
*/
- onIdTokenChanged(listener: Function): () => void;
+ onIdTokenChanged(listener: AuthListenerCallback): () => void;
/**
* Listen for changes in the user.
* This method returns a unsubscribe function to stop listening to events.
* Always ensure you unsubscribe from the listener when no longer needed to prevent updates to components no longer in use.
*/
- onUserChanged(listener: Function): () => void;
+ onUserChanged(listener: AuthListenerCallback): () => void;
signOut(): Promise<void>;
+ /**
+ * @deprecated
+ */
signInAnonymouslyAndRetrieveData(): Promise<UserCredential>;
/**
* Sign an anonymous user.
* If the user has already signed in, that user will be returned
*/
- signInAnonymously(): Promise<User>;
+ signInAnonymously(): Promise<UserCredential>;
+ /**
+ * @deprecated
+ * @param email
+ * @param password
+ */
createUserAndRetrieveDataWithEmailAndPassword(
email: string,
password: string
@@ -901,8 +1090,13 @@
createUserWithEmailAndPassword(
email: string,
password: string
- ): Promise<User>;
+ ): Promise<UserCredential>;
+ /**
+ * @deprecated
+ * @param email
+ * @param password
+ */
signInAndRetrieveDataWithEmailAndPassword(
email: string,
password: string
@@ -915,8 +1109,12 @@
signInWithEmailAndPassword(
email: string,
password: string
- ): Promise<User>;
+ ): Promise<UserCredential>;
+ /**
+ * @deprecated
+ * @param token
+ */
signInAndRetrieveDataWithCustomToken(
token: string
): Promise<UserCredential>;
@@ -927,8 +1125,12 @@
* use the signInWithCustomToken() function.
* It accepts one parameter, the custom token:
*/
- signInWithCustomToken(token: string): Promise<User>;
+ signInWithCustomToken(token: string): Promise<UserCredential>;
+ /**
+ * @deprecated
+ * @param credential
+ */
signInAndRetrieveDataWithCredential(
credential: AuthCredential
): Promise<UserCredential>;
@@ -937,12 +1139,17 @@
* Sign in the user with a 3rd party credential provider.
* credential requires the following properties:
*/
- signInWithCredential(credential: AuthCredential): Promise<User>;
+ signInWithCredential(
+ credential: AuthCredential
+ ): Promise<UserCredential>;
/**
* Asynchronously signs in using a phone number.
*/
- signInWithPhoneNumber(phoneNumber: string, forceResend?: boolean): Promise<ConfirmationResult>;
+ signInWithPhoneNumber(
+ phoneNumber: string,
+ forceResend?: boolean
+ ): Promise<ConfirmationResult>;
/**
* Returns a PhoneAuthListener to listen to phone verification events,
@@ -952,7 +1159,7 @@
verifyPhoneNumber(
phoneNumber: string,
autoVerifyTimeoutOrForceResend?: number | boolean,
- forceResend?: boolean,
+ forceResend?: boolean
): PhoneAuthListener;
/**
@@ -986,9 +1193,9 @@
checkActionCode(code: string): Promise<ActionCodeInfo>;
/**
- * Returns a list of authentication providers that can be used to sign in a given user (identified by its main email address).
+ * Returns a list of authentication methods that can be used to sign in a given user (identified by its main email address).
*/
- fetchProvidersForEmail(email: string): Promise<Array<string>>;
+ fetchSignInMethodsForEmail(email: string): Promise<Array<string>>;
verifyPasswordResetCode(code: string): Promise<string>;
@@ -1015,10 +1222,18 @@
namespace messaging {
interface Messaging {
/**
+ * Returns firebase.messaging.IOSMessaging that gets the
+ * iOS specific methods and properties of messaging.
+ */
+ ios: IOSMessaging;
+
+ /**
* Returns the devices FCM token.
*/
getToken(): Promise<string>;
+ deleteToken(authorizedEntity?: string, scope?: string): Promise<void>;
+
/**
* On a new message,
* the payload object is passed to the listener callback.
@@ -1035,7 +1250,7 @@
/**
* Requests app notification permissions in an Alert dialog.
*/
- requestPermission(): Promise<boolean>;
+ requestPermission(): Promise<void>;
/**
* Checks if the app has notification permissions.
@@ -1053,7 +1268,7 @@
subscribeToTopic(topic: string): void;
/**
- * Unsubscribes the device from a topic.
+ * Unsubscribe the device from a topic.
*/
unsubscribeFromTopic(topic: string): void;
}
@@ -1071,13 +1286,30 @@
constructor();
setCollapseKey(collapseKey: string): RemoteMessage;
+
setData(data: Object): RemoteMessage;
+
setMessageId(messageId: string): RemoteMessage;
+
setMessageType(messageType: string): RemoteMessage;
+
setTo(to: string): RemoteMessage;
+
setTtl(ttl: number): RemoteMessage;
}
+ class IOSMessaging {
+ /**
+ * Returns the devices APNS token.
+ */
+ getAPNSToken(): Promise<string | null>;
+
+ /**
+ * Register for iOS remote notifications
+ */
+ registerForRemoteNotifications(): Promise<void>;
+ }
+
interface MessagingStatics {
RemoteMessage: typeof RemoteMessage;
}
@@ -1086,16 +1318,46 @@
namespace iid {
interface InstanceId {
delete(): Promise<void>;
+
get(): Promise<string>;
+
getToken(authorizedEntity?: string, scope?: string): Promise<string>;
+
deleteToken(authorizedEntity?: string, scope?: string): Promise<void>;
}
}
namespace notifications {
+ interface NativeAndroidChannel {
+ bypassDnd?: boolean;
+ channelId: string;
+ description?: string;
+ group?: string;
+ importance: number;
+ lightColor?: string;
+ lightsEnabled?: boolean;
+ lockScreenVisibility?: number;
+ name: string;
+ showBadge?: boolean;
+ sound?: string;
+ vibrationEnabled?: boolean;
+ vibrationPattern?: number[];
+ }
+
+ interface NativeAndroidChannelGroup {
+ name: string;
+ groupId: string;
+ // Android API >= 28
+ description: string | void;
+ // Android API >= 28
+ channels: void | NativeAndroidChannel[];
+ }
+
interface AndroidNotifications {
createChannel(channel: Android.Channel): Promise<void>;
+
createChannelGroup(channelGroup: Android.ChannelGroup): Promise<void>;
+
createChannelGroups(
channelGroups: Android.ChannelGroup[]
): Promise<void>;
@@ -1099,11 +1361,34 @@
createChannelGroups(
channelGroups: Android.ChannelGroup[]
): Promise<void>;
+
createChannels(channels: Android.Channel[]): Promise<void>;
+
deleteChannelGroup(groupId: string): Promise<void>;
+
deleteChannel(channelId: string): Promise<void>;
+
+ getChannel(channelId: string): Promise<NativeAndroidChannel | null>;
+
+ getChannels(channelId: string): Promise<NativeAndroidChannel[]>;
+
+ getChannelGroup(
+ channelId: string
+ ): Promise<NativeAndroidChannelGroup | null>;
+
+ getChannelGroups(
+ channelId: string
+ ): Promise<NativeAndroidChannelGroup[]>;
}
+ type BackgroundFetchResultValue = string;
+ type CompletionHandler = (
+ backgroundFetchResult: BackgroundFetchResultValue
+ ) => void;
+ type Schedule = {
+ fireDate: number;
+ repeatInterval?: 'minute' | 'hour' | 'day' | 'week';
+ };
interface Notifications {
android: AndroidNotifications;
@@ -1147,7 +1432,10 @@
/**
* Schedule a local notification to be shown on the device.
*/
- scheduleNotification(notification: Notification, schedule: any): any;
+ scheduleNotification(
+ notification: Notification,
+ schedule: Schedule
+ ): any;
/**
* Sets the badge number on the iOS app icon.
@@ -1433,11 +1776,12 @@
class IOSNotification {
alertAction?: string;
attachments: IOSAttachment[];
- badge?: string;
+ badge?: number;
category?: string;
hasAction?: boolean;
launchImage?: string;
threadIdentifier?: string;
+ complete?: CompletionHandler;
addAttachment(
identifier: string,
@@ -1444,11 +1788,17 @@
url: string,
options: IOSAttachmentOptions
): Notification;
+
setAlertAction(alertAction: string): Notification;
- setBadge(badge: string): Notification;
+
+ setBadge(badge: number): Notification;
+
setCategory(category: string): Notification;
+
setHasAction(hasAction: boolean): Notification;
+
setLaunchImage(launchImage: string): Notification;
+
setThreadIdentifier(threadIdentifier: string): Notification;
}
@@ -1631,7 +1982,7 @@
/**
* Return an object of key-value attributes.
*/
- getAttributes(): Promise<Object>
+ getAttributes(): Promise<Object>;
/**
* Set an attribute. Returns true if it was set, false if it was not.
@@ -1716,6 +2067,11 @@
* Set the user ID to show alongside any subsequent crash reports.
*/
setUserIdentifier(userId: string): void;
+
+ /**
+ * Enable Crashlytics reporting. Only avaliable when disabled automatic initialization
+ */
+ enableCrashlyticsCollection(): void;
}
}
@@ -1723,8 +2079,13 @@
interface Links {
/** Creates a standard dynamic link. */
createDynamicLink(dynamicLink: DynamicLink): Promise<string>;
+
/** Creates a short dynamic link. */
- createShortDynamicLink(type: 'SHORT' | 'UNGUESSABLE'): Promise<string>;
+ createShortDynamicLink(
+ dynamicLink: DynamicLink,
+ type: 'SHORT' | 'UNGUESSABLE'
+ ): Promise<string>;
+
/**
* Returns the URL that the app has been launched from. If the app was
* not launched from a URL the return value will be null.
@@ -1862,17 +2240,17 @@
/**
* An HttpsCallableResult wraps a single result from a function call.
*/
- interface HttpsCallableResult {
- readonly data: any;
+ interface HttpsCallableResult<R = any> {
+ readonly data: R;
}
/**
* An HttpsCallable is a reference to a "callable" http trigger in
* Google Cloud Functions.
*/
- interface HttpsCallable {
- (data?: any): Promise<HttpsCallableResult>;
- }
+ type HttpsCallable<Params, Result> = Params extends void
+ ? () => Promise<HttpsCallableResult<Result>>
+ : (data: Params) => Promise<HttpsCallableResult<Result>>;
/**
* `FirebaseFunctions` represents a Functions app, and is the entry point for
@@ -1886,7 +2264,22 @@
* @param name The name of the https callable function.
* @return The `HttpsCallable` instance.
*/
- httpsCallable(name: string): HttpsCallable;
+ httpsCallable<Params = any, Result = any>(
+ name: string
+ ): HttpsCallable<Params, Result>;
+
+ /**
+ * Changes this instance to point to a Cloud Functions emulator running
+ * locally.
+ *
+ * See https://firebase.google.com/docs/functions/local-emulator
+ *
+ * @param origin the origin string of the local emulator started via firebase tools
+ * "http://10.0.0.8:1337".
+ */
+ useFunctionsEmulator(origin: string): Promise<null>;
+
+ [key: string]: any;
}
/**
@@ -1932,7 +2333,9 @@
FieldPath: typeof FieldPath;
FieldValue: typeof FieldValue;
GeoPoint: typeof GeoPoint;
+ Timestamp: typeof Timestamp;
enableLogging(enabled: boolean): void;
+
setLogLevel(logLevel: 'debug' | 'error' | 'silent'): void;
}
@@ -1940,14 +2343,26 @@
readonly firestore: Firestore;
readonly id: string;
readonly parent: DocumentReference;
+ readonly path: string;
+
add(data: object): Promise<DocumentReference>;
+
doc(documentPath?: string): DocumentReference;
+
endAt(snapshot: DocumentSnapshot): Query;
+
endAt(...varargs: any[]): Query;
+
endBefore(snapshot: DocumentSnapshot): Query;
+
endBefore(...varargs: any[]): Query;
+
get(options?: Types.GetOptions): Promise<QuerySnapshot>;
+
limit(limit: number): Query;
+
+ isEqual(otherCollectionReference: CollectionReference): boolean;
+
onSnapshot(
onNext: Query.ObserverOnNext,
onError?: Query.ObserverOnError
@@ -1989,9 +2413,15 @@
readonly id: string | null;
readonly parent: CollectionReference;
readonly path: string;
+
collection(collectionPath: string): CollectionReference;
+
delete(): Promise<void>;
+
+ isEqual(otherDocumentReference: DocumentReference): boolean;
+
get(options?: Types.GetOptions): Promise<DocumentSnapshot>;
+
onSnapshot(
onNext: DocumentReference.ObserverOnNext,
onError?: DocumentReference.ObserverOnError
@@ -2046,9 +2486,11 @@
val5: any
): Promise<void>;
}
+
namespace DocumentReference {
type ObserverOnNext = (documentSnapshot: DocumentSnapshot) => void;
- type ObserverOnError = (err: object) => void;
+ type ObserverOnError = (err: Query.SnapshotError) => void;
+
interface Observer {
next: ObserverOnNext;
error?: ObserverOnError;
@@ -2066,10 +2510,27 @@
class Blob {
static fromBase64String(base64: string): Blob;
+
static fromUint8Array(array: Uint8Array): Blob;
+
isEqual(other: Blob): boolean;
+
toBase64(): string;
- toUint8Array: Uint8Array;
+
+ toUint8Array(): Uint8Array;
+ }
+
+ class Timestamp {
+ readonly seconds: number;
+ readonly nanoseconds: number;
+ static now(): Timestamp;
+ static fromDate(date: Date): Timestamp;
+ static fromMillis(milliseconds: number): Timestamp;
+ constructor(seconds: number, nanoseconds: number);
+ toDate(): Date;
+ toMillis(): number;
+ isEqual(other: Timestamp): boolean;
+ toString(): string;
}
class FieldPath {
@@ -2074,12 +2535,22 @@
class FieldPath {
static documentId(): FieldPath;
+
constructor(...segments: string[]);
}
+ type AnyJs = null | undefined | boolean | number | string | object;
+
class FieldValue {
static delete(): FieldValue;
+
static serverTimestamp(): FieldValue;
+
+ static increment(n: number): FieldValue;
+
+ static arrayUnion(...elements: AnyJs[]): FieldValue;
+
+ static arrayRemove(...elements: AnyJs[]): FieldValue;
}
class GeoPoint {
@@ -2167,8 +2660,21 @@
startAt?: any[];
}
+ interface NativeError extends Error {
+ code: string;
+ message: string;
+ nativeErrorCode?: string;
+ nativeErrorMessage?: string;
+ }
+
+ interface SnapshotError extends NativeError {
+ path: string;
+ appName: string;
+ }
+
type ObserverOnNext = (querySnapshot: QuerySnapshot) => void;
- type ObserverOnError = (err: object) => void;
+ type ObserverOnError = (err: SnapshotError) => void;
+
interface Observer {
next: ObserverOnNext;
error?: ObserverOnError;
@@ -2335,7 +2859,14 @@
}
type QueryDirection = 'asc' | 'ASC' | 'desc' | 'DESC';
- type QueryOperator = '=' | '==' | '>' | '>=' | '<' | '<=';
+ type QueryOperator =
+ | '='
+ | '=='
+ | '>'
+ | '>='
+ | '<'
+ | '<='
+ | 'array-contains';
interface TypeMap {
type:
@@ -2367,3 +2898,115 @@
}
}
}
+
+declare module 'react-native-firebase/storage' {
+ import { RNFirebase } from 'react-native-firebase';
+ export type Storage = RNFirebase.storage.Storage;
+ export type Reference = RNFirebase.storage.Reference;
+ export type FullMetadata = RNFirebase.storage.FullMetadata;
+ export type SettableMetadata = RNFirebase.storage.SettableMetadata;
+ export type StorageTask<T> = RNFirebase.storage.StorageTask<T>;
+ export type UploadTaskSnapshot = RNFirebase.storage.UploadTaskSnapshot;
+ export type DownloadTaskSnapshot = RNFirebase.storage.DownloadTaskSnapshot;
+ export type TaskEvent = RNFirebase.storage.TaskEvent;
+ export type TaskState = RNFirebase.storage.TaskState;
+}
+
+declare module 'react-native-firebase/database' {
+ import { RNFirebase } from 'react-native-firebase';
+ export type Database = RNFirebase.database.Database;
+ export type RnReference = RNFirebase.database.RnReference;
+ export type QueryEventType = RNFirebase.database.QueryEventType;
+ export type QuerySuccessCallback = RNFirebase.database.QuerySuccessCallback;
+ export type QueryErrorCallback = RNFirebase.database.QueryErrorCallback;
+ export type Query = RNFirebase.database.Query;
+ export type DataSnapshot = RNFirebase.database.DataSnapshot;
+ export type Reference = RNFirebase.database.Reference;
+ export type DatabaseStatics = RNFirebase.database.DatabaseStatics;
+
+ interface ThenableReference<T> extends Promise<T> {}
+
+ interface ThenableReference<T> extends RNFirebase.database.Reference {}
+}
+
+declare module 'react-native-firebase/auth' {
+ import { RNFirebase } from 'react-native-firebase';
+ export type AuthResult = RNFirebase.auth.AuthResult;
+ export type AuthProvider = RNFirebase.auth.AuthProvider;
+ export type Auth = RNFirebase.auth.Auth;
+ export type AuthStatics = RNFirebase.auth.AuthStatics;
+}
+
+declare module 'react-native-firebase/messaging' {
+ import { RNFirebase } from 'react-native-firebase';
+ export type Messaging = RNFirebase.messaging.Messaging;
+ export type RemoteMessage = RNFirebase.messaging.RemoteMessage;
+}
+
+declare module 'react-native-firebase/iid' {
+ import { RNFirebase } from 'react-native-firebase';
+ export type InstanceId = RNFirebase.iid.InstanceId;
+}
+
+declare module 'react-native-firebase/notifications' {
+ import { RNFirebase } from 'react-native-firebase';
+ export type AndroidNotifications = RNFirebase.notifications.AndroidNotifications;
+ export type Notifications = RNFirebase.notifications.Notifications;
+ export type Notification = RNFirebase.notifications.Notification;
+ export type NotificationOpen = RNFirebase.notifications.NotificationOpen;
+ export type AndroidNotification = RNFirebase.notifications.AndroidNotification;
+ export type IOSNotification = RNFirebase.notifications.IOSNotification;
+ export type IOSAttachment = RNFirebase.notifications.IOSAttachment;
+ export type IOSAttachmentOptions = RNFirebase.notifications.IOSAttachmentOptions;
+}
+
+declare module 'react-native-firebase/config' {
+ import { RNFirebase } from 'react-native-firebase';
+ export type ConfigSnapshot = RNFirebase.config.ConfigSnapshot;
+ export type Config = RNFirebase.config.Config;
+}
+
+declare module 'react-native-firebase/crashlytics' {
+ import { RNFirebase } from 'react-native-firebase';
+ export type Crashlytics = RNFirebase.crashlytics.Crashlytics;
+}
+
+declare module 'react-native-firebase/links' {
+ import { RNFirebase } from 'react-native-firebase';
+ export type Links = RNFirebase.links.Links;
+ export type DynamicLink = RNFirebase.links.DynamicLink;
+ export type AnalyticsParameters = RNFirebase.links.AnalyticsParameters;
+ export type AndroidParameters = RNFirebase.links.AndroidParameters;
+ export type IOSParameters = RNFirebase.links.IOSParameters;
+ export type ITunesParameters = RNFirebase.links.ITunesParameters;
+ export type NavigationParameters = RNFirebase.links.NavigationParameters;
+ export type SocialParameters = RNFirebase.links.SocialParameters;
+}
+
+declare module 'react-native-firebase/functions' {
+ import { RNFirebase } from 'react-native-firebase';
+ export type HttpsErrorCode = RNFirebase.functions.HttpsErrorCode;
+ export type FunctionsErrorCode = RNFirebase.functions.FunctionsErrorCode;
+ export type HttpsCallableResult = RNFirebase.functions.HttpsCallableResult;
+ export type Functions = RNFirebase.functions.Functions;
+ export type HttpsError = RNFirebase.functions.HttpsError;
+}
+
+declare module 'react-native-firebase/firestore' {
+ import { RNFirebase } from 'react-native-firebase';
+ export type Firestore = RNFirebase.firestore.Firestore;
+ export type FirestoreStatics = RNFirebase.firestore.FirestoreStatics;
+ export type CollectionReference = RNFirebase.firestore.CollectionReference;
+ export type DocumentChange = RNFirebase.firestore.DocumentChange;
+ export type DocumentReference = RNFirebase.firestore.DocumentReference;
+ export type DocumentSnapshot = RNFirebase.firestore.DocumentSnapshot;
+ export type FieldPath = RNFirebase.firestore.FieldPath;
+ export type Timestamp = RNFirebase.firestore.Timestamp;
+ export type FieldValue = RNFirebase.firestore.FieldValue;
+ export type GeoPoint = RNFirebase.firestore.GeoPoint;
+ export type Path = RNFirebase.firestore.Path;
+ export type Query = RNFirebase.firestore.Query;
+ export type SnapshotError = RNFirebase.firestore.Query.SnapshotError;
+ export type QuerySnapshot = RNFirebase.firestore.QuerySnapshot;
+ export type WriteBatch = RNFirebase.firestore.WriteBatch;
+}

dist/index.js

@@ -1,5 +1,6 @@
import firebase from './modules/core/firebase';
export default firebase;
+export * from './modules/core/firebase';
/*
* Export App types
*/
\ No newline at end of file

dist/index.js.flow

@@ -4,6 +4,7 @@
import firebase from './modules/core/firebase';
export default firebase;
+export * from './modules/core/firebase';
/*
* Export App types
@@ -42,7 +43,7 @@
MetadataChanges,
SetOptions,
SnapshotMetadata,
-} from './modules/firestore/types';
+} from './modules/firestore/firestoreTypes.flow';
export type {
default as CollectionReference,
} from './modules/firestore/CollectionReference';

dist/modules/admob/AdMobComponent.js

@@ -1,5 +1,5 @@
import React from 'react';
-import { ViewPropTypes, requireNativeComponent } from 'react-native';
+import { ViewPropTypes, requireNativeComponent, Platform } from 'react-native';
import PropTypes from 'prop-types';
import EventTypes, { NativeExpressEventTypes } from './EventTypes';
import { nativeToJSError } from '../../utils';
@@ -75,7 +75,13 @@
}
}
- if (nativeEvent.type === 'onSizeChange') this.updateSize(nativeEvent.payload);
+ if (nativeEvent.type === 'onSizeChange') {
+ this.updateSize(nativeEvent.payload);
+ }
+
+ if (nativeEvent.type === 'onAdLoaded' && Platform.OS === 'ios') {
+ this.updateSize(nativeEvent.payload);
+ }
};
/**
* Set the JS size of the loaded banner
@@ -87,10 +93,12 @@
width,
height
}) => {
+ if (width !== undefined && height !== undefined) {
this.setState({
width,
height
});
+ }
};
/**
* Render the native component

dist/modules/admob/AdMobComponent.js.flow

@@ -1,5 +1,5 @@
import React from 'react';
-import { ViewPropTypes, requireNativeComponent } from 'react-native';
+import { ViewPropTypes, requireNativeComponent, Platform } from 'react-native';
import PropTypes from 'prop-types';
import EventTypes, { NativeExpressEventTypes } from './EventTypes';
import { nativeToJSError } from '../../utils';
@@ -70,8 +70,13 @@
}
}
- if (nativeEvent.type === 'onSizeChange')
+ if (nativeEvent.type === 'onSizeChange') {
this.updateSize(nativeEvent.payload);
+ }
+
+ if (nativeEvent.type === 'onAdLoaded' && Platform.OS === 'ios') {
+ this.updateSize(nativeEvent.payload);
+ }
};
/**
@@ -80,7 +85,9 @@
* @param height
*/
updateSize = ({ width, height }) => {
+ if (width !== undefined && height !== undefined) {
this.setState({ width, height });
+ }
};
/**

dist/modules/admob/index.js

@@ -21,8 +21,8 @@
super(app, {
events: NATIVE_EVENTS,
moduleName: MODULE_NAME,
- multiApp: false,
- hasShards: false,
+ hasMultiAppSupport: false,
+ hasCustomUrlSupport: false,
namespace: NAMESPACE
});
this._initialized = false;

dist/modules/admob/index.js.flow

@@ -41,8 +41,8 @@
super(app, {
events: NATIVE_EVENTS,
moduleName: MODULE_NAME,
- multiApp: false,
- hasShards: false,
+ hasMultiAppSupport: false,
+ hasCustomUrlSupport: false,
namespace: NAMESPACE,
});

dist/modules/admob/Interstitial.js

@@ -1,5 +1,5 @@
import { Platform } from 'react-native';
-import { statics } from '.';
+import { statics } from './';
import AdRequest from './AdRequest';
import { SharedEventEmitter } from '../../utils/events';
import { getNativeModule } from '../../utils/native';

dist/modules/admob/Interstitial.js.flow

@@ -1,10 +1,10 @@
import { Platform } from 'react-native';
-import { statics } from '.';
+import { statics } from './';
import AdRequest from './AdRequest';
import { SharedEventEmitter } from '../../utils/events';
import { getNativeModule } from '../../utils/native';
import { nativeToJSError } from '../../utils';
-import type AdMob from '.';
+import type AdMob from './';
let subscriptions = [];

dist/modules/admob/RewardedVideo.js

@@ -1,4 +1,4 @@
-import { statics } from '.';
+import { statics } from './';
import AdRequest from './AdRequest';
import { SharedEventEmitter } from '../../utils/events';
import { getNativeModule } from '../../utils/native';

dist/modules/admob/RewardedVideo.js.flow

@@ -1,9 +1,9 @@
-import { statics } from '.';
+import { statics } from './';
import AdRequest from './AdRequest';
import { SharedEventEmitter } from '../../utils/events';
import { getNativeModule } from '../../utils/native';
import { nativeToJSError } from '../../utils';
-import type AdMob from '.';
+import type AdMob from './';
let subscriptions = [];

dist/modules/analytics/index.js

@@ -13,8 +13,8 @@
constructor(app) {
super(app, {
moduleName: MODULE_NAME,
- multiApp: false,
- hasShards: false,
+ hasMultiAppSupport: false,
+ hasCustomUrlSupport: false,
namespace: NAMESPACE
});
}

dist/modules/analytics/index.js.flow

@@ -33,8 +33,8 @@
constructor(app: App) {
super(app, {
moduleName: MODULE_NAME,
- multiApp: false,
- hasShards: false,
+ hasMultiAppSupport: false,
+ hasCustomUrlSupport: false,
namespace: NAMESPACE,
});
}
@@ -99,7 +99,7 @@
* @param screenName
* @param screenClassOverride
*/
- setCurrentScreen(screenName: string, screenClassOverride: string): void {
+ setCurrentScreen(screenName: string, screenClassOverride?: string): void {
getNativeModule(this).setCurrentScreen(screenName, screenClassOverride);
}

dist/modules/auth/AuthSettings.js

@@ -0,0 +1,60 @@
+import { getNativeModule } from '../../utils/native';
+import { isAndroid, isIOS } from '../../utils';
+
+/**
+ * Interface representing an Auth instance's settings, currently used
+ * for enabling/disabling app verification for phone Auth testing. *
+ */
+export default class AuthSettings {
+ constructor(auth) {
+ this._auth = auth;
+ this._appVerificationDisabledForTesting = false;
+ }
+ /**
+ * Flag to determine whether app verification should be disabled for testing or not.
+ *
+ * @platform iOS
+ * @return {boolean}
+ */
+
+
+ get appVerificationDisabledForTesting() {
+ return this._appVerificationDisabledForTesting;
+ }
+ /**
+ * Flag to determine whether app verification should be disabled for testing or not.
+ *
+ * @platform iOS
+ * @param disabled
+ */
+
+
+ set appVerificationDisabledForTesting(disabled) {
+ if (isIOS) {
+ this._appVerificationDisabledForTesting = disabled;
+ getNativeModule(this._auth).setAppVerificationDisabledForTesting(disabled);
+ }
+ }
+ /**
+ * The phone number and SMS code here must have been configured in the
+ * Firebase Console (Authentication > Sign In Method > Phone).
+ *
+ * Calling this method a second time will overwrite the previously passed parameters.
+ * Only one number can be configured at a given time.
+ *
+ * @platform Android
+ * @param phoneNumber
+ * @param smsCode
+ * @return {*}
+ */
+
+
+ setAutoRetrievedSmsCodeForPhoneNumber(phoneNumber, smsCode) {
+ if (isAndroid) {
+ return getNativeModule(this._auth).setAutoRetrievedSmsCodeForPhoneNumber(phoneNumber, smsCode);
+ }
+
+ return Promise.resolve(null);
+ }
+
+}
\ No newline at end of file

dist/modules/auth/AuthSettings.js.flow

@@ -0,0 +1,70 @@
+import { getNativeModule } from '../../utils/native';
+import { isAndroid, isIOS } from '../../utils';
+
+import type Auth from './';
+
+/**
+ * Interface representing an Auth instance's settings, currently used
+ * for enabling/disabling app verification for phone Auth testing. *
+ */
+export default class AuthSettings {
+ _auth: Auth;
+
+ _appVerificationDisabledForTesting: boolean;
+
+ constructor(auth: Auth) {
+ this._auth = auth;
+ this._appVerificationDisabledForTesting = false;
+ }
+
+ /**
+ * Flag to determine whether app verification should be disabled for testing or not.
+ *
+ * @platform iOS
+ * @return {boolean}
+ */
+ get appVerificationDisabledForTesting(): boolean {
+ return this._appVerificationDisabledForTesting;
+ }
+
+ /**
+ * Flag to determine whether app verification should be disabled for testing or not.
+ *
+ * @platform iOS
+ * @param disabled
+ */
+ set appVerificationDisabledForTesting(disabled: boolean) {
+ if (isIOS) {
+ this._appVerificationDisabledForTesting = disabled;
+ getNativeModule(this._auth).setAppVerificationDisabledForTesting(
+ disabled
+ );
+ }
+ }
+
+ /**
+ * The phone number and SMS code here must have been configured in the
+ * Firebase Console (Authentication > Sign In Method > Phone).
+ *
+ * Calling this method a second time will overwrite the previously passed parameters.
+ * Only one number can be configured at a given time.
+ *
+ * @platform Android
+ * @param phoneNumber
+ * @param smsCode
+ * @return {*}
+ */
+ setAutoRetrievedSmsCodeForPhoneNumber(
+ phoneNumber: string,
+ smsCode: string
+ ): Promise<null> {
+ if (isAndroid) {
+ return getNativeModule(this._auth).setAutoRetrievedSmsCodeForPhoneNumber(
+ phoneNumber,
+ smsCode
+ );
+ }
+
+ return Promise.resolve(null);
+ }
+}

dist/modules/auth/index.js

@@ -10,7 +10,8 @@
import { getNativeModule } from '../../utils/native';
import INTERNALS from '../../utils/internals';
import ConfirmationResult from './phone/ConfirmationResult';
-import PhoneAuthListener from './phone/PhoneAuthListener'; // providers
+import PhoneAuthListener from './phone/PhoneAuthListener';
+import AuthSettings from './AuthSettings'; // providers
import EmailAuthProvider from './providers/EmailAuthProvider';
import PhoneAuthProvider from './providers/PhoneAuthProvider';
@@ -27,13 +28,20 @@
super(app, {
events: NATIVE_EVENTS,
moduleName: MODULE_NAME,
- multiApp: true,
- hasShards: false,
+ hasMultiAppSupport: true,
+ hasCustomUrlSupport: false,
namespace: NAMESPACE
});
+ const NativeModule = getNativeModule(this);
this._user = null;
+ this._settings = null;
this._authResult = false;
- this._languageCode = getNativeModule(this).APP_LANGUAGE[app._name] || getNativeModule(this).APP_LANGUAGE['[DEFAULT]'];
+ this._languageCode = NativeModule.APP_LANGUAGE[app._name] || NativeModule.APP_LANGUAGE['[DEFAULT]'];
+
+ if (NativeModule.APP_USER[app._name]) {
+ this._setUser(NativeModule.APP_USER[app._name]);
+ }
+
SharedEventEmitter.addListener( // sub to internal native event - this fans out to
// public event name: onAuthStateChanged
getAppEventName(this, 'auth_state_changed'), state => {
@@ -54,8 +62,8 @@
SharedEventEmitter.emit(getAppEventName(this, 'onIdTokenChanged'), this._user);
});
- getNativeModule(this).addAuthStateListener();
- getNativeModule(this).addIdTokenListener();
+ NativeModule.addAuthStateListener();
+ NativeModule.addIdTokenListener();
}
_setUser(user) {
@@ -137,27 +145,29 @@
}
/**
* Sign a user in anonymously
- * @deprecated Deprecated signInAnonymously in favor of signInAnonymouslyAndRetrieveData.
+ *
* @return {Promise} A promise resolved upon completion
*/
signInAnonymously() {
- console.warn('Deprecated firebase.User.prototype.signInAnonymously in favor of firebase.User.prototype.signInAnonymouslyAndRetrieveData.');
- return getNativeModule(this).signInAnonymously().then(user => this._setUser(user));
+ return getNativeModule(this).signInAnonymously().then(userCredential => this._setUserCredential(userCredential));
}
/**
* Sign a user in anonymously
+ *
+ * @deprecated Deprecated signInAnonymouslyAndRetrieveData in favor of signInAnonymously.
* @return {Promise} A promise resolved upon completion
*/
signInAnonymouslyAndRetrieveData() {
- return getNativeModule(this).signInAnonymouslyAndRetrieveData().then(userCredential => this._setUserCredential(userCredential));
+ console.warn('Deprecated signInAnonymouslyAndRetrieveData in favor of signInAnonymously.');
+ return getNativeModule(this).signInAnonymously().then(userCredential => this._setUserCredential(userCredential));
}
/**
* Create a user with the email/password functionality
- * @deprecated Deprecated createUserWithEmailAndPassword in favor of createUserAndRetrieveDataWithEmailAndPassword.
+ *
* @param {string} email The user's email
* @param {string} password The user's password
* @return {Promise} A promise indicating the completion
@@ -165,11 +175,12 @@
createUserWithEmailAndPassword(email, password) {
- console.warn('Deprecated firebase.User.prototype.createUserWithEmailAndPassword in favor of firebase.User.prototype.createUserAndRetrieveDataWithEmailAndPassword.');
- return getNativeModule(this).createUserWithEmailAndPassword(email, password).then(user => this._setUser(user));
+ return getNativeModule(this).createUserWithEmailAndPassword(email, password).then(userCredential => this._setUserCredential(userCredential));
}
/**
* Create a user with the email/password functionality
+ *
+ * @deprecated Deprecated createUserAndRetrieveDataWithEmailAndPassword in favor of createUserWithEmailAndPassword.
* @param {string} email The user's email
* @param {string} password The user's password
* @return {Promise} A promise indicating the completion
@@ -177,11 +188,12 @@
createUserAndRetrieveDataWithEmailAndPassword(email, password) {
- return getNativeModule(this).createUserAndRetrieveDataWithEmailAndPassword(email, password).then(userCredential => this._setUserCredential(userCredential));
+ console.warn('Deprecated createUserAndRetrieveDataWithEmailAndPassword in favor of createUserWithEmailAndPassword.');
+ return getNativeModule(this).createUserWithEmailAndPassword(email, password).then(userCredential => this._setUserCredential(userCredential));
}
/**
* Sign a user in with email/password
- * @deprecated Deprecated signInWithEmailAndPassword in favor of signInAndRetrieveDataWithEmailAndPassword
+ *
* @param {string} email The user's email
* @param {string} password The user's password
* @return {Promise} A promise that is resolved upon completion
@@ -189,11 +201,12 @@
signInWithEmailAndPassword(email, password) {
- console.warn('Deprecated firebase.User.prototype.signInWithEmailAndPassword in favor of firebase.User.prototype.signInAndRetrieveDataWithEmailAndPassword.');
- return getNativeModule(this).signInWithEmailAndPassword(email, password).then(user => this._setUser(user));
+ return getNativeModule(this).signInWithEmailAndPassword(email, password).then(userCredential => this._setUserCredential(userCredential));
}
/**
* Sign a user in with email/password
+ *
+ * @deprecated Deprecated signInAndRetrieveDataWithEmailAndPassword in favor of signInWithEmailAndPassword
* @param {string} email The user's email
* @param {string} password The user's password
* @return {Promise} A promise that is resolved upon completion
@@ -201,49 +214,54 @@
signInAndRetrieveDataWithEmailAndPassword(email, password) {
- return getNativeModule(this).signInAndRetrieveDataWithEmailAndPassword(email, password).then(userCredential => this._setUserCredential(userCredential));
+ console.warn('Deprecated signInAndRetrieveDataWithEmailAndPassword in favor of signInWithEmailAndPassword.');
+ return getNativeModule(this).signInWithEmailAndPassword(email, password).then(userCredential => this._setUserCredential(userCredential));
}
/**
* Sign the user in with a custom auth token
- * @deprecated Deprecated signInWithCustomToken in favor of signInAndRetrieveDataWithCustomToken
+ *
* @param {string} customToken A self-signed custom auth token.
* @return {Promise} A promise resolved upon completion
*/
signInWithCustomToken(customToken) {
- console.warn('Deprecated firebase.User.prototype.signInWithCustomToken in favor of firebase.User.prototype.signInAndRetrieveDataWithCustomToken.');
- return getNativeModule(this).signInWithCustomToken(customToken).then(user => this._setUser(user));
+ return getNativeModule(this).signInWithCustomToken(customToken).then(userCredential => this._setUserCredential(userCredential));
}
/**
* Sign the user in with a custom auth token
+ *
+ * @deprecated Deprecated signInAndRetrieveDataWithCustomToken in favor of signInWithCustomToken
* @param {string} customToken A self-signed custom auth token.
* @return {Promise} A promise resolved upon completion
*/
signInAndRetrieveDataWithCustomToken(customToken) {
- return getNativeModule(this).signInAndRetrieveDataWithCustomToken(customToken).then(userCredential => this._setUserCredential(userCredential));
+ console.warn('Deprecated signInAndRetrieveDataWithCustomToken in favor of signInWithCustomToken.');
+ return getNativeModule(this).signInWithCustomToken(customToken).then(userCredential => this._setUserCredential(userCredential));
}
/**
* Sign the user in with a third-party authentication provider
- * @deprecated Deprecated signInWithCredential in favor of signInAndRetrieveDataWithCredential.
+ *
* @return {Promise} A promise resolved upon completion
*/
signInWithCredential(credential) {
- console.warn('Deprecated firebase.User.prototype.signInWithCredential in favor of firebase.User.prototype.signInAndRetrieveDataWithCredential.');
- return getNativeModule(this).signInWithCredential(credential.providerId, credential.token, credential.secret).then(user => this._setUser(user));
+ return getNativeModule(this).signInWithCredential(credential.providerId, credential.token, credential.secret).then(userCredential => this._setUserCredential(userCredential));
}
/**
* Sign the user in with a third-party authentication provider
+ *
+ * @deprecated Deprecated signInAndRetrieveDataWithCredential in favor of signInWithCredential.
* @return {Promise} A promise resolved upon completion
*/
signInAndRetrieveDataWithCredential(credential) {
- return getNativeModule(this).signInAndRetrieveDataWithCredential(credential.providerId, credential.token, credential.secret).then(userCredential => this._setUserCredential(userCredential));
+ console.warn('Deprecated signInAndRetrieveDataWithCredential in favor of signInWithCredential.');
+ return getNativeModule(this).signInWithCredential(credential.providerId, credential.token, credential.secret).then(userCredential => this._setUserCredential(userCredential));
}
/**
* Asynchronously signs in using a phone number.
@@ -346,7 +364,9 @@
applyActionCode(code) {
- return getNativeModule(this).applyActionCode(code);
+ return getNativeModule(this).applyActionCode(code).then(user => {
+ this._setUser(user);
+ });
}
/**
* Checks a verification code sent to the user by email or other out-of-band mechanism.
@@ -361,17 +381,6 @@
return getNativeModule(this).checkActionCode(code);
}
/**
- * Returns a list of authentication providers that can be used to sign in a given user (identified by its main email address).
- * @return {Promise}
- * @Deprecated
- */
-
-
- fetchProvidersForEmail(email) {
- console.warn('Deprecated firebase.auth().fetchProvidersForEmail in favor of firebase.auth().fetchSignInMethodsForEmail()');
- return getNativeModule(this).fetchSignInMethodsForEmail(email);
- }
- /**
* Returns a list of authentication methods that can be used to sign in a given user (identified by its main email address).
* @return {Promise}
*/
@@ -385,9 +394,9 @@
return getNativeModule(this).verifyPasswordResetCode(code);
}
/**
- * Sets the language for the auth module
+ * Sets the language for the auth module.
+ *
* @param code
- * @returns {*}
*/
@@ -396,6 +405,32 @@
getNativeModule(this).setLanguageCode(code);
}
/**
+ * The language for the auth module.
+ *
+ * @return {string}
+ */
+
+
+ get languageCode() {
+ return this._languageCode;
+ }
+ /**
+ * The current Auth instance's settings. This is used to edit/read configuration
+ * related options like app verification mode for phone authentication.
+ *
+ * @return {AuthSettings}
+ */
+
+
+ get settings() {
+ if (!this._settings) {
+ // lazy initialize
+ this._settings = new AuthSettings(this);
+ }
+
+ return this._settings;
+ }
+ /**
* Get the currently signed in user
* @return {Promise}
*/
@@ -404,10 +439,6 @@
get currentUser() {
return this._user;
}
-
- get languageCode() {
- return this._languageCode;
- }
/**
* KNOWN UNSUPPORTED METHODS
*/

dist/modules/auth/index.js.flow

@@ -11,6 +11,7 @@
import INTERNALS from '../../utils/internals';
import ConfirmationResult from './phone/ConfirmationResult';
import PhoneAuthListener from './phone/PhoneAuthListener';
+import AuthSettings from './AuthSettings';
// providers
import EmailAuthProvider from './providers/EmailAuthProvider';
@@ -49,21 +50,30 @@
_languageCode: string;
+ _settings: AuthSettings | null;
+
_user: User | null;
constructor(app: App) {
super(app, {
events: NATIVE_EVENTS,
moduleName: MODULE_NAME,
- multiApp: true,
- hasShards: false,
+ hasMultiAppSupport: true,
+ hasCustomUrlSupport: false,
namespace: NAMESPACE,
});
+ const NativeModule = getNativeModule(this);
+
this._user = null;
+ this._settings = null;
this._authResult = false;
this._languageCode =
- getNativeModule(this).APP_LANGUAGE[app._name] ||
- getNativeModule(this).APP_LANGUAGE['[DEFAULT]'];
+ NativeModule.APP_LANGUAGE[app._name] ||
+ NativeModule.APP_LANGUAGE['[DEFAULT]'];
+
+ if (NativeModule.APP_USER[app._name]) {
+ this._setUser(NativeModule.APP_USER[app._name]);
+ }
SharedEventEmitter.addListener(
// sub to internal native event - this fans out to
@@ -101,8 +111,8 @@
}
);
- getNativeModule(this).addAuthStateListener();
- getNativeModule(this).addIdTokenListener();
+ NativeModule.addAuthStateListener();
+ NativeModule.addIdTokenListener();
}
_setUser(user: ?NativeUser): ?User {
@@ -204,31 +214,33 @@
/**
* Sign a user in anonymously
- * @deprecated Deprecated signInAnonymously in favor of signInAnonymouslyAndRetrieveData.
+ *
* @return {Promise} A promise resolved upon completion
*/
- signInAnonymously(): Promise<User> {
- console.warn(
- 'Deprecated firebase.User.prototype.signInAnonymously in favor of firebase.User.prototype.signInAnonymouslyAndRetrieveData.'
- );
+ signInAnonymously(): Promise<UserCredential> {
return getNativeModule(this)
.signInAnonymously()
- .then(user => this._setUser(user));
+ .then(userCredential => this._setUserCredential(userCredential));
}
/**
* Sign a user in anonymously
+ *
+ * @deprecated Deprecated signInAnonymouslyAndRetrieveData in favor of signInAnonymously.
* @return {Promise} A promise resolved upon completion
*/
signInAnonymouslyAndRetrieveData(): Promise<UserCredential> {
+ console.warn(
+ 'Deprecated signInAnonymouslyAndRetrieveData in favor of signInAnonymously.'
+ );
return getNativeModule(this)
- .signInAnonymouslyAndRetrieveData()
+ .signInAnonymously()
.then(userCredential => this._setUserCredential(userCredential));
}
/**
* Create a user with the email/password functionality
- * @deprecated Deprecated createUserWithEmailAndPassword in favor of createUserAndRetrieveDataWithEmailAndPassword.
+ *
* @param {string} email The user's email
* @param {string} password The user's password
* @return {Promise} A promise indicating the completion
@@ -236,17 +248,16 @@
createUserWithEmailAndPassword(
email: string,
password: string
- ): Promise<User> {
- console.warn(
- 'Deprecated firebase.User.prototype.createUserWithEmailAndPassword in favor of firebase.User.prototype.createUserAndRetrieveDataWithEmailAndPassword.'
- );
+ ): Promise<UserCredential> {
return getNativeModule(this)
.createUserWithEmailAndPassword(email, password)
- .then(user => this._setUser(user));
+ .then(userCredential => this._setUserCredential(userCredential));
}
/**
* Create a user with the email/password functionality
+ *
+ * @deprecated Deprecated createUserAndRetrieveDataWithEmailAndPassword in favor of createUserWithEmailAndPassword.
* @param {string} email The user's email
* @param {string} password The user's password
* @return {Promise} A promise indicating the completion
@@ -255,29 +266,34 @@
email: string,
password: string
): Promise<UserCredential> {
+ console.warn(
+ 'Deprecated createUserAndRetrieveDataWithEmailAndPassword in favor of createUserWithEmailAndPassword.'
+ );
return getNativeModule(this)
- .createUserAndRetrieveDataWithEmailAndPassword(email, password)
+ .createUserWithEmailAndPassword(email, password)
.then(userCredential => this._setUserCredential(userCredential));
}
/**
* Sign a user in with email/password
- * @deprecated Deprecated signInWithEmailAndPassword in favor of signInAndRetrieveDataWithEmailAndPassword
+ *
* @param {string} email The user's email
* @param {string} password The user's password
* @return {Promise} A promise that is resolved upon completion
*/
- signInWithEmailAndPassword(email: string, password: string): Promise<User> {
- console.warn(
- 'Deprecated firebase.User.prototype.signInWithEmailAndPassword in favor of firebase.User.prototype.signInAndRetrieveDataWithEmailAndPassword.'
- );
+ signInWithEmailAndPassword(
+ email: string,
+ password: string
+ ): Promise<UserCredential> {
return getNativeModule(this)
.signInWithEmailAndPassword(email, password)
- .then(user => this._setUser(user));
+ .then(userCredential => this._setUserCredential(userCredential));
}
/**
* Sign a user in with email/password
+ *
+ * @deprecated Deprecated signInAndRetrieveDataWithEmailAndPassword in favor of signInWithEmailAndPassword
* @param {string} email The user's email
* @param {string} password The user's password
* @return {Promise} A promise that is resolved upon completion
@@ -286,66 +302,73 @@
email: string,
password: string
): Promise<UserCredential> {
+ console.warn(
+ 'Deprecated signInAndRetrieveDataWithEmailAndPassword in favor of signInWithEmailAndPassword.'
+ );
return getNativeModule(this)
- .signInAndRetrieveDataWithEmailAndPassword(email, password)
+ .signInWithEmailAndPassword(email, password)
.then(userCredential => this._setUserCredential(userCredential));
}
/**
* Sign the user in with a custom auth token
- * @deprecated Deprecated signInWithCustomToken in favor of signInAndRetrieveDataWithCustomToken
+ *
* @param {string} customToken A self-signed custom auth token.
* @return {Promise} A promise resolved upon completion
*/
- signInWithCustomToken(customToken: string): Promise<User> {
- console.warn(
- 'Deprecated firebase.User.prototype.signInWithCustomToken in favor of firebase.User.prototype.signInAndRetrieveDataWithCustomToken.'
- );
+ signInWithCustomToken(customToken: string): Promise<UserCredential> {
return getNativeModule(this)
.signInWithCustomToken(customToken)
- .then(user => this._setUser(user));
+ .then(userCredential => this._setUserCredential(userCredential));
}
/**
* Sign the user in with a custom auth token
+ *
+ * @deprecated Deprecated signInAndRetrieveDataWithCustomToken in favor of signInWithCustomToken
* @param {string} customToken A self-signed custom auth token.
* @return {Promise} A promise resolved upon completion
*/
signInAndRetrieveDataWithCustomToken(
customToken: string
): Promise<UserCredential> {
+ console.warn(
+ 'Deprecated signInAndRetrieveDataWithCustomToken in favor of signInWithCustomToken.'
+ );
return getNativeModule(this)
- .signInAndRetrieveDataWithCustomToken(customToken)
+ .signInWithCustomToken(customToken)
.then(userCredential => this._setUserCredential(userCredential));
}
/**
* Sign the user in with a third-party authentication provider
- * @deprecated Deprecated signInWithCredential in favor of signInAndRetrieveDataWithCredential.
+ *
* @return {Promise} A promise resolved upon completion
*/
- signInWithCredential(credential: AuthCredential): Promise<User> {
- console.warn(
- 'Deprecated firebase.User.prototype.signInWithCredential in favor of firebase.User.prototype.signInAndRetrieveDataWithCredential.'
- );
+ signInWithCredential(credential: AuthCredential): Promise<UserCredential> {
return getNativeModule(this)
.signInWithCredential(
credential.providerId,
credential.token,
credential.secret
)
- .then(user => this._setUser(user));
+ .then(userCredential => this._setUserCredential(userCredential));
}
/**
* Sign the user in with a third-party authentication provider
+ *
+ * @deprecated Deprecated signInAndRetrieveDataWithCredential in favor of signInWithCredential.
* @return {Promise} A promise resolved upon completion
*/
signInAndRetrieveDataWithCredential(
credential: AuthCredential
): Promise<UserCredential> {
+ console.warn(
+ 'Deprecated signInAndRetrieveDataWithCredential in favor of signInWithCredential.'
+ );
return getNativeModule(this)
- .signInAndRetrieveDataWithCredential(
+ .signInWithCredential(
credential.providerId,
credential.token,
credential.secret
@@ -483,7 +506,11 @@
* @return {Promise.<Null>}
*/
applyActionCode(code: string): Promise<void> {
- return getNativeModule(this).applyActionCode(code);
+ return getNativeModule(this)
+ .applyActionCode(code)
+ .then(user => {
+ this._setUser(user);
+ });
}
/**
@@ -498,18 +525,6 @@
}
/**
- * Returns a list of authentication providers that can be used to sign in a given user (identified by its main email address).
- * @return {Promise}
- * @Deprecated
- */
- fetchProvidersForEmail(email: string): Promise<string[]> {
- console.warn(
- 'Deprecated firebase.auth().fetchProvidersForEmail in favor of firebase.auth().fetchSignInMethodsForEmail()'
- );
- return getNativeModule(this).fetchSignInMethodsForEmail(email);
- }
-
- /**
* Returns a list of authentication methods that can be used to sign in a given user (identified by its main email address).
* @return {Promise}
*/
@@ -522,9 +537,9 @@
}
/**
- * Sets the language for the auth module
+ * Sets the language for the auth module.
+ *
* @param code
- * @returns {*}
*/
set languageCode(code: string) {
this._languageCode = code;
@@ -532,6 +547,29 @@
}
/**
+ * The language for the auth module.
+ *
+ * @return {string}
+ */
+ get languageCode(): string {
+ return this._languageCode;
+ }
+
+ /**
+ * The current Auth instance's settings. This is used to edit/read configuration
+ * related options like app verification mode for phone authentication.
+ *
+ * @return {AuthSettings}
+ */
+ get settings(): AuthSettings {
+ if (!this._settings) {
+ // lazy initialize
+ this._settings = new AuthSettings(this);
+ }
+ return this._settings;
+ }
+
+ /**
* Get the currently signed in user
* @return {Promise}
*/
@@ -539,10 +577,6 @@
return this._user;
}
- get languageCode(): string {
- return this._languageCode;
- }
-
/**
* KNOWN UNSUPPORTED METHODS
*/

dist/modules/auth/phone/PhoneAuthListener.js.flow

@@ -12,12 +12,13 @@
import { getNativeModule } from '../../../utils/native';
import type Auth from '..';
+import type { NativeErrorInterface } from '../../../common/commonTypes.flow';
type PhoneAuthSnapshot = {
state: 'sent' | 'timeout' | 'verified' | 'error',
verificationId: string,
code: string | null,
- error: Error | null,
+ error: NativeErrorInterface | null,
};
type PhoneAuthError = {

dist/modules/auth/types.js.flow

@@ -3,12 +3,28 @@
*/
import type User from './User';
+export type IdTokenResult = {
+ token: string,
+ authTime: string,
+ issuedAtTime: string,
+ expirationTime: string,
+ signInProvider: null | string,
+ claims: {
+ [key: string]: any,
+ },
+};
+
export type ActionCodeInfo = {
data: {
email?: string,
fromEmail?: string,
},
- operation: 'PASSWORD_RESET' | 'VERIFY_EMAIL' | 'RECOVER_EMAIL',
+ operation:
+ | 'PASSWORD_RESET'
+ | 'VERIFY_EMAIL'
+ | 'RECOVER_EMAIL'
+ | 'EMAIL_SIGNIN'
+ | 'ERROR',
};
export type ActionCodeSettings = {

dist/modules/auth/User.js

@@ -74,43 +74,45 @@
});
}
/**
- * get the token of current user
- * @return {Promise}
+ * Returns a JWT token used to identify the user to a Firebase service.
+ *
+ * @param forceRefresh boolean Force refresh regardless of token expiration.
+ * @return {Promise<string>}
*/
getIdToken(forceRefresh = false) {
- return getNativeModule(this._auth).getToken(forceRefresh);
+ return getNativeModule(this._auth).getIdToken(forceRefresh);
}
/**
- * get the token of current user
- * @deprecated Deprecated getToken in favor of getIdToken.
- * @return {Promise}
+ * Returns a IdTokenResult object which contains the ID token JWT string and other properties for getting
+ * data associated with the token and all the decoded payload claims.
+ *
+ * @param forceRefresh boolean Force refresh regardless of token expiration.
+ * @return {Promise<IdTokenResult>}
*/
- getToken(forceRefresh = false) {
- console.warn('Deprecated firebase.User.prototype.getToken in favor of firebase.User.prototype.getIdToken.');
- return getNativeModule(this._auth).getToken(forceRefresh);
+ getIdTokenResult(forceRefresh = false) {
+ return getNativeModule(this._auth).getIdTokenResult(forceRefresh);
}
/**
- * @deprecated Deprecated linkWithCredential in favor of linkAndRetrieveDataWithCredential.
* @param credential
*/
linkWithCredential(credential) {
- console.warn('Deprecated firebase.User.prototype.linkWithCredential in favor of firebase.User.prototype.linkAndRetrieveDataWithCredential.');
- return getNativeModule(this._auth).linkWithCredential(credential.providerId, credential.token, credential.secret).then(user => this._auth._setUser(user));
+ return getNativeModule(this._auth).linkWithCredential(credential.providerId, credential.token, credential.secret).then(userCredential => this._auth._setUserCredential(userCredential));
}
/**
- *
+ * @deprecated Deprecated linkAndRetrieveDataWithCredential in favor of linkWithCredential.
* @param credential
*/
linkAndRetrieveDataWithCredential(credential) {
- return getNativeModule(this._auth).linkAndRetrieveDataWithCredential(credential.providerId, credential.token, credential.secret).then(userCredential => this._auth._setUserCredential(userCredential));
+ console.warn('Deprecated linkAndRetrieveDataWithCredential in favor of linkWithCredential.');
+ return getNativeModule(this._auth).linkWithCredential(credential.providerId, credential.token, credential.secret).then(userCredential => this._auth._setUserCredential(userCredential));
}
/**
* Re-authenticate a user with a third-party authentication provider
@@ -119,19 +121,19 @@
reauthenticateWithCredential(credential) {
- console.warn('Deprecated firebase.User.prototype.reauthenticateWithCredential in favor of firebase.User.prototype.reauthenticateAndRetrieveDataWithCredential.');
- return getNativeModule(this._auth).reauthenticateWithCredential(credential.providerId, credential.token, credential.secret).then(user => {
- this._auth._setUser(user);
- });
+ return getNativeModule(this._auth).reauthenticateWithCredential(credential.providerId, credential.token, credential.secret).then(userCredential => this._auth._setUserCredential(userCredential));
}
/**
* Re-authenticate a user with a third-party authentication provider
+ *
+ * @deprecated Deprecated reauthenticateAndRetrieveDataWithCredential in favor of reauthenticateWithCredential.
* @return {Promise} A promise resolved upon completion
*/
reauthenticateAndRetrieveDataWithCredential(credential) {
- return getNativeModule(this._auth).reauthenticateAndRetrieveDataWithCredential(credential.providerId, credential.token, credential.secret).then(userCredential => this._auth._setUserCredential(userCredential));
+ console.warn('Deprecated reauthenticateAndRetrieveDataWithCredential in favor of reauthenticateWithCredential.');
+ return getNativeModule(this._auth).reauthenticateWithCredential(credential.providerId, credential.token, credential.secret).then(userCredential => this._auth._setUserCredential(userCredential));
}
/**
* Reload the current user
@@ -194,6 +196,19 @@
});
}
/**
+ * Update the current user's phone number
+ *
+ * @param {AuthCredential} credential Auth credential with the _new_ phone number
+ * @return {Promise}
+ */
+
+
+ updatePhoneNumber(credential) {
+ return getNativeModule(this._auth).updatePhoneNumber(credential.providerId, credential.token, credential.secret).then(user => {
+ this._auth._setUser(user);
+ });
+ }
+ /**
* Update the current user's profile
* @param {Object} updates An object containing the keys listed [here](https://firebase.google.com/docs/auth/ios/manage-users#update_a_users_profile)
* @return {Promise}
@@ -234,10 +249,6 @@
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD('User', 'reauthenticateWithRedirect'));
}
- updatePhoneNumber() {
- throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD('User', 'updatePhoneNumber'));
- }
-
get refreshToken() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_PROPERTY('User', 'refreshToken'));
}

dist/modules/auth/User.js.flow

@@ -5,7 +5,7 @@
import INTERNALS from '../../utils/internals';
import { getNativeModule } from '../../utils/native';
-import type Auth from '.';
+import type Auth from './';
import type {
ActionCodeSettings,
AuthCredential,
@@ -13,6 +13,7 @@
UserCredential,
UserInfo,
UserMetadata,
+ IdTokenResult,
} from './types';
type UpdateProfile = {
@@ -96,51 +97,51 @@
}
/**
- * get the token of current user
- * @return {Promise}
+ * Returns a JWT token used to identify the user to a Firebase service.
+ *
+ * @param forceRefresh boolean Force refresh regardless of token expiration.
+ * @return {Promise<string>}
*/
getIdToken(forceRefresh: boolean = false): Promise<string> {
- return getNativeModule(this._auth).getToken(forceRefresh);
+ return getNativeModule(this._auth).getIdToken(forceRefresh);
}
/**
- * get the token of current user
- * @deprecated Deprecated getToken in favor of getIdToken.
- * @return {Promise}
+ * Returns a IdTokenResult object which contains the ID token JWT string and other properties for getting
+ * data associated with the token and all the decoded payload claims.
+ *
+ * @param forceRefresh boolean Force refresh regardless of token expiration.
+ * @return {Promise<IdTokenResult>}
*/
- getToken(forceRefresh: boolean = false): Promise<Object> {
- console.warn(
- 'Deprecated firebase.User.prototype.getToken in favor of firebase.User.prototype.getIdToken.'
- );
- return getNativeModule(this._auth).getToken(forceRefresh);
+ getIdTokenResult(forceRefresh: boolean = false): Promise<IdTokenResult> {
+ return getNativeModule(this._auth).getIdTokenResult(forceRefresh);
}
/**
- * @deprecated Deprecated linkWithCredential in favor of linkAndRetrieveDataWithCredential.
* @param credential
*/
- linkWithCredential(credential: AuthCredential): Promise<User> {
- console.warn(
- 'Deprecated firebase.User.prototype.linkWithCredential in favor of firebase.User.prototype.linkAndRetrieveDataWithCredential.'
- );
+ linkWithCredential(credential: AuthCredential): Promise<UserCredential> {
return getNativeModule(this._auth)
.linkWithCredential(
credential.providerId,
credential.token,
credential.secret
)
- .then(user => this._auth._setUser(user));
+ .then(userCredential => this._auth._setUserCredential(userCredential));
}
/**
- *
+ * @deprecated Deprecated linkAndRetrieveDataWithCredential in favor of linkWithCredential.
* @param credential
*/
linkAndRetrieveDataWithCredential(
credential: AuthCredential
): Promise<UserCredential> {
+ console.warn(
+ 'Deprecated linkAndRetrieveDataWithCredential in favor of linkWithCredential.'
+ );
return getNativeModule(this._auth)
- .linkAndRetrieveDataWithCredential(
+ .linkWithCredential(
credential.providerId,
credential.token,
credential.secret
@@ -152,30 +153,32 @@
* Re-authenticate a user with a third-party authentication provider
* @return {Promise} A promise resolved upon completion
*/
- reauthenticateWithCredential(credential: AuthCredential): Promise<void> {
- console.warn(
- 'Deprecated firebase.User.prototype.reauthenticateWithCredential in favor of firebase.User.prototype.reauthenticateAndRetrieveDataWithCredential.'
- );
+ reauthenticateWithCredential(
+ credential: AuthCredential
+ ): Promise<UserCredential> {
return getNativeModule(this._auth)
.reauthenticateWithCredential(
credential.providerId,
credential.token,
credential.secret
)
- .then(user => {
- this._auth._setUser(user);
- });
+ .then(userCredential => this._auth._setUserCredential(userCredential));
}
/**
* Re-authenticate a user with a third-party authentication provider
+ *
+ * @deprecated Deprecated reauthenticateAndRetrieveDataWithCredential in favor of reauthenticateWithCredential.
* @return {Promise} A promise resolved upon completion
*/
reauthenticateAndRetrieveDataWithCredential(
credential: AuthCredential
): Promise<UserCredential> {
+ console.warn(
+ 'Deprecated reauthenticateAndRetrieveDataWithCredential in favor of reauthenticateWithCredential.'
+ );
return getNativeModule(this._auth)
- .reauthenticateAndRetrieveDataWithCredential(
+ .reauthenticateWithCredential(
credential.providerId,
credential.token,
credential.secret
@@ -251,6 +254,24 @@
}
/**
+ * Update the current user's phone number
+ *
+ * @param {AuthCredential} credential Auth credential with the _new_ phone number
+ * @return {Promise}
+ */
+ updatePhoneNumber(credential: AuthCredential): Promise<void> {
+ return getNativeModule(this._auth)
+ .updatePhoneNumber(
+ credential.providerId,
+ credential.token,
+ credential.secret
+ )
+ .then(user => {
+ this._auth._setUser(user);
+ });
+ }
+
+ /**
* Update the current user's profile
* @param {Object} updates An object containing the keys listed [here](https://firebase.google.com/docs/auth/ios/manage-users#update_a_users_profile)
* @return {Promise}
@@ -317,15 +338,6 @@
)
);
}
-
- updatePhoneNumber() {
- throw new Error(
- INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD(
- 'User',
- 'updatePhoneNumber'
- )
- );
- }
get refreshToken(): string {
throw new Error(

dist/modules/config/index.js

@@ -15,8 +15,8 @@
constructor(app) {
super(app, {
moduleName: MODULE_NAME,
- multiApp: false,
- hasShards: false,
+ hasMultiAppSupport: false,
+ hasCustomUrlSupport: false,
namespace: NAMESPACE
});
this._developerModeEnabled = false;

dist/modules/config/index.js.flow

@@ -36,8 +36,8 @@
constructor(app: App) {
super(app, {
moduleName: MODULE_NAME,
- multiApp: false,
- hasShards: false,
+ hasMultiAppSupport: false,
+ hasCustomUrlSupport: false,
namespace: NAMESPACE,
});
this._developerModeEnabled = false;

dist/modules/core/app.js

@@ -114,14 +114,11 @@
delete() {
- throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD('app', 'delete')); // TODO only the ios sdk currently supports delete, add back in when android also supports it
- // if (this._name === APPS.DEFAULT_APP_NAME && this._nativeInitialized) {
- // return Promise.reject(
- // new Error('Unable to delete the default native firebase app instance.'),
- // );
- // }
- //
- // return FirebaseCoreModule.deleteApp(this._name);
+ if (this._name === APPS.DEFAULT_APP_NAME && this._nativeInitialized) {
+ return Promise.reject(new Error('Unable to delete the default native firebase app instance.'));
+ }
+
+ return FirebaseCoreModule.deleteApp(this._name).then(() => APPS.deleteApp(this._name));
}
/**
*

dist/modules/core/app.js.flow

@@ -170,18 +170,16 @@
*
* @return {Promise}
*/
- delete() {
- throw new Error(
- INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD('app', 'delete')
+ delete(): Promise<void> {
+ if (this._name === APPS.DEFAULT_APP_NAME && this._nativeInitialized) {
+ return Promise.reject(
+ new Error('Unable to delete the default native firebase app instance.')
+ );
+ }
+
+ return FirebaseCoreModule.deleteApp(this._name).then(() =>
+ APPS.deleteApp(this._name)
);
- // TODO only the ios sdk currently supports delete, add back in when android also supports it
- // if (this._name === APPS.DEFAULT_APP_NAME && this._nativeInitialized) {
- // return Promise.reject(
- // new Error('Unable to delete the default native firebase app instance.'),
- // );
- // }
- //
- // return FirebaseCoreModule.deleteApp(this._name);
}
/**

dist/modules/core/firebase.js

@@ -94,4 +94,23 @@
}
-export default new Firebase();
\ No newline at end of file
+const firebaseApp = new Firebase();
+export default firebaseApp;
+export const {
+ admob,
+ analytics,
+ auth,
+ config,
+ crashlytics,
+ database,
+ firestore,
+ functions,
+ iid,
+ invites,
+ links,
+ messaging,
+ notifications,
+ perf,
+ storage,
+ utils
+} = firebaseApp;
\ No newline at end of file

dist/modules/core/firebase.js.flow

@@ -240,4 +240,23 @@
}
}
-export default new Firebase();
+const firebaseApp = new Firebase();
+export default firebaseApp;
+export const {
+ admob,
+ analytics,
+ auth,
+ config,
+ crashlytics,
+ database,
+ firestore,
+ functions,
+ iid,
+ invites,
+ links,
+ messaging,
+ notifications,
+ perf,
+ storage,
+ utils,
+} = firebaseApp;

dist/modules/crashlytics/index.js

@@ -10,8 +10,8 @@
constructor(app) {
super(app, {
moduleName: MODULE_NAME,
- multiApp: false,
- hasShards: false,
+ hasMultiAppSupport: false,
+ hasCustomUrlSupport: false,
namespace: NAMESPACE
});
}
@@ -82,6 +82,14 @@
setUserIdentifier(userId) {
getNativeModule(this).setUserIdentifier(userId);
}
+ /**
+ * Enable Crashlytics reporting. Only avaliable when disabled automatic initialization
+ */
+
+
+ enableCrashlyticsCollection() {
+ getNativeModule(this).enableCrashlyticsCollection();
+ }
}
export const statics = {};
\ No newline at end of file

dist/modules/crashlytics/index.js.flow

@@ -14,8 +14,8 @@
constructor(app: App) {
super(app, {
moduleName: MODULE_NAME,
- multiApp: false,
- hasShards: false,
+ hasMultiAppSupport: false,
+ hasCustomUrlSupport: false,
namespace: NAMESPACE,
});
}
@@ -78,6 +78,13 @@
setUserIdentifier(userId: string): void {
getNativeModule(this).setUserIdentifier(userId);
}
+
+ /**
+ * Enable Crashlytics reporting. Only avaliable when disabled automatic initialization
+ */
+ enableCrashlyticsCollection(): void {
+ getNativeModule(this).enableCrashlyticsCollection();
+ }
}
export const statics = {};

dist/modules/database/DataSnapshot.js

@@ -32,10 +32,35 @@
val() {
// clone via JSON stringify/parse - prevent modification of this._value
- if (isObject(this._value) || Array.isArray(this._value)) return JSON.parse(JSON.stringify(this._value));
+ if (isObject(this._value) || Array.isArray(this._value)) {
+ return JSON.parse(JSON.stringify(this._value));
+ }
+
return this._value;
}
/**
+ * Exports the entire contents of the DataSnapshot as a JavaScript object.
+ *
+ * The exportVal() method is similar to val(), except priority information is
+ * included (if available), making it suitable for backing up your data.
+ *
+ * @return {{'.value': *, '.priority': *}}
+ */
+
+
+ exportVal() {
+ let value = this._value;
+
+ if (isObject(this._value) || Array.isArray(this._value)) {
+ value = JSON.parse(JSON.stringify(this._value));
+ }
+
+ return {
+ '.value': value,
+ '.priority': this._priority
+ };
+ }
+ /**
* Gets another DataSnapshot for the location at the specified relative path.
* @param path
* @link https://firebase.google.com/docs/reference/js/firebase.database.DataSnapshot#forEach

dist/modules/database/DataSnapshot.js.flow

@@ -5,6 +5,11 @@
import { isObject, deepGet, deepExists } from '../../utils';
import type Reference from './Reference';
+type ExportedValue = {
+ '.value': any,
+ '.priority': string | number | null,
+};
+
/**
* @class DataSnapshot
* @link https://firebase.google.com/docs/reference/js/firebase.database.DataSnapshot
@@ -42,12 +47,35 @@
*/
val(): any {
// clone via JSON stringify/parse - prevent modification of this._value
- if (isObject(this._value) || Array.isArray(this._value))
+ if (isObject(this._value) || Array.isArray(this._value)) {
return JSON.parse(JSON.stringify(this._value));
+ }
+
return this._value;
}
/**
+ * Exports the entire contents of the DataSnapshot as a JavaScript object.
+ *
+ * The exportVal() method is similar to val(), except priority information is
+ * included (if available), making it suitable for backing up your data.
+ *
+ * @return {{'.value': *, '.priority': *}}
+ */
+ exportVal(): ExportedValue {
+ let value = this._value;
+
+ if (isObject(this._value) || Array.isArray(this._value)) {
+ value = JSON.parse(JSON.stringify(this._value));
+ }
+
+ return {
+ '.value': value,
+ '.priority': this._priority,
+ };
+ }
+
+ /**
* Gets another DataSnapshot for the location at the specified relative path.
* @param path
* @link https://firebase.google.com/docs/reference/js/firebase.database.DataSnapshot#forEach

dist/modules/database/index.js

@@ -16,31 +16,33 @@
*/
export default class Database extends ModuleBase {
- constructor(appOrUrl, options = {}) {
+ constructor(appOrCustomUrl, customUrl) {
let app;
- let serviceUrl;
+ let url;
- if (typeof appOrUrl === 'string') {
+ if (typeof appOrCustomUrl === 'string') {
app = firebase.app();
- serviceUrl = appOrUrl.endsWith('/') ? appOrUrl : `${appOrUrl}/`;
+ url = appOrCustomUrl;
} else {
- app = appOrUrl;
- serviceUrl = app.options.databaseURL;
- }
+ app = appOrCustomUrl;
+ url = customUrl || app.options.databaseURL;
+ } // enforce trailing slash
+
+ url = url.endsWith('/') ? url : `${url}/`;
super(app, {
events: NATIVE_EVENTS,
moduleName: MODULE_NAME,
- multiApp: true,
- hasShards: true,
+ hasMultiAppSupport: true,
+ hasCustomUrlSupport: true,
namespace: NAMESPACE
- }, serviceUrl);
+ }, url);
this._serverTimeOffset = 0;
- this._serviceUrl = serviceUrl;
+ this._databaseURL = url;
this._transactionHandler = new TransactionHandler(this);
- if (options.persistence) {
- getNativeModule(this).setPersistence(options.persistence);
+ if (app.options.persistence) {
+ getNativeModule(this).setPersistence(app.options.persistence);
} // server time listener
// setTimeout used to avoid setPersistence race conditions
// todo move this and persistence to native side, create a db configure() method natively perhaps?
@@ -57,7 +59,7 @@
}
/**
*
- * @return {number}
+ * @return {Date}
*/
@@ -97,7 +99,7 @@
get databaseUrl() {
- return this._serviceUrl;
+ return this._databaseURL;
}
}

dist/modules/database/index.js.flow

@@ -24,43 +24,47 @@
* @class Database
*/
export default class Database extends ModuleBase {
+ _databaseURL: string;
+
_offsetRef: Reference;
_serverTimeOffset: number;
_transactionHandler: TransactionHandler;
- _serviceUrl: string;
-
- constructor(appOrUrl: App | string, options: Object = {}) {
+ constructor(appOrCustomUrl: App | string, customUrl?: string) {
let app;
- let serviceUrl;
- if (typeof appOrUrl === 'string') {
+ let url;
+
+ if (typeof appOrCustomUrl === 'string') {
app = firebase.app();
- serviceUrl = appOrUrl.endsWith('/') ? appOrUrl : `${appOrUrl}/`;
+ url = appOrCustomUrl;
} else {
- app = appOrUrl;
- serviceUrl = app.options.databaseURL;
+ app = appOrCustomUrl;
+ url = customUrl || app.options.databaseURL;
}
+ // enforce trailing slash
+ url = url.endsWith('/') ? url : `${url}/`;
+
super(
app,
{
events: NATIVE_EVENTS,
moduleName: MODULE_NAME,
- multiApp: true,
- hasShards: true,
+ hasMultiAppSupport: true,
+ hasCustomUrlSupport: true,
namespace: NAMESPACE,
},
- serviceUrl
+ url
);
this._serverTimeOffset = 0;
- this._serviceUrl = serviceUrl;
+ this._databaseURL = url;
this._transactionHandler = new TransactionHandler(this);
- if (options.persistence) {
- getNativeModule(this).setPersistence(options.persistence);
+ if (app.options.persistence) {
+ getNativeModule(this).setPersistence(app.options.persistence);
}
// server time listener
@@ -77,9 +81,9 @@
/**
*
- * @return {number}
+ * @return {Date}
*/
- getServerTime(): number {
+ getServerTime(): Date {
return new Date(Date.now() + this._serverTimeOffset);
}
@@ -111,7 +115,7 @@
* @returns {string}
*/
get databaseUrl(): string {
- return this._serviceUrl;
+ return this._databaseURL;
}
}

dist/modules/database/OnDisconnect.js.flow

@@ -4,7 +4,7 @@
*/
import { typeOf } from '../../utils';
import { getNativeModule } from '../../utils/native';
-import type Database from '.';
+import type Database from './';
import type Reference from './Reference';
/**

dist/modules/database/Reference.js

@@ -56,7 +56,6 @@
export default class Reference extends ReferenceBase {
constructor(database, path, existingModifiers) {
super(path);
- this._promise = null;
this._refListeners = {};
this._database = database;
this._query = new Query(this, existingModifiers);
@@ -223,24 +222,25 @@
push(value, onComplete) {
- if (value === null || value === undefined) {
- return new Reference(this._database, `${this.path}/${generatePushID(this._database._serverTimeOffset)}`);
+ const name = generatePushID(this._database._serverTimeOffset);
+ const pushRef = this.child(name);
+ const thennablePushRef = this.child(name);
+ let promise;
+
+ if (value != null) {
+ promise = thennablePushRef.set(value, onComplete).then(() => pushRef);
+ } else {
+ promise = Promise.resolve(pushRef);
}
- const newRef = new Reference(this._database, `${this.path}/${generatePushID(this._database._serverTimeOffset)}`);
- const promise = newRef.set(value); // if callback provided then internally call the set promise with value
+ thennablePushRef.then = promise.then.bind(promise);
+ thennablePushRef.catch = promise.catch.bind(promise);
if (isFunction(onComplete)) {
- return promise // $FlowExpectedError: Reports that onComplete can change to null despite the null check: https://github.com/facebook/flow/issues/1655
- .then(() => onComplete(null, newRef)) // $FlowExpectedError: Reports that onComplete can change to null despite the null check: https://github.com/facebook/flow/issues/1655
- .catch(error => onComplete(error, null));
- } // otherwise attach promise to 'thenable' reference and return the
- // new reference
-
-
- newRef._setThenable(promise);
+ promise.catch(() => {});
+ }
- return newRef;
+ return thennablePushRef;
}
/**
* MODIFIERS
@@ -419,7 +419,16 @@
toString() {
- return `${this._database.databaseUrl}/${this.path}`;
+ return `${this._database.databaseUrl}${this.path}`;
+ }
+ /**
+ * Return a JSON-serializable representation of this object.
+ * @returns {string}
+ */
+
+
+ toJSON() {
+ return this.toString();
}
/**
* Returns whether another Reference represent the same location and are from the
@@ -473,46 +482,6 @@
return new Reference(this._database, '/');
}
/**
- * Access then method of promise if set
- * @return {*}
- */
-
-
- then(fnResolve, fnReject) {
- if (isFunction(fnResolve) && this._promise && this._promise.then) {
- return this._promise.then.bind(this._promise)(result => {
- this._promise = null;
- return fnResolve(result);
- }, possibleErr => {
- this._promise = null;
-
- if (isFunction(fnReject)) {
- return fnReject(possibleErr);
- }
-
- throw possibleErr;
- });
- }
-
- throw new Error("Cannot read property 'then' of undefined.");
- }
- /**
- * Access catch method of promise if set
- * @return {*}
- */
-
-
- catch(fnReject) {
- if (isFunction(fnReject) && this._promise && this._promise.catch) {
- return this._promise.catch.bind(this._promise)(possibleErr => {
- this._promise = null;
- return fnReject(possibleErr);
- });
- }
-
- throw new Error("Cannot read property 'catch' of undefined.");
- }
- /**
* INTERNALS
*/
@@ -539,16 +508,6 @@
return `$${this._database.databaseUrl}$/${this.path}$${this._query.queryIdentifier()}`;
}
/**
- * Set the promise this 'thenable' reference relates to
- * @param promise
- * @private
- */
-
-
- _setThenable(promise) {
- this._promise = promise;
- }
- /**
*
* @param obj
* @returns {Object}
@@ -685,7 +644,7 @@
key: registrationObj.key,
registrationCancellationKey
}
- }); // increment number of listeners - just s short way of making
+ }); // increment number of listeners - just a short way of making
// every registration unique per .on() call
listeners += 1; // return original unbound successCallback for
@@ -752,11 +711,4 @@
return SyncTree.removeListenersForRegistrations(registrations);
}
-} // eslint-disable-next-line no-unused-vars
-// class ThenableReference<+R> extends Reference {
-// then<U>(
-// onFulfill?: (value: R) => Promise<U> | U,
-// onReject?: (error: any) => Promise<U> | U
-// ): Promise<U>;
-// catch<U>(onReject?: (error: any) => Promise<U> | U): Promise<R | U>;
-// }
\ No newline at end of file
+}
\ No newline at end of file

dist/modules/database/Reference.js.flow

@@ -21,7 +21,7 @@
import SyncTree from '../../utils/SyncTree';
-import type Database from '.';
+import type Database from './';
import type { DatabaseModifier, FirebaseError } from '../../types';
// track all event registrations by path
@@ -77,19 +77,20 @@
export default class Reference extends ReferenceBase {
_database: Database;
- _promise: ?Promise<*>;
-
_query: Query;
_refListeners: { [listenerId: number]: DatabaseListener };
+ then: (a?: any) => Promise<any>;
+
+ catch: (a?: any) => Promise<any>;
+
constructor(
database: Database,
path: string,
existingModifiers?: Array<DatabaseModifier>
) {
super(path);
- this._promise = null;
this._refListeners = {};
this._database = database;
this._query = new Query(this, existingModifiers);
@@ -303,34 +304,26 @@
* @returns {*}
*/
push(value: any, onComplete?: Function): Reference | Promise<void> {
- if (value === null || value === undefined) {
- return new Reference(
- this._database,
- `${this.path}/${generatePushID(this._database._serverTimeOffset)}`
- );
+ const name = generatePushID(this._database._serverTimeOffset);
+
+ const pushRef = this.child(name);
+ const thennablePushRef = this.child(name);
+
+ let promise;
+ if (value != null) {
+ promise = thennablePushRef.set(value, onComplete).then(() => pushRef);
+ } else {
+ promise = Promise.resolve(pushRef);
}
- const newRef = new Reference(
- this._database,
- `${this.path}/${generatePushID(this._database._serverTimeOffset)}`
- );
- const promise = newRef.set(value);
+ thennablePushRef.then = promise.then.bind(promise);
+ thennablePushRef.catch = promise.catch.bind(promise);
- // if callback provided then internally call the set promise with value
if (isFunction(onComplete)) {
- return (
- promise
- // $FlowExpectedError: Reports that onComplete can change to null despite the null check: https://github.com/facebook/flow/issues/1655
- .then(() => onComplete(null, newRef))
- // $FlowExpectedError: Reports that onComplete can change to null despite the null check: https://github.com/facebook/flow/issues/1655
- .catch(error => onComplete(error, null))
- );
+ promise.catch(() => {});
}
- // otherwise attach promise to 'thenable' reference and return the
- // new reference
- newRef._setThenable(promise);
- return newRef;
+ return thennablePushRef;
}
/**
@@ -500,7 +493,15 @@
* @returns {string}
*/
toString(): string {
- return `${this._database.databaseUrl}/${this.path}`;
+ return `${this._database.databaseUrl}${this.path}`;
+ }
+
+ /**
+ * Return a JSON-serializable representation of this object.
+ * @returns {string}
+ */
+ toJSON(): string {
+ return this.toString();
}
/**
@@ -559,47 +560,6 @@
}
/**
- * Access then method of promise if set
- * @return {*}
- */
- then(fnResolve: any => any, fnReject: any => any) {
- if (isFunction(fnResolve) && this._promise && this._promise.then) {
- return this._promise.then.bind(this._promise)(
- result => {
- this._promise = null;
- return fnResolve(result);
- },
- possibleErr => {
- this._promise = null;
-
- if (isFunction(fnReject)) {
- return fnReject(possibleErr);
- }
-
- throw possibleErr;
- }
- );
- }
-
- throw new Error("Cannot read property 'then' of undefined.");
- }
-
- /**
- * Access catch method of promise if set
- * @return {*}
- */
- catch(fnReject: any => any) {
- if (isFunction(fnReject) && this._promise && this._promise.catch) {
- return this._promise.catch.bind(this._promise)(possibleErr => {
- this._promise = null;
- return fnReject(possibleErr);
- });
- }
-
- throw new Error("Cannot read property 'catch' of undefined.");
- }
-
- /**
* INTERNALS
*/
@@ -628,15 +588,6 @@
}
/**
- * Set the promise this 'thenable' reference relates to
- * @param promise
- * @private
- */
- _setThenable(promise: Promise<*>) {
- this._promise = promise;
- }
-
- /**
*
* @param obj
* @returns {Object}
@@ -804,7 +755,7 @@
},
});
- // increment number of listeners - just s short way of making
+ // increment number of listeners - just a short way of making
// every registration unique per .on() call
listeners += 1;
@@ -895,12 +846,3 @@
return SyncTree.removeListenersForRegistrations(registrations);
}
}
-
-// eslint-disable-next-line no-unused-vars
-// class ThenableReference<+R> extends Reference {
-// then<U>(
-// onFulfill?: (value: R) => Promise<U> | U,
-// onReject?: (error: any) => Promise<U> | U
-// ): Promise<U>;
-// catch<U>(onReject?: (error: any) => Promise<U> | U): Promise<R | U>;
-// }

dist/modules/database/transaction.js.flow

@@ -5,7 +5,7 @@
import { getAppEventName, SharedEventEmitter } from '../../utils/events';
import { getLogger } from '../../utils/log';
import { getNativeModule } from '../../utils/native';
-import type Database from '.';
+import type Database from './';
let transactionId = 0;

dist/modules/firestore/CollectionReference.js

@@ -2,8 +2,8 @@
*
* CollectionReference representation wrapper
*/
-import DocumentReference from './DocumentReference';
import Query from './Query';
+import DocumentReference from './DocumentReference';
import { firestoreAutoId } from '../../utils';
/**
@@ -30,6 +30,31 @@
return parentPath ? new DocumentReference(this._firestore, parentPath) : null;
}
+ get path() {
+ return this._collectionPath.relativeName;
+ }
+
+ isEqual(otherCollectionReference) {
+ if (!(otherCollectionReference instanceof CollectionReference)) {
+ throw new Error('firebase.firestore.CollectionReference.isEqual(*) expects an instance of CollectionReference.');
+ } // check paths match
+
+
+ if (this.path !== otherCollectionReference.path) return false; // check same firestore app name
+
+ if (this._firestore.app.name !== otherCollectionReference._firestore.app.name) {
+ return false;
+ } // check same firestore app projectId
+ // noinspection RedundantIfStatementJS
+
+
+ if (this._firestore.app.options.projectId !== otherCollectionReference._firestore.app.options.projectId) {
+ return false;
+ }
+
+ return true;
+ }
+
add(data) {
const documentRef = this.doc();
return documentRef.set(data).then(() => Promise.resolve(documentRef));

dist/modules/firestore/CollectionReference.js.flow

@@ -2,17 +2,17 @@
* @flow
* CollectionReference representation wrapper
*/
-import DocumentReference from './DocumentReference';
import Query from './Query';
+import DocumentReference from './DocumentReference';
import { firestoreAutoId } from '../../utils';
-import type Firestore from '.';
+import type Firestore from './';
import type {
GetOptions,
MetadataChanges,
QueryDirection,
QueryOperator,
-} from './types';
+} from './firestoreTypes.flow';
import type FieldPath from './FieldPath';
import type Path from './Path';
import type { Observer, ObserverOnError, ObserverOnNext } from './Query';
@@ -38,7 +38,7 @@
return this._firestore;
}
- get id(): string | null {
+ get id(): string {
return this._collectionPath.id;
}
@@ -49,6 +49,39 @@
: null;
}
+ get path(): string {
+ return this._collectionPath.relativeName;
+ }
+
+ isEqual(otherCollectionReference: CollectionReference) {
+ if (!(otherCollectionReference instanceof CollectionReference)) {
+ throw new Error(
+ 'firebase.firestore.CollectionReference.isEqual(*) expects an instance of CollectionReference.'
+ );
+ }
+
+ // check paths match
+ if (this.path !== otherCollectionReference.path) return false;
+
+ // check same firestore app name
+ if (
+ this._firestore.app.name !== otherCollectionReference._firestore.app.name
+ ) {
+ return false;
+ }
+
+ // check same firestore app projectId
+ // noinspection RedundantIfStatementJS
+ if (
+ this._firestore.app.options.projectId !==
+ otherCollectionReference._firestore.app.options.projectId
+ ) {
+ return false;
+ }
+
+ return true;
+ }
+
add(data: Object): Promise<DocumentReference> {
const documentRef = this.doc();
return documentRef.set(data).then(() => Promise.resolve(documentRef));

dist/modules/firestore/DocumentChange.js.flow

@@ -4,8 +4,8 @@
*/
import DocumentSnapshot from './DocumentSnapshot';
-import type Firestore from '.';
-import type { NativeDocumentChange } from './types';
+import type Firestore from './';
+import type { NativeDocumentChange } from './firestoreTypes.flow';
/**
* @class DocumentChange

dist/modules/firestore/DocumentReference.js

@@ -2,14 +2,14 @@
*
* DocumentReference representation wrapper
*/
-import CollectionReference from './CollectionReference';
+import SnapshotError from './SnapshotError';
import DocumentSnapshot from './DocumentSnapshot';
+import CollectionReference from './CollectionReference';
import { parseUpdateArgs } from './utils';
import { buildNativeMap } from './utils/serialize';
-import { getAppEventName, SharedEventEmitter } from '../../utils/events';
-import { getLogger } from '../../utils/log';
-import { firestoreAutoId, isFunction, isObject } from '../../utils';
import { getNativeModule } from '../../utils/native';
+import { firestoreAutoId, isFunction, isObject } from '../../utils';
+import { getAppEventName, SharedEventEmitter } from '../../utils/events';
/**
* @class DocumentReference
@@ -39,6 +39,27 @@
return this._documentPath.relativeName;
}
+ isEqual(otherDocumentReference) {
+ if (!(otherDocumentReference instanceof DocumentReference)) {
+ throw new Error('firebase.firestore.DocumentReference.isEqual(*) expects an instance of DocumentReference.');
+ } // check paths match
+
+
+ if (this.path !== otherDocumentReference.path) return false; // check same firestore app name
+
+ if (this._firestore.app.name !== otherDocumentReference._firestore.app.name) {
+ return false;
+ } // check same firestore app projectId
+ // noinspection RedundantIfStatementJS
+
+
+ if (this._firestore.app.options.projectId !== otherDocumentReference._firestore.app.options.projectId) {
+ return false;
+ }
+
+ return true;
+ }
+
collection(collectionPath) {
const path = this._documentPath.child(collectionPath);
@@ -141,16 +162,25 @@
}; // Listen to snapshot events
- SharedEventEmitter.addListener(getAppEventName(this._firestore, `onDocumentSnapshot:${listenerId}`), listener); // Listen for snapshot error events
-
- if (observer.error) {
- SharedEventEmitter.addListener(getAppEventName(this._firestore, `onDocumentSnapshotError:${listenerId}`), observer.error);
- } // Add the native listener
+ const snapshotSubscription = SharedEventEmitter.addListener(getAppEventName(this._firestore, `onDocumentSnapshot:${listenerId}`), listener);
+ let unsubscribe; // listen for snapshot error events
+ const errorSubscription = SharedEventEmitter.addListener(getAppEventName(this._firestore, `onDocumentSnapshotError:${listenerId}`), e => {
+ if (unsubscribe) unsubscribe();
+ const error = new SnapshotError(e);
+ if (observer.error) observer.error(error);else this.firestore.log.error(error);
+ }); // Add the native listener
+
+ getNativeModule(this._firestore).documentOnSnapshot(this.path, listenerId, docListenOptions); // return an unsubscribe method
+
+ unsubscribe = () => {
+ snapshotSubscription.remove();
+ errorSubscription.remove(); // cancel native listener
- getNativeModule(this._firestore).documentOnSnapshot(this.path, listenerId, docListenOptions); // Return an unsubscribe method
+ getNativeModule(this._firestore).documentOffSnapshot(this.path, listenerId);
+ };
- return this._offDocumentSnapshot.bind(this, listenerId, listener);
+ return unsubscribe;
}
set(data, options) {
@@ -163,21 +193,5 @@
const nativeData = buildNativeMap(data);
return getNativeModule(this._firestore).documentUpdate(this.path, nativeData);
}
- /**
- * INTERNALS
- */
-
- /**
- * Remove document snapshot listener
- * @param listener
- */
-
-
- _offDocumentSnapshot(listenerId, listener) {
- getLogger(this._firestore).info('Removing onDocumentSnapshot listener');
- SharedEventEmitter.removeListener(getAppEventName(this._firestore, `onDocumentSnapshot:${listenerId}`), listener);
- SharedEventEmitter.removeListener(getAppEventName(this._firestore, `onDocumentSnapshotError:${listenerId}`), listener);
- getNativeModule(this._firestore).documentOffSnapshot(this.path, listenerId);
- }
}
\ No newline at end of file

dist/modules/firestore/DocumentReference.js.flow

@@ -2,25 +2,26 @@
* @flow
* DocumentReference representation wrapper
*/
-import CollectionReference from './CollectionReference';
+import SnapshotError from './SnapshotError';
import DocumentSnapshot from './DocumentSnapshot';
+import CollectionReference from './CollectionReference';
import { parseUpdateArgs } from './utils';
import { buildNativeMap } from './utils/serialize';
-import { getAppEventName, SharedEventEmitter } from '../../utils/events';
-import { getLogger } from '../../utils/log';
-import { firestoreAutoId, isFunction, isObject } from '../../utils';
import { getNativeModule } from '../../utils/native';
+import { firestoreAutoId, isFunction, isObject } from '../../utils';
+import { getAppEventName, SharedEventEmitter } from '../../utils/events';
-import type Firestore from '.';
+import type Firestore from './';
import type {
GetOptions,
MetadataChanges,
NativeDocumentSnapshot,
SetOptions,
-} from './types';
+} from './firestoreTypes.flow';
import type Path from './Path';
+import type { NativeErrorResponse } from '../../common/commonTypes.flow';
-type ObserverOnError = Object => void;
+type ObserverOnError = SnapshotError => void;
type ObserverOnNext = DocumentSnapshot => void;
type Observer = {
@@ -45,7 +46,7 @@
return this._firestore;
}
- get id(): string | null {
+ get id(): string {
return this._documentPath.id;
}
@@ -59,6 +60,35 @@
return this._documentPath.relativeName;
}
+ isEqual(otherDocumentReference: DocumentReference) {
+ if (!(otherDocumentReference instanceof DocumentReference)) {
+ throw new Error(
+ 'firebase.firestore.DocumentReference.isEqual(*) expects an instance of DocumentReference.'
+ );
+ }
+
+ // check paths match
+ if (this.path !== otherDocumentReference.path) return false;
+
+ // check same firestore app name
+ if (
+ this._firestore.app.name !== otherDocumentReference._firestore.app.name
+ ) {
+ return false;
+ }
+
+ // check same firestore app projectId
+ // noinspection RedundantIfStatementJS
+ if (
+ this._firestore.app.options.projectId !==
+ otherDocumentReference._firestore.app.options.projectId
+ ) {
+ return false;
+ }
+
+ return true;
+ }
+
collection(collectionPath: string): CollectionReference {
const path = this._documentPath.child(collectionPath);
if (!path.isCollection) {
@@ -212,21 +242,23 @@
};
// Listen to snapshot events
- SharedEventEmitter.addListener(
+ const snapshotSubscription = SharedEventEmitter.addListener(
getAppEventName(this._firestore, `onDocumentSnapshot:${listenerId}`),
listener
);
- // Listen for snapshot error events
- if (observer.error) {
- SharedEventEmitter.addListener(
- getAppEventName(
- this._firestore,
- `onDocumentSnapshotError:${listenerId}`
- ),
- observer.error
- );
+ let unsubscribe: () => void;
+
+ // listen for snapshot error events
+ const errorSubscription = SharedEventEmitter.addListener(
+ getAppEventName(this._firestore, `onDocumentSnapshotError:${listenerId}`),
+ (e: NativeErrorResponse) => {
+ if (unsubscribe) unsubscribe();
+ const error = new SnapshotError(e);
+ if (observer.error) observer.error(error);
+ else this.firestore.log.error(error);
}
+ );
// Add the native listener
getNativeModule(this._firestore).documentOnSnapshot(
@@ -235,8 +267,18 @@
docListenOptions
);
- // Return an unsubscribe method
- return this._offDocumentSnapshot.bind(this, listenerId, listener);
+ // return an unsubscribe method
+ unsubscribe = () => {
+ snapshotSubscription.remove();
+ errorSubscription.remove();
+ // cancel native listener
+ getNativeModule(this._firestore).documentOffSnapshot(
+ this.path,
+ listenerId
+ );
+ };
+
+ return unsubscribe;
}
set(data: Object, options?: SetOptions): Promise<void> {
@@ -256,25 +298,4 @@
nativeData
);
}
-
- /**
- * INTERNALS
- */
-
- /**
- * Remove document snapshot listener
- * @param listener
- */
- _offDocumentSnapshot(listenerId: string, listener: Function) {
- getLogger(this._firestore).info('Removing onDocumentSnapshot listener');
- SharedEventEmitter.removeListener(
- getAppEventName(this._firestore, `onDocumentSnapshot:${listenerId}`),
- listener
- );
- SharedEventEmitter.removeListener(
- getAppEventName(this._firestore, `onDocumentSnapshotError:${listenerId}`),
- listener
- );
- getNativeModule(this._firestore).documentOffSnapshot(this.path, listenerId);
- }
}

dist/modules/firestore/DocumentSnapshot.js.flow

@@ -8,8 +8,11 @@
import { isObject, deepGet } from '../../utils';
import { parseNativeMap } from './utils/serialize';
-import type Firestore from '.';
-import type { NativeDocumentSnapshot, SnapshotMetadata } from './types';
+import type Firestore from './';
+import type {
+ NativeDocumentSnapshot,
+ SnapshotMetadata,
+} from './firestoreTypes.flow';
const extractFieldPathData = (data: Object | void, segments: string[]): any => {
if (!data || !isObject(data)) {
@@ -45,7 +48,7 @@
return this._data !== undefined;
}
- get id(): string | null {
+ get id(): string {
return this._ref.id;
}

dist/modules/firestore/FieldValue.js

@@ -2,15 +2,52 @@
*
* FieldValue representation wrapper
*/
+import AnyJs from './utils/any';
+import { buildNativeArray } from './utils/serialize'; // TODO: Salakar: Refactor in v6
+
export default class FieldValue {
+ constructor(type, elements) {
+ this._type = type;
+ this._elements = elements;
+ }
+
+ get type() {
+ return this._type;
+ }
+
+ get elements() {
+ return this._elements;
+ }
+
static delete() {
- return DELETE_FIELD_VALUE;
+ return new FieldValue(TypeFieldValueDelete);
+ }
+
+ static increment(n) {
+ return new FieldValue(TypeFieldValueIncrement, n);
}
static serverTimestamp() {
- return SERVER_TIMESTAMP_FIELD_VALUE;
+ return new FieldValue(TypeFieldValueTimestamp);
+ }
+
+ static arrayUnion(...elements) {
+ // TODO Salakar: v6: validate elements, any primitive or FirestoreReference allowed
+ // TODO Salakar: v6: explicitly deny use of serverTimestamp - only allowed on set/update
+ // TODO Salakar: v6: explicitly deny use of nested arrays - not supported on sdk
+ return new FieldValue(TypeFieldValueUnion, buildNativeArray(elements));
+ }
+
+ static arrayRemove(...elements) {
+ // TODO Salakar: v6: validate elements, any primitive or FirestoreReference allowed
+ // TODO Salakar: v6: explicitly deny use of serverTimestamp - only allowed on set/update
+ // TODO Salakar: v6: explicitly deny use of nested arrays - not supported on sdk
+ return new FieldValue(TypeFieldValueRemove, buildNativeArray(elements));
}
}
-export const DELETE_FIELD_VALUE = new FieldValue();
-export const SERVER_TIMESTAMP_FIELD_VALUE = new FieldValue();
\ No newline at end of file
+export const TypeFieldValueDelete = 'delete';
+export const TypeFieldValueIncrement = 'increment';
+export const TypeFieldValueRemove = 'remove';
+export const TypeFieldValueUnion = 'union';
+export const TypeFieldValueTimestamp = 'timestamp';
\ No newline at end of file

dist/modules/firestore/FieldValue.js.flow

@@ -2,16 +2,57 @@
* @flow
* FieldValue representation wrapper
*/
+import AnyJs from './utils/any';
+import { buildNativeArray } from './utils/serialize';
+// TODO: Salakar: Refactor in v6
export default class FieldValue {
+ _type: string;
+
+ _elements: AnyJs[] | any;
+
+ constructor(type: string, elements?: AnyJs[] | number) {
+ this._type = type;
+ this._elements = elements;
+ }
+
+ get type(): string {
+ return this._type;
+ }
+
+ get elements(): AnyJs[] {
+ return this._elements;
+ }
+
static delete(): FieldValue {
- return DELETE_FIELD_VALUE;
+ return new FieldValue(TypeFieldValueDelete);
+ }
+
+ static increment(n: number): FieldValue {
+ return new FieldValue(TypeFieldValueIncrement, n);
}
static serverTimestamp(): FieldValue {
- return SERVER_TIMESTAMP_FIELD_VALUE;
+ return new FieldValue(TypeFieldValueTimestamp);
+ }
+
+ static arrayUnion(...elements: AnyJs[]) {
+ // TODO Salakar: v6: validate elements, any primitive or FirestoreReference allowed
+ // TODO Salakar: v6: explicitly deny use of serverTimestamp - only allowed on set/update
+ // TODO Salakar: v6: explicitly deny use of nested arrays - not supported on sdk
+ return new FieldValue(TypeFieldValueUnion, buildNativeArray(elements));
+ }
+
+ static arrayRemove(...elements: AnyJs[]) {
+ // TODO Salakar: v6: validate elements, any primitive or FirestoreReference allowed
+ // TODO Salakar: v6: explicitly deny use of serverTimestamp - only allowed on set/update
+ // TODO Salakar: v6: explicitly deny use of nested arrays - not supported on sdk
+ return new FieldValue(TypeFieldValueRemove, buildNativeArray(elements));
}
}
-export const DELETE_FIELD_VALUE = new FieldValue();
-export const SERVER_TIMESTAMP_FIELD_VALUE = new FieldValue();
+export const TypeFieldValueDelete = 'delete';
+export const TypeFieldValueIncrement = 'increment';
+export const TypeFieldValueRemove = 'remove';
+export const TypeFieldValueUnion = 'union';
+export const TypeFieldValueTimestamp = 'timestamp';

dist/modules/firestore/firestoreTypes.flow.js.flow

@@ -0,0 +1,70 @@
+/*
+ * @flow
+ */
+import type { NativeErrorInterface } from '../../common/commonTypes.flow';
+
+export type MetadataChanges = {|
+ includeMetadataChanges: boolean,
+|};
+
+export type QueryDirection = 'DESC' | 'desc' | 'ASC' | 'asc';
+
+export type QueryOperator =
+ | '<'
+ | '<='
+ | '='
+ | '=='
+ | '>'
+ | '>='
+ | 'array-contains';
+
+export type GetOptions = {
+ source: 'default' | 'server' | 'cache',
+};
+
+export type SetOptions = {
+ merge?: boolean,
+};
+
+export type SnapshotMetadata = {
+ fromCache: boolean,
+ hasPendingWrites: boolean,
+};
+
+export type NativeDocumentChange = {
+ document: NativeDocumentSnapshot,
+ newIndex: number,
+ oldIndex: number,
+ type: 'added' | 'modified' | 'removed',
+};
+
+export type NativeDocumentSnapshot = {
+ data: { [string]: NativeTypeMap },
+ metadata: SnapshotMetadata,
+ path: string,
+};
+
+export type NativeTypeMap = {
+ type:
+ | 'nan'
+ | 'infinity'
+ | 'array'
+ | 'boolean'
+ | 'date'
+ | 'blob'
+ | 'documentid'
+ | 'fieldvalue'
+ | 'timestamp'
+ | 'geopoint'
+ | 'null'
+ | 'number'
+ | 'object'
+ | 'reference'
+ | 'string',
+ value: any,
+};
+
+export interface SnapshotErrorInterface extends NativeErrorInterface {
+ +path: string;
+ +appName: string;
+}

dist/modules/firestore/index.js

@@ -14,6 +14,7 @@
import Path from './Path';
import WriteBatch from './WriteBatch';
import TransactionHandler from './TransactionHandler';
+import Timestamp from './Timestamp';
import Transaction from './Transaction';
import { isBoolean, isObject, isString, hop } from '../../utils';
import { getNativeModule } from '../../utils/native';
@@ -30,8 +31,8 @@
super(app, {
events: NATIVE_EVENTS,
moduleName: MODULE_NAME,
- multiApp: true,
- hasShards: false,
+ hasMultiAppSupport: true,
+ hasCustomUrlSupport: false,
namespace: NAMESPACE
});
this._referencePath = new Path([]);
@@ -167,7 +168,7 @@
_onCollectionSyncEvent(event) {
if (event.error) {
- SharedEventEmitter.emit(getAppEventName(this, `onQuerySnapshotError:${event.listenerId}`), event.error);
+ SharedEventEmitter.emit(getAppEventName(this, `onQuerySnapshotError:${event.listenerId}`), event);
} else {
SharedEventEmitter.emit(getAppEventName(this, `onQuerySnapshot:${event.listenerId}`), event.querySnapshot);
}
@@ -182,7 +183,7 @@
_onDocumentSyncEvent(event) {
if (event.error) {
- SharedEventEmitter.emit(getAppEventName(this, `onDocumentSnapshotError:${event.listenerId}`), event.error);
+ SharedEventEmitter.emit(getAppEventName(this, `onDocumentSnapshotError:${event.listenerId}`), event);
} else {
SharedEventEmitter.emit(getAppEventName(this, `onDocumentSnapshot:${event.listenerId}`), event.documentSnapshot);
}
@@ -194,6 +195,7 @@
FieldPath,
FieldValue,
GeoPoint,
+ Timestamp,
enableLogging(enabled) {
// DEPRECATED: Remove method in v4.1.0

dist/modules/firestore/index.js.flow

@@ -15,6 +15,7 @@
import Path from './Path';
import WriteBatch from './WriteBatch';
import TransactionHandler from './TransactionHandler';
+import Timestamp from './Timestamp';
import Transaction from './Transaction';
import { isBoolean, isObject, isString, hop } from '../../utils';
import { getNativeModule } from '../../utils/native';
@@ -69,8 +70,8 @@
super(app, {
events: NATIVE_EVENTS,
moduleName: MODULE_NAME,
- multiApp: true,
- hasShards: false,
+ hasMultiAppSupport: true,
+ hasCustomUrlSupport: false,
namespace: NAMESPACE,
});
@@ -226,7 +227,7 @@
if (event.error) {
SharedEventEmitter.emit(
getAppEventName(this, `onQuerySnapshotError:${event.listenerId}`),
- event.error
+ event
);
} else {
SharedEventEmitter.emit(
@@ -246,7 +247,7 @@
if (event.error) {
SharedEventEmitter.emit(
getAppEventName(this, `onDocumentSnapshotError:${event.listenerId}`),
- event.error
+ event
);
} else {
SharedEventEmitter.emit(
@@ -262,6 +263,7 @@
FieldPath,
FieldValue,
GeoPoint,
+ Timestamp,
enableLogging(enabled: boolean): void {
// DEPRECATED: Remove method in v4.1.0
console.warn(

dist/modules/firestore/Path.js

@@ -12,7 +12,8 @@
}
get id() {
- return this._parts.length ? this._parts[this._parts.length - 1] : null;
+ // TODO is length check required?
+ return this._parts.length ? this._parts[this._parts.length - 1] : '';
}
get isDocument() {

dist/modules/firestore/Path.js.flow

@@ -13,8 +13,9 @@
this._parts = pathComponents;
}
- get id(): string | null {
- return this._parts.length ? this._parts[this._parts.length - 1] : null;
+ get id(): string {
+ // TODO is length check required?
+ return this._parts.length ? this._parts[this._parts.length - 1] : '';
}
get isDocument(): boolean {

dist/modules/firestore/Query.js

@@ -2,14 +2,14 @@
*
* Query representation wrapper
*/
-import DocumentSnapshot from './DocumentSnapshot';
import FieldPath from './FieldPath';
import QuerySnapshot from './QuerySnapshot';
+import SnapshotError from './SnapshotError';
+import DocumentSnapshot from './DocumentSnapshot';
+import { getNativeModule } from '../../utils/native';
import { buildNativeArray, buildTypeMap } from './utils/serialize';
-import { getAppEventName, SharedEventEmitter } from '../../utils/events';
-import { getLogger } from '../../utils/log';
import { firestoreAutoId, isFunction, isObject } from '../../utils';
-import { getNativeModule } from '../../utils/native';
+import { getAppEventName, SharedEventEmitter } from '../../utils/events';
const DIRECTIONS = {
ASC: 'ASCENDING',
asc: 'ASCENDING',
@@ -22,10 +22,11 @@
'>': 'GREATER_THAN',
'>=': 'GREATER_THAN_OR_EQUAL',
'<': 'LESS_THAN',
- '<=': 'LESS_THAN_OR_EQUAL'
+ '<=': 'LESS_THAN_OR_EQUAL',
+ 'array-contains': 'ARRAY_CONTAINS'
};
-const buildNativeFieldPath = fieldPath => {
+function buildNativeFieldPath(fieldPath) {
if (fieldPath instanceof FieldPath) {
return {
elements: fieldPath._segments,
@@ -37,7 +38,7 @@
string: fieldPath,
type: 'string'
};
-};
+}
/**
* @class Query
*/
@@ -94,6 +95,7 @@
}
onSnapshot(optionsOrObserverOrOnNext, observerOrOnNextOrOnError, onError) {
+ // TODO refactor this 💩
let observer;
let metadataChanges = {}; // Called with: onNext, ?onError
@@ -164,19 +166,28 @@
const listener = nativeQuerySnapshot => {
const querySnapshot = new QuerySnapshot(this._firestore, this, nativeQuerySnapshot);
observer.next(querySnapshot);
- }; // Listen to snapshot events
+ };
+ let unsubscribe; // Listen to snapshot events
- SharedEventEmitter.addListener(getAppEventName(this._firestore, `onQuerySnapshot:${listenerId}`), listener); // Listen for snapshot error events
+ const snapshotSubscription = SharedEventEmitter.addListener(getAppEventName(this._firestore, `onQuerySnapshot:${listenerId}`), listener); // listen for snapshot error events
- if (observer.error) {
- SharedEventEmitter.addListener(getAppEventName(this._firestore, `onQuerySnapshotError:${listenerId}`), observer.error);
- } // Add the native listener
+ const errorSubscription = SharedEventEmitter.addListener(getAppEventName(this._firestore, `onQuerySnapshotError:${listenerId}`), e => {
+ if (unsubscribe) unsubscribe();
+ const error = new SnapshotError(e);
+ if (observer.error) observer.error(error);else this.firestore.log.error(error);
+ }); // Add the native listener
+ getNativeModule(this._firestore).collectionOnSnapshot(this._referencePath.relativeName, this._fieldFilters, this._fieldOrders, this._queryOptions, listenerId, metadataChanges); // return an unsubscribe method
- getNativeModule(this._firestore).collectionOnSnapshot(this._referencePath.relativeName, this._fieldFilters, this._fieldOrders, this._queryOptions, listenerId, metadataChanges); // Return an unsubscribe method
+ unsubscribe = () => {
+ snapshotSubscription.remove();
+ errorSubscription.remove(); // cancel native listener
- return this._offCollectionSnapshot.bind(this, listenerId, listener);
+ getNativeModule(this._firestore).collectionOffSnapshot(this._referencePath.relativeName, this._fieldFilters, this._fieldOrders, this._queryOptions, listenerId);
+ };
+
+ return unsubscribe;
}
orderBy(fieldPath, directionStr = 'asc') {
@@ -255,17 +266,5 @@
return buildNativeArray(values);
}
- /**
- * Remove query snapshot listener
- * @param listener
- */
-
-
- _offCollectionSnapshot(listenerId, listener) {
- getLogger(this._firestore).info('Removing onQuerySnapshot listener');
- SharedEventEmitter.removeListener(getAppEventName(this._firestore, `onQuerySnapshot:${listenerId}`), listener);
- SharedEventEmitter.removeListener(getAppEventName(this._firestore, `onQuerySnapshotError:${listenerId}`), listener);
- getNativeModule(this._firestore).collectionOffSnapshot(this._referencePath.relativeName, this._fieldFilters, this._fieldOrders, this._queryOptions, listenerId);
- }
}
\ No newline at end of file

dist/modules/firestore/Query.js.flow

@@ -2,23 +2,24 @@
* @flow
* Query representation wrapper
*/
-import DocumentSnapshot from './DocumentSnapshot';
import FieldPath from './FieldPath';
import QuerySnapshot from './QuerySnapshot';
+import SnapshotError from './SnapshotError';
+import DocumentSnapshot from './DocumentSnapshot';
+import { getNativeModule } from '../../utils/native';
import { buildNativeArray, buildTypeMap } from './utils/serialize';
-import { getAppEventName, SharedEventEmitter } from '../../utils/events';
-import { getLogger } from '../../utils/log';
import { firestoreAutoId, isFunction, isObject } from '../../utils';
-import { getNativeModule } from '../../utils/native';
+import { getAppEventName, SharedEventEmitter } from '../../utils/events';
-import type Firestore from '.';
+import type Firestore from './';
import type Path from './Path';
import type {
MetadataChanges,
QueryDirection,
QueryOperator,
GetOptions,
-} from './types';
+} from './firestoreTypes.flow';
+import type { NativeErrorResponse } from '../../common/commonTypes.flow';
const DIRECTIONS: { [QueryDirection]: string } = {
ASC: 'ASCENDING',
@@ -34,6 +35,7 @@
'>=': 'GREATER_THAN_OR_EQUAL',
'<': 'LESS_THAN',
'<=': 'LESS_THAN_OR_EQUAL',
+ 'array-contains': 'ARRAY_CONTAINS',
};
type NativeFieldPath = {|
@@ -60,17 +65,14 @@
startAt?: any[],
};
-export type ObserverOnError = Object => void;
+export type ObserverOnError = SnapshotError => void;
export type ObserverOnNext = QuerySnapshot => void;
-
export type Observer = {
error?: ObserverOnError,
next: ObserverOnNext,
};
-const buildNativeFieldPath = (
- fieldPath: string | FieldPath
-): NativeFieldPath => {
+function buildNativeFieldPath(fieldPath: string | FieldPath): NativeFieldPath {
if (fieldPath instanceof FieldPath) {
return {
elements: fieldPath._segments,
@@ -81,7 +83,7 @@
string: fieldPath,
type: 'string',
};
-};
+}
/**
* @class Query
@@ -200,6 +203,7 @@
observerOrOnNextOrOnError?: Observer | ObserverOnNext | ObserverOnError,
onError?: ObserverOnError
) {
+ // TODO refactor this 💩
let observer: Observer;
let metadataChanges = {};
// Called with: onNext, ?onError
@@ -308,19 +312,24 @@
observer.next(querySnapshot);
};
+ let unsubscribe: () => void;
+
// Listen to snapshot events
- SharedEventEmitter.addListener(
+ const snapshotSubscription = SharedEventEmitter.addListener(
getAppEventName(this._firestore, `onQuerySnapshot:${listenerId}`),
listener
);
- // Listen for snapshot error events
- if (observer.error) {
- SharedEventEmitter.addListener(
+ // listen for snapshot error events
+ const errorSubscription = SharedEventEmitter.addListener(
getAppEventName(this._firestore, `onQuerySnapshotError:${listenerId}`),
- observer.error
- );
+ (e: NativeErrorResponse) => {
+ if (unsubscribe) unsubscribe();
+ const error = new SnapshotError(e);
+ if (observer.error) observer.error(error);
+ else this.firestore.log.error(error);
}
+ );
// Add the native listener
getNativeModule(this._firestore).collectionOnSnapshot(
@@ -332,8 +341,21 @@
metadataChanges
);
- // Return an unsubscribe method
- return this._offCollectionSnapshot.bind(this, listenerId, listener);
+ // return an unsubscribe method
+ unsubscribe = () => {
+ snapshotSubscription.remove();
+ errorSubscription.remove();
+ // cancel native listener
+ getNativeModule(this._firestore).collectionOffSnapshot(
+ this._referencePath.relativeName,
+ this._fieldFilters,
+ this._fieldOrders,
+ this._queryOptions,
+ listenerId
+ );
+ };
+
+ return unsubscribe;
}
orderBy(
@@ -455,27 +478,4 @@
return buildNativeArray(values);
}
-
- /**
- * Remove query snapshot listener
- * @param listener
- */
- _offCollectionSnapshot(listenerId: string, listener: Function) {
- getLogger(this._firestore).info('Removing onQuerySnapshot listener');
- SharedEventEmitter.removeListener(
- getAppEventName(this._firestore, `onQuerySnapshot:${listenerId}`),
- listener
- );
- SharedEventEmitter.removeListener(
- getAppEventName(this._firestore, `onQuerySnapshotError:${listenerId}`),
- listener
- );
- getNativeModule(this._firestore).collectionOffSnapshot(
- this._referencePath.relativeName,
- this._fieldFilters,
- this._fieldOrders,
- this._queryOptions,
- listenerId
- );
- }
}

dist/modules/firestore/QuerySnapshot.js.flow

@@ -5,12 +5,12 @@
import DocumentChange from './DocumentChange';
import DocumentSnapshot from './DocumentSnapshot';
-import type Firestore from '.';
+import type Firestore from './';
import type {
NativeDocumentChange,
NativeDocumentSnapshot,
SnapshotMetadata,
-} from './types';
+} from './firestoreTypes.flow';
import type Query from './Query';
type NativeQuerySnapshot = {

dist/modules/firestore/SnapshotError.js

@@ -0,0 +1,9 @@
+import NativeError from '../../common/NativeError';
+export default class SnapshotError extends NativeError {
+ constructor(nativeErrorMap) {
+ super(nativeErrorMap.error);
+ this.path = nativeErrorMap.path;
+ this.appName = nativeErrorMap.appName;
+ }
+
+}
\ No newline at end of file

dist/modules/firestore/SnapshotError.js.flow

@@ -0,0 +1,12 @@
+import NativeError from '../../common/NativeError';
+import type { SnapshotErrorInterface } from './firestoreTypes.flow';
+import type { NativeErrorResponse } from '../../common/commonTypes.flow';
+
+export default class SnapshotError extends NativeError
+ implements SnapshotErrorInterface {
+ constructor(nativeErrorMap: NativeErrorResponse) {
+ super(nativeErrorMap.error);
+ this.path = nativeErrorMap.path;
+ this.appName = nativeErrorMap.appName;
+ }
+}

dist/modules/firestore/Timestamp.js

@@ -0,0 +1,80 @@
+/**
+ *
+ * Copyright 2017 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * ----
+ *
+ * Some snippets taken from: https://github.com/firebase/firebase-js-sdk/blob/master/packages/firestore/src/api/timestamp.ts
+ * and adapted to work for React Native Firebase
+ */
+
+/**
+ * Timestamp representation wrapper
+ */
+export default class Timestamp {
+ static now() {
+ return Timestamp.fromMillis(Date.now());
+ }
+
+ static fromDate(date) {
+ return Timestamp.fromMillis(date.getTime());
+ }
+
+ static fromMillis(milliseconds) {
+ const seconds = Math.floor(milliseconds / 1000);
+ const nanoseconds = (milliseconds - seconds * 1000) * 1e6;
+ return new Timestamp(seconds, nanoseconds);
+ }
+
+ constructor(seconds, nanoseconds) {
+ if (nanoseconds < 0) {
+ throw new Error(`Timestamp nanoseconds out of range: ${nanoseconds}`);
+ }
+
+ if (nanoseconds >= 1e9) {
+ throw new Error(`Timestamp nanoseconds out of range: ${nanoseconds}`);
+ } // Midnight at the beginning of 1/1/1 is the earliest Firestore supports.
+
+
+ if (seconds < -62135596800) {
+ throw new Error(`Timestamp seconds out of range: ${seconds}`);
+ } // This will break in the year 10,000.
+
+
+ if (seconds >= 253402300800) {
+ throw new Error(`Timestamp seconds out of range: ${seconds}`);
+ }
+
+ this.seconds = seconds;
+ this.nanoseconds = nanoseconds;
+ }
+
+ toDate() {
+ return new Date(this.toMillis());
+ }
+
+ toMillis() {
+ return this.seconds * 1000 + this.nanoseconds / 1e6;
+ }
+
+ isEqual(other) {
+ return other.seconds === this.seconds && other.nanoseconds === this.nanoseconds;
+ }
+
+ toString() {
+ return `Timestamp(seconds=${this.seconds}, nanoseconds=${this.nanoseconds})`;
+ }
+
+}
\ No newline at end of file

dist/modules/firestore/Timestamp.js.flow

@@ -0,0 +1,87 @@
+/**
+ * @flow
+ * Copyright 2017 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * ----
+ *
+ * Some snippets taken from: https://github.com/firebase/firebase-js-sdk/blob/master/packages/firestore/src/api/timestamp.ts
+ * and adapted to work for React Native Firebase
+ */
+
+/**
+ * Timestamp representation wrapper
+ */
+export default class Timestamp {
+ seconds: number;
+
+ nanoseconds: number;
+
+ static now(): Timestamp {
+ return Timestamp.fromMillis(Date.now());
+ }
+
+ static fromDate(date: Date): Timestamp {
+ return Timestamp.fromMillis(date.getTime());
+ }
+
+ static fromMillis(milliseconds: number): Timestamp {
+ const seconds = Math.floor(milliseconds / 1000);
+ const nanoseconds = (milliseconds - seconds * 1000) * 1e6;
+ return new Timestamp(seconds, nanoseconds);
+ }
+
+ constructor(seconds: number, nanoseconds: number) {
+ if (nanoseconds < 0) {
+ throw new Error(`Timestamp nanoseconds out of range: ${nanoseconds}`);
+ }
+
+ if (nanoseconds >= 1e9) {
+ throw new Error(`Timestamp nanoseconds out of range: ${nanoseconds}`);
+ }
+
+ // Midnight at the beginning of 1/1/1 is the earliest Firestore supports.
+ if (seconds < -62135596800) {
+ throw new Error(`Timestamp seconds out of range: ${seconds}`);
+ }
+
+ // This will break in the year 10,000.
+ if (seconds >= 253402300800) {
+ throw new Error(`Timestamp seconds out of range: ${seconds}`);
+ }
+
+ this.seconds = seconds;
+ this.nanoseconds = nanoseconds;
+ }
+
+ toDate(): Date {
+ return new Date(this.toMillis());
+ }
+
+ toMillis(): number {
+ return this.seconds * 1000 + this.nanoseconds / 1e6;
+ }
+
+ isEqual(other: Timestamp): boolean {
+ return (
+ other.seconds === this.seconds && other.nanoseconds === this.nanoseconds
+ );
+ }
+
+ toString(): string {
+ return `Timestamp(seconds=${this.seconds}, nanoseconds=${
+ this.nanoseconds
+ })`;
+ }
+}

dist/modules/firestore/TransactionHandler.js.flow

@@ -5,7 +5,7 @@
import { getAppEventName, SharedEventEmitter } from '../../utils/events';
import { getNativeModule } from '../../utils/native';
import Transaction from './Transaction';
-import type Firestore from '.';
+import type Firestore from './';
let transactionId = 0;

dist/modules/firestore/Transaction.js.flow

@@ -5,7 +5,7 @@
import { parseUpdateArgs } from './utils';
import { buildNativeMap } from './utils/serialize';
-import type Firestore from '.';
+import type Firestore from './';
import type { TransactionMeta } from './TransactionHandler';
import type DocumentReference from './DocumentReference';
import DocumentSnapshot from './DocumentSnapshot';

dist/modules/firestore/types.js.flow

@@ -1,54 +0,0 @@
-/*
- * @flow
- */
-
-export type MetadataChanges = {|
- includeMetadataChanges: boolean,
-|};
-
-export type QueryDirection = 'DESC' | 'desc' | 'ASC' | 'asc';
-
-export type QueryOperator = '<' | '<=' | '=' | '==' | '>' | '>=';
-
-export type GetOptions = {
- source: 'default' | 'server' | 'cache',
-};
-
-export type SetOptions = {
- merge?: boolean,
-};
-
-export type SnapshotMetadata = {
- fromCache: boolean,
- hasPendingWrites: boolean,
-};
-
-export type NativeDocumentChange = {
- document: NativeDocumentSnapshot,
- newIndex: number,
- oldIndex: number,
- type: 'added' | 'modified' | 'removed',
-};
-
-export type NativeDocumentSnapshot = {
- data: { [string]: NativeTypeMap },
- metadata: SnapshotMetadata,
- path: string,
-};
-
-export type NativeTypeMap = {
- type:
- | 'array'
- | 'boolean'
- | 'date'
- | 'blob'
- | 'documentid'
- | 'fieldvalue'
- | 'geopoint'
- | 'null'
- | 'number'
- | 'object'
- | 'reference'
- | 'string',
- value: any,
-};

dist/modules/firestore/utils/any.js.flow

@@ -0,0 +1,4 @@
+/**
+ * @url https://github.com/firebase/firebase-js-sdk/blob/master/packages/firestore/src/util/misc.ts#L26
+ */
+export type AnyJs = null | undefined | boolean | number | string | object;

dist/modules/firestore/utils/serialize.js

@@ -1,11 +1,11 @@
import DocumentReference from '../DocumentReference';
import Blob from '../Blob';
import { DOCUMENT_ID } from '../FieldPath';
-import { DELETE_FIELD_VALUE, SERVER_TIMESTAMP_FIELD_VALUE } from '../FieldValue';
+import FieldValue from '../FieldValue';
import GeoPoint from '../GeoPoint';
import Path from '../Path';
import { typeOf } from '../../../utils';
-
+import Timestamp from '../Timestamp';
/*
* Functions that build up the data needed to represent
* the different types available within Firestore
@@ -44,24 +45,24 @@
export const buildTypeMap = value => {
const type = typeOf(value);
- if (value === null || value === undefined || Number.isNaN(value)) {
+ if (Number.isNaN(value)) {
return {
- type: 'null',
+ type: 'nan',
value: null
};
}
- if (value === DELETE_FIELD_VALUE) {
+ if (value === Infinity) {
return {
- type: 'fieldvalue',
- value: 'delete'
+ type: 'infinity',
+ value: null
};
}
- if (value === SERVER_TIMESTAMP_FIELD_VALUE) {
+ if (value === null || value === undefined) {
return {
- type: 'fieldvalue',
- value: 'timestamp'
+ type: 'null',
+ value: null
};
}
@@ -104,6 +105,16 @@
};
}
+ if (value instanceof Timestamp) {
+ return {
+ type: 'timestamp',
+ value: {
+ seconds: value.seconds,
+ nanoseconds: value.nanoseconds
+ }
+ };
+ }
+
if (value instanceof Date) {
return {
type: 'date',
@@ -116,6 +127,17 @@
type: 'blob',
value: value.toBase64()
};
+ } // TODO: Salakar: Refactor in v6 - add internal `type` flag
+
+
+ if (value instanceof FieldValue) {
+ return {
+ type: 'fieldvalue',
+ value: {
+ elements: value.elements,
+ type: value.type
+ }
+ };
}
return {
@@ -187,6 +209,10 @@
return new GeoPoint(value.latitude, value.longitude);
}
+ if (type === 'timestamp') {
+ return new Timestamp(value.seconds, value.nanoseconds);
+ }
+
if (type === 'date') {
return new Date(value);
}
@@ -195,6 +221,14 @@
return Blob.fromBase64String(value);
}
+ if (type === 'infinity') {
+ return Infinity;
+ }
+
+ if (type === 'nan') {
+ return NaN;
+ }
+
console.warn(`Unknown data type received ${type}`);
return value;
};
\ No newline at end of file

dist/modules/firestore/utils/serialize.js.flow

@@ -5,16 +5,14 @@
import DocumentReference from '../DocumentReference';
import Blob from '../Blob';
import { DOCUMENT_ID } from '../FieldPath';
-import {
- DELETE_FIELD_VALUE,
- SERVER_TIMESTAMP_FIELD_VALUE,
-} from '../FieldValue';
+import FieldValue from '../FieldValue';
import GeoPoint from '../GeoPoint';
import Path from '../Path';
import { typeOf } from '../../../utils';
import type Firestore from '..';
-import type { NativeTypeMap } from '../types';
+import type { NativeTypeMap } from '../firestoreTypes.flow';
+import Timestamp from '../Timestamp';
/*
* Functions that build up the data needed to represent
@@ -50,24 +48,28 @@
export const buildTypeMap = (value: any): NativeTypeMap | null => {
const type = typeOf(value);
- if (value === null || value === undefined || Number.isNaN(value)) {
+
+ if (Number.isNaN(value)) {
return {
- type: 'null',
+ type: 'nan',
value: null,
};
}
- if (value === DELETE_FIELD_VALUE) {
+
+ if (value === Infinity) {
return {
- type: 'fieldvalue',
- value: 'delete',
+ type: 'infinity',
+ value: null,
};
}
- if (value === SERVER_TIMESTAMP_FIELD_VALUE) {
+
+ if (value === null || value === undefined) {
return {
- type: 'fieldvalue',
- value: 'timestamp',
+ type: 'null',
+ value: null,
};
}
+
if (value === DOCUMENT_ID) {
return {
type: 'documentid',
@@ -102,6 +108,17 @@
},
};
}
+
+ if (value instanceof Timestamp) {
+ return {
+ type: 'timestamp',
+ value: {
+ seconds: value.seconds,
+ nanoseconds: value.nanoseconds,
+ },
+ };
+ }
+
if (value instanceof Date) {
return {
type: 'date',
@@ -108,12 +125,25 @@
value: value.getTime(),
};
}
+
if (value instanceof Blob) {
return {
type: 'blob',
value: value.toBase64(),
};
}
+
+ // TODO: Salakar: Refactor in v6 - add internal `type` flag
+ if (value instanceof FieldValue) {
+ return {
+ type: 'fieldvalue',
+ value: {
+ elements: value.elements,
+ type: value.type,
+ },
+ };
+ }
+
return {
type: 'object',
value: buildNativeMap(value),
@@ -172,9 +207,15 @@
if (type === 'reference') {
return new DocumentReference(firestore, Path.fromName(value));
}
+
if (type === 'geopoint') {
return new GeoPoint(value.latitude, value.longitude);
}
+
+ if (type === 'timestamp') {
+ return new Timestamp(value.seconds, value.nanoseconds);
+ }
+
if (type === 'date') {
return new Date(value);
}
@@ -178,9 +219,19 @@
if (type === 'date') {
return new Date(value);
}
+
if (type === 'blob') {
return Blob.fromBase64String(value);
}
+
+ if (type === 'infinity') {
+ return Infinity;
+ }
+
+ if (type === 'nan') {
+ return NaN;
+ }
+
console.warn(`Unknown data type received ${type}`);
return value;
};

dist/modules/firestore/WriteBatch.js.flow

@@ -7,8 +7,8 @@
import { getNativeModule } from '../../utils/native';
import type DocumentReference from './DocumentReference';
-import type Firestore from '.';
-import type { SetOptions } from './types';
+import type Firestore from './';
+import type { SetOptions } from './firestoreTypes.flow';
type DocumentWrite = {
data?: Object,

dist/modules/functions/HttpsError.js

@@ -1,8 +1,9 @@
export default class HttpsError extends Error {
constructor(code, message, details) {
super(message);
- this.details = details;
this.code = code;
+ this.details = details;
+ this.message = message;
}
}
\ No newline at end of file

dist/modules/functions/HttpsError.js.flow

@@ -3,11 +3,14 @@
export default class HttpsError extends Error {
+details: ?any;
+ +message: string;
+
+code: FunctionsErrorCode;
constructor(code: FunctionsErrorCode, message?: string, details?: any) {
super(message);
- this.details = details;
this.code = code;
+ this.details = details;
+ this.message = message;
}
}

dist/modules/functions/index.js

@@ -5,6 +5,7 @@
import ModuleBase from '../../utils/ModuleBase';
import { isObject } from '../../utils';
import { getNativeModule } from '../../utils/native';
+import firebase from '../core/firebase';
import HttpsError from './HttpsError';
export const NAMESPACE = 'functions';
export const MODULE_NAME = 'RNFirebaseFunctions';
@@ -26,15 +27,31 @@
return Promise.resolve(possibleError);
}
+/**
+ * -------------
+ * functions()
+ * -------------
+ */
+
export default class Functions extends ModuleBase {
- constructor(app) {
- super(app, {
- multiApp: false,
- hasShards: false,
+ constructor(appOrRegion, region) {
+ let _app = appOrRegion;
+
+ let _region = region || 'us-central1';
+
+ if (typeof _app === 'string') {
+ _region = _app;
+ _app = firebase.app();
+ }
+
+ super(_app, {
+ hasMultiAppSupport: true,
+ hasCustomUrlSupport: false,
+ hasRegionsSupport: true,
namespace: NAMESPACE,
moduleName: MODULE_NAME
- });
+ }, _region);
}
/**
* -------------
@@ -56,6 +73,20 @@
return promise.then(errorOrResult);
};
}
+ /**
+ * Changes this instance to point to a Cloud Functions emulator running
+ * locally.
+ *
+ * See https://firebase.google.com/docs/functions/local-emulator
+ *
+ * @param origin the origin string of the local emulator started via firebase tools
+ * "http://10.0.0.8:1337".
+ */
+
+
+ useFunctionsEmulator(origin) {
+ return getNativeModule(this).useFunctionsEmulator(origin);
+ }
}
export const statics = {

dist/modules/functions/index.js.flow

@@ -7,6 +7,8 @@
import { getNativeModule } from '../../utils/native';
import type App from '../core/app';
+import firebase from '../core/firebase';
+
import HttpsError from './HttpsError';
import type {
@@ -38,14 +40,32 @@
return Promise.resolve(possibleError);
}
+/**
+ * -------------
+ * functions()
+ * -------------
+ */
export default class Functions extends ModuleBase {
- constructor(app: App) {
- super(app, {
- multiApp: false,
- hasShards: false,
+ constructor(appOrRegion: App, region?: string) {
+ let _app = appOrRegion;
+ let _region = region || 'us-central1';
+
+ if (typeof _app === 'string') {
+ _region = _app;
+ _app = firebase.app();
+ }
+
+ super(
+ _app,
+ {
+ hasMultiAppSupport: true,
+ hasCustomUrlSupport: false,
+ hasRegionsSupport: true,
namespace: NAMESPACE,
moduleName: MODULE_NAME,
- });
+ },
+ _region
+ );
}
/**
@@ -60,10 +80,26 @@
*/
httpsCallable(name: string): HttpsCallable {
return (data?: any): HttpsCallablePromise => {
- const promise = getNativeModule(this).httpsCallable(name, { data });
+ const promise = getNativeModule(this).httpsCallable(name, {
+ data,
+ });
+
return promise.then(errorOrResult);
};
}
+
+ /**
+ * Changes this instance to point to a Cloud Functions emulator running
+ * locally.
+ *
+ * See https://firebase.google.com/docs/functions/local-emulator
+ *
+ * @param origin the origin string of the local emulator started via firebase tools
+ * "http://10.0.0.8:1337".
+ */
+ useFunctionsEmulator(origin: string): Promise<null> {
+ return getNativeModule(this).useFunctionsEmulator(origin);
+ }
}
export const statics: { HttpsErrorCode: HttpsErrorCode } = {

dist/modules/iid/index.js

@@ -9,9 +9,9 @@
export default class InstanceId extends ModuleBase {
constructor(app) {
super(app, {
- hasShards: false,
+ hasCustomUrlSupport: false,
moduleName: MODULE_NAME,
- multiApp: false,
+ hasMultiAppSupport: false,
namespace: NAMESPACE
});
}

dist/modules/iid/index.js.flow

@@ -13,9 +13,9 @@
export default class InstanceId extends ModuleBase {
constructor(app: App) {
super(app, {
- hasShards: false,
+ hasCustomUrlSupport: false,
moduleName: MODULE_NAME,
- multiApp: false,
+ hasMultiAppSupport: false,
namespace: NAMESPACE,
});
}

dist/modules/invites/index.js

@@ -15,9 +15,9 @@
constructor(app) {
super(app, {
events: NATIVE_EVENTS,
- hasShards: false,
+ hasCustomUrlSupport: false,
moduleName: MODULE_NAME,
- multiApp: false,
+ hasMultiAppSupport: false,
namespace: NAMESPACE
});
SharedEventEmitter.addListener( // sub to internal native event - this fans out to

dist/modules/invites/index.js.flow

@@ -24,9 +24,9 @@
constructor(app: App) {
super(app, {
events: NATIVE_EVENTS,
- hasShards: false,
+ hasCustomUrlSupport: false,
moduleName: MODULE_NAME,
- multiApp: false,
+ hasMultiAppSupport: false,
namespace: NAMESPACE,
});
@@ -20,8 +20,8 @@
super(app, {
events: NATIVE_EVENTS,
moduleName: MODULE_NAME,
- multiApp: false,
- hasShards: false,
+ hasMultiAppSupport: false,
+ hasCustomUrlSupport: false,
namespace: NAMESPACE
});
SharedEventEmitter.addListener( // sub to internal native event - this fans out to
@@ -37,7 +37,7 @@
/**
* Create long Dynamic Link from parameters
* @param parameters
- * @returns {Promise.<String>}
+ * @returns {Promise.<string>}
*/
@@ -55,7 +55,7 @@
/**
* Create short Dynamic Link from parameters
* @param parameters
- * @returns {Promise.<String>}
+ * @returns {Promise.<string>}
*/
@@ -72,7 +72,7 @@
}
/**
* Returns the link that triggered application open
- * @returns {Promise.<String>}
+ * @returns {Promise.<string>}
*/
@@ -24,8 +24,8 @@
super(app, {
events: NATIVE_EVENTS,
moduleName: MODULE_NAME,
- multiApp: false,
- hasShards: false,
+ hasMultiAppSupport: false,
+ hasCustomUrlSupport: false,
namespace: NAMESPACE,
});
@@ -47,7 +47,7 @@
/**
* Create long Dynamic Link from parameters
* @param parameters
- * @returns {Promise.<String>}
+ * @returns {Promise.<string>}
*/
createDynamicLink(link: DynamicLink): Promise<string> {
if (!(link instanceof DynamicLink)) {
@@ -67,12 +67,12 @@
/**
* Create short Dynamic Link from parameters
* @param parameters
- * @returns {Promise.<String>}
+ * @returns {Promise.<string>}
*/
createShortDynamicLink(
link: DynamicLink,
type?: 'SHORT' | 'UNGUESSABLE'
- ): Promise<String> {
+ ): Promise<string> {
if (!(link instanceof DynamicLink)) {
return Promise.reject(
new Error(
@@ -89,7 +89,7 @@
/**
* Returns the link that triggered application open
- * @returns {Promise.<String>}
+ * @returns {Promise.<string>}
*/
getInitialLink(): Promise<?string> {
return getNativeModule(this).getInitialLink();

dist/modules/messaging/index.js

@@ -9,6 +9,7 @@
import ModuleBase from '../../utils/ModuleBase';
import { getNativeModule } from '../../utils/native';
import { isFunction, isObject } from '../../utils';
+import IOSMessaging from './IOSMessaging';
import RemoteMessage from './RemoteMessage';
const NATIVE_EVENTS = ['messaging_message_received', 'messaging_token_refreshed'];
export const MODULE_NAME = 'RNFirebaseMessaging';
@@ -22,10 +23,11 @@
super(app, {
events: NATIVE_EVENTS,
moduleName: MODULE_NAME,
- multiApp: false,
- hasShards: false,
+ hasMultiAppSupport: false,
+ hasCustomUrlSupport: false,
namespace: NAMESPACE
});
+ this._ios = new IOSMessaging(this);
SharedEventEmitter.addListener( // sub to internal native event - this fans out to
// public event name: onMessage
'messaging_message_received', message => {
@@ -42,10 +44,18 @@
}
}
+ get ios() {
+ return this._ios;
+ }
+
getToken() {
return getNativeModule(this).getToken();
}
+ deleteToken() {
+ return getNativeModule(this).deleteToken();
+ }
+
onMessage(nextOrObserver) {
let listener;
@@ -75,7 +85,7 @@
} else if (isObject(nextOrObserver) && isFunction(nextOrObserver.next)) {
listener = nextOrObserver.next;
} else {
- throw new Error('Messaging.OnTokenRefresh failed: First argument must be a function or observer object with a `next` function.');
+ throw new Error('Messaging.onTokenRefresh failed: First argument must be a function or observer object with a `next` function.');
}
getLogger(this).info('Creating onTokenRefresh listener');
@@ -122,10 +132,6 @@
*/
- deleteToken() {
- throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD('messaging', 'deleteToken'));
- }
-
setBackgroundMessageHandler() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD('messaging', 'setBackgroundMessageHandler'));
}

dist/modules/messaging/index.js.flow

@@ -9,6 +9,7 @@
import ModuleBase from '../../utils/ModuleBase';
import { getNativeModule } from '../../utils/native';
import { isFunction, isObject } from '../../utils';
+import IOSMessaging from './IOSMessaging';
import RemoteMessage from './RemoteMessage';
import type App from '../core/app';
@@ -38,14 +39,17 @@
* @class Messaging
*/
export default class Messaging extends ModuleBase {
+ _ios: IOSMessaging;
+
constructor(app: App) {
super(app, {
events: NATIVE_EVENTS,
moduleName: MODULE_NAME,
- multiApp: false,
- hasShards: false,
+ hasMultiAppSupport: false,
+ hasCustomUrlSupport: false,
namespace: NAMESPACE,
});
+ this._ios = new IOSMessaging(this);
SharedEventEmitter.addListener(
// sub to internal native event - this fans out to
@@ -71,10 +75,18 @@
}
}
+ get ios(): IOSMessaging {
+ return this._ios;
+ }
+
getToken(): Promise<string> {
return getNativeModule(this).getToken();
}
+ deleteToken(): Promise<void> {
+ return getNativeModule(this).deleteToken();
+ }
+
onMessage(nextOrObserver: OnMessage | OnMessageObserver): () => any {
let listener: RemoteMessage => any;
if (isFunction(nextOrObserver)) {
@@ -109,7 +121,7 @@
listener = nextOrObserver.next;
} else {
throw new Error(
- 'Messaging.OnTokenRefresh failed: First argument must be a function or observer object with a `next` function.'
+ 'Messaging.onTokenRefresh failed: First argument must be a function or observer object with a `next` function.'
);
}
@@ -160,15 +172,6 @@
* KNOWN UNSUPPORTED METHODS
*/
- deleteToken() {
- throw new Error(
- INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD(
- 'messaging',
- 'deleteToken'
- )
- );
- }
-
setBackgroundMessageHandler() {
throw new Error(
INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD(

dist/modules/messaging/IOSMessaging.js

@@ -0,0 +1,24 @@
+import { getNativeModule } from '../../utils/native';
+import { isIOS } from '../../utils';
+export default class IOSMessaging {
+ constructor(messaging) {
+ this._messaging = messaging;
+ }
+
+ getAPNSToken() {
+ if (!isIOS) {
+ return null;
+ }
+
+ return getNativeModule(this._messaging).getAPNSToken();
+ }
+
+ registerForRemoteNotifications() {
+ if (!isIOS) {
+ return undefined;
+ }
+
+ return getNativeModule(this._messaging).registerForRemoteNotifications();
+ }
+
+}
\ No newline at end of file

dist/modules/messaging/IOSMessaging.js.flow

@@ -0,0 +1,25 @@
+import { getNativeModule } from '../../utils/native';
+
+import { isIOS } from '../../utils';
+
+import type Messaging from './';
+
+export default class IOSMessaging {
+ constructor(messaging: Messaging) {
+ this._messaging = messaging;
+ }
+
+ getAPNSToken(): Promise<string | null> {
+ if (!isIOS) {
+ return null;
+ }
+ return getNativeModule(this._messaging).getAPNSToken();
+ }
+
+ registerForRemoteNotifications(): Promise<void> {
+ if (!isIOS) {
+ return undefined;
+ }
+ return getNativeModule(this._messaging).registerForRemoteNotifications();
+ }
+}

dist/modules/notifications/AndroidChannelGroup.js

@@ -3,9 +3,10 @@
* AndroidChannelGroup representation wrapper
*/
export default class AndroidChannelGroup {
- constructor(groupId, name) {
- this._groupId = groupId;
+ constructor(groupId, name, description) {
this._name = name;
+ this._groupId = groupId;
+ this._description = description;
}
get groupId() {
@@ -16,6 +17,10 @@
return this._name;
}
+ get description() {
+ return this._description;
+ }
+
build() {
if (!this._groupId) {
throw new Error('AndroidChannelGroup: Missing required `groupId` property');
@@ -24,8 +29,10 @@
}
return {
+ name: this._name,
groupId: this._groupId,
- name: this._name
+ description: this._description,
+ channels: []
};
}

dist/modules/notifications/AndroidChannelGroup.js.flow

@@ -3,29 +3,42 @@
* AndroidChannelGroup representation wrapper
*/
-type NativeAndroidChannelGroup = {|
- groupId: string,
+import type { NativeAndroidChannel } from './AndroidChannel';
+
+export type NativeAndroidChannelGroup = {|
name: string,
+ groupId: string,
+ // Android API >= 28
+ description: string | void,
+ // Android API >= 28
+ channels: void | NativeAndroidChannel[],
|};
export default class AndroidChannelGroup {
- _groupId: string;
-
- _name: string;
-
- constructor(groupId: string, name: string) {
- this._groupId = groupId;
+ constructor(groupId: string, name: string, description?: string) {
this._name = name;
+ this._groupId = groupId;
+ this._description = description;
}
+ _groupId: string;
+
get groupId(): string {
return this._groupId;
}
+ _name: string;
+
get name(): string {
return this._name;
}
+ _description: string | void;
+
+ get description(): string | void {
+ return this._description;
+ }
+
build(): NativeAndroidChannelGroup {
if (!this._groupId) {
throw new Error(
@@ -36,8 +49,10 @@
}
return {
- groupId: this._groupId,
name: this._name,
+ groupId: this._groupId,
+ description: this._description,
+ channels: [],
};
}
}

dist/modules/notifications/AndroidChannel.js.flow

@@ -5,7 +5,7 @@
import { Importance, Visibility } from './types';
import type { ImportanceType, VisibilityType } from './types';
-type NativeAndroidChannel = {|
+export type NativeAndroidChannel = {|
bypassDnd?: boolean,
channelId: string,
description?: string,

dist/modules/notifications/AndroidNotifications.js

@@ -119,4 +119,44 @@
return Promise.resolve();
}
+ getChannel(channelId) {
+ if (Platform.OS === 'android') {
+ if (typeof channelId !== 'string') {
+ throw new Error(`AndroidNotifications:getChannel expects an 'string' but got type ${typeof channelId}`);
+ }
+
+ return Promise.resolve(getNativeModule(this._notifications).getChannel(channelId));
+ }
+
+ return Promise.resolve(null);
+ }
+
+ getChannels() {
+ if (Platform.OS === 'android') {
+ return Promise.resolve(getNativeModule(this._notifications).getChannels());
+ }
+
+ return Promise.resolve([]);
+ }
+
+ getChannelGroup(channelGroupId) {
+ if (Platform.OS === 'android') {
+ if (typeof channelGroupId !== 'string') {
+ throw new Error(`AndroidNotifications:getChannel expects an 'string' but got type ${typeof channelGroupId}`);
+ }
+
+ return Promise.resolve(getNativeModule(this._notifications).getChannelGroup(channelGroupId));
+ }
+
+ return Promise.resolve(null);
+ }
+
+ getChannelGroups() {
+ if (Platform.OS === 'android') {
+ return Promise.resolve(getNativeModule(this._notifications).getChannelGroups());
+ }
+
+ return Promise.resolve([]);
+ }
+
}
\ No newline at end of file

dist/modules/notifications/AndroidNotifications.js.flow

@@ -7,7 +7,9 @@
import AndroidChannelGroup from './AndroidChannelGroup';
import { getNativeModule } from '../../utils/native';
-import type Notifications from '.';
+import type Notifications from './';
+import type { NativeAndroidChannel } from './AndroidChannel';
+import type { NativeAndroidChannelGroup } from './AndroidChannelGroup';
export default class AndroidNotifications {
_notifications: Notifications;
@@ -129,4 +131,50 @@
}
return Promise.resolve();
}
+
+ getChannel(channelId: string): Promise<?NativeAndroidChannel> {
+ if (Platform.OS === 'android') {
+ if (typeof channelId !== 'string') {
+ throw new Error(
+ `AndroidNotifications:getChannel expects an 'string' but got type ${typeof channelId}`
+ );
+ }
+ return Promise.resolve(
+ getNativeModule(this._notifications).getChannel(channelId)
+ );
+ }
+ return Promise.resolve(null);
+ }
+
+ getChannels(): Promise<NativeAndroidChannel[]> {
+ if (Platform.OS === 'android') {
+ return Promise.resolve(
+ getNativeModule(this._notifications).getChannels()
+ );
+ }
+ return Promise.resolve([]);
+ }
+
+ getChannelGroup(channelGroupId: string): Promise<?NativeAndroidChannelGroup> {
+ if (Platform.OS === 'android') {
+ if (typeof channelGroupId !== 'string') {
+ throw new Error(
+ `AndroidNotifications:getChannel expects an 'string' but got type ${typeof channelGroupId}`
+ );
+ }
+ return Promise.resolve(
+ getNativeModule(this._notifications).getChannelGroup(channelGroupId)
+ );
+ }
+ return Promise.resolve(null);
+ }
+
+ getChannelGroups(): Promise<NativeAndroidChannelGroup[]> {
+ if (Platform.OS === 'android') {
+ return Promise.resolve(
+ getNativeModule(this._notifications).getChannelGroups()
+ );
+ }
+ return Promise.resolve([]);
+ }
}

dist/modules/notifications/index.js

@@ -12,6 +12,7 @@
import AndroidChannel from './AndroidChannel';
import AndroidChannelGroup from './AndroidChannelGroup';
import AndroidNotifications from './AndroidNotifications';
+import IOSNotifications from './IOSNotifications';
import AndroidRemoteInput from './AndroidRemoteInput';
import Notification from './Notification';
import { BadgeIconType, Category, Defaults, GroupAlert, Importance, Priority, SemanticAction, Visibility } from './types';
@@ -37,30 +38,31 @@
constructor(app) {
super(app, {
events: NATIVE_EVENTS,
- hasShards: false,
+ hasCustomUrlSupport: false,
moduleName: MODULE_NAME,
- multiApp: false,
+ hasMultiAppSupport: false,
namespace: NAMESPACE
});
this._android = new AndroidNotifications(this);
+ this._ios = new IOSNotifications(this);
SharedEventEmitter.addListener( // sub to internal native event - this fans out to
// public event name: onNotificationDisplayed
'notifications_notification_displayed', notification => {
- SharedEventEmitter.emit('onNotificationDisplayed', new Notification(notification));
+ SharedEventEmitter.emit('onNotificationDisplayed', new Notification(notification, this));
});
SharedEventEmitter.addListener( // sub to internal native event - this fans out to
// public event name: onNotificationOpened
'notifications_notification_opened', notificationOpen => {
SharedEventEmitter.emit('onNotificationOpened', {
action: notificationOpen.action,
- notification: new Notification(notificationOpen.notification),
+ notification: new Notification(notificationOpen.notification, this),
results: notificationOpen.results
});
});
SharedEventEmitter.addListener( // sub to internal native event - this fans out to
// public event name: onNotification
'notifications_notification_received', notification => {
- SharedEventEmitter.emit('onNotification', new Notification(notification));
+ SharedEventEmitter.emit('onNotification', new Notification(notification, this));
}); // Tell the native module that we're ready to receive events
if (Platform.OS === 'ios') {
@@ -71,6 +73,10 @@
get android() {
return this._android;
}
+
+ get ios() {
+ return this._ios;
+ }
/**
* Cancel all notifications
*/
@@ -120,7 +126,7 @@
if (notificationOpen) {
return {
action: notificationOpen.action,
- notification: new Notification(notificationOpen.notification),
+ notification: new Notification(notificationOpen.notification, this),
results: notificationOpen.results
};
}

dist/modules/notifications/index.js.flow

@@ -12,6 +12,7 @@
import AndroidChannel from './AndroidChannel';
import AndroidChannelGroup from './AndroidChannelGroup';
import AndroidNotifications from './AndroidNotifications';
+import IOSNotifications from './IOSNotifications';
import AndroidRemoteInput from './AndroidRemoteInput';
import Notification from './Notification';
import {
@@ -74,15 +75,18 @@
export default class Notifications extends ModuleBase {
_android: AndroidNotifications;
+ _ios: IOSNotifications;
+
constructor(app: App) {
super(app, {
events: NATIVE_EVENTS,
- hasShards: false,
+ hasCustomUrlSupport: false,
moduleName: MODULE_NAME,
- multiApp: false,
+ hasMultiAppSupport: false,
namespace: NAMESPACE,
});
this._android = new AndroidNotifications(this);
+ this._ios = new IOSNotifications(this);
SharedEventEmitter.addListener(
// sub to internal native event - this fans out to
@@ -91,7 +95,7 @@
(notification: NativeNotification) => {
SharedEventEmitter.emit(
'onNotificationDisplayed',
- new Notification(notification)
+ new Notification(notification, this)
);
}
);
@@ -103,7 +107,7 @@
(notificationOpen: NativeNotificationOpen) => {
SharedEventEmitter.emit('onNotificationOpened', {
action: notificationOpen.action,
- notification: new Notification(notificationOpen.notification),
+ notification: new Notification(notificationOpen.notification, this),
results: notificationOpen.results,
});
}
@@ -116,7 +120,7 @@
(notification: NativeNotification) => {
SharedEventEmitter.emit(
'onNotification',
- new Notification(notification)
+ new Notification(notification, this)
);
}
);
@@ -131,6 +135,10 @@
return this._android;
}
+ get ios(): IOSNotifications {
+ return this._ios;
+ }
+
/**
* Cancel all notifications
*/
@@ -184,7 +192,7 @@
if (notificationOpen) {
return {
action: notificationOpen.action,
- notification: new Notification(notificationOpen.notification),
+ notification: new Notification(notificationOpen.notification, this),
results: notificationOpen.results,
};
}

dist/modules/notifications/IOSNotification.js

@@ -2,28 +2,46 @@
*
* IOSNotification representation wrapper
*/
+import { isIOS } from '../../utils';
+import { getLogger } from '../../utils/log';
+import { getNativeModule } from '../../utils/native';
export default class IOSNotification {
- // alertAction | N/A
- // N/A | attachments
- // applicationIconBadgeNumber | badge
- // hasAction | N/A
- // alertLaunchImage | launchImageName
- // N/A | threadIdentifier
- constructor(notification, data) {
+ constructor(notification, notifications, data) {
this._notification = notification;
if (data) {
this._alertAction = data.alertAction;
- this._attachments = data.attachments;
+ this._attachments = data.attachments || [];
this._badge = data.badge;
this._category = data.category;
this._hasAction = data.hasAction;
this._launchImage = data.launchImage;
this._threadIdentifier = data.threadIdentifier;
- } // Defaults
+ } else {
+ this._attachments = [];
+ }
+
+ if (!isIOS || !notifications || !notifications.ios) {
+ return this;
+ } // IOS + Native Notification Only
+
+
+ const complete = fetchResult => {
+ const {
+ notificationId
+ } = notification; // && notifications check for Flow
+ if (notificationId && notifications) {
+ getLogger(notifications).debug(`Completion handler called for notificationId=${notificationId}`);
+ getNativeModule(notifications).complete(notificationId, fetchResult);
+ }
+ };
- this._attachments = this._attachments || [];
+ if (notifications.ios.shouldAutoComplete) {
+ complete(notifications.ios.backgroundFetchResult.noData);
+ } else {
+ this._complete = complete;
+ }
}
get alertAction() {
@@ -53,6 +71,10 @@
get threadIdentifier() {
return this._threadIdentifier;
}
+
+ get complete() {
+ return this._complete;
+ }
/**
*
* @param identifier

dist/modules/notifications/IOSNotification.js.flow

@@ -3,49 +3,79 @@
* IOSNotification representation wrapper
*/
import type Notification from './Notification';
+import type Notifications from './';
+import { type BackgroundFetchResultValue } from './IOSNotifications';
import type {
IOSAttachment,
IOSAttachmentOptions,
NativeIOSNotification,
} from './types';
+import { isIOS } from '../../utils';
+import { getLogger } from '../../utils/log';
+import { getNativeModule } from '../../utils/native';
+
+type CompletionHandler = BackgroundFetchResultValue => void;
+
export default class IOSNotification {
_alertAction: string | void;
- // alertAction | N/A
_attachments: IOSAttachment[];
- // N/A | attachments
_badge: number | void;
- // applicationIconBadgeNumber | badge
_category: string | void;
_hasAction: boolean | void;
- // hasAction | N/A
_launchImage: string | void;
- // alertLaunchImage | launchImageName
_notification: Notification;
- _threadIdentifier: string | void; // N/A | threadIdentifier
+ _threadIdentifier: string | void;
- constructor(notification: Notification, data?: NativeIOSNotification) {
+ _complete: CompletionHandler;
+
+ constructor(
+ notification: Notification,
+ notifications?: Notifications,
+ data?: NativeIOSNotification
+ ) {
this._notification = notification;
if (data) {
this._alertAction = data.alertAction;
- this._attachments = data.attachments;
+ this._attachments = data.attachments || [];
this._badge = data.badge;
this._category = data.category;
this._hasAction = data.hasAction;
this._launchImage = data.launchImage;
this._threadIdentifier = data.threadIdentifier;
+ } else {
+ this._attachments = [];
+ }
+
+ if (!isIOS || !notifications || !notifications.ios) {
+ return this;
}
- // Defaults
- this._attachments = this._attachments || [];
+ // IOS + Native Notification Only
+ const complete = (fetchResult: BackgroundFetchResultValue) => {
+ const { notificationId } = notification;
+ // && notifications check for Flow
+ if (notificationId && notifications) {
+ getLogger(notifications).debug(
+ `Completion handler called for notificationId=${notificationId}`
+ );
+ getNativeModule(notifications).complete(notificationId, fetchResult);
+ }
+ };
+
+ if (notifications.ios.shouldAutoComplete) {
+ complete(notifications.ios.backgroundFetchResult.noData);
+ } else {
+ this._complete = complete;
+ }
}
get alertAction(): ?string {
@@ -76,6 +106,10 @@
return this._threadIdentifier;
}
+ get complete(): CompletionHandler {
+ return this._complete;
+ }
+
/**
*
* @param identifier

dist/modules/notifications/IOSNotifications.js

@@ -0,0 +1,18 @@
+import { getNativeModule } from '../../utils/native';
+export default class IOSNotifications {
+ constructor(notifications) {
+ this.shouldAutoComplete = true;
+ const nativeModule = getNativeModule(notifications);
+ this._backgroundFetchResult = {
+ noData: nativeModule.backgroundFetchResultNoData,
+ newData: nativeModule.backgroundFetchResultNewData,
+ failure: nativeModule.backgroundFetchResultFailed
+ };
+ }
+
+ get backgroundFetchResult() {
+ return { ...this._backgroundFetchResult
+ };
+ }
+
+}
\ No newline at end of file

dist/modules/notifications/IOSNotifications.js.flow

@@ -0,0 +1,31 @@
+import { getNativeModule } from '../../utils/native';
+
+import type Notifications from './';
+
+export type BackgroundFetchResultValue = string;
+type BackgroundFetchResult = {
+ noData: BackgroundFetchResultValue,
+ newData: BackgroundFetchResultValue,
+ failure: BackgroundFetchResultValue,
+};
+
+export default class IOSNotifications {
+ _backgroundFetchResult: BackgroundFetchResult;
+
+ shouldAutoComplete: boolean;
+
+ constructor(notifications: Notifications) {
+ this.shouldAutoComplete = true;
+
+ const nativeModule = getNativeModule(notifications);
+ this._backgroundFetchResult = {
+ noData: nativeModule.backgroundFetchResultNoData,
+ newData: nativeModule.backgroundFetchResultNewData,
+ failure: nativeModule.backgroundFetchResultFailed,
+ };
+ }
+
+ get backgroundFetchResult(): BackgroundFetchResult {
+ return { ...this._backgroundFetchResult };
+ }
+}

dist/modules/notifications/Notification.js

@@ -13,19 +13,18 @@
// soundName | sound | sound
// N/A | subtitle | subText
// alertTitle | title | contentTitle
- constructor(data) {
- this._android = new AndroidNotification(this, data && data.android);
- this._ios = new IOSNotification(this, data && data.ios);
-
- if (data) {
- this._body = data.body;
- this._data = data.data;
- this._notificationId = data.notificationId;
- this._sound = data.sound;
- this._subtitle = data.subtitle;
- this._title = data.title;
- } // Defaults
+ constructor(nativeNotification, notifications) {
+ if (nativeNotification) {
+ this._body = nativeNotification.body;
+ this._data = nativeNotification.data;
+ this._notificationId = nativeNotification.notificationId;
+ this._sound = nativeNotification.sound;
+ this._subtitle = nativeNotification.subtitle;
+ this._title = nativeNotification.title;
+ }
+ this._android = new AndroidNotification(this, nativeNotification && nativeNotification.android);
+ this._ios = new IOSNotification(this, notifications, nativeNotification && nativeNotification.ios); // Defaults
this._data = this._data || {}; // TODO: Is this the best way to generate an ID?

dist/modules/notifications/Notification.js.flow

@@ -8,6 +8,7 @@
import { generatePushID, isObject } from '../../utils';
import type { NativeNotification } from './types';
+import type Notifications from './';
export type NotificationOpen = {|
action: string,
@@ -37,19 +38,30 @@
// N/A | subtitle | subText
_title: string; // alertTitle | title | contentTitle
- constructor(data?: NativeNotification) {
- this._android = new AndroidNotification(this, data && data.android);
- this._ios = new IOSNotification(this, data && data.ios);
-
- if (data) {
- this._body = data.body;
- this._data = data.data;
- this._notificationId = data.notificationId;
- this._sound = data.sound;
- this._subtitle = data.subtitle;
- this._title = data.title;
+ constructor(
+ nativeNotification?: NativeNotification,
+ notifications?: Notifications
+ ) {
+ if (nativeNotification) {
+ this._body = nativeNotification.body;
+ this._data = nativeNotification.data;
+ this._notificationId = nativeNotification.notificationId;
+ this._sound = nativeNotification.sound;
+ this._subtitle = nativeNotification.subtitle;
+ this._title = nativeNotification.title;
}
+ this._android = new AndroidNotification(
+ this,
+ nativeNotification && nativeNotification.android
+ );
+
+ this._ios = new IOSNotification(
+ this,
+ notifications,
+ nativeNotification && nativeNotification.ios
+ );
+
// Defaults
this._data = this._data || {};
// TODO: Is this the best way to generate an ID?

dist/modules/perf/HttpMetric.js.flow

@@ -3,7 +3,7 @@
* Trace representation wrapper
*/
import { getNativeModule } from '../../utils/native';
-import type PerformanceMonitoring from '.';
+import type PerformanceMonitoring from './';
export default class HttpMetric {
url: string;

dist/modules/perf/index.js

@@ -23,8 +23,8 @@
constructor(app) {
super(app, {
moduleName: MODULE_NAME,
- multiApp: false,
- hasShards: false,
+ hasMultiAppSupport: false,
+ hasCustomUrlSupport: false,
namespace: NAMESPACE
});
}

dist/modules/perf/index.js.flow

@@ -39,8 +39,8 @@
constructor(app: App) {
super(app, {
moduleName: MODULE_NAME,
- multiApp: false,
- hasShards: false,
+ hasMultiAppSupport: false,
+ hasCustomUrlSupport: false,
namespace: NAMESPACE,
});
}

dist/modules/perf/Trace.js.flow

@@ -3,7 +3,7 @@
* Trace representation wrapper
*/
import { getNativeModule } from '../../utils/native';
-import type PerformanceMonitoring from '.';
+import type PerformanceMonitoring from './';
export default class Trace {
identifier: string;

dist/modules/storage/index.js

@@ -23,8 +23,8 @@
super(app, {
events: NATIVE_EVENTS,
moduleName: MODULE_NAME,
- multiApp: true,
- hasShards: false,
+ hasMultiAppSupport: true,
+ hasCustomUrlSupport: false,
namespace: NAMESPACE
});
SharedEventEmitter.addListener(getAppEventName(this, 'storage_event'), this._handleStorageEvent.bind(this));

dist/modules/storage/index.js.flow

@@ -30,8 +30,8 @@
super(app, {
events: NATIVE_EVENTS,
moduleName: MODULE_NAME,
- multiApp: true,
- hasShards: false,
+ hasMultiAppSupport: true,
+ hasCustomUrlSupport: false,
namespace: NAMESPACE,
});

dist/modules/storage/reference.js

@@ -4,6 +4,7 @@
*/
import ReferenceBase from '../../utils/ReferenceBase';
import StorageTask, { UPLOAD_TASK, DOWNLOAD_TASK } from './task';
+import { isIOS } from '../../utils';
import { getNativeModule } from '../../utils/native';
/**
@@ -97,9 +98,9 @@
putFile(filePath, metadata = {}) {
- let _filePath = filePath.replace('file://', '');
+ let _filePath = isIOS ? filePath.replace('file://', '') : filePath;
- if (_filePath.includes('%')) _filePath = decodeURI(_filePath);
+ if (_filePath.includes('%')) _filePath = decodeURIComponent(_filePath);
return new StorageTask(UPLOAD_TASK, getNativeModule(this._storage).putFile(this.path, _filePath, metadata), this);
}

dist/modules/storage/reference.js.flow

@@ -4,8 +4,9 @@
*/
import ReferenceBase from '../../utils/ReferenceBase';
import StorageTask, { UPLOAD_TASK, DOWNLOAD_TASK } from './task';
+import { isIOS } from '../../utils';
import { getNativeModule } from '../../utils/native';
-import type Storage from '.';
+import type Storage from './';
/**
* @url https://firebase.google.com/docs/reference/js/firebase.storage.Reference
@@ -85,7 +86,7 @@
* Alias to putFile
* @returns {StorageReference.putFile}
*/
- get put(): (Object, Object) => StorageTask {
+ get put(): (string, Object) => StorageTask {
return this.putFile;
}
@@ -95,9 +96,9 @@
* @param {object} metadata An object containing metadata
* @return {Promise}
*/
- putFile(filePath: Object, metadata: Object = {}): StorageTask {
- let _filePath = filePath.replace('file://', '');
- if (_filePath.includes('%')) _filePath = decodeURI(_filePath);
+ putFile(filePath: string, metadata: Object = {}): StorageTask {
+ let _filePath = isIOS ? filePath.replace('file://', '') : filePath;
+ if (_filePath.includes('%')) _filePath = decodeURIComponent(_filePath);
return new StorageTask(
UPLOAD_TASK,
getNativeModule(this._storage).putFile(this.path, _filePath, metadata),

dist/modules/storage/task.js

@@ -2,7 +2,7 @@
*
* UploadTask representation wrapper
*/
-import { statics as StorageStatics } from '.';
+import { statics as StorageStatics } from './';
import { isFunction } from '../../utils';
export const UPLOAD_TASK = 'upload';
export const DOWNLOAD_TASK = 'download';

dist/modules/storage/task.js.flow

@@ -2,9 +2,9 @@
* @flow
* UploadTask representation wrapper
*/
-import { statics as StorageStatics } from '.';
+import { statics as StorageStatics } from './';
import { isFunction } from '../../utils';
-import type Storage from '.';
+import type Storage from './';
import type StorageReference from './reference';
export const UPLOAD_TASK = 'upload';

dist/modules/utils/database.js

@@ -0,0 +1,10 @@
+import SyncTree from '../../utils/SyncTree';
+export default {
+ /**
+ * Removes all database listeners (JS & Native)
+ */
+ cleanup() {
+ SyncTree.removeListenersForRegistrations(Object.keys(SyncTree._reverseLookup));
+ }
+
+};
\ No newline at end of file

dist/modules/utils/database.js.flow

@@ -0,0 +1,12 @@
+import SyncTree from '../../utils/SyncTree';
+
+export default {
+ /**
+ * Removes all database listeners (JS & Native)
+ */
+ cleanup(): void {
+ SyncTree.removeListenersForRegistrations(
+ Object.keys(SyncTree._reverseLookup)
+ );
+ },
+};

dist/modules/utils/index.js

@@ -2,6 +2,7 @@
import INTERNALS from '../../utils/internals';
import { isIOS } from '../../utils';
import ModuleBase from '../../utils/ModuleBase';
+import DatabaseUtils from './database';
const FirebaseCoreModule = NativeModules.RNFirebase;
export const MODULE_NAME = 'RNFirebaseUtils';
export const NAMESPACE = 'utils';
@@ -9,11 +10,15 @@
constructor(app) {
super(app, {
moduleName: MODULE_NAME,
- multiApp: false,
- hasShards: false,
+ hasMultiAppSupport: false,
+ hasCustomUrlSupport: false,
namespace: NAMESPACE
});
}
+
+ get database() {
+ return DatabaseUtils;
+ }
/**
*
*/
@@ -41,6 +46,11 @@
}
}
+ getPlayServicesStatus() {
+ if (isIOS) return Promise.resolve(null);
+ return FirebaseCoreModule.getPlayServicesStatus();
+ }
+
promptForPlayServices() {
if (isIOS) return null;
return FirebaseCoreModule.promptForPlayServices();

dist/modules/utils/index.js.flow

@@ -4,6 +4,7 @@
import { isIOS } from '../../utils';
import ModuleBase from '../../utils/ModuleBase';
import type App from '../core/app';
+import DatabaseUtils from './database';
const FirebaseCoreModule = NativeModules.RNFirebase;
@@ -22,12 +23,16 @@
constructor(app: App) {
super(app, {
moduleName: MODULE_NAME,
- multiApp: false,
- hasShards: false,
+ hasMultiAppSupport: false,
+ hasCustomUrlSupport: false,
namespace: NAMESPACE,
});
}
+ get database(): DatabaseUtils {
+ return DatabaseUtils;
+ }
+
/**
*
*/
@@ -55,6 +60,11 @@
}
}
+ getPlayServicesStatus(): Promise<GoogleApiAvailabilityType | null> {
+ if (isIOS) return Promise.resolve(null);
+ return FirebaseCoreModule.getPlayServicesStatus();
+ }
+
promptForPlayServices() {
if (isIOS) return null;
return FirebaseCoreModule.promptForPlayServices();

dist/types/index.js

@@ -1,17 +0,0 @@
-import '../modules/admob';
-import '../modules/analytics';
-import '../modules/auth';
-import '../modules/config';
-import '../modules/crashlytics';
-import '../modules/database';
-import '../modules/firestore';
-import '../modules/functions';
-import '../modules/iid';
-import '../modules/invites';
-import '../modules/links';
-import '../modules/messaging';
-import '../modules/notifications';
-import '../modules/perf';
-import '../modules/storage';
-import '../modules/utils';
-/* Core types */
\ No newline at end of file

dist/types/index.js.flow

@@ -49,8 +49,9 @@
export type FirebaseModuleConfig = {
events?: string[],
moduleName: FirebaseModuleName,
- multiApp: boolean,
- hasShards: boolean,
+ hasMultiAppSupport: boolean,
+ hasCustomUrlSupport?: boolean,
+ hasRegionsSupport?: boolean,
namespace: FirebaseNamespace,
};
@@ -97,6 +98,7 @@
messagingSenderId: string,
projectId: string,
storageBucket: string,
+ persistence?: boolean,
};
export type FirebaseModuleAndStatics<M: FirebaseModule, S: FirebaseStatics> = {

dist/utils/apps.js

@@ -1,11 +1,31 @@
import { NativeModules } from 'react-native';
import App from '../modules/core/app';
import INTERNALS from './internals';
-import { isAndroid, isObject, isString } from '.';
+import { isAndroid, isObject, isString } from './';
const FirebaseCoreModule = NativeModules.RNFirebase;
const APPS = {};
-const APP_MODULES = {};
const DEFAULT_APP_NAME = '[DEFAULT]';
+const APP_MODULES = {};
+const CUSTOM_URL_OR_REGION_NAMESPACES = {
+ database: true,
+ functions: true,
+ storage: false,
+ // TODO true once multi-bucket support added.
+ // for flow:
+ admob: false,
+ analytics: false,
+ auth: false,
+ config: false,
+ crashlytics: false,
+ firestore: false,
+ iid: false,
+ invites: false,
+ links: false,
+ messaging: false,
+ notifications: false,
+ perf: false,
+ utils: false
+};
export default {
DEFAULT_APP_NAME,
@@ -31,38 +51,39 @@
* @private
*/
appModule(app, namespace, InstanceClass) {
- return (serviceUrl = null) => {
- if (serviceUrl && namespace !== 'database') {
- throw new Error(INTERNALS.STRINGS.ERROR_INIT_SERVICE_URL_UNSUPPORTED(namespace));
+ return (customUrlOrRegion = null) => {
+ if (customUrlOrRegion && !CUSTOM_URL_OR_REGION_NAMESPACES[namespace]) {
+ throw new Error(INTERNALS.STRINGS.ERROR_INIT_SERVICE_URL_OR_REGION_UNSUPPORTED(namespace));
}
- const appOrShardName = serviceUrl || app.name;
+ const appInstanceIdentifier = `${app.name}${customUrlOrRegion || ''}`;
- if (!APP_MODULES[appOrShardName]) {
- APP_MODULES[appOrShardName] = {};
+ if (!APP_MODULES[appInstanceIdentifier]) {
+ APP_MODULES[appInstanceIdentifier] = {};
}
+ if (!APP_MODULES[appInstanceIdentifier][namespace]) {
+ APP_MODULES[appInstanceIdentifier][namespace] = new InstanceClass(app, customUrlOrRegion); // only check once on new app namespace instance
+
if (isAndroid && namespace !== 'utils' && !INTERNALS.FLAGS.checkedPlayServices) {
INTERNALS.FLAGS.checkedPlayServices = true;
app.utils().checkPlayServicesAvailability();
}
-
- if (!APP_MODULES[appOrShardName][namespace]) {
- APP_MODULES[appOrShardName][namespace] = new InstanceClass(serviceUrl || app, app.options);
}
- return APP_MODULES[appOrShardName][namespace];
+ return APP_MODULES[appInstanceIdentifier][namespace];
};
},
+ /**
+ *
+ * @param name
+ * @returns {*}
+ */
deleteApp(name) {
const app = APPS[name];
- if (!app) return Promise.resolve(true); // https://firebase.google.com/docs/reference/js/firebase.app.App#delete
-
- return app.delete().then(() => {
+ if (!app) return;
delete APPS[name];
- return true;
- });
},
/**
@@ -129,7 +150,7 @@
const app = FirebaseCoreModule.apps[i];
const options = Object.assign({}, app);
delete options.name;
- APPS[app.name] = new App(app.name, options, true);
+ APPS[app.name.toUpperCase()] = new App(app.name.toUpperCase(), options, true);
}
},
@@ -141,22 +162,28 @@
* @return {function(App=)}
*/
moduleAndStatics(namespace, statics, moduleName) {
- const getModule = appOrUrl => {
- let _app = appOrUrl;
- let _serviceUrl = null;
+ const getModule = (appOrUrlOrRegion, customUrlOrRegion) => {
+ let _app = appOrUrlOrRegion;
- if (typeof appOrUrl === 'string' && namespace === 'database') {
+ let _customUrlOrRegion = customUrlOrRegion || null;
+
+ if (typeof appOrUrlOrRegion === 'string' && CUSTOM_URL_OR_REGION_NAMESPACES[namespace]) {
_app = null;
- _serviceUrl = appOrUrl;
+ _customUrlOrRegion = appOrUrlOrRegion;
} // throw an error if it's not a valid app instance
- if (_app && !(_app instanceof App)) throw new Error(INTERNALS.STRINGS.ERROR_NOT_APP(namespace));else if (!_app) // default to the 'DEFAULT' app if no arg provided - will throw an error
+ if (_app && !(_app instanceof App)) {
+ throw new Error(INTERNALS.STRINGS.ERROR_NOT_APP(namespace));
+ } else if (!_app) {
+ // default to the 'DEFAULT' app if no arg provided - will throw an error
// if default app not initialized
- _app = this.app(DEFAULT_APP_NAME); // $FlowExpectedError: Flow doesn't support indexable signatures on classes: https://github.com/facebook/flow/issues/1323
+ _app = this.app(DEFAULT_APP_NAME);
+ } // $FlowExpectedError: Flow doesn't support indexable signatures on classes: https://github.com/facebook/flow/issues/1323
+
const module = _app[namespace];
- return module(_serviceUrl);
+ return module(_customUrlOrRegion);
};
return Object.assign(getModule, statics, {

dist/utils/apps.js.flow

@@ -4,7 +4,7 @@
import { NativeModules } from 'react-native';
import App from '../modules/core/app';
import INTERNALS from './internals';
-import { isAndroid, isObject, isString } from '.';
+import { isAndroid, isObject, isString } from './';
import type {
FirebaseModule,
@@ -18,8 +18,27 @@
const FirebaseCoreModule = NativeModules.RNFirebase;
const APPS: { [string]: App } = {};
-const APP_MODULES: { [string]: { [string]: FirebaseModule } } = {};
const DEFAULT_APP_NAME = '[DEFAULT]';
+const APP_MODULES: { [string]: { [string]: FirebaseModule } } = {};
+const CUSTOM_URL_OR_REGION_NAMESPACES = {
+ database: true,
+ functions: true,
+ storage: false, // TODO true once multi-bucket support added.
+ // for flow:
+ admob: false,
+ analytics: false,
+ auth: false,
+ config: false,
+ crashlytics: false,
+ firestore: false,
+ iid: false,
+ invites: false,
+ links: false,
+ messaging: false,
+ notifications: false,
+ perf: false,
+ utils: false,
+};
export default {
DEFAULT_APP_NAME,
@@ -49,18 +68,28 @@
namespace: FirebaseNamespace,
InstanceClass: Class<M>
): () => FirebaseModule {
- return (serviceUrl: ?string = null): M => {
- if (serviceUrl && namespace !== 'database') {
+ return (customUrlOrRegion: ?string = null): M => {
+ if (customUrlOrRegion && !CUSTOM_URL_OR_REGION_NAMESPACES[namespace]) {
throw new Error(
- INTERNALS.STRINGS.ERROR_INIT_SERVICE_URL_UNSUPPORTED(namespace)
+ INTERNALS.STRINGS.ERROR_INIT_SERVICE_URL_OR_REGION_UNSUPPORTED(
+ namespace
+ )
);
}
- const appOrShardName = serviceUrl || app.name;
- if (!APP_MODULES[appOrShardName]) {
- APP_MODULES[appOrShardName] = {};
+ const appInstanceIdentifier = `${app.name}${customUrlOrRegion || ''}`;
+
+ if (!APP_MODULES[appInstanceIdentifier]) {
+ APP_MODULES[appInstanceIdentifier] = {};
}
+ if (!APP_MODULES[appInstanceIdentifier][namespace]) {
+ APP_MODULES[appInstanceIdentifier][namespace] = new InstanceClass(
+ app,
+ customUrlOrRegion
+ );
+
+ // only check once on new app namespace instance
if (
isAndroid &&
namespace !== 'utils' &&
@@ -69,27 +98,21 @@
INTERNALS.FLAGS.checkedPlayServices = true;
app.utils().checkPlayServicesAvailability();
}
-
- if (!APP_MODULES[appOrShardName][namespace]) {
- APP_MODULES[appOrShardName][namespace] = new InstanceClass(
- serviceUrl || app,
- app.options
- );
}
- return APP_MODULES[appOrShardName][namespace];
+ return APP_MODULES[appInstanceIdentifier][namespace];
};
},
- deleteApp(name: string): Promise<boolean> {
+ /**
+ *
+ * @param name
+ * @returns {*}
+ */
+ deleteApp(name: string) {
const app = APPS[name];
- if (!app) return Promise.resolve(true);
-
- // https://firebase.google.com/docs/reference/js/firebase.app.App#delete
- return app.delete().then(() => {
+ if (!app) return;
delete APPS[name];
- return true;
- });
},
/**
@@ -157,7 +180,11 @@
const app = FirebaseCoreModule.apps[i];
const options = Object.assign({}, app);
delete options.name;
- APPS[app.name] = new App(app.name, options, true);
+ APPS[app.name.toUpperCase()] = new App(
+ app.name.toUpperCase(),
+ options,
+ true
+ );
}
},
@@ -173,24 +200,32 @@
statics: S,
moduleName: FirebaseModuleName
): FirebaseModuleAndStatics<M, S> {
- const getModule = (appOrUrl?: App | string): FirebaseModule => {
- let _app = appOrUrl;
- let _serviceUrl: ?string = null;
- if (typeof appOrUrl === 'string' && namespace === 'database') {
+ const getModule = (
+ appOrUrlOrRegion?: App | string,
+ customUrlOrRegion?: string
+ ): FirebaseModule => {
+ let _app = appOrUrlOrRegion;
+ let _customUrlOrRegion: ?string = customUrlOrRegion || null;
+
+ if (
+ typeof appOrUrlOrRegion === 'string' &&
+ CUSTOM_URL_OR_REGION_NAMESPACES[namespace]
+ ) {
_app = null;
- _serviceUrl = appOrUrl;
+ _customUrlOrRegion = appOrUrlOrRegion;
}
// throw an error if it's not a valid app instance
- if (_app && !(_app instanceof App))
+ if (_app && !(_app instanceof App)) {
throw new Error(INTERNALS.STRINGS.ERROR_NOT_APP(namespace));
- else if (!_app)
+ } else if (!_app) {
// default to the 'DEFAULT' app if no arg provided - will throw an error
// if default app not initialized
_app = this.app(DEFAULT_APP_NAME);
+ }
// $FlowExpectedError: Flow doesn't support indexable signatures on classes: https://github.com/facebook/flow/issues/1323
const module = _app[namespace];
- return module(_serviceUrl);
+ return module(_customUrlOrRegion);
};
return Object.assign(getModule, statics, {

dist/utils/emitter/EmitterSubscription.js

@@ -1,51 +0,0 @@
-/* eslint-disable */
-
-/**
- * Copyright (c) 2015-present, Facebook, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the BSD-style license found in the
- * LICENSE file in the root directory of this source tree. An additional grant
- * of patent rights can be found in the PATENTS file in the same directory.
- *
- * @noflow
- */
-'use strict';
-
-const EventSubscription = require('./EventSubscription');
-
-/**
- * EmitterSubscription represents a subscription with listener and context data.
- */
-class EmitterSubscription extends EventSubscription {
- /**
- * @param {EventEmitter} emitter - The event emitter that registered this
- * subscription
- * @param {EventSubscriptionVendor} subscriber - The subscriber that controls
- * this subscription
- * @param {function} listener - Function to invoke when the specified event is
- * emitted
- * @param {*} context - Optional context object to use when invoking the
- * listener
- */
- constructor(emitter, subscriber, listener, context) {
- super(subscriber);
- this.emitter = emitter;
- this.listener = listener;
- this.context = context;
- }
- /**
- * Removes this subscription from the emitter that registered it.
- * Note: we're overriding the `remove()` method of EventSubscription here
- * but deliberately not calling `super.remove()` as the responsibility
- * for removing the subscription lies with the EventEmitter.
- */
-
-
- remove() {
- this.emitter.removeSubscription(this);
- }
-
-}
-
-module.exports = EmitterSubscription;
\ No newline at end of file

dist/utils/emitter/EmitterSubscription.js.flow

@@ -1,61 +0,0 @@
-/* eslint-disable */
-/**
- * Copyright (c) 2015-present, Facebook, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the BSD-style license found in the
- * LICENSE file in the root directory of this source tree. An additional grant
- * of patent rights can be found in the PATENTS file in the same directory.
- *
- * @noflow
- */
-'use strict';
-
-const EventSubscription = require('./EventSubscription');
-
-import type EventEmitter from './EventEmitter';
-import type EventSubscriptionVendor from './EventSubscriptionVendor';
-
-/**
- * EmitterSubscription represents a subscription with listener and context data.
- */
-class EmitterSubscription extends EventSubscription {
-
- emitter: EventEmitter;
- listener: Function;
- context: ?Object;
-
- /**
- * @param {EventEmitter} emitter - The event emitter that registered this
- * subscription
- * @param {EventSubscriptionVendor} subscriber - The subscriber that controls
- * this subscription
- * @param {function} listener - Function to invoke when the specified event is
- * emitted
- * @param {*} context - Optional context object to use when invoking the
- * listener
- */
- constructor(
- emitter: EventEmitter,
- subscriber: EventSubscriptionVendor,
- listener: Function,
- context: ?Object
- ) {
- super(subscriber);
- this.emitter = emitter;
- this.listener = listener;
- this.context = context;
- }
-
- /**
- * Removes this subscription from the emitter that registered it.
- * Note: we're overriding the `remove()` method of EventSubscription here
- * but deliberately not calling `super.remove()` as the responsibility
- * for removing the subscription lies with the EventEmitter.
- */
- remove() {
- this.emitter.removeSubscription(this);
- }
-}
-
-module.exports = EmitterSubscription;

dist/utils/emitter/EventEmitter.js

@@ -1,215 +0,0 @@
-/* eslint-disable */
-
-/**
- * Copyright (c) 2015-present, Facebook, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the BSD-style license found in the
- * LICENSE file in the root directory of this source tree. An additional grant
- * of patent rights can be found in the PATENTS file in the same directory.
- *
- * @noflow
- * @typecheck
- */
-'use strict';
-
-const EmitterSubscription = require('./EmitterSubscription');
-
-const EventSubscriptionVendor = require('./EventSubscriptionVendor');
-
-const emptyFunction = require('fbjs/lib/emptyFunction');
-
-const invariant = require('fbjs/lib/invariant');
-/**
- * @class EventEmitter
- * @description
- * An EventEmitter is responsible for managing a set of listeners and publishing
- * events to them when it is told that such events happened. In addition to the
- * data for the given event it also sends a event control object which allows
- * the listeners/handlers to prevent the default behavior of the given event.
- *
- * The emitter is designed to be generic enough to support all the different
- * contexts in which one might want to emit events. It is a simple multicast
- * mechanism on top of which extra functionality can be composed. For example, a
- * more advanced emitter may use an EventHolder and EventFactory.
- */
-
-
-class EventEmitter {
- /**
- * @constructor
- *
- * @param {EventSubscriptionVendor} subscriber - Optional subscriber instance
- * to use. If omitted, a new subscriber will be created for the emitter.
- */
- constructor(subscriber) {
- this._subscriber = subscriber || new EventSubscriptionVendor();
- }
- /**
- * Adds a listener to be invoked when events of the specified type are
- * emitted. An optional calling context may be provided. The data arguments
- * emitted will be passed to the listener function.
- *
- * TODO: Annotate the listener arg's type. This is tricky because listeners
- * can be invoked with varargs.
- *
- * @param {string} eventType - Name of the event to listen to
- * @param {function} listener - Function to invoke when the specified event is
- * emitted
- * @param {*} context - Optional context object to use when invoking the
- * listener
- */
-
-
- addListener(eventType, listener, context) {
- return this._subscriber.addSubscription(eventType, new EmitterSubscription(this, this._subscriber, listener, context));
- }
- /**
- * Similar to addListener, except that the listener is removed after it is
- * invoked once.
- *
- * @param {string} eventType - Name of the event to listen to
- * @param {function} listener - Function to invoke only once when the
- * specified event is emitted
- * @param {*} context - Optional context object to use when invoking the
- * listener
- */
-
-
- once(eventType, listener, context) {
- return this.addListener(eventType, (...args) => {
- this.removeCurrentListener();
- listener.apply(context, args);
- });
- }
- /**
- * Removes all of the registered listeners, including those registered as
- * listener maps.
- *
- * @param {?string} eventType - Optional name of the event whose registered
- * listeners to remove
- */
-
-
- removeAllListeners(eventType) {
- this._subscriber.removeAllSubscriptions(eventType);
- }
- /**
- * Provides an API that can be called during an eventing cycle to remove the
- * last listener that was invoked. This allows a developer to provide an event
- * object that can remove the listener (or listener map) during the
- * invocation.
- *
- * If it is called when not inside of an emitting cycle it will throw.
- *
- * @throws {Error} When called not during an eventing cycle
- *
- * @example
- * var subscription = emitter.addListenerMap({
- * someEvent: function(data, event) {
- * console.log(data);
- * emitter.removeCurrentListener();
- * }
- * });
- *
- * emitter.emit('someEvent', 'abc'); // logs 'abc'
- * emitter.emit('someEvent', 'def'); // does not log anything
- */
-
-
- removeCurrentListener() {
- invariant(!!this._currentSubscription, 'Not in an emitting cycle; there is no current subscription');
- this.removeSubscription(this._currentSubscription);
- }
- /**
- * Removes a specific subscription. Called by the `remove()` method of the
- * subscription itself to ensure any necessary cleanup is performed.
- */
-
-
- removeSubscription(subscription) {
- invariant(subscription.emitter === this, 'Subscription does not belong to this emitter.');
-
- this._subscriber.removeSubscription(subscription);
- }
- /**
- * Returns an array of listeners that are currently registered for the given
- * event.
- *
- * @param {string} eventType - Name of the event to query
- * @returns {array}
- */
-
-
- listeners(eventType) {
- const subscriptions = this._subscriber.getSubscriptionsForType(eventType);
-
- return subscriptions ? subscriptions.filter(emptyFunction.thatReturnsTrue).map(function (subscription) {
- return subscription.listener;
- }) : [];
- }
- /**
- * Emits an event of the given type with the given data. All handlers of that
- * particular type will be notified.
- *
- * @param {string} eventType - Name of the event to emit
- * @param {...*} Arbitrary arguments to be passed to each registered listener
- *
- * @example
- * emitter.addListener('someEvent', function(message) {
- * console.log(message);
- * });
- *
- * emitter.emit('someEvent', 'abc'); // logs 'abc'
- */
-
-
- emit(eventType) {
- const subscriptions = this._subscriber.getSubscriptionsForType(eventType);
-
- if (subscriptions) {
- for (let i = 0, l = subscriptions.length; i < l; i++) {
- const subscription = subscriptions[i]; // The subscription may have been removed during this event loop.
-
- if (subscription) {
- this._currentSubscription = subscription;
- subscription.listener.apply(subscription.context, Array.prototype.slice.call(arguments, 1));
- }
- }
-
- this._currentSubscription = null;
- }
- }
- /**
- * Removes the given listener for event of specific type.
- *
- * @param {string} eventType - Name of the event to emit
- * @param {function} listener - Function to invoke when the specified event is
- * emitted
- *
- * @example
- * emitter.removeListener('someEvent', function(message) {
- * console.log(message);
- * }); // removes the listener if already registered
- *
- */
-
-
- removeListener(eventType, listener) {
- const subscriptions = this._subscriber.getSubscriptionsForType(eventType);
-
- if (subscriptions) {
- for (let i = 0, l = subscriptions.length; i < l; i++) {
- const subscription = subscriptions[i]; // The subscription may have been removed during this event loop.
- // its listener matches the listener in method parameters
-
- if (subscription && subscription.listener === listener) {
- subscription.remove();
- }
- }
- }
- }
-
-}
-
-module.exports = EventEmitter;
\ No newline at end of file

dist/utils/emitter/EventEmitter.js.flow

@@ -1,220 +0,0 @@
-/* eslint-disable */
-/**
- * Copyright (c) 2015-present, Facebook, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the BSD-style license found in the
- * LICENSE file in the root directory of this source tree. An additional grant
- * of patent rights can be found in the PATENTS file in the same directory.
- *
- * @noflow
- * @typecheck
- */
-'use strict';
-
-const EmitterSubscription = require('./EmitterSubscription');
-const EventSubscriptionVendor = require('./EventSubscriptionVendor');
-
-const emptyFunction = require('fbjs/lib/emptyFunction');
-const invariant = require('fbjs/lib/invariant');
-
-/**
- * @class EventEmitter
- * @description
- * An EventEmitter is responsible for managing a set of listeners and publishing
- * events to them when it is told that such events happened. In addition to the
- * data for the given event it also sends a event control object which allows
- * the listeners/handlers to prevent the default behavior of the given event.
- *
- * The emitter is designed to be generic enough to support all the different
- * contexts in which one might want to emit events. It is a simple multicast
- * mechanism on top of which extra functionality can be composed. For example, a
- * more advanced emitter may use an EventHolder and EventFactory.
- */
-class EventEmitter {
-
- _subscriber: EventSubscriptionVendor;
- _currentSubscription: ?EmitterSubscription;
-
- /**
- * @constructor
- *
- * @param {EventSubscriptionVendor} subscriber - Optional subscriber instance
- * to use. If omitted, a new subscriber will be created for the emitter.
- */
- constructor(subscriber: ?EventSubscriptionVendor) {
- this._subscriber = subscriber || new EventSubscriptionVendor();
- }
-
- /**
- * Adds a listener to be invoked when events of the specified type are
- * emitted. An optional calling context may be provided. The data arguments
- * emitted will be passed to the listener function.
- *
- * TODO: Annotate the listener arg's type. This is tricky because listeners
- * can be invoked with varargs.
- *
- * @param {string} eventType - Name of the event to listen to
- * @param {function} listener - Function to invoke when the specified event is
- * emitted
- * @param {*} context - Optional context object to use when invoking the
- * listener
- */
- addListener(
- eventType: string, listener: Function, context: ?Object): EmitterSubscription {
-
- return (this._subscriber.addSubscription(
- eventType,
- new EmitterSubscription(this, this._subscriber, listener, context)
- ) : any);
- }
-
- /**
- * Similar to addListener, except that the listener is removed after it is
- * invoked once.
- *
- * @param {string} eventType - Name of the event to listen to
- * @param {function} listener - Function to invoke only once when the
- * specified event is emitted
- * @param {*} context - Optional context object to use when invoking the
- * listener
- */
- once(eventType: string, listener: Function, context: ?Object): EmitterSubscription {
- return this.addListener(eventType, (...args) => {
- this.removeCurrentListener();
- listener.apply(context, args);
- });
- }
-
- /**
- * Removes all of the registered listeners, including those registered as
- * listener maps.
- *
- * @param {?string} eventType - Optional name of the event whose registered
- * listeners to remove
- */
- removeAllListeners(eventType: ?string) {
- this._subscriber.removeAllSubscriptions(eventType);
- }
-
- /**
- * Provides an API that can be called during an eventing cycle to remove the
- * last listener that was invoked. This allows a developer to provide an event
- * object that can remove the listener (or listener map) during the
- * invocation.
- *
- * If it is called when not inside of an emitting cycle it will throw.
- *
- * @throws {Error} When called not during an eventing cycle
- *
- * @example
- * var subscription = emitter.addListenerMap({
- * someEvent: function(data, event) {
- * console.log(data);
- * emitter.removeCurrentListener();
- * }
- * });
- *
- * emitter.emit('someEvent', 'abc'); // logs 'abc'
- * emitter.emit('someEvent', 'def'); // does not log anything
- */
- removeCurrentListener() {
- invariant(
- !!this._currentSubscription,
- 'Not in an emitting cycle; there is no current subscription'
- );
- this.removeSubscription(this._currentSubscription);
- }
-
- /**
- * Removes a specific subscription. Called by the `remove()` method of the
- * subscription itself to ensure any necessary cleanup is performed.
- */
- removeSubscription(subscription: EmitterSubscription) {
- invariant(
- subscription.emitter === this,
- 'Subscription does not belong to this emitter.'
- );
- this._subscriber.removeSubscription(subscription);
- }
-
- /**
- * Returns an array of listeners that are currently registered for the given
- * event.
- *
- * @param {string} eventType - Name of the event to query
- * @returns {array}
- */
- listeners(eventType: string): [EmitterSubscription] {
- const subscriptions: ?[EmitterSubscription] = (this._subscriber.getSubscriptionsForType(eventType): any);
- return subscriptions
- ? subscriptions.filter(emptyFunction.thatReturnsTrue).map(
- function(subscription) {
- return subscription.listener;
- })
- : [];
- }
-
- /**
- * Emits an event of the given type with the given data. All handlers of that
- * particular type will be notified.
- *
- * @param {string} eventType - Name of the event to emit
- * @param {...*} Arbitrary arguments to be passed to each registered listener
- *
- * @example
- * emitter.addListener('someEvent', function(message) {
- * console.log(message);
- * });
- *
- * emitter.emit('someEvent', 'abc'); // logs 'abc'
- */
- emit(eventType: string) {
- const subscriptions: ?[EmitterSubscription] = (this._subscriber.getSubscriptionsForType(eventType): any);
- if (subscriptions) {
- for (let i = 0, l = subscriptions.length; i < l; i++) {
- const subscription = subscriptions[i];
-
- // The subscription may have been removed during this event loop.
- if (subscription) {
- this._currentSubscription = subscription;
- subscription.listener.apply(
- subscription.context,
- Array.prototype.slice.call(arguments, 1)
- );
- }
- }
- this._currentSubscription = null;
- }
- }
-
- /**
- * Removes the given listener for event of specific type.
- *
- * @param {string} eventType - Name of the event to emit
- * @param {function} listener - Function to invoke when the specified event is
- * emitted
- *
- * @example
- * emitter.removeListener('someEvent', function(message) {
- * console.log(message);
- * }); // removes the listener if already registered
- *
- */
- removeListener(eventType: string, listener) {
- const subscriptions: ?[EmitterSubscription] = (this._subscriber.getSubscriptionsForType(eventType): any);
- if (subscriptions) {
- for (let i = 0, l = subscriptions.length; i < l; i++) {
- const subscription = subscriptions[i];
-
- // The subscription may have been removed during this event loop.
- // its listener matches the listener in method parameters
- if (subscription && subscription.listener === listener) {
- subscription.remove();
- }
- }
- }
- }
-}
-
-module.exports = EventEmitter;

dist/utils/emitter/EventSubscription.js

@@ -1,38 +0,0 @@
-/* eslint-disable */
-
-/**
- * Copyright (c) 2015-present, Facebook, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the BSD-style license found in the
- * LICENSE file in the root directory of this source tree. An additional grant
- * of patent rights can be found in the PATENTS file in the same directory.
- *
- *
- */
-'use strict';
-
-/**
- * EventSubscription represents a subscription to a particular event. It can
- * remove its own subscription.
- */
-class EventSubscription {
- /**
- * @param {EventSubscriptionVendor} subscriber the subscriber that controls
- * this subscription.
- */
- constructor(subscriber) {
- this.subscriber = subscriber;
- }
- /**
- * Removes this subscription from the subscriber that controls it.
- */
-
-
- remove() {
- this.subscriber.removeSubscription(this);
- }
-
-}
-
-module.exports = EventSubscription;
\ No newline at end of file

dist/utils/emitter/EventSubscription.js.flow

@@ -1,42 +0,0 @@
-/* eslint-disable */
-/**
- * Copyright (c) 2015-present, Facebook, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the BSD-style license found in the
- * LICENSE file in the root directory of this source tree. An additional grant
- * of patent rights can be found in the PATENTS file in the same directory.
- *
- * @flow
- */
-'use strict';
-
-import type EventSubscriptionVendor from './EventSubscriptionVendor';
-
-/**
- * EventSubscription represents a subscription to a particular event. It can
- * remove its own subscription.
- */
-class EventSubscription {
-
- eventType: string;
- key: number;
- subscriber: EventSubscriptionVendor;
-
- /**
- * @param {EventSubscriptionVendor} subscriber the subscriber that controls
- * this subscription.
- */
- constructor(subscriber: EventSubscriptionVendor) {
- this.subscriber = subscriber;
- }
-
- /**
- * Removes this subscription from the subscriber that controls it.
- */
- remove() {
- this.subscriber.removeSubscription(this);
- }
-}
-
-module.exports = EventSubscription;

dist/utils/emitter/EventSubscriptionVendor.js

@@ -1,101 +0,0 @@
-/* eslint-disable */
-
-/**
- * Copyright (c) 2015-present, Facebook, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the BSD-style license found in the
- * LICENSE file in the root directory of this source tree. An additional grant
- * of patent rights can be found in the PATENTS file in the same directory.
- *
- *
- */
-'use strict';
-
-const invariant = require('fbjs/lib/invariant');
-
-/**
- * EventSubscriptionVendor stores a set of EventSubscriptions that are
- * subscribed to a particular event type.
- */
-class EventSubscriptionVendor {
- constructor() {
- this._subscriptionsForType = {};
- this._currentSubscription = null;
- }
- /**
- * Adds a subscription keyed by an event type.
- *
- * @param {string} eventType
- * @param {EventSubscription} subscription
- */
-
-
- addSubscription(eventType, subscription) {
- invariant(subscription.subscriber === this, 'The subscriber of the subscription is incorrectly set.');
-
- if (!this._subscriptionsForType[eventType]) {
- this._subscriptionsForType[eventType] = [];
- }
-
- const key = this._subscriptionsForType[eventType].length;
-
- this._subscriptionsForType[eventType].push(subscription);
-
- subscription.eventType = eventType;
- subscription.key = key;
- return subscription;
- }
- /**
- * Removes a bulk set of the subscriptions.
- *
- * @param {?string} eventType - Optional name of the event type whose
- * registered supscriptions to remove, if null remove all subscriptions.
- */
-
-
- removeAllSubscriptions(eventType) {
- if (eventType === undefined) {
- this._subscriptionsForType = {};
- } else {
- delete this._subscriptionsForType[eventType];
- }
- }
- /**
- * Removes a specific subscription. Instead of calling this function, call
- * `subscription.remove()` directly.
- *
- * @param {object} subscription
- */
-
-
- removeSubscription(subscription) {
- const eventType = subscription.eventType;
- const key = subscription.key;
- const subscriptionsForType = this._subscriptionsForType[eventType];
-
- if (subscriptionsForType) {
- delete subscriptionsForType[key];
- }
- }
- /**
- * Returns the array of subscriptions that are currently registered for the
- * given event type.
- *
- * Note: This array can be potentially sparse as subscriptions are deleted
- * from it when they are removed.
- *
- * TODO: This returns a nullable array. wat?
- *
- * @param {string} eventType
- * @returns {?array}
- */
-
-
- getSubscriptionsForType(eventType) {
- return this._subscriptionsForType[eventType];
- }
-
-}
-
-module.exports = EventSubscriptionVendor;
\ No newline at end of file

dist/utils/emitter/EventSubscriptionVendor.js.flow

@@ -1,100 +0,0 @@
-/* eslint-disable */
-/**
- * Copyright (c) 2015-present, Facebook, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the BSD-style license found in the
- * LICENSE file in the root directory of this source tree. An additional grant
- * of patent rights can be found in the PATENTS file in the same directory.
- *
- * @flow
- */
-'use strict';
-
-const invariant = require('fbjs/lib/invariant');
-
-import type EventSubscription from './EventSubscription';
-
-/**
- * EventSubscriptionVendor stores a set of EventSubscriptions that are
- * subscribed to a particular event type.
- */
-class EventSubscriptionVendor {
-
- _subscriptionsForType: Object;
- _currentSubscription: ?EventSubscription;
-
- constructor() {
- this._subscriptionsForType = {};
- this._currentSubscription = null;
- }
-
- /**
- * Adds a subscription keyed by an event type.
- *
- * @param {string} eventType
- * @param {EventSubscription} subscription
- */
- addSubscription(
- eventType: string, subscription: EventSubscription): EventSubscription {
- invariant(
- subscription.subscriber === this,
- 'The subscriber of the subscription is incorrectly set.');
- if (!this._subscriptionsForType[eventType]) {
- this._subscriptionsForType[eventType] = [];
- }
- const key = this._subscriptionsForType[eventType].length;
- this._subscriptionsForType[eventType].push(subscription);
- subscription.eventType = eventType;
- subscription.key = key;
- return subscription;
- }
-
- /**
- * Removes a bulk set of the subscriptions.
- *
- * @param {?string} eventType - Optional name of the event type whose
- * registered supscriptions to remove, if null remove all subscriptions.
- */
- removeAllSubscriptions(eventType: ?string) {
- if (eventType === undefined) {
- this._subscriptionsForType = {};
- } else {
- delete this._subscriptionsForType[eventType];
- }
- }
-
- /**
- * Removes a specific subscription. Instead of calling this function, call
- * `subscription.remove()` directly.
- *
- * @param {object} subscription
- */
- removeSubscription(subscription: Object) {
- const eventType = subscription.eventType;
- const key = subscription.key;
-
- const subscriptionsForType = this._subscriptionsForType[eventType];
- if (subscriptionsForType) {
- delete subscriptionsForType[key];
- }
- }
-
- /**
- * Returns the array of subscriptions that are currently registered for the
- * given event type.
- *
- * Note: This array can be potentially sparse as subscriptions are deleted
- * from it when they are removed.
- *
- * TODO: This returns a nullable array. wat?
- *
- * @param {string} eventType
- * @returns {?array}
- */
- getSubscriptionsForType(eventType: string): ?[EventSubscription] {
- return this._subscriptionsForType[eventType];
- }
-}
-
-module.exports = EventSubscriptionVendor;

dist/utils/events.js

@@ -1,5 +1,5 @@
import { NativeEventEmitter, NativeModules } from 'react-native';
-import EventEmitter from './emitter/EventEmitter';
+import EventEmitter from 'react-native/Libraries/vendor/emitter/EventEmitter';
const NATIVE_EMITTERS = {};
const NATIVE_SUBSCRIPTIONS = {};
export const SharedEventEmitter = new EventEmitter();
@@ -21,6 +21,7 @@
* so we use a single event send it to js and js then internally can prefix it
* and distribute dynamically.
*
+ * @param moduleName
* @param module
* @param eventName
* @private

dist/utils/events.js.flow

@@ -2,7 +2,7 @@
* @flow
*/
import { NativeEventEmitter, NativeModules } from 'react-native';
-import EventEmitter from './emitter/EventEmitter';
+import EventEmitter from 'react-native/Libraries/vendor/emitter/EventEmitter';
import type ModuleBase from './ModuleBase';
import type { FirebaseModuleConfig, FirebaseModuleName } from '../types';
@@ -35,6 +37,7 @@
* so we use a single event send it to js and js then internally can prefix it
* and distribute dynamically.
*
+ * @param moduleName
* @param module
* @param eventName
* @private

dist/utils/index.js

@@ -105,7 +105,7 @@
* @returns {boolean}
*/
-export function isObject(item): %checks {
+export function isObject(item) {
return item ? typeof item === 'object' && !Array.isArray(item) && item !== null : false;
}
/**
@@ -114,7 +114,7 @@
* @returns {*|boolean}
*/
-export function isFunction(item): %checks {
+export function isFunction(item) {
return item ? typeof item === 'function' : false;
}
/**
@@ -123,7 +123,7 @@
* @return {boolean}
*/
-export function isString(value): %checks {
+export function isString(value) {
return typeof value === 'string';
}
/**
@@ -132,7 +132,7 @@
* @return {boolean}
*/
-export function isBoolean(value): %checks {
+export function isBoolean(value) {
return typeof value === 'boolean';
} // platform checks

dist/utils/internals.js

@@ -75,8 +75,8 @@
/**
* @return {string}
*/
- ERROR_INIT_SERVICE_URL_UNSUPPORTED(namespace) {
- return `${namespace} does not support URL as a param, please pass in an app.`;
+ ERROR_INIT_SERVICE_URL_OR_REGION_UNSUPPORTED(namespace) {
+ return `${namespace} does not support a URL or region as a param, please pass in an app.`;
},
/**

dist/utils/internals.js.flow

@@ -100,8 +100,8 @@
/**
* @return {string}
*/
- ERROR_INIT_SERVICE_URL_UNSUPPORTED(namespace: string) {
- return `${namespace} does not support URL as a param, please pass in an app.`;
+ ERROR_INIT_SERVICE_URL_OR_REGION_UNSUPPORTED(namespace: string) {
+ return `${namespace} does not support a URL or region as a param, please pass in an app.`;
},
/**

dist/utils/ModuleBase.js

@@ -1,12 +1,13 @@
-import { initialiseLogger } from './log';
+import { initialiseLogger, getLogger } from './log';
import { initialiseNativeModule } from './native';
export default class ModuleBase {
/**
*
* @param app
* @param config
+ * @param customUrlOrRegion
*/
- constructor(app, config, serviceUrl) {
+ constructor(app, config, customUrlOrRegion) {
if (!config.moduleName) {
throw new Error('Missing module name');
}
@@ -19,10 +20,10 @@
moduleName
} = config;
this._app = app;
- this._serviceUrl = serviceUrl;
+ this._customUrlOrRegion = customUrlOrRegion;
this.namespace = config.namespace; // check if native module exists as all native
- initialiseNativeModule(this, config, serviceUrl);
+ initialiseNativeModule(this, config, customUrlOrRegion);
initialiseLogger(this, `${app.name}:${moduleName.replace('RNFirebase', '')}`);
}
/**
@@ -35,4 +36,8 @@
return this._app;
}
+ get log() {
+ return getLogger(this);
+ }
+
}
\ No newline at end of file

dist/utils/ModuleBase.js.flow

@@ -1,7 +1,7 @@
/**
* @flow
*/
-import { initialiseLogger } from './log';
+import { initialiseLogger, getLogger } from './log';
import { initialiseNativeModule } from './native';
import type App from '../modules/core/app';
@@ -10,7 +10,7 @@
export default class ModuleBase {
_app: App;
- _serviceUrl: ?string;
+ _customUrlOrRegion: ?string;
namespace: FirebaseNamespace;
@@ -18,8 +18,13 @@
*
* @param app
* @param config
+ * @param customUrlOrRegion
*/
- constructor(app: App, config: FirebaseModuleConfig, serviceUrl: ?string) {
+ constructor(
+ app: App,
+ config: FirebaseModuleConfig,
+ customUrlOrRegion: ?string
+ ) {
if (!config.moduleName) {
throw new Error('Missing module name');
}
@@ -26,13 +32,15 @@
if (!config.namespace) {
throw new Error('Missing namespace');
}
+
const { moduleName } = config;
this._app = app;
- this._serviceUrl = serviceUrl;
+ this._customUrlOrRegion = customUrlOrRegion;
this.namespace = config.namespace;
// check if native module exists as all native
- initialiseNativeModule(this, config, serviceUrl);
+ initialiseNativeModule(this, config, customUrlOrRegion);
+
initialiseLogger(
this,
`${app.name}:${moduleName.replace('RNFirebase', '')}`
@@ -46,4 +54,8 @@
get app(): App {
return this._app;
}
+
+ get log() {
+ return getLogger(this);
+ }
}

dist/utils/native.js

@@ -15,20 +15,25 @@
for (let i = 0, len = methods.length; i < len; i++) {
const method = methods[i];
+ if (typeof NativeModule[method] === 'function') {
native[method] = (...args) => NativeModule[method](...[...argToPrepend, ...args]);
+ } else {
+ native[method] = NativeModule[method];
+ }
}
return native;
};
-const nativeModuleKey = module => `${module._serviceUrl || module.app.name}:${module.namespace}`;
+const nativeModuleKey = module => `${module._customUrlOrRegion || module.app.name}:${module.namespace}`;
export const getNativeModule = module => NATIVE_MODULES[nativeModuleKey(module)];
-export const initialiseNativeModule = (module, config, serviceUrl) => {
+export const initialiseNativeModule = (module, config, customUrlOrRegion) => {
const {
moduleName,
- multiApp,
- hasShards,
+ hasMultiAppSupport,
+ hasCustomUrlSupport,
+ hasRegionsSupport,
namespace
} = config;
const nativeModule = NativeModules[moduleName];
@@ -42,12 +47,12 @@
const argToPrepend = [];
- if (multiApp) {
+ if (hasMultiAppSupport) {
argToPrepend.push(module.app.name);
}
- if (hasShards) {
- argToPrepend.push(serviceUrl);
+ if (hasCustomUrlSupport || hasRegionsSupport) {
+ argToPrepend.push(customUrlOrRegion);
}
if (argToPrepend.length) {

dist/utils/native.js.flow

@@ -24,15 +24,19 @@
for (let i = 0, len = methods.length; i < len; i++) {
const method = methods[i];
+ if (typeof NativeModule[method] === 'function') {
native[method] = (...args) =>
NativeModule[method](...[...argToPrepend, ...args]);
+ } else {
+ native[method] = NativeModule[method];
+ }
}
return native;
};
const nativeModuleKey = (module: ModuleBase): string =>
- `${module._serviceUrl || module.app.name}:${module.namespace}`;
+ `${module._customUrlOrRegion || module.app.name}:${module.namespace}`;
export const getNativeModule = (module: ModuleBase): Object =>
NATIVE_MODULES[nativeModuleKey(module)];
@@ -40,9 +44,15 @@
export const initialiseNativeModule = (
module: ModuleBase,
config: FirebaseModuleConfig,
- serviceUrl: ?string
+ customUrlOrRegion: ?string
): Object => {
- const { moduleName, multiApp, hasShards, namespace } = config;
+ const {
+ moduleName,
+ hasMultiAppSupport,
+ hasCustomUrlSupport,
+ hasRegionsSupport,
+ namespace,
+ } = config;
const nativeModule = NativeModules[moduleName];
const key = nativeModuleKey(module);
@@ -55,11 +65,13 @@
// used by the modules that extend ModuleBase
// to access their native module counterpart
const argToPrepend = [];
- if (multiApp) {
+
+ if (hasMultiAppSupport) {
argToPrepend.push(module.app.name);
}
- if (hasShards) {
- argToPrepend.push(serviceUrl);
+
+ if (hasCustomUrlSupport || hasRegionsSupport) {
+ argToPrepend.push(customUrlOrRegion);
}
if (argToPrepend.length) {

dist/utils/SyncTree.js

@@ -2,7 +2,7 @@
import { SharedEventEmitter } from './events';
import DataSnapshot from '../modules/database/DataSnapshot';
import DatabaseReference from '../modules/database/Reference';
-import { isString, nativeToJSError } from '.';
+import { isString, nativeToJSError } from './';
/**
* Internally used to manage firebase database realtime event

dist/utils/SyncTree.js.flow

@@ -6,7 +6,7 @@
import { SharedEventEmitter } from './events';
import DataSnapshot from '../modules/database/DataSnapshot';
import DatabaseReference from '../modules/database/Reference';
-import { isString, nativeToJSError } from '.';
+import { isString, nativeToJSError } from './';
type Listener = DataSnapshot => any;

dist/version.js

@@ -1,2 +1,2 @@
// generated by genversion
-module.exports = '5.0.0-rc0';
\ No newline at end of file
+module.exports = '5.4.0';
\ No newline at end of file

dist/version.js.flow

@@ -1,2 +1,2 @@
// generated by genversion
-module.exports = '5.0.0-rc0'
+module.exports = '5.4.0'

.eslintignore

@@ -1 +0,0 @@
-src/version.js

ios/RNFirebase/admob/BannerComponent.m

@@ -42,18 +42,20 @@
[self initBanner:[RNFirebaseAdMob stringToAdSize:_size]];
[self addSubview:_banner];
-
+ _banner.adUnitID = _unitId;
+ [self setRequested:YES];
+ [_banner loadRequest:[RNFirebaseAdMob buildRequest:_request]];
[self sendEvent:@"onSizeChange" payload:@{
@"width": @(_banner.bounds.size.width),
@"height": @(_banner.bounds.size.height),
}];
-
- _banner.adUnitID = _unitId;
- [self setRequested:YES];
- [_banner loadRequest:[RNFirebaseAdMob buildRequest:_request]];
}
- (void)sendEvent:(NSString *)type payload:(NSDictionary *_Nullable)payload {
+ if (!self.onBannerEvent) {
+ return;
+ }
+
self.onBannerEvent(@{
@"type": type,
@"payload": payload != nil ? payload : [NSNull null],

ios/RNFirebase/admob/RNFirebaseAdMobBannerManager.m

@@ -7,12 +7,17 @@
#if __has_include(<GoogleMobileAds/GADMobileAds.h>)
+RCT_EXPORT_VIEW_PROPERTY(size, NSString);
+RCT_EXPORT_VIEW_PROPERTY(unitId, NSString);
+RCT_EXPORT_VIEW_PROPERTY(request, NSDictionary);
+RCT_EXPORT_VIEW_PROPERTY(onBannerEvent, RCTBubblingEventBlock);
@synthesize bridge = _bridge;
- (UIView *)view
{
- return [[BannerComponent alloc] init];
+ BannerComponent *banner = [BannerComponent new];
+ return banner;
}
- (dispatch_queue_t)methodQueue
@@ -20,13 +25,6 @@
return dispatch_get_main_queue();
}
-
-RCT_EXPORT_VIEW_PROPERTY(size, NSString);
-RCT_EXPORT_VIEW_PROPERTY(unitId, NSString);
-RCT_EXPORT_VIEW_PROPERTY(request, NSDictionary);
-
-RCT_EXPORT_VIEW_PROPERTY(onBannerEvent, RCTBubblingEventBlock);
-
#endif
@end
\ No newline at end of file

ios/RNFirebase/auth/RNFirebaseAuth.h

@@ -8,11 +8,68 @@
#import <React/RCTEventEmitter.h>
@interface RNFirebaseAuth : RCTEventEmitter <RCTBridgeModule> {};
-@property NSMutableDictionary *authStateHandlers;
-@property NSMutableDictionary *idTokenHandlers;
-
@end
+extern NSString * const AuthErrorCode_toJSErrorCode[];
+NSString * const AuthErrorCode_toJSErrorCode[] = {
+ [FIRAuthErrorCodeInvalidCustomToken] = @"auth/invalid-custom-token",
+ [FIRAuthErrorCodeCustomTokenMismatch] = @"auth/custom-token-mismatch",
+ [FIRAuthErrorCodeInvalidCredential] = @"auth/invalid-credential",
+ [FIRAuthErrorCodeUserDisabled] = @"auth/user-disabled",
+ [FIRAuthErrorCodeOperationNotAllowed] = @"auth/operation-not-allowed",
+ [FIRAuthErrorCodeEmailAlreadyInUse] = @"auth/email-already-in-use",
+ [FIRAuthErrorCodeInvalidEmail] = @"auth/invalid-email",
+ [FIRAuthErrorCodeWrongPassword] = @"auth/wrong-password",
+ [FIRAuthErrorCodeTooManyRequests] = @"auth/too-many-requests",
+ [FIRAuthErrorCodeUserNotFound] = @"auth/user-not-found",
+ [FIRAuthErrorCodeAccountExistsWithDifferentCredential] = @"auth/account-exists-with-different-credential",
+ [FIRAuthErrorCodeRequiresRecentLogin] = @"auth/requires-recent-login",
+ [FIRAuthErrorCodeProviderAlreadyLinked] = @"auth/provider-already-linked",
+ [FIRAuthErrorCodeNoSuchProvider] = @"auth/no-such-provider",
+ [FIRAuthErrorCodeInvalidUserToken] = @"auth/invalid-user-token",
+ [FIRAuthErrorCodeNetworkError] = @"auth/network-request-failed",
+ [FIRAuthErrorCodeUserTokenExpired] = @"auth/user-token-expired",
+ [FIRAuthErrorCodeInvalidAPIKey] = @"auth/invalid-api-key",
+ [FIRAuthErrorCodeUserMismatch] = @"auth/user-mismatch",
+ [FIRAuthErrorCodeCredentialAlreadyInUse] = @"auth/credential-already-in-use",
+ [FIRAuthErrorCodeWeakPassword] = @"auth/weak-password",
+ [FIRAuthErrorCodeAppNotAuthorized] = @"auth/app-not-authorized",
+ [FIRAuthErrorCodeExpiredActionCode] = @"auth/expired-action-code",
+ [FIRAuthErrorCodeInvalidActionCode] = @"auth/invalid-action-code",
+ [FIRAuthErrorCodeInvalidMessagePayload] = @"auth/invalid-message-payload",
+ [FIRAuthErrorCodeInvalidSender] = @"auth/invalid-sender",
+ [FIRAuthErrorCodeInvalidRecipientEmail] = @"auth/invalid-recipient-email",
+ [FIRAuthErrorCodeMissingEmail] = @"auth/invalid-email",
+ [FIRAuthErrorCodeMissingIosBundleID] = @"auth/missing-ios-bundle-id",
+ [FIRAuthErrorCodeMissingAndroidPackageName] = @"auth/missing-android-pkg-name",
+ [FIRAuthErrorCodeUnauthorizedDomain] = @"auth/unauthorized-domain",
+ [FIRAuthErrorCodeInvalidContinueURI] = @"auth/invalid-continue-uri",
+ [FIRAuthErrorCodeMissingContinueURI] = @"auth/missing-continue-uri",
+ [FIRAuthErrorCodeMissingPhoneNumber] = @"auth/missing-phone-number",
+ [FIRAuthErrorCodeInvalidPhoneNumber] = @"auth/invalid-phone-number",
+ [FIRAuthErrorCodeMissingVerificationCode] = @"auth/missing-verification-code",
+ [FIRAuthErrorCodeInvalidVerificationCode] = @"auth/invalid-verification-code",
+ [FIRAuthErrorCodeMissingVerificationID] = @"auth/missing-verification-id",
+ [FIRAuthErrorCodeInvalidVerificationID] = @"auth/invalid-verification-id",
+ [FIRAuthErrorCodeMissingAppCredential] = @"auth/missing-app-credential",
+ [FIRAuthErrorCodeInvalidAppCredential] = @"auth/invalid-app-credential",
+ [FIRAuthErrorCodeSessionExpired] = @"auth/code-expired",
+ [FIRAuthErrorCodeQuotaExceeded] = @"auth/quota-exceeded",
+ [FIRAuthErrorCodeMissingAppToken] = @"auth/missing-apns-token",
+ [FIRAuthErrorCodeNotificationNotForwarded] = @"auth/notification-not-forwarded",
+ [FIRAuthErrorCodeAppNotVerified] = @"auth/app-not-verified",
+ [FIRAuthErrorCodeCaptchaCheckFailed] = @"auth/captcha-check-failed",
+ [FIRAuthErrorCodeWebContextAlreadyPresented] = @"auth/cancelled-popup-request",
+ [FIRAuthErrorCodeWebContextCancelled] = @"auth/popup-closed-by-user",
+ [FIRAuthErrorCodeAppVerificationUserInteractionFailure] = @"auth/app-verification-user-interaction-failure",
+ [FIRAuthErrorCodeInvalidClientID] = @"auth/invalid-oauth-client-id",
+ [FIRAuthErrorCodeWebNetworkRequestFailed] = @"auth/network-request-failed",
+ [FIRAuthErrorCodeWebInternalError] = @"auth/internal-error",
+ [FIRAuthErrorCodeNullUser] = @"auth/null-user",
+ [FIRAuthErrorCodeKeychainError] = @"auth/keychain-error",
+ [FIRAuthErrorCodeInternalError] = @"auth/internal-error",
+ [FIRAuthErrorCodeMalformedJWT] = @"auth/malformed-jwt"
+};
#else
@interface RNFirebaseAuth : NSObject
@end

ios/RNFirebase/auth/RNFirebaseAuth.m

@@ -1,76 +1,140 @@
#import "RNFirebaseAuth.h"
#import "RNFirebaseEvents.h"
#import "RNFirebaseUtil.h"
-#import "RCTDefines.h"
-
#if __has_include(<FirebaseAuth/FIRAuth.h>)
+static NSString *const keyIOS = @"iOS";
+static NSString *const keyUrl = @"url";
+static NSString *const keyUid = @"uid";
+static NSString *const keyUser = @"user";
+static NSString *const keyEmail = @"email";
+static NSString *const keyAndroid = @"android";
+static NSString *const keyProfile = @"profile";
+static NSString *const keyNewUser = @"isNewUser";
+static NSString *const keyUsername = @"username";
+static NSString *const keyPhotoUrl = @"photoURL";
+static NSString *const keyBundleId = @"bundleId";
+static NSString *const keyInstallApp = @"installApp";
+static NSString *const keyProviderId = @"providerId";
+static NSString *const keyPhoneNumber = @"phoneNumber";
+static NSString *const keyDisplayName = @"displayName";
+static NSString *const keyPackageName = @"packageName";
+static NSString *const keyMinVersion = @"minimumVersion";
+static NSString *const constAppLanguage = @"APP_LANGUAGE";
+static NSString *const constAppUser = @"APP_USER";
+static NSString *const keyHandleCodeInApp = @"handleCodeInApp";
+static NSString *const keyAdditionalUserInfo = @"additionalUserInfo";
+
+static NSMutableDictionary *authStateHandlers;
+static NSMutableDictionary *idTokenHandlers;
+
@implementation RNFirebaseAuth
RCT_EXPORT_MODULE();
- (id)init {
self = [super init];
- if (self != nil) {
- _authStateHandlers = [[NSMutableDictionary alloc] init];
- _idTokenHandlers = [[NSMutableDictionary alloc] init];
- }
+
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ authStateHandlers = [[NSMutableDictionary alloc] init];
+ idTokenHandlers = [[NSMutableDictionary alloc] init];
+ DLog(@"RNFirebaseAuth:instance-created");
+ });
+
+ DLog(@"RNFirebaseAuth:instance-initialized");
return self;
}
-/**
- addAuthStateListener
+- (void)dealloc {
+ DLog(@"RNFirebaseAuth:instance-destroyed");
+
+ for(NSString* key in authStateHandlers) {
+ FIRApp *firApp = [RNFirebaseUtil getApp:key];
+ [[FIRAuth authWithApp:firApp] removeAuthStateDidChangeListener:[authStateHandlers valueForKey:key]];
+ [authStateHandlers removeObjectForKey:key];
+ }
+
+ for(NSString* key in idTokenHandlers) {
+ FIRApp *firApp = [RNFirebaseUtil getApp:key];
+ [[FIRAuth authWithApp:firApp] removeIDTokenDidChangeListener:[idTokenHandlers valueForKey:key]];
+ [idTokenHandlers removeObjectForKey:key];
+ }
+}
+
+- (void)invalidate {
+ // dealloc sometimes is not called when app is reloaded.
+ for(NSString* key in authStateHandlers) {
+ FIRApp *firApp = [RNFirebaseUtil getApp:key];
+ [[FIRAuth authWithApp:firApp] removeAuthStateDidChangeListener:[authStateHandlers valueForKey:key]];
+ [authStateHandlers removeObjectForKey:key];
+ }
+
+ for(NSString* key in idTokenHandlers) {
+ FIRApp *firApp = [RNFirebaseUtil getApp:key];
+ [[FIRAuth authWithApp:firApp] removeIDTokenDidChangeListener:[idTokenHandlers valueForKey:key]];
+ [idTokenHandlers removeObjectForKey:key];
+ }
+}
+
+/**
+ * addAuthStateListener
+ *
*/
RCT_EXPORT_METHOD(addAuthStateListener:
(NSString *) appDisplayName) {
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
- if (![_authStateHandlers valueForKey:firApp.name]) {
- FIRAuthStateDidChangeListenerHandle newListenerHandle = [[FIRAuth authWithApp:firApp] addAuthStateDidChangeListener:^(FIRAuth *_Nonnull auth, FIRUser *_Nullable user) {
+ if (![authStateHandlers valueForKey:firApp.name]) {
+ FIRAuthStateDidChangeListenerHandle newListenerHandle =
+ [[FIRAuth authWithApp:firApp] addAuthStateDidChangeListener:^(FIRAuth *_Nonnull auth, FIRUser *_Nullable user) {
if (user != nil) {
- [RNFirebaseUtil sendJSEventWithAppName:self app:firApp name:AUTH_STATE_CHANGED_EVENT body:@{@"user": [self firebaseUserToDict:user]}];
+ [RNFirebaseUtil sendJSEventWithAppName:self app:firApp name:AUTH_STATE_CHANGED_EVENT body:@{
+ keyUser: [self firebaseUserToDict:user]}];
} else {
[RNFirebaseUtil sendJSEventWithAppName:self app:firApp name:AUTH_STATE_CHANGED_EVENT body:@{}];
}
}];
- _authStateHandlers[firApp.name] = [NSValue valueWithNonretainedObject:newListenerHandle];
+ authStateHandlers[firApp.name] = [NSValue valueWithNonretainedObject:newListenerHandle];
}
}
/**
- removeAuthStateListener
-
+ * removeAuthStateListener
+ *
*/
RCT_EXPORT_METHOD(removeAuthStateListener:
(NSString *) appDisplayName) {
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
- if ([_authStateHandlers valueForKey:firApp.name]) {
- [[FIRAuth authWithApp:firApp] removeAuthStateDidChangeListener:[_authStateHandlers valueForKey:firApp.name]];
- [_authStateHandlers removeObjectForKey:firApp.name];
+ if ([authStateHandlers valueForKey:firApp.name]) {
+ [[FIRAuth authWithApp:firApp] removeAuthStateDidChangeListener:[authStateHandlers valueForKey:firApp.name]];
+ [authStateHandlers removeObjectForKey:firApp.name];
}
}
/**
- addIdTokenListener
-
+ * addIdTokenListener
+ *
*/
RCT_EXPORT_METHOD(addIdTokenListener:
(NSString *) appDisplayName) {
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
- if (![_idTokenHandlers valueForKey:firApp.name]) {
- FIRIDTokenDidChangeListenerHandle newListenerHandle = [[FIRAuth authWithApp:firApp] addIDTokenDidChangeListener:^(FIRAuth * _Nonnull auth, FIRUser * _Nullable user) {
+ if (![idTokenHandlers valueForKey:firApp.name]) {
+ FIRIDTokenDidChangeListenerHandle newListenerHandle =
+ [[FIRAuth authWithApp:firApp] addIDTokenDidChangeListener:^(FIRAuth *_Nonnull auth, FIRUser *_Nullable user) {
if (user != nil) {
- [RNFirebaseUtil sendJSEventWithAppName:self app:firApp name:AUTH_ID_TOKEN_CHANGED_EVENT body:@{@"user": [self firebaseUserToDict:user]}];
+ [RNFirebaseUtil sendJSEventWithAppName:self app:firApp name:AUTH_ID_TOKEN_CHANGED_EVENT body:@{
+ keyUser: [self firebaseUserToDict:user]}];
} else {
[RNFirebaseUtil sendJSEventWithAppName:self app:firApp name:AUTH_ID_TOKEN_CHANGED_EVENT body:@{}];
}
}];
- _idTokenHandlers[firApp.name] = [NSValue valueWithNonretainedObject:newListenerHandle];
+ idTokenHandlers[firApp.name] = [NSValue valueWithNonretainedObject:newListenerHandle];
}
}
@@ -82,12 +146,26 @@
(NSString *) appDisplayName) {
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
- if ([_idTokenHandlers valueForKey:firApp.name]) {
- [[FIRAuth authWithApp:firApp] removeIDTokenDidChangeListener:[_idTokenHandlers valueForKey:firApp.name]];
- [_idTokenHandlers removeObjectForKey:firApp.name];
+ if ([idTokenHandlers valueForKey:firApp.name]) {
+ [[FIRAuth authWithApp:firApp] removeIDTokenDidChangeListener:[idTokenHandlers valueForKey:firApp.name]];
+ [idTokenHandlers removeObjectForKey:firApp.name];
}
}
+/**
+ * Flag to determine whether app verification should be disabled for testing or not.
+ *
+ * @return
+ */
+RCT_EXPORT_METHOD(
+ setAppVerificationDisabledForTesting:
+ (NSString *) appDisplayName
+ disabled:
+ (BOOL) disabled
+) {
+ FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
+ [FIRAuth authWithApp:firApp].settings.appVerificationDisabledForTesting = disabled;
+}
/**
@@ -125,38 +203,19 @@
@param RCTPromiseRejectBlock reject
@return
*/
-RCT_EXPORT_METHOD(signInAnonymously:(NSString *) appDisplayName
- resolver:(RCTPromiseResolveBlock) resolve
- rejecter:(RCTPromiseRejectBlock) reject) {
- [self signInAnonymously:appDisplayName withData:false resolver:resolve rejecter:reject];
-}
-
-/**
- signInAnonymouslyAndRetrieveData
-
- @param RCTPromiseResolveBlock resolve
- @param RCTPromiseRejectBlock reject
- @return
- */
-RCT_EXPORT_METHOD(signInAnonymouslyAndRetrieveData:(NSString *) appDisplayName
- resolver:(RCTPromiseResolveBlock) resolve
- rejecter:(RCTPromiseRejectBlock) reject) {
- [self signInAnonymously:appDisplayName withData:true resolver:resolve rejecter:reject];
-}
-
--(void)signInAnonymously:(NSString *)appDisplayName
- withData:(BOOL)withData
- resolver:(RCTPromiseResolveBlock) resolve
- rejecter:(RCTPromiseRejectBlock) reject {
+RCT_EXPORT_METHOD(signInAnonymously:
+ (NSString *) appDisplayName
+ resolver:
+ (RCTPromiseResolveBlock) resolve
+ rejecter:
+ (RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
[[FIRAuth authWithApp:firApp] signInAnonymouslyWithCompletion:^(FIRAuthDataResult *authResult, NSError *error) {
if (error) {
[self promiseRejectAuthException:reject error:error];
- } else if (withData) {
- [self promiseWithAuthResult:resolve rejecter:reject authResult:authResult];
} else {
- [self promiseWithUser:resolve rejecter:reject user:authResult.user];
+ [self promiseWithAuthResult:resolve rejecter:reject authResult:authResult];
}
}];
}
@@ -170,46 +229,24 @@
@param RCTPromiseRejectBlock reject
@return return
*/
-RCT_EXPORT_METHOD(signInWithEmailAndPassword:(NSString *) appDisplayName
- email:(NSString *) email
- pass:(NSString *) password
- resolver:(RCTPromiseResolveBlock) resolve
- rejecter:(RCTPromiseRejectBlock) reject) {
- [self signInWithEmail:appDisplayName email:email password:password withData:false resolver:resolve rejecter:reject];
-}
-
-/**
- signInAndRetrieveDataWithEmailAndPassword
-
- @param NSString NSString email
- @param NSString NSString password
- @param RCTPromiseResolveBlock resolve
- @param RCTPromiseRejectBlock reject
- @return return
- */
-RCT_EXPORT_METHOD(signInAndRetrieveDataWithEmailAndPassword:(NSString *) appDisplayName
- email:(NSString *) email
- password:(NSString *) password
- resolver:(RCTPromiseResolveBlock) resolve
- rejecter:(RCTPromiseRejectBlock) reject) {
- [self signInWithEmail:appDisplayName email:email password:password withData:true resolver:resolve rejecter:reject];
-}
-
--(void)signInWithEmail:(NSString *)appDisplayName
- email:(NSString *)email
- password:(NSString *)password
- withData:(BOOL)withData
- resolver:(RCTPromiseResolveBlock) resolve
- rejecter:(RCTPromiseRejectBlock) reject {
+RCT_EXPORT_METHOD(signInWithEmailAndPassword:
+ (NSString *) appDisplayName
+ email:
+ (NSString *) email
+ password:
+ (NSString *) password
+ resolver:
+ (RCTPromiseResolveBlock) resolve
+ rejecter:
+ (RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
- [[FIRAuth authWithApp:firApp] signInWithEmail:email password:password completion:^(FIRAuthDataResult *authResult, NSError *error) {
+ [[FIRAuth authWithApp:firApp] signInWithEmail:email password:password completion:^(FIRAuthDataResult *authResult,
+ NSError *error) {
if (error) {
[self promiseRejectAuthException:reject error:error];
- } else if (withData) {
- [self promiseWithAuthResult:resolve rejecter:reject authResult:authResult];
} else {
- [self promiseWithUser:resolve rejecter:reject user:authResult.user];
+ [self promiseWithAuthResult:resolve rejecter:reject authResult:authResult];
}
}];
}
@@ -223,14 +260,20 @@
@param RCTPromiseRejectBlock reject
@return return
*/
-RCT_EXPORT_METHOD(signInWithEmailLink:(NSString *) appDisplayName
- email:(NSString *) email
- emailLink:(NSString *) emailLink
- resolver:(RCTPromiseResolveBlock) resolve
- rejecter:(RCTPromiseRejectBlock) reject) {
+RCT_EXPORT_METHOD(signInWithEmailLink:
+ (NSString *) appDisplayName
+ email:
+ (NSString *) email
+ emailLink:
+ (NSString *) emailLink
+ resolver:
+ (RCTPromiseResolveBlock) resolve
+ rejecter:
+ (RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
- [[FIRAuth authWithApp:firApp] signInWithEmail:email link:emailLink completion:^(FIRAuthDataResult *authResult, NSError *error) {
+ [[FIRAuth authWithApp:firApp] signInWithEmail:email link:emailLink completion:^(FIRAuthDataResult *authResult,
+ NSError *error) {
if (error) {
[self promiseRejectAuthException:reject error:error];
} else {
@@ -248,46 +291,25 @@
@param RCTPromiseRejectBlock reject
@return return
*/
-RCT_EXPORT_METHOD(createUserWithEmailAndPassword:(NSString *) appDisplayName
- email:(NSString *) email
- pass:(NSString *) password
- resolver:(RCTPromiseResolveBlock) resolve
- rejecter:(RCTPromiseRejectBlock) reject) {
- [self createUserWithEmail:appDisplayName email:email password:password withData:false resolver:resolve rejecter:reject];
-}
-
-/**
- createUserAndRetrieveDataWithEmailAndPassword
-
- @param NSString NSString email
- @param NSString NSString password
- @param RCTPromiseResolveBlock resolve
- @param RCTPromiseRejectBlock reject
- @return return
- */
-RCT_EXPORT_METHOD(createUserAndRetrieveDataWithEmailAndPassword:(NSString *) appDisplayName
- email:(NSString *) email
- password:(NSString *) password
- resolver:(RCTPromiseResolveBlock) resolve
- rejecter:(RCTPromiseRejectBlock) reject) {
- [self createUserWithEmail:appDisplayName email:email password:password withData:true resolver:resolve rejecter:reject];
-}
--(void)createUserWithEmail:(NSString *)appDisplayName
- email:(NSString *)email
- password:(NSString *)password
- withData:(BOOL)withData
- resolver:(RCTPromiseResolveBlock) resolve
- rejecter:(RCTPromiseRejectBlock) reject {
+RCT_EXPORT_METHOD(createUserWithEmailAndPassword:
+ (NSString *) appDisplayName
+ email:
+ (NSString *) email
+ password:
+ (NSString *) password
+ resolver:
+ (RCTPromiseResolveBlock) resolve
+ rejecter:
+ (RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
- [[FIRAuth authWithApp:firApp] createUserWithEmail:email password:password completion:^(FIRAuthDataResult *authResult, NSError *error) {
+ [[FIRAuth authWithApp:firApp] createUserWithEmail:email password:password completion:^(FIRAuthDataResult *authResult,
+ NSError *error) {
if (error) {
[self promiseRejectAuthException:reject error:error];
- } else if (withData) {
- [self promiseWithAuthResult:resolve rejecter:reject authResult:authResult];
} else {
- [self promiseWithUser:resolve rejecter:reject user:authResult.user];
+ [self promiseWithAuthResult:resolve rejecter:reject authResult:authResult];
}
}];
}
@@ -339,7 +361,7 @@
FIRUser *user = [FIRAuth authWithApp:firApp].currentUser;
if (user) {
- [self reloadAndReturnUser:user resolver:resolve rejecter: reject];
+ [self reloadAndReturnUser:user resolver:resolve rejecter:reject];
} else {
[self promiseNoUser:resolve rejecter:reject isError:YES];
}
@@ -352,10 +374,14 @@
@param RCTPromiseRejectBlock reject
@return return
*/
-RCT_EXPORT_METHOD(sendEmailVerification:(NSString *) appDisplayName
- actionCodeSettings:(NSDictionary *) actionCodeSettings
- resolver:(RCTPromiseResolveBlock) resolve
- rejecter:(RCTPromiseRejectBlock) reject) {
+RCT_EXPORT_METHOD(sendEmailVerification:
+ (NSString *) appDisplayName
+ actionCodeSettings:
+ (NSDictionary *) actionCodeSettings
+ resolver:
+ (RCTPromiseResolveBlock) resolve
+ rejecter:
+ (RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
FIRUser *user = [FIRAuth authWithApp:firApp].currentUser;
@@ -403,7 +429,7 @@
if (error) {
[self promiseRejectAuthException:reject error:error];
} else {
- [self reloadAndReturnUser:user resolver:resolve rejecter: reject];
+ [self reloadAndReturnUser:user resolver:resolve rejecter:reject];
}
}];
} else {
@@ -445,6 +471,57 @@
}
}
+
+/**
+ updatePhoneNumber
+
+ @param NSString password
+ @param NSString provider
+ @param NSString authToken
+ @param NSString authSecret
+ @param RCTPromiseResolveBlock resolve
+ @param RCTPromiseRejectBlock reject
+ @return
+ */
+RCT_EXPORT_METHOD(updatePhoneNumber:
+ (NSString *) appDisplayName
+ provider:
+ (NSString *) provider
+ token:
+ (NSString *) authToken
+ secret:
+ (NSString *) authSecret
+ resolver:
+ (RCTPromiseResolveBlock) resolve
+ rejecter:
+ (RCTPromiseRejectBlock) reject) {
+ FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
+
+ FIRUser *user = [FIRAuth authWithApp:firApp].currentUser;
+
+ if (user) {
+ FIRPhoneAuthCredential *credential =
+ (FIRPhoneAuthCredential *) [self getCredentialForProvider:provider token:authToken secret:authSecret];
+
+ if (credential == nil) {
+ return reject(@"auth/invalid-credential",
+ @"The supplied auth credential is malformed, has expired or is not currently supported.",
+ nil);
+ }
+
+ [user updatePhoneNumberCredential:credential completion:^(NSError *_Nullable error) {
+ if (error) {
+ [self promiseRejectAuthException:reject error:error];
+ } else {
+ FIRUser *userAfterUpdate = [FIRAuth authWithApp:firApp].currentUser;
+ [self promiseWithUser:resolve rejecter:reject user:userAfterUpdate];
+ }
+ }];
+ } else {
+ [self promiseNoUser:resolve rejecter:reject isError:YES];
+ }
+}
+
/**
updateProfile
@@ -471,14 +548,14 @@
for (NSString *key in allKeys) {
@try {
- if ([key isEqualToString:@"photoURL"]) {
+ if ([key isEqualToString:keyPhotoUrl]) {
NSURL *url = [NSURL URLWithString:[props valueForKey:key]];
[changeRequest setValue:url forKey:key];
} else {
[changeRequest setValue:props[key] forKey:key];
}
} @catch (NSException *exception) {
- NSLog(@"Exception occurred while configuring: %@", exception);
+ DLog(@"Exception occurred while configuring: %@", exception);
}
}
@@ -486,7 +563,7 @@
if (error) {
[self promiseRejectAuthException:reject error:error];
} else {
- [self reloadAndReturnUser:user resolver:resolve rejecter: reject];
+ [self reloadAndReturnUser:user resolver:resolve rejecter:reject];
}
}];
} else {
@@ -495,13 +572,13 @@
}
/**
- getToken
+ getIdToken
@param RCTPromiseResolveBlock resolve
@param RCTPromiseRejectBlock reject
@return
*/
-RCT_EXPORT_METHOD(getToken:
+RCT_EXPORT_METHOD(getIdToken:
(NSString *) appDisplayName
forceRefresh:
(BOOL) forceRefresh
@@ -527,26 +604,53 @@
}
/**
- signInWithCredential
-
- @param NSString provider
- @param NSString authToken
- @param NSString authSecret
- @param RCTPromiseResolveBlock resolve
- @param RCTPromiseRejectBlock reject
- @return
+ * getIdTokenResult
+ *
+ * @param RCTPromiseResolveBlock resolve
+ * @param RCTPromiseRejectBlock reject
+ * @return
*/
-RCT_EXPORT_METHOD(signInWithCredential:(NSString *) appDisplayName
- provider:(NSString *) provider
- token:(NSString *) authToken
- secret:(NSString *) authSecret
- resolver:(RCTPromiseResolveBlock) resolve
- rejecter:(RCTPromiseRejectBlock) reject) {
- [self signInWithCredential:appDisplayName provider:provider token:authToken secret:authSecret withData:false resolver:resolve rejecter:reject];
+RCT_EXPORT_METHOD(getIdTokenResult:
+ (NSString *) appDisplayName
+ forceRefresh:
+ (BOOL) forceRefresh
+ resolver:
+ (RCTPromiseResolveBlock) resolve
+ rejecter:
+ (RCTPromiseRejectBlock) reject) {
+ FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
+ FIRUser *user = [FIRAuth authWithApp:firApp].currentUser;
+
+ if (user) {
+ [user getIDTokenResultForcingRefresh:(BOOL) forceRefresh completion:^(FIRAuthTokenResult *_Nullable tokenResult,
+ NSError *_Nullable error) {
+ if (error) {
+ [self promiseRejectAuthException:reject error:error];
+ } else {
+ NSMutableDictionary *tokenResultDict = [NSMutableDictionary dictionary];
+ [tokenResultDict setValue:[RNFirebaseUtil getISO8601String:tokenResult.authDate] forKey:@"authTime"];
+ [tokenResultDict setValue:[RNFirebaseUtil getISO8601String:tokenResult.issuedAtDate] forKey:@"issuedAtTime"];
+ [tokenResultDict setValue:[RNFirebaseUtil getISO8601String:tokenResult.expirationDate] forKey:@"expirationTime"];
+
+ [tokenResultDict setValue:tokenResult.token forKey:@"token"];
+ [tokenResultDict setValue:tokenResult.claims forKey:@"claims"];
+
+ NSString *provider = tokenResult.signInProvider;
+ if (!provider) {
+ provider = tokenResult.claims[@"firebase"][@"sign_in_provider"];
+ }
+
+ [tokenResultDict setValue:provider forKey:@"signInProvider"];
+ resolve(tokenResultDict);
+ }
+ }];
+ } else {
+ [self promiseNoUser:resolve rejecter:reject isError:YES];
+ }
}
/**
- signInAndRetrieveDataWithCredential
+ signInWithCredential
@param NSString provider
@param NSString authToken
@@ -555,37 +659,34 @@
@param RCTPromiseRejectBlock reject
@return
*/
-RCT_EXPORT_METHOD(signInAndRetrieveDataWithCredential:(NSString *) appDisplayName
- provider:(NSString *) provider
- token:(NSString *) authToken
- secret:(NSString *) authSecret
- resolver:(RCTPromiseResolveBlock) resolve
- rejecter:(RCTPromiseRejectBlock) reject) {
- [self signInWithCredential:appDisplayName provider:provider token:authToken secret:authSecret withData:true resolver:resolve rejecter:reject];
-}
-
--(void)signInWithCredential:(NSString *)appDisplayName
- provider:(NSString *) provider
- token:(NSString *) authToken
- secret:(NSString *) authSecret
- withData:(BOOL)withData
- resolver:(RCTPromiseResolveBlock) resolve
- rejecter:(RCTPromiseRejectBlock) reject {
+RCT_EXPORT_METHOD(signInWithCredential:
+ (NSString *) appDisplayName
+ provider:
+ (NSString *) provider
+ token:
+ (NSString *) authToken
+ secret:
+ (NSString *) authSecret
+ resolver:
+ (RCTPromiseResolveBlock) resolve
+ rejecter:
+ (RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
FIRAuthCredential *credential = [self getCredentialForProvider:provider token:authToken secret:authSecret];
if (credential == nil) {
- return reject(@"auth/invalid-credential", @"The supplied auth credential is malformed, has expired or is not currently supported.", nil);
+ return reject(@"auth/invalid-credential",
+ @"The supplied auth credential is malformed, has expired or is not currently supported.",
+ nil);
}
- [[FIRAuth authWithApp:firApp] signInAndRetrieveDataWithCredential:credential completion:^(FIRAuthDataResult *authResult, NSError *error) {
+ [[FIRAuth authWithApp:firApp] signInAndRetrieveDataWithCredential:credential completion:^(FIRAuthDataResult *authResult,
+ NSError *error) {
if (error) {
[self promiseRejectAuthException:reject error:error];
- } else if (withData) {
- [self promiseWithAuthResult:resolve rejecter:reject authResult:authResult];
} else {
- [self promiseWithUser:resolve rejecter:reject user:authResult.user];
+ [self promiseWithAuthResult:resolve rejecter:reject authResult:authResult];
}
}];
}
@@ -643,7 +744,7 @@
if (error) {
[self promiseRejectAuthException:reject error:error];
} else {
- [self promiseNoUser:resolve rejecter:reject isError:NO];
+ [self promiseWithUser:resolve rejecter:reject user:[FIRAuth authWithApp:firApp].currentUser];
}
}];
}
@@ -666,35 +767,31 @@
(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
- [[FIRAuth authWithApp:firApp] checkActionCode:code completion:^(FIRActionCodeInfo *_Nullable info, NSError *_Nullable error) {
+ [[FIRAuth authWithApp:firApp] checkActionCode:code completion:^(FIRActionCodeInfo *_Nullable info,
+ NSError *_Nullable error) {
if (error) {
[self promiseRejectAuthException:reject error:error];
} else {
NSString *actionType = @"ERROR";
switch (info.operation) {
- case FIRActionCodeOperationPasswordReset:
- actionType = @"PASSWORD_RESET";
+ case FIRActionCodeOperationPasswordReset:actionType = @"PASSWORD_RESET";
break;
- case FIRActionCodeOperationVerifyEmail:
- actionType = @"VERIFY_EMAIL";
+ case FIRActionCodeOperationVerifyEmail:actionType = @"VERIFY_EMAIL";
break;
- case FIRActionCodeOperationUnknown:
- actionType = @"UNKNOWN";
+ case FIRActionCodeOperationUnknown:actionType = @"UNKNOWN";
break;
- case FIRActionCodeOperationRecoverEmail:
- actionType = @"RECOVER_EMAIL";
+ case FIRActionCodeOperationRecoverEmail:actionType = @"RECOVER_EMAIL";
break;
- case FIRActionCodeOperationEmailLink:
- actionType = @"EMAIL_SIGNIN";
+ case FIRActionCodeOperationEmailLink:actionType = @"EMAIL_SIGNIN";
break;
}
NSMutableDictionary *data = [NSMutableDictionary dictionary];
if ([info dataForKey:FIRActionCodeEmailKey] != nil) {
- [data setValue:[info dataForKey:FIRActionCodeEmailKey] forKey:@"email"];
+ [data setValue:[info dataForKey:FIRActionCodeEmailKey] forKey:keyEmail];
} else {
- [data setValue:[NSNull null] forKey:@"email"];
+ [data setValue:[NSNull null] forKey:keyEmail];
}
if ([info dataForKey:FIRActionCodeFromEmailKey] != nil) {
@@ -703,7 +800,7 @@
[data setValue:[NSNull null] forKey:@"fromEmail"];
}
- NSDictionary *result = @{ @"data": data, @"actionType": actionType };
+ NSDictionary *result = @{@"data": data, @"operation": actionType};
resolve(result);
}
@@ -718,11 +815,16 @@
@param RCTPromiseRejectBlock reject
@return
*/
-RCT_EXPORT_METHOD(sendPasswordResetEmail:(NSString *) appDisplayName
- email:(NSString *) email
- actionCodeSettings:(NSDictionary *) actionCodeSettings
- resolver:(RCTPromiseResolveBlock) resolve
- rejecter:(RCTPromiseRejectBlock) reject) {
+RCT_EXPORT_METHOD(sendPasswordResetEmail:
+ (NSString *) appDisplayName
+ email:
+ (NSString *) email
+ actionCodeSettings:
+ (NSDictionary *) actionCodeSettings
+ resolver:
+ (RCTPromiseResolveBlock) resolve
+ rejecter:
+ (RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
id handler = ^(NSError *_Nullable error) {
@@ -750,11 +852,16 @@
@param RCTPromiseRejectBlock reject
@return
*/
-RCT_EXPORT_METHOD(sendSignInLinkToEmail:(NSString *) appDisplayName
- email:(NSString *) email
- actionCodeSettings:(NSDictionary *) actionCodeSettings
- resolver:(RCTPromiseResolveBlock) resolve
- rejecter:(RCTPromiseRejectBlock) reject) {
+RCT_EXPORT_METHOD(sendSignInLinkToEmail:
+ (NSString *) appDisplayName
+ email:
+ (NSString *) email
+ actionCodeSettings:
+ (NSDictionary *) actionCodeSettings
+ resolver:
+ (RCTPromiseResolveBlock) resolve
+ rejecter:
+ (RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
id handler = ^(NSError *_Nullable error) {
@@ -765,54 +872,34 @@
}
};
-
FIRActionCodeSettings *settings = [self buildActionCodeSettings:actionCodeSettings];
[[FIRAuth authWithApp:firApp] sendSignInLinkToEmail:email actionCodeSettings:settings completion:handler];
}
/**
- signInAndRetrieveDataWithCustomToken
-
- @param RCTPromiseResolveBlock resolve
- @param RCTPromiseRejectBlock reject
- @return
- */
-RCT_EXPORT_METHOD(signInAndRetrieveDataWithCustomToken:(NSString *) appDisplayName
- customToken:(NSString *) customToken
- resolver:(RCTPromiseResolveBlock) resolve
- rejecter:(RCTPromiseRejectBlock) reject) {
- [self signInWithCustomToken:appDisplayName customToken:customToken withData:true resolver:resolve rejecter:reject];
-}
-
-/**
signInWithCustomToken
@param RCTPromiseResolveBlock resolve
@param RCTPromiseRejectBlock reject
@return
*/
-RCT_EXPORT_METHOD(signInWithCustomToken:(NSString *) appDisplayName
- customToken:(NSString *) customToken
- resolver:(RCTPromiseResolveBlock) resolve
- rejecter:(RCTPromiseRejectBlock) reject) {
- [self signInWithCustomToken:appDisplayName customToken:customToken withData:false resolver:resolve rejecter:reject];
-}
-
--(void)signInWithCustomToken:(NSString *)appDisplayName
- customToken:(NSString *) customToken
- withData:(BOOL)withData
- resolver:(RCTPromiseResolveBlock) resolve
- rejecter:(RCTPromiseRejectBlock) reject {
+RCT_EXPORT_METHOD(signInWithCustomToken:
+ (NSString *) appDisplayName
+ customToken:
+ (NSString *) customToken
+ resolver:
+ (RCTPromiseResolveBlock) resolve
+ rejecter:
+ (RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
- [[FIRAuth authWithApp:firApp] signInWithCustomToken:customToken completion:^(FIRAuthDataResult *authResult, NSError *error) {
+ [[FIRAuth authWithApp:firApp] signInWithCustomToken:customToken completion:^(FIRAuthDataResult *authResult,
+ NSError *error) {
if (error) {
[self promiseRejectAuthException:reject error:error];
- } else if (withData) {
- [self promiseWithAuthResult:resolve rejecter:reject authResult:authResult];
} else {
- [self promiseWithUser:resolve rejecter:reject user:authResult.user];
+ [self promiseWithAuthResult:resolve rejecter:reject authResult:authResult];
}
}];
}
@@ -825,13 +912,19 @@
@param RCTPromiseRejectBlock reject
@return
*/
-RCT_EXPORT_METHOD(signInWithPhoneNumber:(NSString *) appDisplayName
- phoneNumber:(NSString *) phoneNumber
- resolver:(RCTPromiseResolveBlock) resolve
- rejecter:(RCTPromiseRejectBlock) reject) {
+RCT_EXPORT_METHOD(signInWithPhoneNumber:
+ (NSString *) appDisplayName
+ phoneNumber:
+ (NSString *) phoneNumber
+ resolver:
+ (RCTPromiseResolveBlock) resolve
+ rejecter:
+ (RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
- [[FIRPhoneAuthProvider providerWithAuth:[FIRAuth authWithApp:firApp]] verifyPhoneNumber:phoneNumber UIDelegate:nil completion:^(NSString * _Nullable verificationID, NSError * _Nullable error) {
+ [[FIRPhoneAuthProvider providerWithAuth:[FIRAuth authWithApp:firApp]] verifyPhoneNumber:phoneNumber UIDelegate:nil completion:^(
+ NSString *_Nullable verificationID,
+ NSError *_Nullable error) {
if (error) {
[self promiseRejectAuthException:reject error:error];
} else {
@@ -852,17 +945,22 @@
@param RCTPromiseRejectBlock reject
@return
*/
-RCT_EXPORT_METHOD(verifyPhoneNumber:(NSString *) appDisplayName
- phoneNumber:(NSString *) phoneNumber
- requestKey:(NSString *) requestKey) {
+RCT_EXPORT_METHOD(verifyPhoneNumber:
+ (NSString *) appDisplayName
+ phoneNumber:
+ (NSString *) phoneNumber
+ requestKey:
+ (NSString *) requestKey) {
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
- [[FIRPhoneAuthProvider providerWithAuth:[FIRAuth authWithApp:firApp]] verifyPhoneNumber:phoneNumber UIDelegate:nil completion:^(NSString * _Nullable verificationID, NSError * _Nullable error) {
+ [[FIRPhoneAuthProvider providerWithAuth:[FIRAuth authWithApp:firApp]] verifyPhoneNumber:phoneNumber UIDelegate:nil completion:^(
+ NSString *_Nullable verificationID,
+ NSError *_Nullable error) {
if (error) {
- NSDictionary * jsError = [self getJSError:(error)];
+ NSDictionary *jsError = [self getJSError:(error)];
NSDictionary *body = @{
@"type": @"onVerificationFailed",
- @"requestKey":requestKey,
+ @"requestKey": requestKey,
@"state": @{@"error": jsError},
};
[RNFirebaseUtil sendJSEventWithAppName:self app:firApp name:PHONE_AUTH_STATE_CHANGED_EVENT body:body];
@@ -871,7 +969,7 @@
[defaults setObject:verificationID forKey:@"authVerificationID"];
NSDictionary *body = @{
@"type": @"onCodeSent",
- @"requestKey":requestKey,
+ @"requestKey": requestKey,
@"state": @{@"verificationId": verificationID},
};
[RNFirebaseUtil sendJSEventWithAppName:self app:firApp name:PHONE_AUTH_STATE_CHANGED_EVENT body:body];
@@ -879,16 +977,23 @@
}];
}
-RCT_EXPORT_METHOD(_confirmVerificationCode:(NSString *) appDisplayName
- verificationCode:(NSString *) verificationCode
- resolver:(RCTPromiseResolveBlock) resolve
- rejecter:(RCTPromiseRejectBlock) reject) {
+RCT_EXPORT_METHOD(_confirmVerificationCode:
+ (NSString *) appDisplayName
+ verificationCode:
+ (NSString *) verificationCode
+ resolver:
+ (RCTPromiseResolveBlock) resolve
+ rejecter:
+ (RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *verificationId = [defaults stringForKey:@"authVerificationID"];
- FIRAuthCredential *credential = [[FIRPhoneAuthProvider provider] credentialWithVerificationID:verificationId verificationCode:verificationCode];
- [[FIRAuth authWithApp:firApp] signInAndRetrieveDataWithCredential:credential completion:^(FIRAuthDataResult *authResult, NSError *error) {
+ FIRAuthCredential *credential =
+ [[FIRPhoneAuthProvider provider] credentialWithVerificationID:verificationId verificationCode:verificationCode];
+
+ [[FIRAuth authWithApp:firApp] signInAndRetrieveDataWithCredential:credential completion:^(FIRAuthDataResult *authResult,
+ NSError *error) {
if (error) {
[self promiseRejectAuthException:reject error:error];
} else {
@@ -907,58 +1012,35 @@
@param RCTPromiseRejectBlock reject
@return
*/
-RCT_EXPORT_METHOD(linkWithCredential:(NSString *) appDisplayName
- provider:(NSString *) provider
- authToken:(NSString *) authToken
- authSecret:(NSString *) authSecret
- resolver:(RCTPromiseResolveBlock) resolve
- rejecter:(RCTPromiseRejectBlock) reject) {
- [self linkWithCredential:appDisplayName provider:provider authToken:authToken authSecret:authSecret withData:false resolver:resolve rejecter:reject];
-}
-
-/**
- linkAndRetrieveDataWithCredential
-
- @param NSString provider
- @param NSString authToken
- @param NSString authSecret
- @param RCTPromiseResolveBlock resolve
- @param RCTPromiseRejectBlock reject
- @return
- */
-RCT_EXPORT_METHOD(linkAndRetrieveDataWithCredential:(NSString *) appDisplayName
- provider:(NSString *) provider
- authToken:(NSString *) authToken
- authSecret:(NSString *) authSecret
- resolver:(RCTPromiseResolveBlock) resolve
- rejecter:(RCTPromiseRejectBlock) reject) {
- [self linkWithCredential:appDisplayName provider:provider authToken:authToken authSecret:authSecret withData:true resolver:resolve rejecter:reject];
-}
-
--(void)linkWithCredential:(NSString *)appDisplayName
- provider:(NSString *)provider
- authToken:(NSString *)authToken
- authSecret:(NSString *)authSecret
- withData:(BOOL)withData
- resolver:(RCTPromiseResolveBlock)resolve
- rejecter:(RCTPromiseRejectBlock)reject {
+RCT_EXPORT_METHOD(linkWithCredential:
+ (NSString *) appDisplayName
+ provider:
+ (NSString *) provider
+ authToken:
+ (NSString *) authToken
+ authSecret:
+ (NSString *) authSecret
+ resolver:
+ (RCTPromiseResolveBlock) resolve
+ rejecter:
+ (RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
FIRAuthCredential *credential = [self getCredentialForProvider:provider token:authToken secret:authSecret];
if (credential == nil) {
- return reject(@"auth/invalid-credential", @"The supplied auth credential is malformed, has expired or is not currently supported.", nil);
+ return reject(@"auth/invalid-credential",
+ @"The supplied auth credential is malformed, has expired or is not currently supported.",
+ nil);
}
FIRUser *user = [FIRAuth authWithApp:firApp].currentUser;
if (user) {
[user linkAndRetrieveDataWithCredential:credential
- completion:^(FIRAuthDataResult * _Nullable authResult, NSError * _Nullable error) {
+ completion:^(FIRAuthDataResult *_Nullable authResult, NSError *_Nullable error) {
if (error) {
[self promiseRejectAuthException:reject error:error];
- } else if (withData) {
- [self promiseWithAuthResult:resolve rejecter:reject authResult:authResult];
} else {
- [self promiseWithUser:resolve rejecter:reject user:authResult.user];
+ [self promiseWithAuthResult:resolve rejecter:reject authResult:authResult];
}
}];
} else {
@@ -992,7 +1074,7 @@
if (error) {
[self promiseRejectAuthException:reject error:error];
} else {
- [self reloadAndReturnUser:user resolver:resolve rejecter: reject];
+ [self reloadAndReturnUser:user resolver:resolve rejecter:reject];
}
}];
} else {
@@ -1010,59 +1092,37 @@
@param RCTPromiseRejectBlock reject
@return
*/
-RCT_EXPORT_METHOD(reauthenticateWithCredential:(NSString *) appDisplayName
- provider:(NSString *) provider
- authToken:(NSString *) authToken
- authSecret:(NSString *) authSecret
- resolver:(RCTPromiseResolveBlock) resolve
- rejecter:(RCTPromiseRejectBlock) reject) {
- [self reauthenticateWithCredential:appDisplayName provider:provider authToken:authToken authSecret:authSecret withData:false resolver:resolve rejecter:reject];
-}
-
-/**
- reauthenticateAndRetrieveDataWithCredential
-
- @param NSString provider
- @param NSString authToken
- @param NSString authSecret
- @param RCTPromiseResolveBlock resolve
- @param RCTPromiseRejectBlock reject
- @return
- */
-RCT_EXPORT_METHOD(reauthenticateAndRetrieveDataWithCredential:(NSString *) appDisplayName
- provider:(NSString *) provider
- authToken:(NSString *) authToken
- authSecret:(NSString *) authSecret
- resolver:(RCTPromiseResolveBlock) resolve
- rejecter:(RCTPromiseRejectBlock) reject) {
- [self reauthenticateWithCredential:appDisplayName provider:provider authToken:authToken authSecret:authSecret withData:true resolver:resolve rejecter:reject];
-}
-
--(void)reauthenticateWithCredential:(NSString *) appDisplayName
- provider:(NSString *) provider
- authToken:(NSString *) authToken
- authSecret:(NSString *) authSecret
- withData:(BOOL) withData
- resolver:(RCTPromiseResolveBlock) resolve
- rejecter:(RCTPromiseRejectBlock) reject {
+RCT_EXPORT_METHOD(reauthenticateWithCredential:
+ (NSString *) appDisplayName
+ provider:
+ (NSString *) provider
+ authToken:
+ (NSString *) authToken
+ authSecret:
+ (NSString *) authSecret
+ resolver:
+ (RCTPromiseResolveBlock) resolve
+ rejecter:
+ (RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
FIRAuthCredential *credential = [self getCredentialForProvider:provider token:authToken secret:authSecret];
if (credential == nil) {
- return reject(@"auth/invalid-credential", @"The supplied auth credential is malformed, has expired or is not currently supported.", nil);
+ return reject(@"auth/invalid-credential",
+ @"The supplied auth credential is malformed, has expired or is not currently supported.",
+ nil);
}
FIRUser *user = [FIRAuth authWithApp:firApp].currentUser;
if (user) {
- [user reauthenticateAndRetrieveDataWithCredential:credential completion:^(FIRAuthDataResult * _Nullable authResult, NSError * _Nullable error) {
+ [user reauthenticateAndRetrieveDataWithCredential:credential completion:^(FIRAuthDataResult *_Nullable authResult,
+ NSError *_Nullable error) {
if (error) {
[self promiseRejectAuthException:reject error:error];
- } else if (withData) {
- [self promiseWithAuthResult:resolve rejecter:reject authResult:authResult];
} else {
- [self promiseWithUser:resolve rejecter:reject user:authResult.user];
+ [self promiseWithAuthResult:resolve rejecter:reject authResult:authResult];
}
}];
} else {
@@ -1088,7 +1148,8 @@
(RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
- [[FIRAuth authWithApp:firApp] fetchSignInMethodsForEmail:email completion:^(NSArray<NSString *> *_Nullable providers, NSError *_Nullable error) {
+ [[FIRAuth authWithApp:firApp] fetchSignInMethodsForEmail:email completion:^(NSArray<NSString *> *_Nullable providers,
+ NSError *_Nullable error) {
if (error) {
[self promiseRejectAuthException:reject error:error];
} else if (!providers) {
@@ -1124,11 +1185,12 @@
} else if ([provider compare:@"github.com" options:NSCaseInsensitiveSearch] == NSOrderedSame) {
credential = [FIRGitHubAuthProvider credentialWithToken:authToken];
} else if ([provider compare:@"phone" options:NSCaseInsensitiveSearch] == NSOrderedSame) {
- credential = [[FIRPhoneAuthProvider provider] credentialWithVerificationID:authToken verificationCode:authTokenSecret];
+ credential =
+ [[FIRPhoneAuthProvider provider] credentialWithVerificationID:authToken verificationCode:authTokenSecret];
} else if ([provider compare:@"oauth" options:NSCaseInsensitiveSearch] == NSOrderedSame) {
credential = [FIROAuthProvider credentialWithProviderID:@"oauth" IDToken:authToken accessToken:authTokenSecret];
} else {
- NSLog(@"Provider not yet handled: %@", provider);
+ DLog(@"Provider not yet handled: %@", provider);
}
return credential;
@@ -1155,18 +1216,23 @@
@param NSString code
@return
*/
-RCT_EXPORT_METHOD(useDeviceLanguage:(NSString *) appDisplayName) {
+RCT_EXPORT_METHOD(useDeviceLanguage:
+ (NSString *) appDisplayName) {
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
-
[[FIRAuth authWithApp:firApp] useAppLanguage];
}
-RCT_EXPORT_METHOD(verifyPasswordResetCode:(NSString *) appDisplayName
- code:(NSString *)code
- resolver:(RCTPromiseResolveBlock)resolve
- rejecter:(RCTPromiseRejectBlock)reject) {
+RCT_EXPORT_METHOD(verifyPasswordResetCode:
+ (NSString *) appDisplayName
+ code:
+ (NSString *) code
+ resolver:
+ (RCTPromiseResolveBlock) resolve
+ rejecter:
+ (RCTPromiseRejectBlock) reject) {
FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName];
- [[FIRAuth authWithApp:firApp] verifyPasswordResetCode:code completion:^(NSString * _Nullable email, NSError * _Nullable error) {
+ [[FIRAuth authWithApp:firApp] verifyPasswordResetCode:code completion:^(NSString *_Nullable email,
+ NSError *_Nullable error) {
if (error) {
[self promiseRejectAuthException:reject error:error];
} else {
@@ -1180,7 +1246,7 @@
- (void)reloadAndReturnUser:(FIRUser *)user
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject {
- [user reloadWithCompletion:^(NSError * _Nullable error) {
+ [user reloadWithCompletion:^(NSError *_Nullable error) {
if (error) {
[self promiseRejectAuthException:reject error:error];
} else {
@@ -1211,7 +1277,7 @@
@param error NSError
*/
- (void)promiseRejectAuthException:(RCTPromiseRejectBlock)reject error:(NSError *)error {
- NSDictionary * jsError = [self getJSError:(error)];
+ NSDictionary *jsError = [self getJSError:(error)];
reject([jsError valueForKey:@"code"], [jsError valueForKey:@"message"], error);
}
@@ -1221,115 +1287,70 @@
@param error NSError
*/
- (NSDictionary *)getJSError:(NSError *)error {
- NSString *code = @"auth/unknown";
+ NSString *code = AuthErrorCode_toJSErrorCode[error.code];
NSString *message = [error localizedDescription];
NSString *nativeErrorMessage = [error localizedDescription];
+ if (code == nil) code = @"auth/unknown";
+
+ // TODO: Salakar: replace these with a AuthErrorCode_toJSErrorMessage map (like codes now does)
switch (error.code) {
case FIRAuthErrorCodeInvalidCustomToken:
- code = @"auth/invalid-custom-token";
message = @"The custom token format is incorrect. Please check the documentation.";
break;
case FIRAuthErrorCodeCustomTokenMismatch:
- code = @"auth/custom-token-mismatch";
message = @"The custom token corresponds to a different audience.";
break;
case FIRAuthErrorCodeInvalidCredential:
- code = @"auth/invalid-credential";
message = @"The supplied auth credential is malformed or has expired.";
break;
case FIRAuthErrorCodeInvalidEmail:
- code = @"auth/invalid-email";
message = @"The email address is badly formatted.";
break;
case FIRAuthErrorCodeWrongPassword:
- code = @"auth/wrong-password";
message = @"The password is invalid or the user does not have a password.";
break;
case FIRAuthErrorCodeUserMismatch:
- code = @"auth/user-mismatch";
message = @"The supplied credentials do not correspond to the previously signed in user.";
break;
case FIRAuthErrorCodeRequiresRecentLogin:
- code = @"auth/requires-recent-login";
- message = @"This operation is sensitive and requires recent authentication. Log in again before retrying this request.";
+ message =
+ @"This operation is sensitive and requires recent authentication. Log in again before retrying this request.";
break;
case FIRAuthErrorCodeAccountExistsWithDifferentCredential:
- code = @"auth/account-exists-with-different-credential";
- message = @"An account already exists with the same email address but different sign-in credentials. Sign in using a provider associated with this email address.";
+ message =
+ @"An account already exists with the same email address but different sign-in credentials. Sign in using a provider associated with this email address.";
break;
case FIRAuthErrorCodeEmailAlreadyInUse:
- code = @"auth/email-already-in-use";
message = @"The email address is already in use by another account.";
break;
case FIRAuthErrorCodeCredentialAlreadyInUse:
- code = @"auth/credential-already-in-use";
message = @"This credential is already associated with a different user account.";
break;
case FIRAuthErrorCodeUserDisabled:
- code = @"auth/user-disabled";
message = @"The user account has been disabled by an administrator.";
break;
case FIRAuthErrorCodeUserTokenExpired:
- code = @"auth/user-token-expired";
message = @"The user's credential is no longer valid. The user must sign in again.";
break;
case FIRAuthErrorCodeUserNotFound:
- code = @"auth/user-not-found";
message = @"There is no user record corresponding to this identifier. The user may have been deleted.";
break;
case FIRAuthErrorCodeInvalidUserToken:
- code = @"auth/invalid-user-token";
message = @"The user's credential is no longer valid. The user must sign in again.";
break;
case FIRAuthErrorCodeWeakPassword:
- code = @"auth/weak-password";
message = @"The given password is invalid.";
break;
case FIRAuthErrorCodeOperationNotAllowed:
- code = @"auth/operation-not-allowed";
message = @"This operation is not allowed. You must enable this service in the console.";
break;
case FIRAuthErrorCodeNetworkError:
- code = @"auth/network-error";
message = @"A network error has occurred, please try again.";
break;
case FIRAuthErrorCodeInternalError:
- code = @"auth/internal-error";
message = @"An internal error has occurred, please try again.";
break;
-
- // unsure of the below codes so leaving them as the default error message
- case FIRAuthErrorCodeTooManyRequests:
- code = @"auth/too-many-requests";
- break;
- case FIRAuthErrorCodeProviderAlreadyLinked:
- code = @"auth/provider-already-linked";
- break;
- case FIRAuthErrorCodeNoSuchProvider:
- code = @"auth/no-such-provider";
- break;
- case FIRAuthErrorCodeInvalidAPIKey:
- code = @"auth/invalid-api-key";
- break;
- case FIRAuthErrorCodeAppNotAuthorized:
- code = @"auth/app-not-authorised";
- break;
- case FIRAuthErrorCodeExpiredActionCode:
- code = @"auth/expired-action-code";
- break;
- case FIRAuthErrorCodeInvalidMessagePayload:
- code = @"auth/invalid-message-payload";
- break;
- case FIRAuthErrorCodeInvalidSender:
- code = @"auth/invalid-sender";
- break;
- case FIRAuthErrorCodeInvalidRecipientEmail:
- code = @"auth/invalid-recipient-email";
- break;
- case FIRAuthErrorCodeKeychainError:
- code = @"auth/keychain-error";
- break;
default:
break;
}
@@ -1341,13 +1362,12 @@
};
}
-
/**
- Resolve or reject a promise based on FIRUser value existance
-
- @param resolve RCTPromiseResolveBlock
- @param reject RCTPromiseRejectBlock
- @param user FIRUser
+ * Resolve or reject a promise based on FIRUser value existence
+ *
+ * @param resolve RCTPromiseResolveBlock
+ * @param reject RCTPromiseRejectBlock
+ * @param user FIRUser
*/
- (void)promiseWithUser:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject user:(FIRUser *)user {
if (user) {
@@ -1360,24 +1380,51 @@
}
/**
- Resolve or reject a promise based on FIRAuthResult value existance
-
- @param resolve RCTPromiseResolveBlock
- @param reject RCTPromiseRejectBlock
- @param authResult FIRAuthDataResult
+ * Resolve or reject a promise based on FIRAuthResult value existence
+ *
+ * @param resolve RCTPromiseResolveBlock
+ * @param reject RCTPromiseRejectBlock
+ * @param authResult FIRAuthDataResult
*/
- (void)promiseWithAuthResult:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject authResult:(FIRAuthDataResult *)authResult {
if (authResult && authResult.user) {
- NSDictionary *userDict = [self firebaseUserToDict:authResult.user];
- NSDictionary *authResultDict = @{
- @"additionalUserInfo": authResult.additionalUserInfo ? @{
- @"isNewUser": @(authResult.additionalUserInfo.isNewUser),
- @"profile": authResult.additionalUserInfo.profile ? authResult.additionalUserInfo.profile : [NSNull null],
- @"providerId": authResult.additionalUserInfo.providerID ? authResult.additionalUserInfo.providerID : [NSNull null],
- @"username": authResult.additionalUserInfo.username ? authResult.additionalUserInfo.username : [NSNull null]
- } : [NSNull null],
- @"user": userDict
- };
+ NSMutableDictionary *authResultDict = [NSMutableDictionary dictionary];
+
+ // additionalUserInfo
+ if (authResult.additionalUserInfo) {
+ NSMutableDictionary *additionalUserInfo = [NSMutableDictionary dictionary];
+
+ // isNewUser
+ [additionalUserInfo setValue:@(authResult.additionalUserInfo.isNewUser) forKey:keyNewUser];
+
+ // profile
+ if (authResult.additionalUserInfo.profile) {
+ [additionalUserInfo setValue:authResult.additionalUserInfo.profile forKey:keyProfile];
+ } else {
+ [additionalUserInfo setValue:[NSNull null] forKey:keyProfile];
+ }
+
+ // providerId
+ if (authResult.additionalUserInfo.providerID) {
+ [additionalUserInfo setValue:authResult.additionalUserInfo.providerID forKey:keyProviderId];
+ } else {
+ [additionalUserInfo setValue:[NSNull null] forKey:keyProviderId];
+ }
+
+ // username
+ if (authResult.additionalUserInfo.username) {
+ [additionalUserInfo setValue:authResult.additionalUserInfo.username forKey:keyUsername];
+ } else {
+ [additionalUserInfo setValue:[NSNull null] forKey:keyUsername];
+ }
+
+ [authResultDict setValue:additionalUserInfo forKey:keyAdditionalUserInfo];
+ } else {
+ [authResultDict setValue:[NSNull null] forKey:keyAdditionalUserInfo];
+ }
+
+ // user
+ [authResultDict setValue:[self firebaseUserToDict:authResult.user] forKey:keyUser];
resolve(authResultDict);
} else {
[self promiseNoUser:resolve rejecter:reject isError:YES];
@@ -1386,39 +1433,39 @@
}
/**
- Converts an array of FIRUserInfo instances into the correct format to match the web sdk
-
- @param providerData FIRUser.providerData
- @return NSArray
+ * Converts an array of FIRUserInfo instances into a web sdk compatible format
+ *
+ * @param providerData NSArray
+ * @return NSArray
*/
-- (NSArray <NSObject *> *)convertProviderData:(NSArray <id <FIRUserInfo>> *)providerData {
+- (NSArray <NSObject *> *)convertProviderData:(NSArray <id<FIRUserInfo>> *)providerData {
NSMutableArray *output = [NSMutableArray array];
- for (id <FIRUserInfo> userInfo in providerData) {
+ for (id<FIRUserInfo> userInfo in providerData) {
NSMutableDictionary *pData = [NSMutableDictionary dictionary];
if (userInfo.providerID != nil) {
- [pData setValue:userInfo.providerID forKey:@"providerId"];
+ [pData setValue:userInfo.providerID forKey:keyProviderId];
}
if (userInfo.uid != nil) {
- [pData setValue:userInfo.uid forKey:@"uid"];
+ [pData setValue:userInfo.uid forKey:keyUid];
}
if (userInfo.displayName != nil) {
- [pData setValue:userInfo.displayName forKey:@"displayName"];
+ [pData setValue:userInfo.displayName forKey:keyDisplayName];
}
if (userInfo.photoURL != nil) {
- [pData setValue:[userInfo.photoURL absoluteString] forKey:@"photoURL"];
+ [pData setValue:[userInfo.photoURL absoluteString] forKey:keyPhotoUrl];
}
if (userInfo.email != nil) {
- [pData setValue:userInfo.email forKey:@"email"];
+ [pData setValue:userInfo.email forKey:keyEmail];
}
if (userInfo.phoneNumber != nil) {
- [pData setValue:userInfo.phoneNumber forKey:@"phoneNumber"];
+ [pData setValue:userInfo.phoneNumber forKey:keyPhoneNumber];
}
[output addObject:pData];
@@ -1429,66 +1476,93 @@
/**
* React native constant exports - exports native firebase apps mainly
+ *
* @return NSDictionary
*/
- (NSDictionary *)constantsToExport {
- NSMutableDictionary *constants = [NSMutableDictionary new];
NSDictionary *firApps = [FIRApp allApps];
+ NSMutableDictionary *constants = [NSMutableDictionary new];
NSMutableDictionary *appLanguage = [NSMutableDictionary new];
+ NSMutableDictionary *appUser = [NSMutableDictionary new];
for (id key in firApps) {
FIRApp *firApp = firApps[key];
+ NSString *appName = firApp.name;
+ FIRUser *user = [FIRAuth authWithApp:firApp].currentUser;
- appLanguage[firApp.name] = [FIRAuth authWithApp:firApp].languageCode;
+ if ([appName isEqualToString:@"__FIRAPP_DEFAULT"]) {
+ appName = @"[DEFAULT]";
}
- constants[@"APP_LANGUAGE"] = appLanguage;
+ appLanguage[appName] = [FIRAuth authWithApp:firApp].languageCode;
+
+ if (user != nil) {
+ appUser[appName] = [self firebaseUserToDict: user];
+ }
+ }
+
+ constants[constAppLanguage] = appLanguage;
+ constants[constAppUser] = appUser;
return constants;
}
/**
- Converts a FIRUser instance into a dictionary to send via RNBridge
-
- @param user FIRUser
- @return NSDictionary
+ * Converts a FIRUser instance into a dictionary to send via RNBridge
+ *
+ * @param user FIRUser
+ * @return NSDictionary
*/
- (NSDictionary *)firebaseUserToDict:(FIRUser *)user {
return @{
- @"displayName": user.displayName ? user.displayName : [NSNull null],
- @"email": user.email ? user.email : [NSNull null],
+ keyDisplayName: user.displayName ? user.displayName : [NSNull null],
+ keyEmail: user.email ? user.email : [NSNull null],
@"emailVerified": @(user.emailVerified),
@"isAnonymous": @(user.anonymous),
@"metadata": @{
- @"creationTime": user.metadata.creationDate ? @(round([user.metadata.creationDate timeIntervalSince1970] * 1000.0)): [NSNull null],
- @"lastSignInTime": user.metadata.lastSignInDate ? @(round([user.metadata.lastSignInDate timeIntervalSince1970] * 1000.0)) : [NSNull null],
+ @"creationTime": user.metadata.creationDate ? @(round(
+ [user.metadata.creationDate timeIntervalSince1970] * 1000.0)) : [NSNull null],
+ @"lastSignInTime": user.metadata.lastSignInDate ? @(round(
+ [user.metadata.lastSignInDate timeIntervalSince1970] * 1000.0)) : [NSNull null],
},
- @"phoneNumber": user.phoneNumber ? user.phoneNumber : [NSNull null],
- @"photoURL": user.photoURL ? [user.photoURL absoluteString] : [NSNull null],
+ keyPhoneNumber: user.phoneNumber ? user.phoneNumber : [NSNull null],
+ keyPhotoUrl: user.photoURL ? [user.photoURL absoluteString] : [NSNull null],
@"providerData": [self convertProviderData:user.providerData],
- @"providerId": [user.providerID lowercaseString],
+ keyProviderId: [user.providerID lowercaseString],
@"refreshToken": user.refreshToken,
- @"uid": user.uid
+ keyUid: user.uid
};
}
+/**
+ * Create a FIRActionCodeSettings instance from JS args
+ *
+ * @param actionCodeSettings NSDictionary
+ * @return FIRActionCodeSettings
+ */
- (FIRActionCodeSettings *)buildActionCodeSettings:(NSDictionary *)actionCodeSettings {
+ NSString *url = actionCodeSettings[keyUrl];
+ NSDictionary *ios = actionCodeSettings[keyIOS];
+ NSDictionary *android = actionCodeSettings[keyAndroid];
+ BOOL handleCodeInApp = [actionCodeSettings[keyHandleCodeInApp] boolValue];
+
FIRActionCodeSettings *settings = [[FIRActionCodeSettings alloc] init];
- NSDictionary *android = actionCodeSettings[@"android"];
- BOOL handleCodeInApp = actionCodeSettings[@"handleCodeInApp"];
- NSDictionary *ios = actionCodeSettings[@"iOS"];
- NSString *url = actionCodeSettings[@"url"];
+
if (android) {
- BOOL installApp = android[@"installApp"];
- NSString *minimumVersion = android[@"minimumVersion"];
- NSString *packageName = android[@"packageName"];
+ NSString *packageName = android[keyPackageName];
+ NSString *minimumVersion = android[keyMinVersion];
+ BOOL installApp = [android[keyInstallApp] boolValue];
+
[settings setAndroidPackageName:packageName installIfNotAvailable:installApp minimumVersion:minimumVersion];
}
+
if (handleCodeInApp) {
[settings setHandleCodeInApp:handleCodeInApp];
}
- if (ios && ios[@"bundleId"]) {
- [settings setIOSBundleID:ios[@"bundleId"]];
+
+ if (ios && ios[keyBundleId]) {
+ [settings setIOSBundleID:ios[keyBundleId]];
}
+
if (url) {
[settings setURL:[NSURL URLWithString:url]];
}
@@ -1499,8 +1574,7 @@
return @[AUTH_STATE_CHANGED_EVENT, AUTH_ID_TOKEN_CHANGED_EVENT, PHONE_AUTH_STATE_CHANGED_EVENT];
}
-+ (BOOL)requiresMainQueueSetup
-{
++ (BOOL)requiresMainQueueSetup {
return YES;
}

ios/RNFirebase/converters/RCTConvert+UIBackgroundFetchResult.h

@@ -0,0 +1,5 @@
+#import <React/RCTConvert.h>
+
+@interface RCTConvert (UIBackgroundFetchResult)
+
+@end

ios/RNFirebase/converters/RCTConvert+UIBackgroundFetchResult.m

@@ -0,0 +1,16 @@
+#import "RCTConvert+UIBackgroundFetchResult.h"
+
+@implementation RCTConvert (UIBackgroundFetchResult)
+
+RCT_ENUM_CONVERTER(
+ UIBackgroundFetchResult,
+ (@{
+ @"backgroundFetchResultNoData" : @(UIBackgroundFetchResultNoData),
+ @"backgroundFetchResultNewData" : @(UIBackgroundFetchResultNewData),
+ @"backgroundFetchResultFailed" : @(UIBackgroundFetchResultFailed)}
+ ),
+ UIBackgroundFetchResultNoData,
+ integerValue
+)
+
+@end

ios/RNFirebase/database/RNFirebaseDatabase.m

@@ -72,7 +72,7 @@
});
if (!transactionState) {
- NSLog(@"tryCommitTransaction for unknown ID %@", transactionId);
+ DLog(@"tryCommitTransaction for unknown ID %@", transactionId);
return;
}

ios/RNFirebase/database/RNFirebaseDatabaseReference.m

@@ -38,7 +38,7 @@
[self handleDatabaseEvent:eventType registration:registration dataSnapshot:snapshot previousChildName:previousChildName];
};
id errorBlock = ^(NSError *_Nonnull error) {
- NSLog(@"Error onDBEvent: %@", [error debugDescription]);
+ DLog(@"Error onDBEvent: %@", [error debugDescription]);
[self removeEventListener:eventRegistrationKey];
[self handleDatabaseError:registration error:error];
};
@@ -56,7 +56,7 @@
NSDictionary *data = [RNFirebaseDatabaseReference snapshotToDictionary:snapshot previousChildName:previousChildName];
resolve(data);
} withCancelBlock:^(NSError *_Nonnull error) {
- NSLog(@"Error onDBEventOnce: %@", [error debugDescription]);
+ DLog(@"Error onDBEventOnce: %@", [error debugDescription]);
[RNFirebaseDatabase handlePromise:resolve rejecter:reject databaseError:error];
}];
}

ios/RNFirebase/fabric/crashlytics/RNFirebaseCrashlytics.h

@@ -2,18 +2,25 @@
#define RNFirebaseCrashlytics_h
#import <Foundation/Foundation.h>
-#if __has_include(<Crashlytics/Crashlytics/Crashlytics.h>)
-#import <React/RCTBridgeModule.h>
-
-@interface RNFirebaseCrashlytics : NSObject <RCTBridgeModule> {
-
-}
-
-@end
-
-#else
-@interface RNFirebaseCrashlytics : NSObject
-@end
-#endif
+ #if __has_include(<Crashlytics/Crashlytics/Crashlytics.h>)
+ #define HAS_CRASHLYTICS 1
+ #else
+ #if __has_include(<Crashlytics/Crashlytics.h>)
+ #define HAS_CRASHLYTICS 2
+ #endif
+ #endif
+
+ #ifdef HAS_CRASHLYTICS
+ #import <React/RCTBridgeModule.h>
+
+ @interface RNFirebaseCrashlytics : NSObject <RCTBridgeModule> {
+
+ }
+
+ @end
+ #else
+ @interface RNFirebaseCrashlytics : NSObject
+ @end
+ #endif
#endif

ios/RNFirebase/fabric/crashlytics/RNFirebaseCrashlytics.m

@@ -1,47 +1,64 @@
#import "RNFirebaseCrashlytics.h"
#if __has_include(<Crashlytics/Crashlytics/Crashlytics.h>)
-#import <Crashlytics/Crashlytics/Crashlytics.h>
+ #define HAS_CRASHLYTICS 1
+ #import <Crashlytics/Crashlytics/Crashlytics.h>
+#else
+ #if __has_include(<Crashlytics/Crashlytics.h>)
+ #define HAS_CRASHLYTICS 2
+ #import <Crashlytics/Crashlytics.h>
+ #endif
+#endif
-@implementation RNFirebaseCrashlytics
-RCT_EXPORT_MODULE();
+#if __has_include(<Fabric/Fabric.h>)
+ #import <Fabric/Fabric.h>
+#endif
-RCT_EXPORT_METHOD(crash) {
+#ifdef HAS_CRASHLYTICS
+ @implementation RNFirebaseCrashlytics
+ RCT_EXPORT_MODULE();
+
+ RCT_EXPORT_METHOD(crash) {
[[Crashlytics sharedInstance] crash];
-}
+ }
-RCT_EXPORT_METHOD(log:(NSString *)message) {
+ RCT_EXPORT_METHOD(log:(NSString *)message) {
CLS_LOG(@"%@", message);
-}
+ }
-RCT_EXPORT_METHOD(recordError:(nonnull NSNumber *)code domain:(NSString *)domain) {
+ RCT_EXPORT_METHOD(recordError:(nonnull NSNumber *)code domain:(NSString *)domain) {
NSError *error = [NSError errorWithDomain:domain code:[code integerValue] userInfo:nil];
[CrashlyticsKit recordError:error];
-}
+ }
-RCT_EXPORT_METHOD(setBoolValue:(NSString *)key boolValue:(BOOL *)boolValue) {
+ RCT_EXPORT_METHOD(setBoolValue:(NSString *)key boolValue:(BOOL *)boolValue) {
[CrashlyticsKit setBoolValue:boolValue forKey:key];
-}
+ }
-RCT_EXPORT_METHOD(setFloatValue:(NSString *)key floatValue:(nonnull NSNumber *)floatValue) {
+ RCT_EXPORT_METHOD(setFloatValue:(NSString *)key floatValue:(nonnull NSNumber *)floatValue) {
[CrashlyticsKit setFloatValue:[floatValue floatValue] forKey:key];
-}
+ }
-RCT_EXPORT_METHOD(setIntValue:(NSString *)key intValue:(nonnull NSNumber *)intValue) {
+ RCT_EXPORT_METHOD(setIntValue:(NSString *)key intValue:(nonnull NSNumber *)intValue) {
[CrashlyticsKit setIntValue:[intValue integerValue] forKey:key];
-}
+ }
-RCT_EXPORT_METHOD(setStringValue:(NSString *)key stringValue:(NSString *)stringValue) {
+ RCT_EXPORT_METHOD(setStringValue:(NSString *)key stringValue:(NSString *)stringValue) {
[CrashlyticsKit setObjectValue:stringValue forKey:key];
-}
+ }
-RCT_EXPORT_METHOD(setUserIdentifier:(NSString *)userId) {
+ RCT_EXPORT_METHOD(setUserIdentifier:(NSString *)userId) {
[CrashlyticsKit setUserIdentifier:userId];
-}
+ }
-@end
+ RCT_EXPORT_METHOD(enableCrashlyticsCollection) {
+#if __has_include(<Fabric/Fabric.h>)
+ [Fabric with:@[[Crashlytics class]]];
+#endif
+ }
+ @end
#else
-@implementation RNFirebaseCrashlytics
-@end
+ @implementation RNFirebaseCrashlytics
+ @end
#endif

ios/RNFirebase/firestore/RNFirebaseFirestoreCollectionReference.m

@@ -6,12 +6,12 @@
static NSMutableDictionary *_listeners;
-- (id)initWithPathAndModifiers:(RCTEventEmitter *) emitter
- appDisplayName:(NSString *) appDisplayName
- path:(NSString *) path
- filters:(NSArray *) filters
- orders:(NSArray *) orders
- options:(NSDictionary *) options {
+- (id)initWithPathAndModifiers:(RCTEventEmitter *)emitter
+ appDisplayName:(NSString *)appDisplayName
+ path:(NSString *)path
+ filters:(NSArray *)filters
+ orders:(NSArray *)orders
+ options:(NSDictionary *)options {
self = [super init];
if (self) {
_emitter = emitter;
@@ -29,9 +29,9 @@
return self;
}
-- (void)get:(NSDictionary *) getOptions
- resolver:(RCTPromiseResolveBlock) resolve
- rejecter:(RCTPromiseRejectBlock) reject {
+- (void)get:(NSDictionary *)getOptions
+ resolver:(RCTPromiseResolveBlock)resolve
+ rejecter:(RCTPromiseRejectBlock)reject {
FIRFirestoreSource source;
if (getOptions && getOptions[@"source"]) {
if ([getOptions[@"source"] isEqualToString:@"server"]) {
@@ -44,7 +44,7 @@
} else {
source = FIRFirestoreSourceDefault;
}
- [_query getDocumentsWithSource:source completion:^(FIRQuerySnapshot * _Nullable snapshot, NSError * _Nullable error) {
+ [_query getDocumentsWithSource:source completion:^(FIRQuerySnapshot *_Nullable snapshot, NSError *_Nullable error) {
if (error) {
[RNFirebaseFirestore promiseRejectException:reject error:error];
} else {
@@ -54,7 +54,7 @@
}];
}
-+ (void)offSnapshot:(NSString *) listenerId {
++ (void)offSnapshot:(NSString *)listenerId {
id<FIRListenerRegistration> listener = _listeners[listenerId];
if (listener) {
[_listeners removeObjectForKey:listenerId];
@@ -62,10 +62,10 @@
}
}
-- (void)onSnapshot:(NSString *) listenerId
-queryListenOptions:(NSDictionary *) queryListenOptions {
+- (void)onSnapshot:(NSString *)listenerId
+queryListenOptions:(NSDictionary *)queryListenOptions {
if (_listeners[listenerId] == nil) {
- id listenerBlock = ^(FIRQuerySnapshot * _Nullable snapshot, NSError * _Nullable error) {
+ id listenerBlock = ^(FIRQuerySnapshot *_Nullable snapshot, NSError *_Nullable error) {
if (error) {
id<FIRListenerRegistration> listener = _listeners[listenerId];
if (listener) {
@@ -85,14 +85,15 @@
includeMetadataChanges = false;
}
- id<FIRListenerRegistration> listener = [_query addSnapshotListenerWithIncludeMetadataChanges:includeMetadataChanges listener:listenerBlock];
+ id<FIRListenerRegistration>
+ listener = [_query addSnapshotListenerWithIncludeMetadataChanges:includeMetadataChanges listener:listenerBlock];
_listeners[listenerId] = listener;
}
}
- (FIRQuery *)buildQuery {
FIRFirestore *firestore = [RNFirebaseFirestore getFirestoreForApp:_appDisplayName];
- FIRQuery *query = (FIRQuery*)[firestore collectionWithPath:_path];
+ FIRQuery *query = (FIRQuery *) [firestore collectionWithPath:_path];
query = [self applyFilters:firestore query:query];
query = [self applyOrders:query];
query = [self applyOptions:firestore query:query];
@@ -100,8 +101,8 @@
return query;
}
-- (FIRQuery *)applyFilters:(FIRFirestore *) firestore
- query:(FIRQuery *) query {
+- (FIRQuery *)applyFilters:(FIRFirestore *)firestore
+ query:(FIRQuery *)query {
for (NSDictionary *filter in _filters) {
NSDictionary *fieldPathDictionary = filter[@"fieldPath"];
NSString *fieldPathType = fieldPathDictionary[@"type"];
@@ -121,6 +122,8 @@
query = [query queryWhereField:fieldPath isLessThan:value];
} else if ([operator isEqualToString:@"LESS_THAN_OR_EQUAL"]) {
query = [query queryWhereField:fieldPath isLessThanOrEqualTo:value];
+ } else if ([operator isEqualToString:@"ARRAY_CONTAINS"]) {
+ query = [query queryWhereField:fieldPath arrayContains:value];
}
} else {
NSArray *fieldPathElements = fieldPathDictionary[@"elements"];
@@ -135,6 +138,8 @@
query = [query queryWhereFieldPath:fieldPath isLessThan:value];
} else if ([operator isEqualToString:@"LESS_THAN_OR_EQUAL"]) {
query = [query queryWhereFieldPath:fieldPath isLessThanOrEqualTo:value];
+ } else if ([operator isEqualToString:@"ARRAY_CONTAINS"]) {
+ query = [query queryWhereFieldPath:fieldPath arrayContains:value];
}
}
}
@@ -138,10 +143,11 @@
}
}
}
+
return query;
}
-- (FIRQuery *)applyOrders:(FIRQuery *) query {
+- (FIRQuery *)applyOrders:(FIRQuery *)query {
for (NSDictionary *order in _orders) {
NSString *direction = order[@"direction"];
NSDictionary *fieldPathDictionary = order[@"fieldPath"];
@@ -159,13 +165,15 @@
return query;
}
-- (FIRQuery *)applyOptions:(FIRFirestore *) firestore
- query:(FIRQuery *) query {
+- (FIRQuery *)applyOptions:(FIRFirestore *)firestore
+ query:(FIRQuery *)query {
if (_options[@"endAt"]) {
- query = [query queryEndingAtValues:[RNFirebaseFirestoreDocumentReference parseJSArray:firestore jsArray:_options[@"endAt"]]];
+ query =
+ [query queryEndingAtValues:[RNFirebaseFirestoreDocumentReference parseJSArray:firestore jsArray:_options[@"endAt"]]];
}
if (_options[@"endBefore"]) {
- query = [query queryEndingBeforeValues:[RNFirebaseFirestoreDocumentReference parseJSArray:firestore jsArray:_options[@"endBefore"]]];
+ query =
+ [query queryEndingBeforeValues:[RNFirebaseFirestoreDocumentReference parseJSArray:firestore jsArray:_options[@"endBefore"]]];
}
if (_options[@"limit"]) {
query = [query queryLimitedTo:[_options[@"limit"] intValue]];
@@ -177,10 +185,12 @@
// iOS doesn't support selectFields
}
if (_options[@"startAfter"]) {
- query = [query queryStartingAfterValues:[RNFirebaseFirestoreDocumentReference parseJSArray:firestore jsArray:_options[@"startAfter"]]];
+ query =
+ [query queryStartingAfterValues:[RNFirebaseFirestoreDocumentReference parseJSArray:firestore jsArray:_options[@"startAfter"]]];
}
if (_options[@"startAt"]) {
- query = [query queryStartingAtValues:[RNFirebaseFirestoreDocumentReference parseJSArray:firestore jsArray:_options[@"startAt"]]];
+ query =
+ [query queryStartingAtValues:[RNFirebaseFirestoreDocumentReference parseJSArray:firestore jsArray:_options[@"startAt"]]];
}
return query;
}
@@ -221,7 +231,7 @@
return snapshot;
}
-+ (NSArray *)documentChangesToArray:(NSArray<FIRDocumentChange *> *) documentChanges {
++ (NSArray *)documentChangesToArray:(NSArray<FIRDocumentChange *> *)documentChanges {
NSMutableArray *changes = [[NSMutableArray alloc] init];
for (FIRDocumentChange *change in documentChanges) {
[changes addObject:[self documentChangeToDictionary:change]];
@@ -247,7 +257,7 @@
return change;
}
-+ (NSArray *)documentSnapshotsToArray:(NSArray<FIRDocumentSnapshot *> *) documentSnapshots {
++ (NSArray *)documentSnapshotsToArray:(NSArray<FIRDocumentSnapshot *> *)documentSnapshots {
NSMutableArray *snapshots = [[NSMutableArray alloc] init];
for (FIRDocumentSnapshot *snapshot in documentSnapshots) {
[snapshots addObject:[RNFirebaseFirestoreDocumentReference snapshotToDictionary:snapshot]];

ios/RNFirebase/firestore/RNFirebaseFirestoreDocumentReference.h

@@ -24,7 +24,6 @@
- (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;
+ (NSDictionary *)snapshotToDictionary:(FIRDocumentSnapshot *)documentSnapshot;
+ (NSDictionary *)parseJSMap:(FIRFirestore *) firestore jsMap:(NSDictionary *) jsMap;
+ (NSArray *)parseJSArray:(FIRFirestore *) firestore jsArray:(NSArray *) jsArray;

ios/RNFirebase/firestore/RNFirebaseFirestoreDocumentReference.m

@@ -4,12 +4,53 @@
#if __has_include(<FirebaseFirestore/FirebaseFirestore.h>)
-static NSMutableDictionary *_listeners;
-
-- (id)initWithPath:(RCTEventEmitter *)emitter
- appDisplayName:(NSString *) appDisplayName
- path:(NSString *) path {
+ static NSMutableDictionary *_listeners;
+ static NSString *const typeKey = @"type";
+ static NSString *const keyPath = @"path";
+ static NSString *const keyData = @"data";
+ static NSString *const keyError = @"error";
+ static NSString *const valueKey = @"value";
+ static NSString *const keyMerge = @"merge";
+ static NSString *const keyAppName = @"appName";
+ static NSString *const keySeconds = @"seconds";
+ static NSString *const keyLatitude = @"latitude";
+ static NSString *const keyMetadata = @"metadata";
+ static NSString *const keyLongitude = @"longitude";
+ static NSString *const keyFromCache = @"fromCache";
+ static NSString *const keyListenerId = @"listenerId";
+ static NSString *const keyNanoSeconds = @"nanoseconds";
+ static NSString *const keyDocumentSnapshot = @"documentSnapshot";
+ static NSString *const keyHasPendingWrites = @"hasPendingWrites";
+ static NSString *const keyIncludeMetaChanges = @"includeMetadataChanges";
+
+ static NSString *const typeNaN = @"nan";
+ static NSString *const typeNull = @"null";
+ static NSString *const typeBlob = @"blob";
+ static NSString *const typeDate = @"date";
+ static NSString *const typeArray = @"array";
+ static NSString *const typeObject = @"object";
+ static NSString *const typeString = @"string";
+ static NSString *const typeNumber = @"number";
+ static NSString *const typeDelete = @"delete";
+ static NSString *const typeBoolean = @"boolean";
+ static NSString *const typeInfinity = @"infinity";
+ static NSString *const typeGeoPoint = @"geopoint";
+ static NSString *const typeTimestamp = @"timestamp";
+ static NSString *const typeReference = @"reference";
+ static NSString *const typeDocumentId = @"documentid";
+ static NSString *const typeFieldValue = @"fieldvalue";
+ static NSString *const typeFieldValueUnion = @"union";
+ static NSString *const typeFieldValueRemove = @"remove";
+ static NSString *const typeFieldValueType = @"type";
+ static NSString *const typeFieldValueElements = @"elements";
+ static NSString *const typeFieldValueIncrement = @"increment";
+
+
+ - (id)initWithPath:(RCTEventEmitter *)emitter
+ appDisplayName:(NSString *)appDisplayName
+ path:(NSString *)path {
self = [super init];
+
if (self) {
_emitter = emitter;
_appDisplayName = appDisplayName;
@@ -20,19 +62,20 @@
if (!_listeners) {
_listeners = [[NSMutableDictionary alloc] init];
}
+
return self;
-}
+ }
-- (void)delete:(RCTPromiseResolveBlock) resolve
- rejecter:(RCTPromiseRejectBlock) reject {
- [_ref deleteDocumentWithCompletion:^(NSError * _Nullable error) {
+ - (void)delete:(RCTPromiseResolveBlock)resolve
+ rejecter:(RCTPromiseRejectBlock)reject {
+ [_ref deleteDocumentWithCompletion:^(NSError *_Nullable error) {
[RNFirebaseFirestoreDocumentReference handleWriteResponse:error resolver:resolve rejecter:reject];
}];
-}
+ }
-- (void)get:(NSDictionary *) getOptions
- resolver:(RCTPromiseResolveBlock) resolve
- rejecter:(RCTPromiseRejectBlock) reject {
+ - (void)get:(NSDictionary *)getOptions
+ resolver:(RCTPromiseResolveBlock)resolve
+ rejecter:(RCTPromiseRejectBlock)reject {
FIRFirestoreSource source;
if (getOptions && getOptions[@"source"]) {
if ([getOptions[@"source"] isEqualToString:@"server"]) {
@@ -45,7 +88,7 @@
} else {
source = FIRFirestoreSourceDefault;
}
- [_ref getDocumentWithSource:source completion:^(FIRDocumentSnapshot * _Nullable snapshot, NSError * _Nullable error) {
+ [_ref getDocumentWithSource:source completion:^(FIRDocumentSnapshot *_Nullable snapshot, NSError *_Nullable error) {
if (error) {
[RNFirebaseFirestore promiseRejectException:reject error:error];
} else {
@@ -53,22 +96,22 @@
resolve(data);
}
}];
-}
+ }
-+ (void)offSnapshot:(NSString *) listenerId {
- id<FIRListenerRegistration> listener = _listeners[listenerId];
+ + (void)offSnapshot:(NSString *)listenerId {
+ id <FIRListenerRegistration> listener = _listeners[listenerId];
if (listener) {
[_listeners removeObjectForKey:listenerId];
[listener remove];
}
-}
+ }
-- (void)onSnapshot:(NSString *) listenerId
- docListenOptions:(NSDictionary *) docListenOptions {
+ - (void)onSnapshot:(NSString *)listenerId
+ docListenOptions:(NSDictionary *)docListenOptions {
if (_listeners[listenerId] == nil) {
- id listenerBlock = ^(FIRDocumentSnapshot * _Nullable snapshot, NSError * _Nullable error) {
+ id listenerBlock = ^(FIRDocumentSnapshot *_Nullable snapshot, NSError *_Nullable error) {
if (error) {
- id<FIRListenerRegistration> listener = _listeners[listenerId];
+ id <FIRListenerRegistration> listener = _listeners[listenerId];
if (listener) {
[_listeners removeObjectForKey:listenerId];
[listener remove];
@@ -79,167 +122,233 @@
}
};
bool includeMetadataChanges;
- if (docListenOptions && docListenOptions[@"includeMetadataChanges"]) {
+ if (docListenOptions && docListenOptions[keyIncludeMetaChanges]) {
includeMetadataChanges = true;
} else {
includeMetadataChanges = false;
}
- id<FIRListenerRegistration> listener = [_ref addSnapshotListenerWithIncludeMetadataChanges:includeMetadataChanges listener:listenerBlock];
+ id <FIRListenerRegistration>
+ listener = [_ref addSnapshotListenerWithIncludeMetadataChanges:includeMetadataChanges listener:listenerBlock];
_listeners[listenerId] = listener;
}
-}
+ }
-- (void)set:(NSDictionary *) data
- options:(NSDictionary *) options
- resolver:(RCTPromiseResolveBlock) resolve
- rejecter:(RCTPromiseRejectBlock) reject {
- NSDictionary *dictionary = [RNFirebaseFirestoreDocumentReference parseJSMap:[RNFirebaseFirestore getFirestoreForApp:_appDisplayName] jsMap:data];
- if (options && options[@"merge"]) {
- [_ref setData:dictionary merge:true completion:^(NSError * _Nullable error) {
+ - (void)set:(NSDictionary *)data
+ options:(NSDictionary *)options
+ resolver:(RCTPromiseResolveBlock)resolve
+ rejecter:(RCTPromiseRejectBlock)reject {
+ NSDictionary *dictionary =
+ [RNFirebaseFirestoreDocumentReference parseJSMap:[RNFirebaseFirestore getFirestoreForApp:_appDisplayName] jsMap:data];
+ if (options && options[keyMerge]) {
+ [_ref setData:dictionary merge:true completion:^(NSError *_Nullable error) {
[RNFirebaseFirestoreDocumentReference handleWriteResponse:error resolver:resolve rejecter:reject];
}];
} else {
- [_ref setData:dictionary completion:^(NSError * _Nullable error) {
+ [_ref setData:dictionary completion:^(NSError *_Nullable error) {
[RNFirebaseFirestoreDocumentReference handleWriteResponse:error resolver:resolve rejecter:reject];
}];
}
-}
+ }
-- (void)update:(NSDictionary *) data
- resolver:(RCTPromiseResolveBlock) resolve
- rejecter:(RCTPromiseRejectBlock) reject {
- NSDictionary *dictionary = [RNFirebaseFirestoreDocumentReference parseJSMap:[RNFirebaseFirestore getFirestoreForApp:_appDisplayName] jsMap:data];
- [_ref updateData:dictionary completion:^(NSError * _Nullable error) {
+ - (void)update:(NSDictionary *)data
+ resolver:(RCTPromiseResolveBlock)resolve
+ rejecter:(RCTPromiseRejectBlock)reject {
+ NSDictionary *dictionary =
+ [RNFirebaseFirestoreDocumentReference parseJSMap:[RNFirebaseFirestore getFirestoreForApp:_appDisplayName] jsMap:data];
+ [_ref updateData:dictionary completion:^(NSError *_Nullable error) {
[RNFirebaseFirestoreDocumentReference handleWriteResponse:error resolver:resolve rejecter:reject];
}];
-}
+ }
-- (BOOL)hasListeners {
- return [[_listeners allKeys] count] > 0;
-}
-
-+ (void)handleWriteResponse:(NSError *) error
- resolver:(RCTPromiseResolveBlock) resolve
- rejecter:(RCTPromiseRejectBlock) reject {
+ + (void)handleWriteResponse:(NSError *)error
+ resolver:(RCTPromiseResolveBlock)resolve
+ rejecter:(RCTPromiseRejectBlock)reject {
if (error) {
[RNFirebaseFirestore promiseRejectException:reject error:error];
} else {
resolve(nil);
}
-}
+ }
-+ (NSDictionary *)snapshotToDictionary:(FIRDocumentSnapshot *)documentSnapshot {
+ + (NSDictionary *)snapshotToDictionary:(FIRDocumentSnapshot *)documentSnapshot {
NSMutableDictionary *snapshot = [[NSMutableDictionary alloc] init];
- [snapshot setValue:documentSnapshot.reference.path forKey:@"path"];
+ [snapshot setValue:documentSnapshot.reference.path forKey:keyPath];
if (documentSnapshot.exists) {
- [snapshot setValue:[RNFirebaseFirestoreDocumentReference buildNativeMap:documentSnapshot.data] forKey:@"data"];
+ [snapshot setValue:[RNFirebaseFirestoreDocumentReference buildNativeMap:documentSnapshot.data] forKey:keyData];
}
if (documentSnapshot.metadata) {
NSMutableDictionary *metadata = [[NSMutableDictionary alloc] init];
- [metadata setValue:@(documentSnapshot.metadata.fromCache) forKey:@"fromCache"];
- [metadata setValue:@(documentSnapshot.metadata.hasPendingWrites) forKey:@"hasPendingWrites"];
- [snapshot setValue:metadata forKey:@"metadata"];
+ [metadata setValue:@(documentSnapshot.metadata.fromCache) forKey:keyFromCache];
+ [metadata setValue:@(documentSnapshot.metadata.hasPendingWrites) forKey:keyHasPendingWrites];
+ [snapshot setValue:metadata forKey:keyMetadata];
}
return snapshot;
-}
+ }
-- (void)handleDocumentSnapshotError:(NSString *)listenerId
+ - (void)handleDocumentSnapshotError:(NSString *)listenerId
error:(NSError *)error {
NSMutableDictionary *event = [[NSMutableDictionary alloc] init];
- [event setValue:_appDisplayName forKey:@"appName"];
- [event setValue:_path forKey:@"path"];
- [event setValue:listenerId forKey:@"listenerId"];
- [event setValue:[RNFirebaseFirestore getJSError:error] forKey:@"error"];
+ [event setValue:_path forKey:keyPath];
+ [event setValue:listenerId forKey:keyListenerId];
+ [event setValue:_appDisplayName forKey:keyAppName];
+ [event setValue:[RNFirebaseFirestore getJSError:error] forKey:keyError];
[RNFirebaseUtil sendJSEvent:self.emitter name:FIRESTORE_DOCUMENT_SYNC_EVENT body:event];
-}
+ }
-- (void)handleDocumentSnapshotEvent:(NSString *)listenerId
+ - (void)handleDocumentSnapshotEvent:(NSString *)listenerId
documentSnapshot:(FIRDocumentSnapshot *)documentSnapshot {
NSMutableDictionary *event = [[NSMutableDictionary alloc] init];
- [event setValue:_appDisplayName forKey:@"appName"];
- [event setValue:_path forKey:@"path"];
- [event setValue:listenerId forKey:@"listenerId"];
- [event setValue:[RNFirebaseFirestoreDocumentReference snapshotToDictionary:documentSnapshot] forKey:@"documentSnapshot"];
+ [event setValue:_path forKey:keyPath];
+ [event setValue:listenerId forKey:keyListenerId];
+ [event setValue:_appDisplayName forKey:keyAppName];
+ [event setValue:[RNFirebaseFirestoreDocumentReference snapshotToDictionary:documentSnapshot] forKey:keyDocumentSnapshot];
[RNFirebaseUtil sendJSEvent:self.emitter name:FIRESTORE_DOCUMENT_SYNC_EVENT body:event];
-}
-
+ }
-+ (NSDictionary *)buildNativeMap:(NSDictionary *)nativeMap {
+ + (NSDictionary *)buildNativeMap:(NSDictionary *)nativeMap {
NSMutableDictionary *map = [[NSMutableDictionary alloc] init];
- [nativeMap enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
+ [nativeMap enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL *_Nonnull stop) {
NSDictionary *typeMap = [RNFirebaseFirestoreDocumentReference buildTypeMap:obj];
map[key] = typeMap;
}];
return map;
-}
+ }
-+ (NSArray *)buildNativeArray:(NSArray *)nativeArray {
+ + (NSArray *)buildNativeArray:(NSArray *)nativeArray {
NSMutableArray *array = [[NSMutableArray alloc] init];
- [nativeArray enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
+ [nativeArray enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL *_Nonnull stop) {
NSDictionary *typeMap = [RNFirebaseFirestoreDocumentReference buildTypeMap:obj];
[array addObject:typeMap];
}];
return array;
-}
+ }
-+ (NSDictionary *)buildTypeMap:(id) value {
+ + (NSDictionary *)buildTypeMap:(id)value {
NSMutableDictionary *typeMap = [[NSMutableDictionary alloc] init];
- if (!value) {
- typeMap[@"type"] = @"null";
- } else if ([value isKindOfClass:[NSString class]]) {
- typeMap[@"type"] = @"string";
- typeMap[@"value"] = value;
- } else if ([value isKindOfClass:[NSDictionary class]]) {
- typeMap[@"type"] = @"object";
- typeMap[@"value"] = [RNFirebaseFirestoreDocumentReference buildNativeMap:value];
- } else if ([value isKindOfClass:[NSArray class]]) {
- typeMap[@"type"] = @"array";
- typeMap[@"value"] = [RNFirebaseFirestoreDocumentReference buildNativeArray:value];
- } else if ([value isKindOfClass:[FIRDocumentReference class]]) {
- typeMap[@"type"] = @"reference";
- FIRDocumentReference *ref = (FIRDocumentReference *)value;
- typeMap[@"value"] = [ref path];
- } else if ([value isKindOfClass:[FIRGeoPoint class]]) {
- typeMap[@"type"] = @"geopoint";
- FIRGeoPoint *point = (FIRGeoPoint *)value;
+
+ // null
+ if (value == nil || [value isKindOfClass:[NSNull class]]) {
+ typeMap[typeKey] = typeNull;
+ return typeMap;
+ }
+
+ // strings
+ if ([value isKindOfClass:[NSString class]]) {
+ typeMap[typeKey] = typeString;
+ typeMap[valueKey] = value;
+ return typeMap;
+ }
+
+ // objects
+ if ([value isKindOfClass:[NSDictionary class]]) {
+ typeMap[typeKey] = typeObject;
+ typeMap[valueKey] = [RNFirebaseFirestoreDocumentReference buildNativeMap:value];
+ return typeMap;
+ }
+
+ // array
+ if ([value isKindOfClass:[NSArray class]]) {
+ typeMap[typeKey] = typeArray;
+ typeMap[valueKey] = [RNFirebaseFirestoreDocumentReference buildNativeArray:value];
+ return typeMap;
+ }
+
+ // reference
+ if ([value isKindOfClass:[FIRDocumentReference class]]) {
+ typeMap[typeKey] = typeReference;
+ FIRDocumentReference *ref = (FIRDocumentReference *) value;
+ typeMap[valueKey] = [ref path];
+ return typeMap;
+ }
+
+ // geopoint
+ if ([value isKindOfClass:[FIRGeoPoint class]]) {
+ typeMap[typeKey] = typeGeoPoint;
+ FIRGeoPoint *point = (FIRGeoPoint *) value;
NSMutableDictionary *geopoint = [[NSMutableDictionary alloc] init];
- geopoint[@"latitude"] = @([point latitude]);
- geopoint[@"longitude"] = @([point longitude]);
- typeMap[@"value"] = geopoint;
- } else if ([value isKindOfClass:[NSDate class]]) {
- typeMap[@"type"] = @"date";
- // NOTE: The round() is important as iOS ends up giving .999 otherwise,
- // and loses a millisecond when going between native and JS
- typeMap[@"value"] = @(round([(NSDate *)value timeIntervalSince1970] * 1000.0));
- } else if ([value isKindOfClass:[NSNumber class]]) {
- NSNumber *number = (NSNumber *)value;
- if (number == (void*)kCFBooleanFalse || number == (void*)kCFBooleanTrue) {
- typeMap[@"type"] = @"boolean";
- } else {
- typeMap[@"type"] = @"number";
+ geopoint[keyLatitude] = @([point latitude]);
+ geopoint[keyLongitude] = @([point longitude]);
+ typeMap[valueKey] = geopoint;
+ return typeMap;
}
- typeMap[@"value"] = value;
- } else if ([value isKindOfClass:[NSData class]]) {
- typeMap[@"type"] = @"blob";
- NSData *blob = (NSData *)value;
- typeMap[@"value"] = [blob base64EncodedStringWithOptions:0];
- } else {
- // TODO: Log an error
- typeMap[@"type"] = @"null";
+
+ // date
+ if ([value isKindOfClass:[NSDate class]]) {
+ typeMap[typeKey] = typeDate;
+ // round is required otherwise iOS ends up with .999 and loses a millisecond
+ // when going between native and JS
+ typeMap[valueKey] = @(round([(NSDate *) value timeIntervalSince1970] * 1000.0));
+ return typeMap;
+ }
+
+ // FIRTimestamps
+ if ([value isKindOfClass:[FIRTimestamp class]]) {
+ typeMap[typeKey] = typeTimestamp;
+ FIRTimestamp *firTimestamp = (FIRTimestamp *) value;
+ NSMutableDictionary *timestamp = [NSMutableDictionary dictionary];
+ int64_t seconds = (int64_t) firTimestamp.seconds;
+ int32_t nanoseconds = (int32_t) firTimestamp.nanoseconds;
+ timestamp[keySeconds] = @(seconds);
+ timestamp[keyNanoSeconds] = @(nanoseconds);
+ typeMap[valueKey] = timestamp;
+ return typeMap;
+ }
+
+ // number / boolean / infinity / nan
+ if ([value isKindOfClass:[NSNumber class]]) {
+ NSNumber *number = (NSNumber *) value;
+
+ // infinity
+ if ([number isEqual:@(INFINITY)]) {
+ typeMap[typeKey] = typeInfinity;
+ return typeMap;
+ }
+
+ // boolean
+ if (number == [NSValue valueWithPointer:(void *) kCFBooleanFalse]
+ || number == [NSValue valueWithPointer:(void *) kCFBooleanTrue]) {
+ typeMap[typeKey] = typeBoolean;
+ typeMap[valueKey] = value;
+ return typeMap;
+ }
+
+ // nan
+ if ([[value description].lowercaseString isEqual:@"nan"]) {
+ typeMap[typeKey] = typeNaN;
+ return typeMap;
+ }
+
+ // number
+ typeMap[typeKey] = typeNumber;
+ typeMap[valueKey] = value;
+ return typeMap;
}
+ // blobs (converted to base64)
+ if ([value isKindOfClass:[NSData class]]) {
+ NSData *blob = (NSData *) value;
+ typeMap[typeKey] = typeBlob;
+ typeMap[valueKey] = [blob base64EncodedStringWithOptions:0];
return typeMap;
-}
+ }
+
+ DLog(@"RNFirebaseFirestore: Unsupported value sent to buildTypeMap - class type is %@",
+ NSStringFromClass([value class]));
+
+ typeMap[typeKey] = typeNull;
+ return typeMap;
+ }
+
+ + (NSDictionary *)parseJSMap:(FIRFirestore *)firestore
+ jsMap:(NSDictionary *)jsMap {
+ NSMutableDictionary *map = [[NSMutableDictionary alloc] init];
-+(NSDictionary *)parseJSMap:(FIRFirestore *) firestore
- jsMap:(NSDictionary *) jsMap {
- NSMutableDictionary* map = [[NSMutableDictionary alloc] init];
if (jsMap) {
- [jsMap enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
+ [jsMap enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL *_Nonnull stop) {
map[key] = [RNFirebaseFirestoreDocumentReference parseJSTypeMap:firestore jsTypeMap:obj];
}];
}
@@ -243,14 +352,16 @@
map[key] = [RNFirebaseFirestoreDocumentReference parseJSTypeMap:firestore jsTypeMap:obj];
}];
}
+
return map;
-}
+ }
+
+ + (NSArray *)parseJSArray:(FIRFirestore *)firestore
+ jsArray:(NSArray *)jsArray {
+ NSMutableArray *array = [[NSMutableArray alloc] init];
-+(NSArray *)parseJSArray:(FIRFirestore *) firestore
- jsArray:(NSArray *) jsArray {
- NSMutableArray* array = [[NSMutableArray alloc] init];
if (jsArray) {
- [jsArray enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
+ [jsArray enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL *_Nonnull stop) {
[array addObject:[RNFirebaseFirestoreDocumentReference parseJSTypeMap:firestore jsTypeMap:obj]];
}];
}
@@ -254,47 +365,99 @@
[array addObject:[RNFirebaseFirestoreDocumentReference parseJSTypeMap:firestore jsTypeMap:obj]];
}];
}
+
return array;
-}
+ }
+
+ + (id)parseJSTypeMap:(FIRFirestore *)firestore
+ jsTypeMap:(NSDictionary *)jsTypeMap {
+ id value = jsTypeMap[valueKey];
+ NSString *type = jsTypeMap[typeKey];
+
+ if ([type isEqualToString:typeArray]) {
+ return [self parseJSArray:firestore jsArray:value];
+ }
+
+ if ([type isEqualToString:typeObject]) {
+ return [self parseJSMap:firestore jsMap:value];
+ }
-+(id)parseJSTypeMap:(FIRFirestore *) firestore
- jsTypeMap:(NSDictionary *) jsTypeMap {
- NSString *type = jsTypeMap[@"type"];
- id value = jsTypeMap[@"value"];
- if ([type isEqualToString:@"array"]) {
- return [RNFirebaseFirestoreDocumentReference parseJSArray:firestore jsArray:value];
- } else if ([type isEqualToString:@"object"]) {
- return [RNFirebaseFirestoreDocumentReference parseJSMap:firestore jsMap:value];
- } else if ([type isEqualToString:@"reference"]) {
+ if ([type isEqualToString:typeReference]) {
return [firestore documentWithPath:value];
- } else if ([type isEqualToString:@"blob"]) {
+ }
+
+ if ([type isEqualToString:typeBlob]) {
return [[NSData alloc] initWithBase64EncodedString:(NSString *) value options:0];
- } else if ([type isEqualToString:@"geopoint"]) {
- NSDictionary *geopoint = (NSDictionary*)value;
- NSNumber *latitude = geopoint[@"latitude"];
- NSNumber *longitude = geopoint[@"longitude"];
+ }
+
+ if ([type isEqualToString:typeGeoPoint]) {
+ NSDictionary *geopoint = (NSDictionary *) value;
+ NSNumber *latitude = geopoint[keyLatitude];
+ NSNumber *longitude = geopoint[keyLongitude];
return [[FIRGeoPoint alloc] initWithLatitude:[latitude doubleValue] longitude:[longitude doubleValue]];
- } else if ([type isEqualToString:@"date"]) {
- return [NSDate dateWithTimeIntervalSince1970:([(NSNumber *)value doubleValue] / 1000.0)];
- } else if ([type isEqualToString:@"documentid"]) {
+ }
+
+ if ([type isEqualToString:typeDate]) {
+ return [NSDate dateWithTimeIntervalSince1970:([(NSNumber *) value doubleValue] / 1000.0)];
+ }
+
+ if ([type isEqualToString:typeTimestamp]) {
+ NSDictionary *timestampDict = (NSDictionary *) value;
+ int64_t seconds = [timestampDict[keySeconds] longLongValue];
+ int32_t nanoseconds = [timestampDict[keyNanoSeconds] intValue];
+ return [[FIRTimestamp alloc] initWithSeconds:seconds nanoseconds:nanoseconds];
+ }
+
+ if ([type isEqualToString:typeDocumentId]) {
return [FIRFieldPath documentID];
- } else if ([type isEqualToString:@"fieldvalue"]) {
- NSString *string = (NSString*)value;
- if ([string isEqualToString:@"delete"]) {
+ }
+
+ if ([type isEqualToString:typeFieldValue]) {
+ NSDictionary *fieldValueMap = (NSDictionary *) value;
+ NSString *string = (NSString *) fieldValueMap[typeFieldValueType];
+
+ if ([string isEqualToString:typeDelete]) {
return [FIRFieldValue fieldValueForDelete];
- } else if ([string isEqualToString:@"timestamp"]) {
+ }
+
+ if ([string isEqualToString:typeTimestamp]) {
return [FIRFieldValue fieldValueForServerTimestamp];
- } else {
- // TODO: Log warning
+ }
+
+ if ([string isEqualToString:typeFieldValueIncrement]) {
+ double doubleValue = [value[typeFieldValueElements] doubleValue];
+ return [FIRFieldValue fieldValueForDoubleIncrement:doubleValue];
+ }
+
+ if ([string isEqualToString:typeFieldValueUnion]) {
+ NSArray *elements = [self parseJSArray:firestore jsArray:value[typeFieldValueElements]];
+ return [FIRFieldValue fieldValueForArrayUnion:elements];
+ }
+
+ if ([string isEqualToString:typeFieldValueRemove]) {
+ NSArray *elements = [self parseJSArray:firestore jsArray:value[typeFieldValueElements]];
+ return [FIRFieldValue fieldValueForArrayRemove:elements];
+ }
+
+ DLog(@"RNFirebaseFirestore: Unsupported field-value sent to parseJSTypeMap - value is %@", NSStringFromClass([value class]));
return nil;
}
- } else if ([type isEqualToString:@"boolean"] || [type isEqualToString:@"number"] || [type isEqualToString:@"string"] || [type isEqualToString:@"null"]) {
+
+ if ([type isEqualToString:typeInfinity]) {
+ return @(INFINITY);
+ }
+
+ if ([type isEqualToString:typeNaN]) {
+ return [NSDecimalNumber notANumber];
+ }
+
+ if ([type isEqualToString:typeBoolean] || [type isEqualToString:typeNumber] || [type isEqualToString:typeString]
+ || [type isEqualToString:typeNull]) {
return value;
- } else {
- // TODO: Log error
+ }
+
return nil;
}
-}
#endif

ios/RNFirebase/firestore/RNFirebaseFirestore.h

@@ -11,7 +11,6 @@
@interface RNFirebaseFirestore : RCTEventEmitter <RCTBridgeModule> {}
@property NSMutableDictionary *transactions;
-@property dispatch_queue_t transactionQueue;
+ (void)promiseRejectException:(RCTPromiseRejectBlock)reject error:(NSError *)error;

ios/RNFirebase/firestore/RNFirebaseFirestore.m

@@ -26,7 +26,6 @@
if (self != nil) {
initialisedApps = [[NSMutableDictionary alloc] init];
_transactions = [[NSMutableDictionary alloc] init];
- _transactionQueue = dispatch_queue_create("io.invertase.react-native-firebase.firestore.transactions", DISPATCH_QUEUE_CONCURRENT);
}
return self;
}
@@ -34,20 +33,16 @@
/**
* TRANSACTIONS
*/
-
RCT_EXPORT_METHOD(transactionGetDocument:(NSString *)appDisplayName
transactionId:(nonnull NSNumber *)transactionId
path:(NSString *)path
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
- __block NSMutableDictionary *transactionState;
-
- dispatch_sync(_transactionQueue, ^{
- transactionState = _transactions[[transactionId stringValue]];
- });
+ @synchronized (self->_transactions[[transactionId stringValue]]) {
+ __block NSMutableDictionary *transactionState = self->_transactions[[transactionId stringValue]];
if (!transactionState) {
- NSLog(@"transactionGetDocument called for non-existant transactionId %@", transactionId);
+ DLog(@"transactionGetDocument called for non-existant transactionId %@", transactionId);
return;
}
@@ -64,94 +60,102 @@
if (path == nil) {
[snapshotDict setValue:ref.path forKey:@"path"];
}
+
resolve(snapshotDict);
}
+ }
}
RCT_EXPORT_METHOD(transactionDispose:(NSString *)appDisplayName
transactionId:(nonnull NSNumber *)transactionId) {
- __block NSMutableDictionary *transactionState;
-
- dispatch_sync(_transactionQueue, ^{
- transactionState = _transactions[[transactionId stringValue]];
- });
+ @synchronized (self->_transactions[[transactionId stringValue]]) {
+ __block NSMutableDictionary *transactionState = self->_transactions[[transactionId stringValue]];
if (!transactionState) {
- NSLog(@"transactionGetDocument called for non-existant transactionId %@", transactionId);
+ DLog(@"transactionGetDocument called for non-existant transactionId %@", transactionId);
return;
}
dispatch_semaphore_t semaphore = [transactionState valueForKey:@"semaphore"];
[transactionState setValue:@true forKey:@"abort"];
dispatch_semaphore_signal(semaphore);
+ }
}
RCT_EXPORT_METHOD(transactionApplyBuffer:(NSString *)appDisplayName
transactionId:(nonnull NSNumber *)transactionId
commandBuffer:(NSArray *)commandBuffer) {
- __block NSMutableDictionary *transactionState;
-
- dispatch_sync(_transactionQueue, ^{
- transactionState = _transactions[[transactionId stringValue]];
- });
+ @synchronized (self->_transactions[[transactionId stringValue]]) {
+ __block NSMutableDictionary *transactionState = self->_transactions[[transactionId stringValue]];
if (!transactionState) {
- NSLog(@"transactionGetDocument called for non-existant transactionId %@", transactionId);
+ DLog(@"transactionGetDocument called for non-existant transactionId %@", transactionId);
return;
}
dispatch_semaphore_t semaphore = [transactionState valueForKey:@"semaphore"];
[transactionState setValue:commandBuffer forKey:@"commandBuffer"];
dispatch_semaphore_signal(semaphore);
+ }
}
RCT_EXPORT_METHOD(transactionBegin:(NSString *)appDisplayName
transactionId:(nonnull NSNumber *)transactionId) {
FIRFirestore *firestore = [RNFirebaseFirestore getFirestoreForApp:appDisplayName];
__block BOOL aborted = false;
+ __block BOOL completed = false;
+ __block NSMutableDictionary *transactionState = [NSMutableDictionary new];
- dispatch_async(_transactionQueue, ^{
- NSMutableDictionary *transactionState = [NSMutableDictionary new];
+ [firestore runTransactionWithBlock:^id (FIRTransaction *transaction, NSError * *errorPointer) {
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
- transactionState[@"semaphore"] = semaphore;
- [firestore runTransactionWithBlock:^id (FIRTransaction *transaction, NSError * *errorPointer) {
+ @synchronized (transactionState) {
+ transactionState[@"semaphore"] = semaphore;
transactionState[@"transaction"] = transaction;
+ if (!self->_transactions[[transactionId stringValue]]) {
+ [self->_transactions setValue:transactionState forKey:[transactionId stringValue]];
+ }
+
// Build and send transaction update event
- dispatch_barrier_async(_transactionQueue, ^{
- [_transactions setValue:transactionState forKey:[transactionId stringValue]];
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSMutableDictionary *eventMap = [NSMutableDictionary new];
eventMap[@"type"] = @"update";
eventMap[@"id"] = transactionId;
eventMap[@"appName"] = appDisplayName;
[RNFirebaseUtil sendJSEvent:self name:FIRESTORE_TRANSACTION_EVENT body:eventMap];
});
+ }
// wait for the js event handler to call transactionApplyBuffer
// this wait occurs on the RNFirestore Worker Queue so if transactionApplyBuffer fails to
// signal the semaphore then no further blocks will be executed by RNFirestore until the timeout expires
- dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW, 3000 * NSEC_PER_SEC);
-
+ dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW, 5000 * NSEC_PER_SEC);
BOOL timedOut = dispatch_semaphore_wait(semaphore, delayTime) != 0;
+
+ @synchronized (transactionState) {
aborted = [transactionState valueForKey:@"abort"];
- // dispose of transaction dictionary
- dispatch_barrier_async(_transactionQueue, ^{
- [_transactions removeObjectForKey:[transactionId stringValue]];
- });
+ if (transactionState[@"semaphore"] != semaphore) {
+ return nil;
+ }
- if (aborted) {
+ if (aborted == YES) {
*errorPointer = [NSError errorWithDomain:FIRFirestoreErrorDomain code:FIRFirestoreErrorCodeAborted userInfo:@{}];
return nil;
}
- if (timedOut) {
+ if (timedOut == YES) {
*errorPointer = [NSError errorWithDomain:FIRFirestoreErrorDomain code:FIRFirestoreErrorCodeDeadlineExceeded userInfo:@{}];
return nil;
}
+ if (completed == YES) {
+ return nil;
+ }
+
NSArray *commandBuffer = [transactionState valueForKey:@"commandBuffer"];
+
for (NSDictionary *command in commandBuffer) {
NSString *type = command[@"type"];
NSString *path = command[@"path"];
@@ -174,7 +178,12 @@
}
return nil;
+ }
} completion:^(id result, NSError *error) {
+ if (completed == YES) return;
+ completed = YES;
+
+ @synchronized (transactionState) {
if (aborted == NO) {
NSMutableDictionary *eventMap = [NSMutableDictionary new];
eventMap[@"id"] = transactionId;
@@ -189,8 +198,11 @@
[RNFirebaseUtil sendJSEvent:self name:FIRESTORE_TRANSACTION_EVENT body:eventMap];
}
+
+ [self->_transactions removeObjectForKey:[transactionId stringValue]];
+ }
+
}];
- });
}
/**
@@ -372,8 +384,7 @@
firestoreSettings.sslEnabled = firestore.settings.sslEnabled;
}
if (settings[@"timestampsInSnapshots"]) {
- // TODO: Enable when available on Android
- // firestoreSettings.timestampsInSnapshotsEnabled = settings[@"timestampsInSnapshots"];
+ firestoreSettings.timestampsInSnapshotsEnabled = settings[@"timestampsInSnapshots"];
}
[firestore setSettings:firestoreSettings];

ios/RNFirebase/functions/RNFirebaseFunctions.m

@@ -1,4 +1,5 @@
#import "RNFirebaseFunctions.h"
+#import "RNFirebaseUtil.h"
#if __has_include(<FirebaseFunctions/FIRFunctions.h>)
#import <FirebaseFunctions/FIRFunctions.h>
@@ -9,6 +10,10 @@
RCT_EXPORT_MODULE();
RCT_EXPORT_METHOD(httpsCallable:
+ (NSString *) appName
+ region:
+ (NSString *) region
+ name:
(NSString *) name
wrapper:
(NSDictionary *) wrapper
@@ -16,10 +21,15 @@
(RCTPromiseResolveBlock) resolve
rejecter:
(RCTPromiseRejectBlock) reject
- ) {
- FIRFunctions *functions = [FIRFunctions functions];
-
- [[functions HTTPSCallableWithName:name] callWithObject:[wrapper valueForKey:@"data"] completion:^(FIRHTTPSCallableResult * _Nullable result, NSError * _Nullable error) {
+ ){
+ FIRApp *firebaseApp = [RNFirebaseUtil getApp:appName];
+ FIRFunctions *functions = [FIRFunctions functionsForApp:firebaseApp region:region];
+
+ FIRHTTPSCallable *callable = [functions HTTPSCallableWithName:name];
+
+ [callable
+ callWithObject:[wrapper valueForKey:@"data"]
+ completion:^(FIRHTTPSCallableResult * _Nullable result, NSError * _Nullable error) {
if (error) {
NSObject *details = [NSNull null];
NSString *message = error.localizedDescription;
@@ -40,7 +51,23 @@
resolve(@{ @"data": [result data] });
}
}];
+}
+RCT_EXPORT_METHOD(useFunctionsEmulator:
+ (NSString *) appName
+ region:
+ (NSString *) region
+ origin:
+ (NSString *) origin
+ resolver:
+ (RCTPromiseResolveBlock) resolve
+ rejecter:
+ (RCTPromiseRejectBlock) reject
+ ){
+ FIRApp *firebaseApp = [RNFirebaseUtil getApp:appName];
+ FIRFunctions *functions = [FIRFunctions functionsForApp:firebaseApp region:region];
+ [functions useFunctionsEmulatorOrigin:origin];
+ resolve([NSNull null]);
}
- (NSString *)getErrorCodeName:(NSError *)error {

ios/RNFirebase/invites/RNFirebaseInvites.m

@@ -31,7 +31,7 @@
- (id)init {
self = [super init];
if (self != nil) {
- NSLog(@"Setting up RNFirebaseInvites instance");
+ DLog(@"Setting up RNFirebaseInvites instance");
// Set static instance for use from AppDelegate
theRNFirebaseInvites = self;
}
@@ -101,7 +101,7 @@
if (url) {
[FIRInvites handleUniversalLink:url completion:^(FIRReceivedInvite * _Nullable receivedInvite, NSError * _Nullable error) {
if (error) {
- NSLog(@"Failed to handle universal link: %@", [error localizedDescription]);
+ DLog(@"Failed to handle universal link: %@", [error localizedDescription]);
reject(@"invites/initial-invitation-error", @"Failed to handle invitation", error);
} else if (receivedInvite && receivedInvite.inviteId) {
resolve(@{
@@ -168,7 +168,7 @@
- (BOOL)handleUrl:(NSURL *)url {
return [FIRInvites handleUniversalLink:url completion:^(FIRReceivedInvite * _Nullable receivedInvite, NSError * _Nullable error) {
if (error) {
- NSLog(@"Failed to handle invitation: %@", [error localizedDescription]);
+ DLog(@"Failed to handle invitation: %@", [error localizedDescription]);
} else if (receivedInvite && receivedInvite.inviteId) {
[self sendJSEvent:self name:INVITES_INVITATION_RECEIVED body:@{
@"deepLink": receivedInvite.deepLink,
@@ -188,7 +188,7 @@
} else if (!initialInvite) {
initialInvite = body;
} else {
- NSLog(@"Multiple invite events received before the JS invites module has been initialised");
+ DLog(@"Multiple invite events received before the JS invites module has been initialised");
}
}
@@ -26,7 +26,7 @@
- (id)init {
self = [super init];
if (self != nil) {
- NSLog(@"Setting up RNFirebaseLinks instance");
+ DLog(@"Setting up RNFirebaseLinks instance");
// Set static instance for use from AppDelegate
theRNFirebaseLinks = self;
}
@@ -63,8 +63,24 @@
if (dynamicLink && dynamicLink.url && error == nil) {
NSURL* url = dynamicLink.url;
[self sendJSEvent:self name:LINKS_LINK_RECEIVED body:url.absoluteString];
+ } else if (error != nil && [NSPOSIXErrorDomain isEqualToString:error.domain] && error.code == 53) {
+ DLog(@"Failed to handle universal link on first attempt, retrying: %@", userActivity.webpageURL);
+
+ // Per Apple Tech Support, this could occur when returning from background on iOS 12.
+ // https://github.com/AFNetworking/AFNetworking/issues/4279#issuecomment-447108981
+ // Retry the request once
+ [[FIRDynamicLinks dynamicLinks]
+ handleUniversalLink:userActivity.webpageURL
+ completion:^(FIRDynamicLink * _Nullable dynamicLink, NSError * _Nullable error) {
+ if (dynamicLink && dynamicLink.url && error == nil) {
+ NSURL* url = dynamicLink.url;
+ [self sendJSEvent:self name:LINKS_LINK_RECEIVED body:url.absoluteString];
} else {
- NSLog(@"Failed to handle universal link: %@", userActivity.webpageURL);
+ DLog(@"Failed to handle universal link during retry: %@", userActivity.webpageURL);
+ }
+ }];
+ } else {
+ DLog(@"Failed to handle universal link: %@", userActivity.webpageURL);
}
}];
}
@@ -89,12 +105,12 @@
reject(@"links/failure", @"Failed to create Dynamic Link", nil);
} else {
NSString *longLink = dynamicLink.url.absoluteString;
- NSLog(@"created long dynamic link: %@", longLink);
+ DLog(@"created long dynamic link: %@", longLink);
resolve(longLink);
}
}
@catch(NSException * e) {
- NSLog(@"create dynamic link failure %@", e);
+ DLog(@"create dynamic link failure %@", e);
reject(@"links/failure",[e reason], nil);
}
}
@@ -116,17 +132,17 @@
}
[components shortenWithCompletion:^(NSURL *_Nullable shortURL, NSArray *_Nullable warnings, NSError *_Nullable error) {
if (error) {
- NSLog(@"create short dynamic link failure %@", [error localizedDescription]);
+ DLog(@"create short dynamic link failure %@", [error localizedDescription]);
reject(@"links/failure", @"Failed to create Short Dynamic Link", error);
} else {
NSString *shortLink = shortURL.absoluteString;
- NSLog(@"created short dynamic link: %@", shortLink);
+ DLog(@"created short dynamic link: %@", shortLink);
resolve(shortLink);
}
}];
}
@catch(NSException * e) {
- NSLog(@"create short dynamic link failure %@", e);
+ DLog(@"create short dynamic link failure %@", e);
reject(@"links/failure",[e reason], nil);
}
}
@@ -140,17 +156,20 @@
&& [self.bridge.launchOptions[UIApplicationLaunchOptionsUserActivityDictionaryKey][UIApplicationLaunchOptionsUserActivityTypeKey] isEqualToString:NSUserActivityTypeBrowsingWeb]) {
NSDictionary *dictionary = self.bridge.launchOptions[UIApplicationLaunchOptionsUserActivityDictionaryKey];
NSUserActivity* userActivity = (NSUserActivity*) dictionary[@"UIApplicationLaunchOptionsUserActivityKey"];
- [[FIRDynamicLinks dynamicLinks] handleUniversalLink:userActivity.webpageURL
+ BOOL handled = [[FIRDynamicLinks dynamicLinks] handleUniversalLink:userActivity.webpageURL
completion:^(FIRDynamicLink * _Nullable dynamicLink, NSError * _Nullable error) {
if (error != nil){
- NSLog(@"Failed to handle universal link: %@", [error localizedDescription]);
+ DLog(@"Failed to handle universal link: %@", [error localizedDescription]);
reject(@"links/failure", @"Failed to handle universal link", error);
} else {
NSString* urlString = dynamicLink ? dynamicLink.url.absoluteString : userActivity.webpageURL.absoluteString;
- NSLog(@"initial link is: %@", urlString);
+ DLog(@"initial link is: %@", urlString);
resolve(urlString);
}
}];
+ if (!handled) {
+ resolve(nil);
+ }
} else {
resolve(initialLink);
}
@@ -171,7 +190,7 @@
} else if (!initialLink) {
initialLink = body;
} else {
- NSLog(@"Multiple link events received before the JS links module has been initialised");
+ DLog(@"Multiple link events received before the JS links module has been initialised");
}
}
@@ -190,7 +209,7 @@
return components;
}
@catch(NSException * e) {
- NSLog(@"error while building componets from meta data %@", e);
+ DLog(@"error while building componets from meta data %@", e);
@throw;
}
}

ios/RNFirebase/messaging/RNFirebaseMessaging.m

@@ -5,7 +5,6 @@
#import "RNFirebaseEvents.h"
#import "RNFirebaseUtil.h"
#import <FirebaseMessaging/FirebaseMessaging.h>
-#import <FirebaseInstanceID/FIRInstanceID.h>
#import <React/RCTEventDispatcher.h>
#import <React/RCTConvert.h>
@@ -31,7 +30,7 @@
- (id)init {
self = [super init];
if (self != nil) {
- NSLog(@"Setting up RNFirebaseMessaging instance");
+ DLog(@"Setting up RNFirebaseMessaging instance");
[self configure];
}
return self;
@@ -84,7 +83,7 @@
// Listen for FCM tokens
- (void)messaging:(FIRMessaging *)messaging didReceiveRegistrationToken:(NSString *)fcmToken {
- NSLog(@"Received new FCM token: %@", fcmToken);
+ DLog(@"Received new FCM token: %@", fcmToken);
[self sendJSEvent:self name:MESSAGING_TOKEN_REFRESHED body:fcmToken];
}
@@ -110,8 +109,9 @@
RCT_EXPORT_METHOD(getToken:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
if (initialToken) {
resolve(initialToken);
- } else if ([[FIRInstanceID instanceID] token]) {
- resolve([[FIRInstanceID instanceID] token]);
+ initialToken = nil;
+ } else if ([[FIRMessaging messaging] FCMToken]) {
+ resolve([[FIRMessaging messaging] FCMToken]);
} else {
NSString * senderId = [[FIRApp defaultApp] options].GCMSenderID;
[[FIRMessaging messaging] retrieveFCMTokenForSenderID:senderId completion:^(NSString * _Nullable FCMToken, NSError * _Nullable error) {
@@ -126,6 +126,32 @@
}
}
+RCT_EXPORT_METHOD(deleteToken:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
+ NSString * senderId = [[FIRApp defaultApp] options].GCMSenderID;
+ [[FIRMessaging messaging] deleteFCMTokenForSenderID:senderId completion:^(NSError * _Nullable error) {
+ if (error) {
+ reject(@"messaging/fcm-token-error", @"Failed to delete FCM token.", error);
+ } else {
+ resolve([NSNull null]);
+ }
+ }];
+}
+
+
+RCT_EXPORT_METHOD(getAPNSToken:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
+ NSData *apnsToken = [FIRMessaging messaging].APNSToken;
+ if (apnsToken) {
+ const char *data = [apnsToken bytes];
+ NSMutableString *token = [NSMutableString string];
+ for (NSInteger i = 0; i < apnsToken.length; i++) {
+ [token appendFormat:@"%02.2hhX", data[i]];
+ }
+ resolve([token copy]);
+ } else {
+ resolve([NSNull null]);
+ }
+}
+
RCT_EXPORT_METHOD(requestPermission:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
if (RCTRunningInAppExtension()) {
reject(@"messaging/request-permission-unavailable", @"requestPermission is not supported in App Extensions", nil);
@@ -160,16 +186,23 @@
});
}
+RCT_EXPORT_METHOD(registerForRemoteNotifications:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
+ [RCTSharedApplication() registerForRemoteNotifications];
+ resolve(nil);
+}
+
// Non Web SDK methods
RCT_EXPORT_METHOD(hasPermission:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_9_x_Max) {
dispatch_async(dispatch_get_main_queue(), ^{
- resolve(@([RCTSharedApplication() currentUserNotificationSettings].types != UIUserNotificationTypeNone));
+ BOOL hasPermission = [RCTConvert BOOL:@([RCTSharedApplication() currentUserNotificationSettings].types != UIUserNotificationTypeNone)];
+ resolve(@(hasPermission));
});
} else {
if (@available(iOS 10.0, *)) {
[[UNUserNotificationCenter currentNotificationCenter] getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
- resolve(@(settings.alertSetting == UNNotificationSettingEnabled));
+ BOOL hasPermission = [RCTConvert BOOL:@(settings.alertSetting == UNNotificationSettingEnabled)];
+ resolve(@(hasPermission));
}];
}
}
@@ -237,7 +270,7 @@
}
[pendingMessages addObject:body];
} else {
- NSLog(@"Received unexpected message type");
+ DLog(@"Received unexpected message type");
}
}
}

ios/RNFirebase/notifications/RNFirebaseNotifications.m

@@ -16,7 +16,10 @@
#endif
@end
-@implementation RNFirebaseNotifications
+@implementation RNFirebaseNotifications {
+ NSMutableDictionary<NSString *, void (^)(UIBackgroundFetchResult)> *fetchCompletionHandlers;
+ NSMutableDictionary<NSString *, void(^)(void)> *completionHandlers;
+}
static RNFirebaseNotifications *theRNFirebaseNotifications = nil;
// PRE-BRIDGE-EVENTS: Consider enabling this to allow events built up before the bridge is built to be sent to the JS side
@@ -40,7 +43,7 @@
- (id)init {
self = [super init];
if (self != nil) {
- NSLog(@"Setting up RNFirebaseNotifications instance");
+ DLog(@"Setting up RNFirebaseNotifications instance");
[self initialise];
}
return self;
@@ -54,6 +57,8 @@
// Set static instance for use from AppDelegate
theRNFirebaseNotifications = self;
+ completionHandlers = [[NSMutableDictionary alloc] init];
+ fetchCompletionHandlers = [[NSMutableDictionary alloc] init];
}
// PRE-BRIDGE-EVENTS: Consider enabling this to allow events built up before the bridge is built to be sent to the JS side
@@ -95,6 +100,22 @@
}
}
+RCT_EXPORT_METHOD(complete:(NSString*)handlerKey fetchResult:(UIBackgroundFetchResult)fetchResult) {
+ if (handlerKey != nil) {
+ void (^fetchCompletionHandler)(UIBackgroundFetchResult) = fetchCompletionHandlers[handlerKey];
+ if (fetchCompletionHandler != nil) {
+ fetchCompletionHandlers[handlerKey] = nil;
+ fetchCompletionHandler(fetchResult);
+ } else {
+ void(^completionHandler)(void) = completionHandlers[handlerKey];
+ if (completionHandler != nil) {
+ completionHandlers[handlerKey] = nil;
+ completionHandler();
+ }
+ }
+ }
+}
+
// Listen for background messages
- (void)didReceiveRemoteNotification:(NSDictionary *)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
@@ -106,6 +127,9 @@
return;
}
+ NSDictionary *notification = [self parseUserInfo:userInfo];
+ NSString *handlerKey = notification[@"notificationId"];
+
NSString *event;
if (RCTSharedApplication().applicationState == UIApplicationStateBackground) {
event = NOTIFICATIONS_NOTIFICATION_DISPLAYED;
@@ -124,7 +148,6 @@
return;
}
- NSDictionary *notification = [self parseUserInfo:userInfo];
// For onOpened events, we set the default action name as iOS 8/9 has no concept of actions
if (event == NOTIFICATIONS_NOTIFICATION_OPENED) {
notification = @{
@@ -133,8 +156,13 @@
};
}
- [self sendJSEvent:self name:event body:notification];
+ if (handlerKey != nil) {
+ fetchCompletionHandlers[handlerKey] = completionHandler;
+ } else {
completionHandler(UIBackgroundFetchResultNoData);
+ }
+
+ [self sendJSEvent:self name:event body:notification];
}
// *******************************************************
@@ -195,8 +223,14 @@
#endif
NSDictionary *message = [self parseUNNotificationResponse:response];
+ NSString *handlerKey = message[@"notification"][@"notificationId"];
+
[self sendJSEvent:self name:NOTIFICATIONS_NOTIFICATION_OPENED body:message];
+ if (handlerKey != nil) {
+ completionHandlers[handlerKey] = completionHandler;
+ } else {
completionHandler();
+ }
}
#endif
@@ -226,7 +260,7 @@
if ([self isIOS89]) {
for (UILocalNotification *notification in RCTSharedApplication().scheduledLocalNotifications) {
NSDictionary *notificationInfo = notification.userInfo;
- if ([notificationId isEqualToString:[notificationInfo valueForKey:@"notificationId"]]) {
+ if ([notificationId isEqualToString:notificationInfo[@"notificationId"]]) {
[RCTSharedApplication() cancelLocalNotification:notification];
}
}
@@ -388,7 +422,7 @@
if ([name isEqualToString:NOTIFICATIONS_NOTIFICATION_OPENED] && !initialNotification) {
initialNotification = body;
} else if ([name isEqualToString:NOTIFICATIONS_NOTIFICATION_OPENED]) {
- NSLog(@"Multiple notification open events received before the JS Notifications module has been initialised");
+ DLog(@"Multiple notification open events received before the JS Notifications module has been initialised");
}
// PRE-BRIDGE-EVENTS: Consider enabling this to allow events built up before the bridge is built to be sent to the JS side
// [pendingEvents addObject:@{@"name":name, @"body":body}];
@@ -510,7 +544,7 @@
if (attachment) {
[attachments addObject:attachment];
} else {
- NSLog(@"Failed to create attachment: %@", error);
+ DLog(@"Failed to create attachment: %@", error);
}
}
content.attachments = attachments;
@@ -546,9 +580,11 @@
calendarUnit = NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond;
} else if ([interval isEqualToString:@"week"]) {
calendarUnit = NSCalendarUnitWeekday | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond;
+ } else {
+ calendarUnit = NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond;
}
} else {
- // Needs to match exactly to the secpmd
+ // Needs to match exactly to the second
calendarUnit = NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond;
}
@@ -602,6 +638,9 @@
NSDictionary *notification = [self parseUNNotification:response.notification];
notificationResponse[@"notification"] = notification;
notificationResponse[@"action"] = response.actionIdentifier;
+ if ([response isKindOfClass:[UNTextInputNotificationResponse class]]) {
+ notificationResponse[@"results"] = @{@"resultKey": ((UNTextInputNotificationResponse *)response).userText};
+ }
return notificationResponse;
}
@@ -698,7 +737,7 @@
|| [k3 isEqualToString:@"title-loc-key"]) {
// Ignore known keys
} else {
- NSLog(@"Unknown alert key: %@", k2);
+ DLog(@"Unknown alert key: %@", k2);
}
}
} else {
@@ -711,7 +750,7 @@
} else if ([k2 isEqualToString:@"sound"]) {
notification[@"sound"] = aps[k2];
} else {
- NSLog(@"Unknown aps key: %@", k2);
+ DLog(@"Unknown aps key: %@", k2);
}
}
} else if ([k1 isEqualToString:@"gcm.message_id"]) {
@@ -740,6 +779,12 @@
return @[NOTIFICATIONS_NOTIFICATION_DISPLAYED, NOTIFICATIONS_NOTIFICATION_OPENED, NOTIFICATIONS_NOTIFICATION_RECEIVED];
}
+- (NSDictionary *) constantsToExport {
+ return @{ @"backgroundFetchResultNoData" : @(UIBackgroundFetchResultNoData),
+ @"backgroundFetchResultNewData" : @(UIBackgroundFetchResultNewData),
+ @"backgroundFetchResultFailed" : @(UIBackgroundFetchResultFailed)};
+}
+
+ (BOOL)requiresMainQueueSetup
{
return YES;

ios/RNFirebase/perf/RNFirebasePerformance.m

@@ -105,7 +105,7 @@
RCT_EXPORT_METHOD(incrementTraceMetric:
(NSString *) identifier
metricName:(NSString *) metricName
- incrementBy:(NSNumber *) incrementBy
+ incrementBy:(nonnull NSNumber *) incrementBy
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
int64_t byInt = [incrementBy intValue];
@@ -132,7 +132,7 @@
RCT_EXPORT_METHOD(putTraceMetric:
(NSString *) identifier
attribute:(NSString *) attribute
- value:(NSNumber *) value
+ value:(nonnull NSNumber *) value
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
int64_t byInt = [value intValue];
@@ -220,7 +220,7 @@
RCT_EXPORT_METHOD(setHttpMetricResponseCode:
(NSString *) url
httpMethod:(NSString *) httpMethod
- code:(NSNumber *) code
+ code:(nonnull NSNumber *) code
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
[[self getOrCreateHttpMetric:url httpMethod:httpMethod] setResponseCode:[code integerValue]];
@@ -230,7 +230,7 @@
RCT_EXPORT_METHOD(setHttpMetricRequestPayloadSize:
(NSString *) url
httpMethod:(NSString *) httpMethod
- bytes:(NSNumber *) bytes
+ bytes:(nonnull NSNumber *) bytes
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
[[self getOrCreateHttpMetric:url httpMethod:httpMethod] setRequestPayloadSize:[bytes longLongValue]];
@@ -250,7 +250,7 @@
RCT_EXPORT_METHOD(setHttpMetricResponsePayloadSize:
(NSString *) url
httpMethod:(NSString *) httpMethod
- bytes:(NSNumber *) bytes
+ bytes:(nonnull NSNumber *) bytes
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
[[self getOrCreateHttpMetric:url httpMethod:httpMethod] setResponsePayloadSize:[bytes longLongValue]];

ios/RNFirebase/RNFirebase.m

@@ -3,13 +3,22 @@
#import <FirebaseCore/FirebaseCore.h>
#import <React/RCTUtils.h>
+
+#if __has_include(<FirebaseCore/FIRAppInternal.h>)
+ #import <FirebaseCore/FIRAppInternal.h>
+ #define REGISTER_LIB
+#endif
+
@implementation RNFirebase
RCT_EXPORT_MODULE(RNFirebase);
- (id)init {
self = [super init];
if (self != nil) {
- NSLog(@"Setting up RNFirebase instance");
+ DLog(@"Setting up RNFirebase instance");
+#ifdef REGISTER_LIB
+ [FIRApp registerLibrary:@"react-native-firebase" withVersion:@"5.3.0"];
+#endif
}
return self;
}

ios/RNFirebase/RNFirebaseUtil.h

@@ -5,8 +5,15 @@
#import <React/RCTEventEmitter.h>
#import <Firebase.h>
+#ifdef DEBUG
+#define DLog(fmt, ...) NSLog((@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__);
+#else
+#define DLog(...)
+#endif
+
@interface RNFirebaseUtil : NSObject
++ (NSString *)getISO8601String:(NSDate *)date;
+ (FIRApp *)getApp:(NSString *)appDisplayName;
+ (NSString *)getAppName:(NSString *)appDisplayName;
+ (NSString *)getAppDisplayName:(NSString *)appName;

ios/RNFirebase/RNFirebaseUtil.m

@@ -5,6 +5,21 @@
static NSString *const DEFAULT_APP_DISPLAY_NAME = @"[DEFAULT]";
static NSString *const DEFAULT_APP_NAME = @"__FIRAPP_DEFAULT";
++ (NSString *)getISO8601String:(NSDate *)date {
+ static NSDateFormatter *formatter = nil;
+
+ if (!formatter) {
+ formatter = [[NSDateFormatter alloc] init];
+ [formatter setLocale:[NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"]];
+ formatter.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"UTC"];
+ [formatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss"];
+ }
+
+ NSString *iso8601String = [formatter stringFromDate:date];
+
+ return [iso8601String stringByAppendingString:@"Z"];
+}
+
+ (FIRApp *)getApp:(NSString *)appDisplayName {
NSString *appName = [RNFirebaseUtil getAppName:appDisplayName];
return [FIRApp appNamed:appName];
@@ -32,7 +47,7 @@
[emitter sendEventWithName:name body:body];
}
} @catch (NSException *error) {
- NSLog(@"An error occurred in sendJSEvent: %@", [error debugDescription]);
+ DLog(@"An error occurred in sendJSEvent: %@", [error debugDescription]);
}
}

ios/RNFirebase.podspec

@@ -4,15 +4,15 @@
Pod::Spec.new do |s|
s.name = "RNFirebase"
s.version = package["version"]
- s.summary = package["description"]
- s.description = <<-DESC
+ s.description = package["description"]
+ s.summary = <<-DESC
A well tested feature rich Firebase implementation for React Native, supporting iOS & Android.
DESC
- s.homepage = "http://invertase.io/react-native-firebase"
+ s.homepage = "http://invertase.io/oss/react-native-firebase"
s.license = package['license']
s.authors = "Invertase Limited"
s.source = { :git => "https://github.com/invertase/react-native-firebase.git", :tag => "v#{s.version}" }
- s.social_media_url = 'http://twitter.com/RNFirebase'
+ s.social_media_url = 'http://twitter.com/invertaseio'
s.platform = :ios, "9.0"
s.source_files = 'RNFirebase/**/*.{h,m}'
s.dependency 'React'

ios/RNFirebase.xcodeproj/project.pbxproj

@@ -7,6 +7,7 @@
objects = {
/* Begin PBXBuildFile section */
+ 168785AD210B584E00E4BD57 /* RCTConvert+UIBackgroundFetchResult.m in Sources */ = {isa = PBXBuildFile; fileRef = 168785AB210B584E00E4BD57 /* RCTConvert+UIBackgroundFetchResult.m */; };
17AF4F6B1F59CDBF00C02336 /* RNFirebaseLinks.m in Sources */ = {isa = PBXBuildFile; fileRef = 17AF4F6A1F59CDBF00C02336 /* RNFirebaseLinks.m */; };
27540F9A209F3641001F4AF4 /* RNFirebaseFunctions.m in Sources */ = {isa = PBXBuildFile; fileRef = 27540F99209F3641001F4AF4 /* RNFirebaseFunctions.m */; };
8300A7AE1F31E143001B16AB /* RNFirebaseDatabaseReference.m in Sources */ = {isa = PBXBuildFile; fileRef = 8300A7AD1F31E143001B16AB /* RNFirebaseDatabaseReference.m */; };
@@ -49,6 +50,8 @@
/* Begin PBXFileReference section */
134814201AA4EA6300B7C361 /* libRNFirebase.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNFirebase.a; sourceTree = BUILT_PRODUCTS_DIR; };
+ 168785AB210B584E00E4BD57 /* RCTConvert+UIBackgroundFetchResult.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "RCTConvert+UIBackgroundFetchResult.m"; sourceTree = "<group>"; };
+ 168785AC210B584E00E4BD57 /* RCTConvert+UIBackgroundFetchResult.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RCTConvert+UIBackgroundFetchResult.h"; sourceTree = "<group>"; };
17AF4F691F59CDBF00C02336 /* RNFirebaseLinks.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNFirebaseLinks.h; sourceTree = "<group>"; };
17AF4F6A1F59CDBF00C02336 /* RNFirebaseLinks.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFirebaseLinks.m; sourceTree = "<group>"; };
27540F98209F361B001F4AF4 /* RNFirebaseFunctions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNFirebaseFunctions.h; sourceTree = "<group>"; };
@@ -123,6 +126,16 @@
name = Products;
sourceTree = "<group>";
};
+ 168785A0210B50AA00E4BD57 /* converters */ = {
+ isa = PBXGroup;
+ children = (
+ 168785AC210B584E00E4BD57 /* RCTConvert+UIBackgroundFetchResult.h */,
+ 168785AB210B584E00E4BD57 /* RCTConvert+UIBackgroundFetchResult.m */,
+ );
+ name = converters;
+ path = RNFirebase/converters;
+ sourceTree = "<group>";
+ };
17AF4F681F59CDBF00C02336 /* links */ = {
isa = PBXGroup;
children = (
@@ -146,6 +159,7 @@
58B511D21A9E6C8500147676 = {
isa = PBXGroup;
children = (
+ 168785A0210B50AA00E4BD57 /* converters */,
27540F97209F35DF001F4AF4 /* functions */,
83AAA0762063DEC2007EC5F7 /* invites */,
838E372420231E15004DCD3A /* notifications */,
@@ -351,7 +365,7 @@
58B511D31A9E6C8500147676 /* Project object */ = {
isa = PBXProject;
attributes = {
- LastUpgradeCheck = 0610;
+ LastUpgradeCheck = 1000;
ORGANIZATIONNAME = Invertase;
TargetAttributes = {
58B511DA1A9E6C8500147676 = {
@@ -384,6 +398,7 @@
27540F9A209F3641001F4AF4 /* RNFirebaseFunctions.m in Sources */,
838E372320231DF0004DCD3A /* RNFirebaseInstanceId.m in Sources */,
839D916E1EF3E20B0077C7C8 /* RNFirebaseAdMobRewardedVideo.m in Sources */,
+ 168785AD210B584E00E4BD57 /* RCTConvert+UIBackgroundFetchResult.m in Sources */,
839D916C1EF3E20B0077C7C8 /* RNFirebaseAdMob.m in Sources */,
17AF4F6B1F59CDBF00C02336 /* RNFirebaseLinks.m in Sources */,
8376F7161F7C149100D45A85 /* RNFirebaseFirestoreCollectionReference.m in Sources */,
@@ -492,9 +507,9 @@
58B511F01A9E6C8500147676 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
+ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO;
CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES;
DEFINES_MODULE = NO;
- EMBEDDED_CONTENT_CONTAINS_SWIFT = NO;
ENABLE_BITCODE = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
@@ -508,6 +523,7 @@
"$(SRCROOT)/../../../ios/Pods/FirebaseDatabase/Frameworks",
"$(SRCROOT)/../../../ios/Pods/FirebaseDynamicLinks/Frameworks",
"$(SRCROOT)/../../../ios/Pods/FirebaseFirestore/Frameworks",
+ "$(SRCROOT)/../../../ios/Pods/FirebaseFunctions/Frameworks",
"$(SRCROOT)/../../../ios/Pods/FirebaseInstanceID/Frameworks",
"$(SRCROOT)/../../../ios/Pods/FirebaseInvites/Frameworks",
"$(SRCROOT)/../../../ios/Pods/FirebaseMessaging/Frameworks",
@@ -522,7 +538,7 @@
"${SRCROOT}/../../../ios/Firebase/**",
"${SRCROOT}/../../../ios/Pods/Headers/Public/**",
);
- IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LIBRARY_SEARCH_PATHS = "$(inherited)";
MACH_O_TYPE = staticlib;
ONLY_ACTIVE_ARCH = YES;
@@ -535,9 +551,9 @@
58B511F11A9E6C8500147676 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
+ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO;
CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES;
DEFINES_MODULE = NO;
- EMBEDDED_CONTENT_CONTAINS_SWIFT = NO;
ENABLE_BITCODE = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
@@ -551,6 +567,7 @@
"$(SRCROOT)/../../../ios/Pods/FirebaseDatabase/Frameworks",
"$(SRCROOT)/../../../ios/Pods/FirebaseDynamicLinks/Frameworks",
"$(SRCROOT)/../../../ios/Pods/FirebaseFirestore/Frameworks",
+ "$(SRCROOT)/../../../ios/Pods/FirebaseFunctions/Frameworks",
"$(SRCROOT)/../../../ios/Pods/FirebaseInstanceID/Frameworks",
"$(SRCROOT)/../../../ios/Pods/FirebaseInvites/Frameworks",
"$(SRCROOT)/../../../ios/Pods/FirebaseMessaging/Frameworks",
@@ -565,7 +582,7 @@
"${SRCROOT}/../../../ios/Firebase/**",
"${SRCROOT}/../../../ios/Pods/Headers/Public/**",
);
- IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LIBRARY_SEARCH_PATHS = "$(inherited)";
MACH_O_TYPE = staticlib;
OTHER_LDFLAGS = "$(inherited)";

lib/version.js

@@ -1,2 +0,0 @@
-// generated by genversion
-module.exports = '4.3.8'

package.json

@@ -1,24 +1,25 @@
{
"name": "react-native-firebase",
- "version": "5.0.0-rc0",
+ "version": "5.4.0",
"author": "Invertase <oss@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, Dynamic Links, Functions, Messaging (FCM), Remote Config, Storage and more.",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"scripts": {
- "build": "genversion src/version.js && npm run validate-ts-declarations && npm run build-src && npm run build-flow",
+ "build": "genversion src/version.js && npm run build-src",
+ "build-src": "cross-env BABEL_ENV=publish babel src -d dist --ignore __tests__ --copy-files",
"build-flow": "flow-copy-source -i */__tests__* src dist",
- "build-src": "BABEL_ENV=publish babel src -d dist --ignore __tests__ --copy-files",
- "build-src-watch": "genversion src/version.js && BABEL_ENV=publish babel src -d dist --ignore __tests__ --copy-files --watch",
+ "build-src-watch": "genversion src/version.js && BABEL_ENV=development babel src -d dist --ignore __tests__ --copy-files --watch",
+ "lint": "eslint ./src",
"clean": "rimraf dist/",
- "flow": "flow",
- "format:assets": "prettier --ignore-path .gitignore --write \"**/*.{json,md}\"",
+ "validate-ts-declarations": "tsc --project ./",
+ "validate-flow-declarations": "flow",
"format": "npm run format:assets && npm run lint -- --fix",
- "lint": "eslint ./src",
+ "format:assets": "prettier --ignore-path .gitignore --write \"**/*.{json,md}\"",
+ "prepare": "npm run clean && npm run build && npm run build-flow",
"precommit": "lint-staged",
- "prepublish": "npm run clean && npm run build",
- "postinstall": "postinstall-build dist && opencollective postinstall || exit 0",
- "validate-ts-declarations": "tsc --project ./"
+ "postinstall": "opencollective-postinstall || exit 0",
+ "prepublishOnly": "npm run validate-ts-declarations && npm run validate-flow-declarations"
},
"repository": {
"type": "git",
@@ -69,38 +70,40 @@
"crashlytics"
],
"peerDependencies": {
- "react": ">= 16.4.1",
- "react-native": ">= 0.56.0",
- "fbjs": "*"
+ "react": ">= 16.5.0",
+ "react-native": ">= 0.57.0 < 1"
},
"devDependencies": {
- "@babel/core": "7.0.0-beta.47",
- "@babel/cli": "7.0.0-beta.47",
- "babel-eslint": "^8.2.6",
- "babel-preset-react-native": "^5.0.2",
- "@invertase/babel-preset-react-native-syntax": "^0.1.0",
- "eslint": "^5.3.0",
+ "@babel/cli": "^7.0.0",
+ "@babel/core": "^7.0.0",
+ "@babel/runtime": "^7.0.0",
+ "@invertase/babel-preset-react-native-syntax": "^0.1.3",
+ "axios": "^0.18.0",
+ "babel-eslint": "^9.0.0",
+ "codecov": "^3.1.0",
+ "cross-env": "^5.1.0",
+ "eslint": "^5.5.0",
"eslint-config-airbnb": "^17.0.0",
- "eslint-config-prettier": "^2.9.0",
- "eslint-plugin-flowtype": "^2.50.0",
- "eslint-plugin-import": "^2.13.0",
+ "eslint-config-prettier": "^3.0.1",
+ "eslint-plugin-flowtype": "^2.50.1",
+ "eslint-plugin-import": "^2.14.0",
"eslint-plugin-jsx-a11y": "^6.1.1",
"eslint-plugin-prettier": "^2.6.2",
"eslint-plugin-react": "^7.10.0",
- "flow-bin": "^0.78.0",
+ "flow-bin": "^0.80.0",
"flow-copy-source": "^2.0.2",
"genversion": "^2.1.0",
"husky": "^0.14.3",
- "lint-staged": "^7.2.0",
- "prettier": "1.14.0",
- "react": "^16.4.1",
- "react-dom": "^16.4.1",
- "react-native": "^0.56.0",
- "typescript": "^3.0.1"
+ "lint-staged": "^7.2.2",
+ "prettier": "^1.14.2",
+ "react": "^16.5.0",
+ "react-dom": "^16.5.0",
+ "react-native": "0.57.1",
+ "rimraf": "^2.6.2",
+ "typescript": "^3.0.3"
},
"dependencies": {
- "opencollective": "^1.0.3",
- "postinstall-build": "^5.0.1",
+ "opencollective-postinstall": "^2.0.0",
"prop-types": "^15.6.2"
},
"rnpm": {

prettier.config.js

@@ -5,7 +5,7 @@
{
files: '*.json',
options: {
- printWidth: 400,
+ printWidth: 100,
},
},
],

README.md

@@ -1,6 +1,6 @@
<p align="center">
- <a href="https://rnfirebase.io">
- <img src="https://i.imgur.com/eBNJlHd.png"><br/>
+ <a href="https://invertase.io/oss/react-native-firebase">
+ <img width="180px" src="https://i.imgur.com/JIyBtKW.png"><br/>
</a>
<h2 align="center">React Native Firebase</h2>
</p>
@@ -17,7 +17,7 @@
## Introduction
-**React Native Firebase** is a _light-weight_ javascript layer connecting you to the native Firebase SDKs for both iOS and Android which aimes to mirror the offical Firebase Web SDK as closely as possible.
+**React Native Firebase** is a _light-weight_ javascript layer connecting you to the native Firebase SDKs for both iOS and Android which aims to mirror the official Firebase Web SDK as closely as possible.
Although the official [Firebase JS SDK](https://www.npmjs.com/package/firebase) will work with React Native; it is mainly built for the web and has a limited feature-set compared to native.
@@ -31,27 +31,27 @@
> '**?**' indicates partial support
-| Firebase Features | v2.2.x | v3.3.x | v4.3.x | Web SDK |
-| --------------------------------------------------------------------------------------------------------------------------------- | :----: | :----: | :----: | :-----: |
-| **AdMob** | ✅ | ✅ | ✅ | ❌ |
-| **Analytics**             | ✅ | ✅ | ✅ | ❌ |
-| **App Indexing**           | ❌ | ❌ | ❌ | ❌ |
-| **Authentication** | ✅ | ✅ | ✅ | ✅ |
-| _-- Phone Auth_ | ❌ | ✅ | ✅ | ❌ |
-| **Core** | **?** | ✅ | ✅ | ✅ |
-| _-- Multiple Apps_ | ❌ | ✅ | ✅ | ✅ |
-| **Cloud Firestore** | ❌ | ✅ | ✅ | **?** |
-| **Cloud Messaging (FCM)** | **?** | **?** | ✅ | ❌ |
-| **Crashlytics**           | ❌ | ✅ | ✅ | ❌ |
-| **Dynamic Links** | ❌ | ✅ | ✅ | ❌ |
-| **[Functions Callable](https://firebase.googleblog.com/2018/04/launching-cloud-functions-for-firebase-1-0.html?m=1)**             |   ❌   |   ❌   | ✅ |   ✅   |
-| **Invites** | ❌ | ❌ | ✅ | ❌ |
-| **Instance ID**          | ❌ | ❌ | **?** | ❌ |
-| **Performance Monitoring** | ✅ | ✅ | ✅ | ❌ |
-| **Realtime Database** | ✅ | ✅ | ✅ | ✅ |
-| _-- Offline Persistence_ | ✅ | ✅ | ✅ | **?** |
-| **Remote Config** | ✅ | ✅ | ✅ | ❌ |
-| **Storage** | ✅ | ✅ | ✅ | **?** |
+| Firebase Features | v5.x.x | Web SDK |
+| --------------------------------------------------------------------------------------------------------------------------------- | :----: | :-----: |
+| **AdMob** | ✅ | ❌ |
+| **Analytics**             | ✅ | ❌ |
+| **App Indexing**           | ❌ | ❌ |
+| **Authentication** | ✅ | ✅ |
+| _-- Phone Auth_ | ✅ | ✅ |
+| **Core** | ✅ | ✅ |
+| _-- Multiple Apps_ | ✅ | ✅ |
+| **Cloud Firestore** | ✅ | ✅ |
+| **Cloud Messaging (FCM)** | ✅ | ❌ |
+| **Crashlytics**           | ✅ | ❌ |
+| **Dynamic Links** | ✅ | ❌ |
+| **[Functions Callable](https://firebase.googleblog.com/2018/04/launching-cloud-functions-for-firebase-1-0.html?m=1)**             |  ✅ |   ✅   |
+| **Invites** | ✅ | ❌ |
+| **Instance ID**          | ✅ | ❌ |
+| **Performance Monitoring** | ✅ | ❌ |
+| **Realtime Database** | ✅ | ✅ |
+| _-- Offline Persistence_ | ✅ | **?** |
+| **Remote Config** | ✅ | ❌ |
+| **Storage** | ✅ | ✅ |
---
@@ -59,17 +59,18 @@
> The table below shows the supported versions of React Native and the Firebase SDKs for different versions of `react-native-firebase`.
-| | 3.3.x | 4.3.x | 5.0.x |
-| ------------------------- | :------: | :-----: | :---: |
-| React Native | 0.50 + | 0.52-55 | 0.56+ |
-| Play Services Android SDK | 11.8.0 + | 15.0.1 | TBC |
-| Firebase iOS SDK | 4.7.0 + | 5.3.0 | TBC |
+| | 3.3.x | 5.2.x | 5.3.x |
+| ------------------------- | :------: | :----------------: | :---------------: |
+| React Native | 0.50-52 | 0.52-58 | ^0.59.3 |
+| Play Services Android SDK | 11.8.0 + | ^16.1.0 | ^16.1.0 |
+| Firebase iOS SDK | 4.7.0 + | ^5.10.x -^5.18.x | ^5.19.x - ^5.20.x |
+
---
## Documentation
-To check out our latest docs, visit [https://invertase.io/oss/react-native-firebase](https://invertase.io/oss/react-native-firebase)
+To check out our latest docs, visit [https://rnfirebase.io/docs](https://rnfirebase.io/docs)
## Questions
@@ -87,7 +88,7 @@
Detailed changes for each release are documented in the [releases notes](https://github.com/invertase/react-native-firebase/releases).
+---
## Supporting RNFirebase