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) {
+ [