Merge branch 'master' into background-notification-fix-ios

This commit is contained in:
Ryan Grey 2018-08-13 08:28:37 +01:00
commit 89f7698e5f
393 changed files with 8652 additions and 53280 deletions

View File

@ -7,7 +7,7 @@
]
},
"publish": {
"presets": ["react-native-syntax"],
"presets": ["@invertase/react-native-syntax"],
"plugins": [
"transform-flow-strip-types"
]

1
.eslintignore Normal file
View File

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

View File

@ -30,7 +30,8 @@
"no-plusplus": 0,
"no-undef": 0,
"no-underscore-dangle": "off",
"no-use-before-define": 0
"no-use-before-define": 0,
"import/no-cycle": 0
},
"globals": {
"__DEV__": true,

View File

@ -22,9 +22,6 @@
# Ignore dist folder
.*/dist/.*
# Ignore tests project
.*/tests/.*
# Ignore bridge project
.*/bridge/.*
@ -38,7 +35,22 @@ node_modules/react-native/flow-github/
[options]
emoji=true
# Taken from: https://github.com/facebook/react-native/issues/19766
module.system=haste
module.system.haste.use_name_reducers=true
# keep the following in sync with server/haste/hasteImpl.js
# get basename
module.system.haste.name_reducers='^.*/\([a-zA-Z0-9$_.-]+\.js\(\.flow\)?\)$' -> '\1'
# strip .js or .js.flow suffix
module.system.haste.name_reducers='^\(.*\)\.js\(\.flow\)?$' -> '\1'
# strip .ios suffix
module.system.haste.name_reducers='^\(.*\)\.ios$' -> '\1'
module.system.haste.name_reducers='^\(.*\)\.android$' -> '\1'
module.system.haste.name_reducers='^\(.*\)\.native$' -> '\1'
module.system.haste.paths.blacklist=.*/__tests__/.*
module.system.haste.paths.blacklist=.*/__mocks__/.*
module.system.haste.paths.blacklist=<PROJECT_ROOT>/node_modules/react-native/Libraries/Animated/src/polyfills/.*
module.system.haste.paths.whitelist=<PROJECT_ROOT>/node_modules/react-native/Libraries/.*
munge_underscores=true
@ -60,4 +72,4 @@ suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError
[version]
^0.65.0
^0.78.0

View File

@ -2,7 +2,6 @@
name: ⚠️ Bug/Issue report
about: Please provide as much detail as possible to help us with a bug or issue. Issues
will be closed if they do not follow the template.
---
<!---
@ -10,7 +9,7 @@ BEFORE YOU MAKE AN ISSUE
The issue list of this repo is exclusively for bug reports.
1) For feature requests, please use our Canny board: https://react-native-firebase.canny.io/feature-requests
1) For feature requests please visit our [Feature Request Board](https://boards.invertase.io/react-native-firebase).
2) For questions and support please use our Discord chat: https://discord.gg/C9aK28N or Stack Overflow: https://stackoverflow.com/questions/tagged/react-native-firebase
@ -23,7 +22,7 @@ The issue list of this repo is exclusively for bug reports.
### Environment
1. Application Target Platform:
1. Application Target Platform:
<!--- (e.g. iOS, Android, Both) --->
@ -47,14 +46,14 @@ The issue list of this repo is exclusively for bug reports.
<!--- (e.g. database, auth, messaging, analytics etc - or N/A if not applicable) --->
7. Are you using `typescript`?
7. Are you using `typescript`?
<!--- yes/no --->
---
Loving `react-native-firebase`? Please consider supporting them with any of the below:
- 👉 Back financially via [Open Collective](https://opencollective.com/react-native-firebase/donate)
- 👉 Follow [`React Native Firebase`](https://twitter.com/rnfirebase) and [`Invertase`](https://twitter.com/invertaseio) on Twitter
- 👉 Star this repo on GitHub ⭐️
* 👉 Back financially via [Open Collective](https://opencollective.com/react-native-firebase/donate)
* 👉 Follow [`React Native Firebase`](https://twitter.com/rnfirebase) and [`Invertase`](https://twitter.com/invertaseio) on Twitter
* 👉 Star this repo on GitHub ⭐️

View File

@ -1,7 +1,7 @@
---
name: 🎁 Feature request
about: Please create feature requests on our canny board: [https://react-native-firebase.canny.io/feature-requests](https://react-native-firebase.canny.io/feature-requests)
about: For feature requests please visit our [Feature Request Board](https://boards.invertase.io/react-native-firebase).
---
[https://react-native-firebase.canny.io/feature-requests](https://react-native-firebase.canny.io/feature-requests)
For feature requests please visit our [Feature Request Board](https://boards.invertase.io/react-native-firebase).

View File

@ -15,7 +15,7 @@ project.xcworkspace/
xcuserdata/
# Config files
.babelrc
# .babelrc
.editorconfig
.eslintrc
.flowconfig
@ -87,3 +87,4 @@ CONTRIBUTING.md
CODE_OF_CONDUCT.md
android/.settings
README.md
src

View File

@ -1,12 +0,0 @@
{
"ignore_dirs": [
".git",
"node_modules",
"android/build",
"android/.idea",
"ios/.idea",
"android/.gradle",
"android/gradle",
".idea"
]
}

View File

@ -17,15 +17,11 @@
## Introduction
**RNFirebase** makes using [Firebase](http://firebase.com) with React Native simple. It is a _light-weight_ layer sitting on-top of the native Firebase libraries for both iOS and Android which mirrors the Firebase Web SDK as closely as possible.
**React Native Firebase** is a _light-weight_ javascript layer connecting you to the native Firebase SDKs for both iOS and Android which aimes to mirror the offical Firebase Web SDK as closely as possible.
Although the [Firebase Web SDK](https://www.npmjs.com/package/firebase) library will work with React Native, it is mainly built for the web.
Although the official [Firebase JS SDK](https://www.npmjs.com/package/firebase) will work with React Native; it is mainly built for the web and has a limited feature-set compared to native.
RNFirebase provides a JavaScript bridge to the native Firebase SDKs for both iOS and Android therefore Firebase will run on the native thread, allowing the rest of your app to run on the [JS thread](https://facebook.github.io/react-native/docs/performance.html#javascript-frame-rate). The Firebase Web SDK also runs on the JS thread, therefore potentially affecting the frame rate causing jank with animations, touch events etc.
The native SDKs also allow us to hook into device sdk's which are not possible with the web SDK, for example crash reporting, offline realtime database support, analytics and more!
All in all, RNFirebase provides much faster performance (~2x) over the web SDK and provides device sdk's not found in the web sdk (see the feature table below).
Using the native Firebase SDKs with **React Native Firebase** allows you to consume device SDKs which don't exist on the Firebase JS SDK - for example; Remote Config, Performance Monitoring, Dynamic Links, Analytics and more (see the feature table below for comparison).
---
@ -35,7 +31,7 @@ All in all, RNFirebase provides much faster performance (~2x) over the web SDK a
> '**?**' indicates partial support
| Firebase Features | v2.2.x | v3.3.x | v4.2.x | Web SDK |
| Firebase Features | v2.2.x | v3.3.x | v4.3.x | Web SDK |
| --------------------------------------------------------------------------------------------------------------------------------- | :----: | :----: | :----: | :-----: |
| **AdMob** | ✅ | ✅ | ✅ | ❌ |
| **Analytics**             | ✅ | ✅ | ✅ | ❌ |
@ -47,9 +43,8 @@ All in all, RNFirebase provides much faster performance (~2x) over the web SDK a
| **Cloud Firestore** | ❌ | ✅ | ✅ | **?** |
| **Cloud Messaging (FCM)** | **?** | **?** | ✅ | ❌ |
| **Crashlytics**           | ❌ | ✅ | ✅ | ❌ |
| **Crash Reporting** | ✅ | ✅ | ✅ | ❌ |
| **Dynamic Links** | ❌ | ✅ | ✅ | ❌ |
| **[Functions Callable](https://firebase.googleblog.com/2018/04/launching-cloud-functions-for-firebase-1-0.html?m=1)**             |    |    | ✅ |    |
| **[Functions Callable](https://firebase.googleblog.com/2018/04/launching-cloud-functions-for-firebase-1-0.html?m=1)**             |    |    | ✅ |    |
| **Invites** | ❌ | ❌ | ✅ | ❌ |
| **Instance ID**          | ❌ | ❌ | **?** | ❌ |
| **Performance Monitoring** | ✅ | ✅ | ✅ | ❌ |
@ -64,17 +59,17 @@ All in all, RNFirebase provides much faster performance (~2x) over the web SDK a
> The table below shows the supported versions of React Native and the Firebase SDKs for different versions of `react-native-firebase`.
| | 2.2.x | 3.3.x | 4.0.x | 4.1.x | 4.2.x |
| -------------------- | -------- | -------- | -------- | -------- | -------- |
| React Native | 0.47 + | 0.50 + | 0.52 + | 0.52 + | 0.52-55.x |
| Firebase Android SDK | 11.0.0 + | 11.8.0 + | 12.0.0 + | 15.0.0 + | 15.0.0 + |
| Firebase iOS SDK | 4.0.0 + | 4.7.0 + | 4.11.0 + | 4.13.0 + | 5.0.0 + |
| | 3.3.x | 4.3.x | 5.0.x |
| ------------------------- | :------: | :-----: | :---: |
| React Native | 0.50 + | 0.52-55 | 0.56+ |
| Play Services Android SDK | 11.8.0 + | 15.0.1 | TBC |
| Firebase iOS SDK | 4.7.0 + | 5.3.0 | TBC |
---
## Documentation
To check out our latest docs, visit [rnfirebase.io](https://rnfirebase.io)
To check out our latest docs, visit [https://invertase.io/oss/react-native-firebase](https://invertase.io/oss/react-native-firebase)
## Questions
@ -86,19 +81,19 @@ Please make sure to complete the issue template before opening an issue. Issues
## Feature Requests
For feature requests please use our [Canny Board](http://invertase.link/requests).
For feature requests please visit our [Feature Request Board](https://boards.invertase.io/react-native-firebase).
## Changelog
Detailed changes for each release are documented in the [releases notes](https://github.com/invertase/react-native-firebase/releases).
<hr>
----
## Supporting RNFirebase
RNFirebase is an Apache-2.0 licensed open source project. It's an independent project with its ongoing development made possible entirely thanks to the support by these awesome [sponsors](#sponsors) and [backers](#backers). If you'd like to join them, please consider:
* [Become a backer or sponsor on Open Collective](https://opencollective.com/react-native-firebase).
- [Become a backer or sponsor on Open Collective](https://opencollective.com/react-native-firebase).
### Sponsors
@ -133,4 +128,4 @@ Thank you to all the people who have already contributed to RNFirebase!
## License
* See [LICENSE](/LICENSE)
- See [LICENSE](/LICENSE)

View File

@ -1,32 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 21.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 183 197" style="enable-background:new 0 0 183 197;" xml:space="preserve">
<style type="text/css">
.st0{fill:#E88634;}
.st1{fill:#F9CB52;}
.st2{fill:#F4A73E;}
</style>
<title>Slice 1</title>
<g>
<path class="st0" d="M1,98c0.6,20.7,40.7,37,90,37v-8c-39.7-0.2-81.3-12.4-82-29C9,98,1,98,1,98z"/>
<path class="st1" d="M135.5,21.8c-18.3-9.8-52.4,16.7-77,59.4l6.9,4c20-34.3,51.4-64.3,66.1-56.5L135.5,21.8L135.5,21.8z"/>
<path class="st0" d="M1,98c0.6-20.7,40.7-37,90-37v8C51.3,69.2,9.7,81.4,9,98C9,98,1,98,1,98z"/>
<path class="st2" d="M46,20c-17.6,10.9-11.7,53.7,13,96.4l6.9-4C46.3,78,36,35.8,50,26.9L46,20L46,20z"/>
<path class="st1" d="M46,176.4C28.4,165.5,34.3,122.7,59,80l6.9,4c-19.7,34.5-30,76.6-15.9,85.5L46,176.4L46,176.4z"/>
<path class="st2" d="M45,20.5c18.3-9.8,52.4,16.7,77,59.4l-6.9,4C95.1,49.7,63.7,19.7,49,27.4L45,20.5z"/>
<path class="st0" d="M181,98c-0.6-20.7-40.7-37-90-37v8c39.7,0.2,81.3,12.4,82,29H181z"/>
<path class="st1" d="M45.2,176.5c0.1,0.1,0.2,0.1,0.3,0.2c18.1,10.5,52.6-16.1,77.5-59.2l-6.9-4.1C96,148,64.2,178.2,49.6,169.8
c-0.1-0.1-0.3-0.2-0.4-0.3L45.2,176.5L45.2,176.5z"/>
<path class="st2" d="M136,175.9c-18.3,9.8-52.4-16.7-77-59.4l-0.3-0.4l6.8-4.2c20,34.5,51.6,64.9,66.4,57.1L136,175.9L136,175.9z"
/>
<path class="st0" d="M181,97c0,0.2,0,0.3,0,0.5c0,20.9-40.3,37.5-90,37.5v-8c40-0.2,82-12.6,82-29.4c0-0.2,0-0.4,0-0.6H181z"/>
<path class="st2" d="M135,176.4c17.6-10.9,11.7-53.7-13-96.4l-0.4-0.8l-7,3.8l0,0.1c20,34.6,30.5,77.2,16.5,86.3L135,176.4z"/>
<path class="st1" d="M135.5,21.3c17.6,10.9,11.7,53.7-13,96.4l-6.9-4c19.7-34.5,30-76.6,15.9-85.5
C131.5,28.2,135.5,21.3,135.5,21.3z"/>
<path class="st2" d="M81,103.1c0,5.5,4.5,10,10,10l0,0c5.5,0,10-4.5,10-10c0-5.6-2.8-10-0.9-13.9c0,0-3,0.6-4.7,5
c0,0-5.6-5.6-3.2-13.4c0,0-5.7,2.2-5.1,9.5c0.5,6.2-0.6,7.3-0.6,7.3c0-5-3.9-7.3-3.9-7.3C83.7,96.4,81,98.1,81,103.1z"/>
<path class="st1" d="M98.2,105.5c0,4.2-3.2,7.7-7.4,7.7c-3.9-0.1-6.9-3.1-6.5-7c0.1-1.7,1.5-4.6,3.6-7.4l1.4,0.9
c0-3.5,1.6-8.2,1.6-8.2C92.1,96.4,98.2,97.7,98.2,105.5z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -76,6 +76,15 @@ rootProject.gradle.buildFinished { buildResult ->
}
}
repositories {
google()
jcenter()
maven {
url "$rootDir/../node_modules/react-native/android"
name 'React Native (local)'
}
}
def supportVersion = rootProject.hasProperty('supportLibVersion') ? rootProject.supportLibVersion : DEFAULT_SUPPORT_LIB_VERSION
dependencies {

View File

@ -1,30 +1,30 @@
package io.invertase.firebase;
import android.util.Log;
import android.app.Activity;
import android.content.IntentSender;
import android.util.Log;
import java.util.Map;
import java.util.List;
import java.util.HashMap;
import java.util.ArrayList;
// react
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
// play services
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GoogleApiAvailability;
import com.google.firebase.FirebaseApp;
import com.google.firebase.FirebaseOptions;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
// react
// play services
@SuppressWarnings("WeakerAccess")
public class RNFirebaseModule extends ReactContextBaseJavaModule {
private static final String TAG = "RNFirebase";
@ -103,7 +103,9 @@ public class RNFirebaseModule extends ReactContextBaseJavaModule {
if (status != ConnectionResult.SUCCESS && gapi.isUserResolvableError(status)) {
Activity activity = getCurrentActivity();
if (activity != null) {
gapi.getErrorDialog(activity, status, status).show();
gapi
.getErrorDialog(activity, status, status)
.show();
}
}
}
@ -113,7 +115,9 @@ public class RNFirebaseModule extends ReactContextBaseJavaModule {
*/
@ReactMethod
public void resolutionForPlayServices() {
int status = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(getReactApplicationContext());
int status = GoogleApiAvailability
.getInstance()
.isGooglePlayServicesAvailable(getReactApplicationContext());
ConnectionResult connectionResult = new ConnectionResult(status);
if (!connectionResult.isSuccess() && connectionResult.hasResolution()) {

View File

@ -8,9 +8,9 @@ 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 RNFirebasePackage implements ReactPackage {

View File

@ -4,16 +4,15 @@ import android.app.ActivityManager;
import android.content.Context;
import android.util.Log;
import java.util.List;
import java.util.Map;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.facebook.react.bridge.ReadableArray;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
@ -47,7 +46,9 @@ public class Utils {
if (value == null) {
map.putNull(key);
} else {
String type = value.getClass().getName();
String type = value
.getClass()
.getName();
switch (type) {
case "java.lang.Boolean":
map.putBoolean(key, (Boolean) value);
@ -156,4 +157,14 @@ public class Utils {
return false;
}
public static int getResId(Context ctx, String resName) {
int resourceId = ctx
.getResources()
.getIdentifier(resName, "string", ctx.getPackageName());
if (resourceId == 0) {
Log.e(TAG, "resource " + resName + " could not be found");
}
return resourceId;
}
}

View File

@ -18,6 +18,13 @@ import java.util.Map;
public class RNFirebaseAdMob extends ReactContextBaseJavaModule {
private static final String TAG = "RNFirebaseAdMob";
private HashMap<String, RNFirebaseAdmobInterstitial> interstitials = new HashMap<>();
private HashMap<String, RNFirebaseAdMobRewardedVideo> rewardedVideos = new HashMap<>();
public RNFirebaseAdMob(ReactApplicationContext reactContext) {
super(reactContext);
Log.d(TAG, "New instance");
}
ReactApplicationContext getContext() {
return getReactApplicationContext();
@ -27,14 +34,6 @@ public class RNFirebaseAdMob extends ReactContextBaseJavaModule {
return getCurrentActivity();
}
private HashMap<String, RNFirebaseAdmobInterstitial> interstitials = new HashMap<>();
private HashMap<String, RNFirebaseAdMobRewardedVideo> rewardedVideos = new HashMap<>();
public RNFirebaseAdMob(ReactApplicationContext reactContext) {
super(reactContext);
Log.d(TAG, "New instance");
}
@Override
public String getName() {
return TAG;
@ -53,7 +52,9 @@ public class RNFirebaseAdMob extends ReactContextBaseJavaModule {
@ReactMethod
public void interstitialLoadAd(String adUnit, ReadableMap request) {
RNFirebaseAdmobInterstitial interstitial = getOrCreateInterstitial(adUnit);
interstitial.loadAd(RNFirebaseAdMobUtils.buildRequest(request).build());
interstitial.loadAd(RNFirebaseAdMobUtils
.buildRequest(request)
.build());
}
@ReactMethod
@ -65,7 +66,9 @@ public class RNFirebaseAdMob extends ReactContextBaseJavaModule {
@ReactMethod
public void rewardedVideoLoadAd(String adUnit, ReadableMap request) {
RNFirebaseAdMobRewardedVideo rewardedVideo = getOrCreateRewardedVideo(adUnit);
rewardedVideo.loadAd(RNFirebaseAdMobUtils.buildRequest(request).build());
rewardedVideo.loadAd(RNFirebaseAdMobUtils
.buildRequest(request)
.build());
}
@ReactMethod
@ -75,7 +78,6 @@ public class RNFirebaseAdMob extends ReactContextBaseJavaModule {
}
/**
*
* @param adUnit
* @return
*/
@ -89,7 +91,6 @@ public class RNFirebaseAdMob extends ReactContextBaseJavaModule {
}
/**
*
* @param adUnit
* @return
*/

View File

@ -23,38 +23,15 @@ public class RNFirebaseAdMobBanner extends SimpleViewManager<ReactViewGroup> {
public static final String REACT_CLASS = "RNFirebaseAdMobBanner";
public static final String BANNER_EVENT = "onBannerEvent";
public enum Events {
EVENT_AD_SIZE_CHANGE("onSizeChange"),
EVENT_AD_LOADED("onAdLoaded"),
EVENT_AD_FAILED_TO_LOAD("onAdFailedToLoad"),
EVENT_AD_OPENED("onAdOpened"),
EVENT_AD_CLOSED("onAdClosed"),
EVENT_AD_LEFT_APPLICATION("onAdLeftApplication");
private final String event;
Events(final String name) {
event = name;
}
@Override
public String toString() {
return event;
}
}
private ThemedReactContext context;
private ReactViewGroup viewGroup;
private RCTEventEmitter emitter;
private Boolean requested = false;
// Internal prop values
private AdRequest.Builder request;
private AdSize size;
private String unitId;
@Override
public String getName() {
return REACT_CLASS;
@ -197,15 +174,22 @@ public class RNFirebaseAdMobBanner extends SimpleViewManager<ReactViewGroup> {
int left = adView.getLeft();
int top = adView.getTop();
int width = adView.getAdSize().getWidthInPixels(context);
int height = adView.getAdSize().getHeightInPixels(context);
int width = adView
.getAdSize()
.getWidthInPixels(context);
int height = adView
.getAdSize()
.getHeightInPixels(context);
adView.measure(width, height);
adView.layout(left, top, left + width, top + height);
WritableMap payload = Arguments.createMap();
payload.putBoolean(RNFirebaseAdMobNativeExpress.Events.EVENT_AD_VIDEO_CONTENT.toString(), false);
payload.putBoolean(
RNFirebaseAdMobNativeExpress.Events.EVENT_AD_VIDEO_CONTENT.toString(),
false
);
payload.putInt("width", width);
payload.putInt("height", height);
@ -251,4 +235,24 @@ public class RNFirebaseAdMobBanner extends SimpleViewManager<ReactViewGroup> {
emitter.receiveEvent(viewGroup.getId(), BANNER_EVENT, event);
}
public enum Events {
EVENT_AD_SIZE_CHANGE("onSizeChange"),
EVENT_AD_LOADED("onAdLoaded"),
EVENT_AD_FAILED_TO_LOAD("onAdFailedToLoad"),
EVENT_AD_OPENED("onAdOpened"),
EVENT_AD_CLOSED("onAdClosed"),
EVENT_AD_LEFT_APPLICATION("onAdLeftApplication");
private final String event;
Events(final String name) {
event = name;
}
@Override
public String toString() {
return event;
}
}
}

View File

@ -25,38 +25,10 @@ public class RNFirebaseAdMobNativeExpress extends SimpleViewManager<ReactViewGro
public static final String REACT_CLASS = "RNFirebaseAdMobNativeExpress";
public static final String BANNER_EVENT = "onBannerEvent";
public enum Events {
EVENT_AD_SIZE_CHANGE("onSizeChange"),
EVENT_AD_LOADED("onAdLoaded"),
EVENT_AD_FAILED_TO_LOAD("onAdFailedToLoad"),
EVENT_AD_OPENED("onAdOpened"),
EVENT_AD_CLOSED("onAdClosed"),
EVENT_AD_LEFT_APPLICATION("onAdLeftApplication"),
EVENT_AD_VIDEO_END("onVideoEnd"),
EVENT_AD_VIDEO_MUTE("onVideoMute"),
EVENT_AD_VIDEO_PAUSE("onVideoPause"),
EVENT_AD_VIDEO_PLAY("onVideoPlay"),
EVENT_AD_VIDEO_START("onVideoStart"),
EVENT_AD_VIDEO_CONTENT("hasVideoContent");
private final String event;
Events(final String name) {
event = name;
}
@Override
public String toString() {
return event;
}
}
private ThemedReactContext context;
private ReactViewGroup viewGroup;
private RCTEventEmitter emitter;
private Boolean requested = false;
// Internal prop values
private AdRequest.Builder request;
private VideoOptions.Builder videoOptions;
@ -70,6 +42,7 @@ public class RNFirebaseAdMobNativeExpress extends SimpleViewManager<ReactViewGro
/**
* Create & return view instance
*
* @param themedReactContext
* @return
*/
@ -105,6 +78,7 @@ public class RNFirebaseAdMobNativeExpress extends SimpleViewManager<ReactViewGro
/**
* Declare custom events
*
* @return
*/
@Override
@ -116,6 +90,7 @@ public class RNFirebaseAdMobNativeExpress extends SimpleViewManager<ReactViewGro
/**
* Handle unitId prop
*
* @param view
* @param value
*/
@ -127,6 +102,7 @@ public class RNFirebaseAdMobNativeExpress extends SimpleViewManager<ReactViewGro
/**
* Handle request prop
*
* @param view
* @param map
*/
@ -138,6 +114,7 @@ public class RNFirebaseAdMobNativeExpress extends SimpleViewManager<ReactViewGro
/**
* Handle video prop
*
* @param view
* @param map
*/
@ -149,6 +126,7 @@ public class RNFirebaseAdMobNativeExpress extends SimpleViewManager<ReactViewGro
/**
* Handle size prop
*
* @param view
* @param value
*/
@ -210,8 +188,12 @@ public class RNFirebaseAdMobNativeExpress extends SimpleViewManager<ReactViewGro
int left = adView.getLeft();
int top = adView.getTop();
int width = adView.getAdSize().getWidthInPixels(context);
int height = adView.getAdSize().getHeightInPixels(context);
int width = adView
.getAdSize()
.getWidthInPixels(context);
int height = adView
.getAdSize()
.getHeightInPixels(context);
adView.measure(width, height);
adView.layout(left, top, left + width, top + height);
@ -230,17 +212,21 @@ public class RNFirebaseAdMobNativeExpress extends SimpleViewManager<ReactViewGro
public void onVideoEnd() {
sendEvent(Events.EVENT_AD_VIDEO_END.toString(), null);
}
public void onVideoMute(boolean isMuted) {
WritableMap videoMutePayload = Arguments.createMap();
videoMutePayload.putBoolean("isMuted", isMuted);
sendEvent(Events.EVENT_AD_VIDEO_MUTE.toString(), videoMutePayload);
}
public void onVideoPause() {
sendEvent(Events.EVENT_AD_VIDEO_PAUSE.toString(), null);
}
public void onVideoPlay() {
sendEvent(Events.EVENT_AD_VIDEO_PLAY.toString(), null);
}
public void onVideoStart() {
sendEvent(Events.EVENT_AD_VIDEO_START.toString(), null);
}
@ -273,6 +259,7 @@ public class RNFirebaseAdMobNativeExpress extends SimpleViewManager<ReactViewGro
/**
* Sends an event back to the JS component to handle
*
* @param type
* @param payload
*/
@ -287,4 +274,30 @@ public class RNFirebaseAdMobNativeExpress extends SimpleViewManager<ReactViewGro
int id = viewGroup.getId();
emitter.receiveEvent(viewGroup.getId(), BANNER_EVENT, event);
}
public enum Events {
EVENT_AD_SIZE_CHANGE("onSizeChange"),
EVENT_AD_LOADED("onAdLoaded"),
EVENT_AD_FAILED_TO_LOAD("onAdFailedToLoad"),
EVENT_AD_OPENED("onAdOpened"),
EVENT_AD_CLOSED("onAdClosed"),
EVENT_AD_LEFT_APPLICATION("onAdLeftApplication"),
EVENT_AD_VIDEO_END("onVideoEnd"),
EVENT_AD_VIDEO_MUTE("onVideoMute"),
EVENT_AD_VIDEO_PAUSE("onVideoPause"),
EVENT_AD_VIDEO_PLAY("onVideoPlay"),
EVENT_AD_VIDEO_START("onVideoStart"),
EVENT_AD_VIDEO_CONTENT("hasVideoContent");
private final String event;
Events(final String name) {
event = name;
}
@Override
public String toString() {
return event;
}
}
}

View File

@ -1,7 +1,6 @@
package io.invertase.firebase.admob;
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;
@ -9,7 +8,6 @@ import com.facebook.react.uimanager.ViewManager;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@SuppressWarnings("unused")

View File

@ -18,6 +18,7 @@ class RNFirebaseAdMobUtils {
/**
* Convert common AdMob errors into a standard format
*
* @param errorCode
* @return
*/
@ -27,11 +28,17 @@ class RNFirebaseAdMobUtils {
switch (errorCode) {
case AdRequest.ERROR_CODE_INTERNAL_ERROR:
map.putString("code", "admob/error-code-internal-error");
map.putString("message", "Something happened internally; for instance, an invalid response was received from the ad server.");
map.putString(
"message",
"Something happened internally; for instance, an invalid response was received from the ad server."
);
break;
case AdRequest.ERROR_CODE_INVALID_REQUEST:
map.putString("code", "admob/error-code-invalid-request");
map.putString("message", "The ad request was invalid; for instance, the ad unit ID was incorrect.");
map.putString(
"message",
"The ad request was invalid; for instance, the ad unit ID was incorrect."
);
break;
case AdRequest.ERROR_CODE_NETWORK_ERROR:
map.putString("code", "admob/error-code-network-error");
@ -39,7 +46,10 @@ class RNFirebaseAdMobUtils {
break;
case AdRequest.ERROR_CODE_NO_FILL:
map.putString("code", "admob/error-code-no-fill");
map.putString("message", "The ad request was successful, but no ad was returned due to lack of ad inventory.");
map.putString(
"message",
"The ad request was successful, but no ad was returned due to lack of ad inventory."
);
break;
}
@ -114,6 +124,7 @@ class RNFirebaseAdMobUtils {
/**
* Map the size prop to the AdSize
*
* @param value
* @return
*/

View File

@ -3,10 +3,8 @@ package io.invertase.firebase.admob;
import android.app.Activity;
import android.support.annotation.Nullable;
import android.util.Log;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.WritableMap;
import com.google.android.gms.ads.AdListener;
import com.google.android.gms.ads.AdRequest;
@ -60,6 +58,7 @@ class RNFirebaseAdmobInterstitial {
/**
* Load an Ad with a AdRequest instance
*
* @param adRequest
*/
void loadAd(final AdRequest adRequest) {
@ -93,6 +92,7 @@ class RNFirebaseAdmobInterstitial {
/**
* Send a native event over the bridge with a type and optional payload
*
* @param type
* @param payload
*/

View File

@ -1,15 +1,15 @@
package io.invertase.firebase.analytics;
import android.util.Log;
import android.app.Activity;
import android.support.annotation.Nullable;
import android.util.Log;
import com.facebook.react.bridge.Arguments;
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.google.firebase.analytics.FirebaseAnalytics;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
public class RNFirebaseAnalytics extends ReactContextBaseJavaModule {
@ -31,7 +31,9 @@ public class RNFirebaseAnalytics extends ReactContextBaseJavaModule {
@ReactMethod
public void logEvent(final String name, @Nullable final ReadableMap params) {
FirebaseAnalytics.getInstance(getReactApplicationContext()).logEvent(name, Arguments.toBundle(params));
FirebaseAnalytics
.getInstance(getReactApplicationContext())
.logEvent(name, Arguments.toBundle(params));
}
/**
@ -39,7 +41,9 @@ public class RNFirebaseAnalytics extends ReactContextBaseJavaModule {
*/
@ReactMethod
public void setAnalyticsCollectionEnabled(final Boolean enabled) {
FirebaseAnalytics.getInstance(getReactApplicationContext()).setAnalyticsCollectionEnabled(enabled);
FirebaseAnalytics
.getInstance(getReactApplicationContext())
.setAnalyticsCollectionEnabled(enabled);
}
/**
@ -55,7 +59,9 @@ public class RNFirebaseAnalytics extends ReactContextBaseJavaModule {
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
FirebaseAnalytics.getInstance(getReactApplicationContext()).setCurrentScreen(activity, screenName, screenClassOverride);
FirebaseAnalytics
.getInstance(getReactApplicationContext())
.setCurrentScreen(activity, screenName, screenClassOverride);
}
});
}
@ -66,7 +72,9 @@ public class RNFirebaseAnalytics extends ReactContextBaseJavaModule {
*/
@ReactMethod
public void setMinimumSessionDuration(final double milliseconds) {
FirebaseAnalytics.getInstance(getReactApplicationContext()).setMinimumSessionDuration((long) milliseconds);
FirebaseAnalytics
.getInstance(getReactApplicationContext())
.setMinimumSessionDuration((long) milliseconds);
}
/**
@ -74,7 +82,9 @@ public class RNFirebaseAnalytics extends ReactContextBaseJavaModule {
*/
@ReactMethod
public void setSessionTimeoutDuration(final double milliseconds) {
FirebaseAnalytics.getInstance(getReactApplicationContext()).setSessionTimeoutDuration((long) milliseconds);
FirebaseAnalytics
.getInstance(getReactApplicationContext())
.setSessionTimeoutDuration((long) milliseconds);
}
/**
@ -82,7 +92,9 @@ public class RNFirebaseAnalytics extends ReactContextBaseJavaModule {
*/
@ReactMethod
public void setUserId(final String id) {
FirebaseAnalytics.getInstance(getReactApplicationContext()).setUserId(id);
FirebaseAnalytics
.getInstance(getReactApplicationContext())
.setUserId(id);
}
/**
@ -91,6 +103,8 @@ public class RNFirebaseAnalytics extends ReactContextBaseJavaModule {
*/
@ReactMethod
public void setUserProperty(final String name, final String value) {
FirebaseAnalytics.getInstance(getReactApplicationContext()).setUserProperty(name, value);
FirebaseAnalytics
.getInstance(getReactApplicationContext())
.setUserProperty(name, value);
}
}

View File

@ -3,7 +3,6 @@ package io.invertase.firebase.analytics;
import android.support.annotation.RequiresPermission;
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;

View File

@ -1,7 +1,6 @@
package io.invertase.firebase.auth;
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;

View File

@ -3,25 +3,25 @@ package io.invertase.firebase.config;
import android.support.annotation.NonNull;
import android.util.Log;
import com.google.android.gms.tasks.Task;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.WritableArray;
import com.google.android.gms.tasks.OnCompleteListener;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.google.firebase.FirebaseApp;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import com.google.firebase.remoteconfig.FirebaseRemoteConfig;
import com.google.firebase.remoteconfig.FirebaseRemoteConfigValue;
import com.google.firebase.remoteconfig.FirebaseRemoteConfigFetchThrottledException;
import com.google.firebase.remoteconfig.FirebaseRemoteConfigSettings;
import com.google.firebase.remoteconfig.FirebaseRemoteConfigValue;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.List;
import io.invertase.firebase.Utils;
@ -48,11 +48,14 @@ class RNFirebaseRemoteConfig extends ReactContextBaseJavaModule {
return TAG;
}
@ReactMethod public void enableDeveloperMode() {
@ReactMethod
public void enableDeveloperMode() {
FirebaseRemoteConfigSettings.Builder settings = new FirebaseRemoteConfigSettings.Builder();
settings.setDeveloperModeEnabled(true);
FirebaseRemoteConfig.getInstance().setConfigSettings(settings.build());
FirebaseRemoteConfig
.getInstance()
.setConfigSettings(settings.build());
}
@ReactMethod
@ -67,13 +70,19 @@ class RNFirebaseRemoteConfig extends ReactContextBaseJavaModule {
@ReactMethod
public void activateFetched(final Promise promise) {
Boolean status = FirebaseRemoteConfig.getInstance().activateFetched();
Boolean status = FirebaseRemoteConfig
.getInstance()
.activateFetched();
promise.resolve(status);
}
@ReactMethod
public void getValue(String key, final Promise promise) {
FirebaseRemoteConfigValue value = FirebaseRemoteConfig.getInstance().getValue(key);
FirebaseRemoteConfigValue value = FirebaseRemoteConfig
.getInstance()
.getValue(key);
promise.resolve(convertRemoteConfigValue(value));
}
@ -83,7 +92,10 @@ class RNFirebaseRemoteConfig extends ReactContextBaseJavaModule {
List<Object> keysList = Utils.recursivelyDeconstructReadableArray(keys);
for (Object key : keysList) {
FirebaseRemoteConfigValue value = FirebaseRemoteConfig.getInstance().getValue((String) key);
FirebaseRemoteConfigValue value = FirebaseRemoteConfig
.getInstance()
.getValue((String) key);
array.pushMap(convertRemoteConfigValue(value));
}
@ -92,7 +104,10 @@ class RNFirebaseRemoteConfig extends ReactContextBaseJavaModule {
@ReactMethod
public void getKeysByPrefix(String prefix, final Promise promise) {
Set<String> keys = FirebaseRemoteConfig.getInstance().getKeysByPrefix(prefix);
Set<String> keys = FirebaseRemoteConfig
.getInstance()
.getKeysByPrefix(prefix);
WritableArray array = Arguments.createArray();
for (String key : keys) {
@ -105,23 +120,45 @@ class RNFirebaseRemoteConfig extends ReactContextBaseJavaModule {
@ReactMethod
public void setDefaults(ReadableMap map) {
Map<String, Object> convertedMap = Utils.recursivelyDeconstructReadableMap(map);
FirebaseRemoteConfig.getInstance().setDefaults(convertedMap);
FirebaseRemoteConfig
.getInstance()
.setDefaults(convertedMap);
}
@ReactMethod
public void setDefaultsFromResource(int resourceId) {
FirebaseRemoteConfig.getInstance().setDefaults(resourceId);
FirebaseRemoteConfig
.getInstance()
.setDefaults(resourceId);
}
private void fetchInternal(final Promise promise, Boolean withExpiration, long expirationDuration) {
FirebaseRemoteConfig.getInstance().fetch(withExpiration ? expirationDuration : 43200) // 12 hours default
private void fetchInternal(
final Promise promise,
Boolean withExpiration,
long expirationDuration
) {
FirebaseRemoteConfig
.getInstance()
.fetch(withExpiration ? expirationDuration : 43200) // 12 hours default
.addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
public void onComplete(@NonNull Task<Void> task) {
if (task.isSuccessful()) {
promise.resolve("remoteConfigFetchStatusSuccess");
promise.resolve(null);
} else {
promise.reject("config/failure", task.getException().getMessage(), task.getException());
if (task.getException() instanceof FirebaseRemoteConfigFetchThrottledException) {
promise.reject(
"config/throttled",
"fetch() operation cannot be completed successfully, due to throttling.",
task.getException()
);
} else {
promise.reject(
"config/failure",
"fetch() operation cannot be completed successfully.",
task.getException()
);
}
}
}
});
@ -154,7 +191,6 @@ class RNFirebaseRemoteConfig extends ReactContextBaseJavaModule {
map.putNull(NUMBER_VALUE);
}
// TODO check with ios
switch (value.getSource()) {
case FirebaseRemoteConfig.VALUE_SOURCE_DEFAULT:
map.putString(SOURCE, "default");

View File

@ -1,7 +1,6 @@
package io.invertase.firebase.config;
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;

View File

@ -1,52 +0,0 @@
package io.invertase.firebase.crash;
import android.util.Log;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.google.firebase.crash.FirebaseCrash;
public class RNFirebaseCrash extends ReactContextBaseJavaModule {
private static final String TAG = "RNFirebaseCrash";
public RNFirebaseCrash(ReactApplicationContext reactContext) {
super(reactContext);
Log.d(TAG, "New instance");
}
@Override
public String getName() {
return TAG;
}
@ReactMethod
public void log(final String message) {
FirebaseCrash.log(message);
}
@ReactMethod
public void logcat(final int level, final String tag, final String message) {
FirebaseCrash.logcat(level, tag, message);
}
@ReactMethod
public void report(String message) {
FirebaseCrash.report(new Exception(message));
}
@ReactMethod
public void setCrashCollectionEnabled(Boolean enabled) {
FirebaseCrash.setCrashCollectionEnabled(enabled);
}
@ReactMethod
public void isCrashCollectionEnabled(Promise promise) {
Boolean isEnabled = FirebaseCrash.isCrashCollectionEnabled();
promise.resolve(isEnabled);
}
}

View File

@ -1,39 +0,0 @@
package io.invertase.firebase.crash;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.UIManagerModule;
import com.facebook.react.uimanager.ViewManager;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@SuppressWarnings("unused")
public class RNFirebaseCrashPackage implements ReactPackage {
public RNFirebaseCrashPackage() {
}
/**
* @param reactContext react application context that can be used to create modules
* @return list of native modules to register with the newly created catalyst instance
*/
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>();
modules.add(new RNFirebaseCrash(reactContext));
return modules;
}
/**
* @param reactContext
* @return a list of view managers that should be registered with {@link UIManagerModule}
*/
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
}

View File

@ -4,26 +4,25 @@ import android.os.AsyncTask;
import android.util.Log;
import android.util.SparseArray;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.google.firebase.FirebaseApp;
import com.google.firebase.database.DataSnapshot;
import com.google.firebase.database.DatabaseError;
import com.google.firebase.database.DatabaseException;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;
import com.google.firebase.database.Logger;
import com.google.firebase.database.MutableData;
import com.google.firebase.database.OnDisconnect;
import com.google.firebase.database.ServerValue;
import com.google.firebase.database.Transaction;
import com.google.firebase.database.DataSnapshot;
import com.google.firebase.database.DatabaseError;
import com.google.firebase.database.FirebaseDatabase;
import com.google.firebase.database.DatabaseReference;
import java.util.HashMap;
import java.util.List;
@ -32,12 +31,11 @@ import java.util.Map;
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 HashMap<String, RNFirebaseDatabaseReference> references = new HashMap<>();
private static HashMap<String, Boolean> loggingLevelSet = new HashMap<>();
private HashMap<String, RNFirebaseDatabaseReference> references = new HashMap<>();
private SparseArray<RNFirebaseTransactionHandler> transactionHandlers = new SparseArray<>();
RNFirebaseDatabase(ReactApplicationContext reactContext) {
@ -49,6 +47,210 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
* REACT NATIVE METHODS
*/
/**
* Resolve null or reject with a js like error if databaseError exists
*
* @param promise
* @param databaseError
*/
static void handlePromise(Promise promise, DatabaseError databaseError) {
if (databaseError != null) {
WritableMap jsError = getJSError(databaseError);
promise.reject(
jsError.getString("code"),
jsError.getString("message"),
databaseError.toException()
);
} else {
promise.resolve(null);
}
}
/**
* Get a database instance for a specific firebase app instance
*
* @param appName
* @param dbURL
* @return
*/
public static FirebaseDatabase getDatabaseForApp(String appName, String dbURL) {
FirebaseDatabase firebaseDatabase;
if (dbURL != null && dbURL.length() > 0) {
if (appName != null && appName.length() > 0) {
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
firebaseDatabase = FirebaseDatabase.getInstance(firebaseApp, dbURL);
} else {
firebaseDatabase = FirebaseDatabase.getInstance(dbURL);
}
} else {
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
firebaseDatabase = FirebaseDatabase.getInstance(firebaseApp);
}
Boolean logLevel = loggingLevelSet.get(firebaseDatabase
.getApp()
.getName());
if (enableLogging && (logLevel == null || !logLevel)) {
try {
loggingLevelSet.put(firebaseDatabase
.getApp()
.getName(), enableLogging);
firebaseDatabase.setLogLevel(Logger.Level.DEBUG);
} catch (DatabaseException dex) {
// do nothing - to catch 'calls to setLogLevel must be made for use of database' errors
// only occurs in dev after reloading or if user has actually incorrectly called it.
Log.w(
TAG,
"WARNING: enableLogging(bool) must be called before any other use of database(). \n" +
"If you are sure you've done this then this message can be ignored during development as \n" +
"RN reloads can cause false positives. APP: " + firebaseDatabase
.getApp()
.getName()
);
}
} else if (!enableLogging && (logLevel != null && logLevel)) {
try {
loggingLevelSet.put(firebaseDatabase
.getApp()
.getName(), enableLogging);
firebaseDatabase.setLogLevel(Logger.Level.WARN);
} catch (DatabaseException dex) {
// do nothing - to catch 'calls to setLogLevel must be made for use of database' errors
// only occurs in dev after reloading or if user has actually incorrectly called it.
Log.w(
TAG,
"WARNING: enableLogging(bool) must be called before any other use of database(). \n" +
"If you are sure you've done this then this message can be ignored during development as \n" +
"RN reloads can cause false positives. APP: " + firebaseDatabase
.getApp()
.getName()
);
}
}
return firebaseDatabase;
}
/**
* Convert as firebase DatabaseError instance into a writable map
* with the correct web-like error codes.
*
* @param nativeError
* @return
*/
static WritableMap getJSError(DatabaseError nativeError) {
WritableMap errorMap = Arguments.createMap();
errorMap.putInt("nativeErrorCode", nativeError.getCode());
errorMap.putString("nativeErrorMessage", nativeError.getMessage());
String code;
String message;
String service = "Database";
switch (nativeError.getCode()) {
case DatabaseError.DATA_STALE:
code = ErrorUtils.getCodeWithService(service, "data-stale");
message = ErrorUtils.getMessageWithService(
"The transaction needs to be run again with current data.",
service,
code
);
break;
case DatabaseError.OPERATION_FAILED:
code = ErrorUtils.getCodeWithService(service, "failure");
message = ErrorUtils.getMessageWithService(
"The server indicated that this operation failed.",
service,
code
);
break;
case DatabaseError.PERMISSION_DENIED:
code = ErrorUtils.getCodeWithService(service, "permission-denied");
message = ErrorUtils.getMessageWithService(
"Client doesn't have permission to access the desired data.",
service,
code
);
break;
case DatabaseError.DISCONNECTED:
code = ErrorUtils.getCodeWithService(service, "disconnected");
message = ErrorUtils.getMessageWithService(
"The operation had to be aborted due to a network disconnect.",
service,
code
);
break;
case DatabaseError.EXPIRED_TOKEN:
code = ErrorUtils.getCodeWithService(service, "expired-token");
message = ErrorUtils.getMessageWithService(
"The supplied auth token has expired.",
service,
code
);
break;
case DatabaseError.INVALID_TOKEN:
code = ErrorUtils.getCodeWithService(service, "invalid-token");
message = ErrorUtils.getMessageWithService(
"The supplied auth token was invalid.",
service,
code
);
break;
case DatabaseError.MAX_RETRIES:
code = ErrorUtils.getCodeWithService(service, "max-retries");
message = ErrorUtils.getMessageWithService(
"The transaction had too many retries.",
service,
code
);
break;
case DatabaseError.OVERRIDDEN_BY_SET:
code = ErrorUtils.getCodeWithService(service, "overridden-by-set");
message = ErrorUtils.getMessageWithService(
"The transaction was overridden by a subsequent set.",
service,
code
);
break;
case DatabaseError.UNAVAILABLE:
code = ErrorUtils.getCodeWithService(service, "unavailable");
message = ErrorUtils.getMessageWithService("The service is unavailable.", service, code);
break;
case DatabaseError.USER_CODE_EXCEPTION:
code = ErrorUtils.getCodeWithService(service, "user-code-exception");
message = ErrorUtils.getMessageWithService(
"User code called from the Firebase Database runloop threw an exception.",
service,
code
);
break;
case DatabaseError.NETWORK_ERROR:
code = ErrorUtils.getCodeWithService(service, "network-error");
message = ErrorUtils.getMessageWithService(
"The operation could not be performed due to a network error.",
service,
code
);
break;
case DatabaseError.WRITE_CANCELED:
code = ErrorUtils.getCodeWithService(service, "write-cancelled");
message = ErrorUtils.getMessageWithService(
"The write was canceled by the user.",
service,
code
);
break;
default:
code = ErrorUtils.getCodeWithService(service, "unknown");
message = ErrorUtils.getMessageWithService("An unknown error occurred.", service, code);
}
errorMap.putString("code", code);
errorMap.putString("message", message);
return errorMap;
}
/**
* @param appName
*/
@ -74,6 +276,11 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
getDatabaseForApp(appName, dbURL).setPersistenceEnabled(state);
}
/*
* TRANSACTIONS
*/
/**
* @param appName
* @param size
@ -83,7 +290,6 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
getDatabaseForApp(appName, dbURL).setPersistenceCacheSizeBytes((long) size);
}
/**
* @param enabled
*/
@ -95,42 +301,62 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
loggingLevelSet.put(app.getName(), enabled);
try {
if (enableLogging) {
FirebaseDatabase.getInstance(app).setLogLevel(Logger.Level.DEBUG);
FirebaseDatabase
.getInstance(app)
.setLogLevel(Logger.Level.DEBUG);
} else {
FirebaseDatabase.getInstance(app).setLogLevel(Logger.Level.WARN);
FirebaseDatabase
.getInstance(app)
.setLogLevel(Logger.Level.WARN);
}
} catch (DatabaseException dex) {
// do nothing - to catch 'calls to setLogLevel must be made for use of database' errors
// only occurs in dev after reloading or if user has actually incorrectly called it.
Log.w(TAG, "WARNING: enableLogging(bool) must be called before any other use of database(). \n" +
"If you are sure you've done this then this message can be ignored during development as \n" +
"RN reloads can cause false positives. APP: " + app.getName());
Log.w(
TAG,
"WARNING: enableLogging(bool) must be called before any other use of database(). \n" +
"If you are sure you've done this then this message can be ignored during development as \n" +
"RN reloads can cause false positives. APP: " + app.getName()
);
}
}
}
/*
* ON DISCONNECT
*/
/**
* @param appName
* @param path
* @param state
*/
@ReactMethod
public void keepSynced(String appName, String dbURL, String key, String path, ReadableArray modifiers, Boolean state) {
getInternalReferenceForApp(appName, dbURL, key, path, modifiers).getQuery().keepSynced(state);
public void keepSynced(
String appName,
String dbURL,
String key,
String path,
ReadableArray modifiers,
Boolean state
) {
getInternalReferenceForApp(appName, dbURL, key, path, modifiers)
.getQuery()
.keepSynced(state);
}
/*
* TRANSACTIONS
*/
/**
* @param transactionId
* @param updates
*/
@ReactMethod
public void transactionTryCommit(String appName, String dbURL, int transactionId, ReadableMap updates) {
public void transactionTryCommit(
String appName,
String dbURL,
int transactionId,
ReadableMap updates
) {
RNFirebaseTransactionHandler handler = transactionHandlers.get(transactionId);
if (handler != null) {
@ -147,7 +373,13 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
* @param applyLocally
*/
@ReactMethod
public void transactionStart(final String appName, final String dbURL, final String path, final int transactionId, final Boolean applyLocally) {
public void transactionStart(
final String appName,
final String dbURL,
final String path,
final int transactionId,
final Boolean applyLocally
) {
AsyncTask.execute(new Runnable() {
@Override
public void run() {
@ -156,7 +388,11 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
reference.runTransaction(new Transaction.Handler() {
@Override
public Transaction.Result doTransaction(MutableData mutableData) {
final RNFirebaseTransactionHandler transactionHandler = new RNFirebaseTransactionHandler(transactionId, appName, dbURL);
final RNFirebaseTransactionHandler transactionHandler = new RNFirebaseTransactionHandler(
transactionId,
appName,
dbURL
);
transactionHandlers.put(transactionId, transactionHandler);
final WritableMap updatesMap = transactionHandler.createUpdateMap(mutableData);
@ -165,7 +401,11 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
AsyncTask.execute(new Runnable() {
@Override
public void run() {
Utils.sendEvent(getReactApplicationContext(), "database_transaction_event", updatesMap);
Utils.sendEvent(
getReactApplicationContext(),
"database_transaction_event",
updatesMap
);
}
});
@ -198,11 +438,6 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
});
}
/*
* ON DISCONNECT
*/
/**
* Set a value on a ref when the client disconnects from the firebase server.
*
@ -212,7 +447,13 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
* @param promise
*/
@ReactMethod
public void onDisconnectSet(String appName, String dbURL, String path, ReadableMap props, final Promise promise) {
public void onDisconnectSet(
String appName,
String dbURL,
String path,
ReadableMap props,
final Promise promise
) {
String type = props.getString("type");
DatabaseReference ref = getReferenceForAppPath(appName, dbURL, path);
@ -257,7 +498,13 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
* @param promise
*/
@ReactMethod
public void onDisconnectUpdate(String appName, String dbURL, String path, ReadableMap props, final Promise promise) {
public void onDisconnectUpdate(
String appName,
String dbURL,
String path,
ReadableMap props,
final Promise promise
) {
DatabaseReference ref = getReferenceForAppPath(appName, dbURL, path);
OnDisconnect ondDisconnect = ref.onDisconnect();
@ -318,9 +565,17 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
* @param promise
*/
@ReactMethod
public void set(String appName, String dbURL, String path, ReadableMap props, final Promise promise) {
public void set(
String appName,
String dbURL,
String path,
ReadableMap props,
final Promise promise
) {
DatabaseReference ref = getReferenceForAppPath(appName, dbURL, path);
Object value = Utils.recursivelyDeconstructReadableMap(props).get("value");
Object value = Utils
.recursivelyDeconstructReadableMap(props)
.get("value");
DatabaseReference.CompletionListener listener = new DatabaseReference.CompletionListener() {
@Override
@ -339,9 +594,17 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
* @param promise
*/
@ReactMethod
public void setPriority(String appName, String dbURL, String path, ReadableMap priority, final Promise promise) {
public void setPriority(
String appName,
String dbURL,
String path,
ReadableMap priority,
final Promise promise
) {
DatabaseReference ref = getReferenceForAppPath(appName, dbURL, path);
Object priorityValue = Utils.recursivelyDeconstructReadableMap(priority).get("value");
Object priorityValue = Utils
.recursivelyDeconstructReadableMap(priority)
.get("value");
DatabaseReference.CompletionListener listener = new DatabaseReference.CompletionListener() {
@Override
@ -361,10 +624,21 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
* @param promise
*/
@ReactMethod
public void setWithPriority(String appName, String dbURL, String path, ReadableMap data, ReadableMap priority, final Promise promise) {
public void setWithPriority(
String appName,
String dbURL,
String path,
ReadableMap data,
ReadableMap priority,
final Promise promise
) {
DatabaseReference ref = getReferenceForAppPath(appName, dbURL, path);
Object dataValue = Utils.recursivelyDeconstructReadableMap(data).get("value");
Object priorityValue = Utils.recursivelyDeconstructReadableMap(priority).get("value");
Object dataValue = Utils
.recursivelyDeconstructReadableMap(data)
.get("value");
Object priorityValue = Utils
.recursivelyDeconstructReadableMap(priority)
.get("value");
DatabaseReference.CompletionListener listener = new DatabaseReference.CompletionListener() {
@Override
@ -383,7 +657,13 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
* @param promise
*/
@ReactMethod
public void update(String appName, String dbURL, String path, ReadableMap props, final Promise promise) {
public void update(
String appName,
String dbURL,
String path,
ReadableMap props,
final Promise promise
) {
DatabaseReference ref = getReferenceForAppPath(appName, dbURL, path);
Map<String, Object> updates = Utils.recursivelyDeconstructReadableMap(props);
@ -416,6 +696,9 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
ref.removeValue(listener);
}
/*
* INTERNALS/UTILS
*/
/**
* Subscribe once to a firebase reference.
@ -428,7 +711,15 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
* @param promise
*/
@ReactMethod
public void once(String appName, String dbURL, String key, String path, ReadableArray modifiers, String eventType, Promise promise) {
public void once(
String appName,
String dbURL,
String key,
String path,
ReadableArray modifiers,
String eventType,
Promise promise
) {
getInternalReferenceForApp(appName, dbURL, key, path, modifiers).once(eventType, promise);
}
@ -466,80 +757,6 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
}
}
/*
* INTERNALS/UTILS
*/
/**
* Resolve null or reject with a js like error if databaseError exists
*
* @param promise
* @param databaseError
*/
static void handlePromise(Promise promise, DatabaseError databaseError) {
if (databaseError != null) {
WritableMap jsError = getJSError(databaseError);
promise.reject(
jsError.getString("code"),
jsError.getString("message"),
databaseError.toException()
);
} else {
promise.resolve(null);
}
}
/**
* Get a database instance for a specific firebase app instance
*
* @param appName
* @param dbURL
* @return
*/
public static FirebaseDatabase getDatabaseForApp(String appName, String dbURL) {
FirebaseDatabase firebaseDatabase;
if(dbURL != null && dbURL.length() > 0) {
if (appName != null && appName.length() > 0) {
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
firebaseDatabase = FirebaseDatabase.getInstance(firebaseApp, dbURL);
} else {
firebaseDatabase = FirebaseDatabase.getInstance(dbURL);
}
} else {
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
firebaseDatabase = FirebaseDatabase.getInstance(firebaseApp);
}
Boolean logLevel = loggingLevelSet.get(firebaseDatabase.getApp().getName());
if (enableLogging && (logLevel == null || !logLevel)) {
try {
loggingLevelSet.put(firebaseDatabase.getApp().getName(), enableLogging);
firebaseDatabase.setLogLevel(Logger.Level.DEBUG);
} catch (DatabaseException dex) {
// do nothing - to catch 'calls to setLogLevel must be made for use of database' errors
// only occurs in dev after reloading or if user has actually incorrectly called it.
Log.w(TAG, "WARNING: enableLogging(bool) must be called before any other use of database(). \n" +
"If you are sure you've done this then this message can be ignored during development as \n" +
"RN reloads can cause false positives. APP: " + firebaseDatabase.getApp().getName());
}
} else if (!enableLogging && (logLevel != null && logLevel)) {
try {
loggingLevelSet.put(firebaseDatabase.getApp().getName(), enableLogging);
firebaseDatabase.setLogLevel(Logger.Level.WARN);
} catch (DatabaseException dex) {
// do nothing - to catch 'calls to setLogLevel must be made for use of database' errors
// only occurs in dev after reloading or if user has actually incorrectly called it.
Log.w(TAG, "WARNING: enableLogging(bool) must be called before any other use of database(). \n" +
"If you are sure you've done this then this message can be ignored during development as \n" +
"RN reloads can cause false positives. APP: " + firebaseDatabase.getApp().getName());
}
}
return firebaseDatabase;
}
/**
* Get a database reference for a specific app and path
*
@ -560,7 +777,13 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
* @param modifiers
* @return
*/
private RNFirebaseDatabaseReference getInternalReferenceForApp(String appName, String dbURL, String key, String path, ReadableArray modifiers) {
private RNFirebaseDatabaseReference getInternalReferenceForApp(
String appName,
String dbURL,
String key,
String path,
ReadableArray modifiers
) {
return new RNFirebaseDatabaseReference(
getReactApplicationContext(),
appName,
@ -578,7 +801,11 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
* @param props
* @return
*/
private RNFirebaseDatabaseReference getCachedInternalReferenceForApp(String appName, String dbURL, ReadableMap props) {
private RNFirebaseDatabaseReference getCachedInternalReferenceForApp(
String appName,
String dbURL,
ReadableMap props
) {
String key = props.getString("key");
String path = props.getString("path");
ReadableArray modifiers = props.getArray("modifiers");
@ -593,81 +820,6 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
return existingRef;
}
/**
* Convert as firebase DatabaseError instance into a writable map
* with the correct web-like error codes.
*
* @param nativeError
* @return
*/
static WritableMap getJSError(DatabaseError nativeError) {
WritableMap errorMap = Arguments.createMap();
errorMap.putInt("nativeErrorCode", nativeError.getCode());
errorMap.putString("nativeErrorMessage", nativeError.getMessage());
String code;
String message;
String service = "Database";
switch (nativeError.getCode()) {
case DatabaseError.DATA_STALE:
code = ErrorUtils.getCodeWithService(service, "data-stale");
message = ErrorUtils.getMessageWithService("The transaction needs to be run again with current data.", service, code);
break;
case DatabaseError.OPERATION_FAILED:
code = ErrorUtils.getCodeWithService(service, "failure");
message = ErrorUtils.getMessageWithService("The server indicated that this operation failed.", service, code);
break;
case DatabaseError.PERMISSION_DENIED:
code = ErrorUtils.getCodeWithService(service, "permission-denied");
message = ErrorUtils.getMessageWithService("Client doesn't have permission to access the desired data.", service, code);
break;
case DatabaseError.DISCONNECTED:
code = ErrorUtils.getCodeWithService(service, "disconnected");
message = ErrorUtils.getMessageWithService("The operation had to be aborted due to a network disconnect.", service, code);
break;
case DatabaseError.EXPIRED_TOKEN:
code = ErrorUtils.getCodeWithService(service, "expired-token");
message = ErrorUtils.getMessageWithService("The supplied auth token has expired.", service, code);
break;
case DatabaseError.INVALID_TOKEN:
code = ErrorUtils.getCodeWithService(service, "invalid-token");
message = ErrorUtils.getMessageWithService("The supplied auth token was invalid.", service, code);
break;
case DatabaseError.MAX_RETRIES:
code = ErrorUtils.getCodeWithService(service, "max-retries");
message = ErrorUtils.getMessageWithService("The transaction had too many retries.", service, code);
break;
case DatabaseError.OVERRIDDEN_BY_SET:
code = ErrorUtils.getCodeWithService(service, "overridden-by-set");
message = ErrorUtils.getMessageWithService("The transaction was overridden by a subsequent set.", service, code);
break;
case DatabaseError.UNAVAILABLE:
code = ErrorUtils.getCodeWithService(service, "unavailable");
message = ErrorUtils.getMessageWithService("The service is unavailable.", service, code);
break;
case DatabaseError.USER_CODE_EXCEPTION:
code = ErrorUtils.getCodeWithService(service, "user-code-exception");
message = ErrorUtils.getMessageWithService("User code called from the Firebase Database runloop threw an exception.", service, code);
break;
case DatabaseError.NETWORK_ERROR:
code = ErrorUtils.getCodeWithService(service, "network-error");
message = ErrorUtils.getMessageWithService("The operation could not be performed due to a network error.", service, code);
break;
case DatabaseError.WRITE_CANCELED:
code = ErrorUtils.getCodeWithService(service, "write-cancelled");
message = ErrorUtils.getMessageWithService("The write was canceled by the user.", service, code);
break;
default:
code = ErrorUtils.getCodeWithService(service, "unknown");
message = ErrorUtils.getMessageWithService("An unknown error occurred.", service, code);
}
errorMap.putString("code", code);
errorMap.putString("message", message);
return errorMap;
}
/**
* React Method - returns this module name
*

View File

@ -1,7 +1,6 @@
package io.invertase.firebase.database;
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;

View File

@ -1,35 +1,38 @@
package io.invertase.firebase.database;
import java.util.Map;
import java.util.List;
import java.util.HashMap;
import android.util.Log;
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.Promise;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReadableArray;
import com.google.firebase.database.Query;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap;
import com.google.firebase.database.ChildEventListener;
import com.google.firebase.database.DataSnapshot;
import com.google.firebase.database.DatabaseError;
import com.google.firebase.database.FirebaseDatabase;
import com.google.firebase.database.ChildEventListener;
import com.google.firebase.database.Query;
import com.google.firebase.database.ValueEventListener;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import io.invertase.firebase.Utils;
class RNFirebaseDatabaseReference {
private static final String TAG = "RNFirebaseDBReference";
private String key;
private Query query;
private String appName;
private String dbURL;
private ReactContext reactContext;
private static final String TAG = "RNFirebaseDBReference";
private HashMap<String, ChildEventListener> childEventListeners = new HashMap<>();
private HashMap<String, ValueEventListener> valueEventListeners = new HashMap<>();
@ -43,7 +46,14 @@ class RNFirebaseDatabaseReference {
* @param refPath
* @param modifiersArray
*/
RNFirebaseDatabaseReference(ReactContext context, String app, String url, String refKey, String refPath, ReadableArray modifiersArray) {
RNFirebaseDatabaseReference(
ReactContext context,
String app,
String url,
String refKey,
String refPath,
ReadableArray modifiersArray
) {
key = refKey;
query = null;
appName = app;
@ -52,7 +62,6 @@ class RNFirebaseDatabaseReference {
buildDatabaseQueryAtPathAndModifiers(refPath, modifiersArray);
}
/**
* Used outside of class for keepSynced etc.
*
@ -69,7 +78,8 @@ class RNFirebaseDatabaseReference {
* @return
*/
private Boolean hasEventListener(String eventRegistrationKey) {
return valueEventListeners.containsKey(eventRegistrationKey) || childEventListeners.containsKey(eventRegistrationKey);
return valueEventListeners.containsKey(eventRegistrationKey) || childEventListeners.containsKey(
eventRegistrationKey);
}
/**
@ -111,7 +121,6 @@ class RNFirebaseDatabaseReference {
}
/**
* Add a ChildEventListener to the query and internally keep a reference to it.
*
@ -130,15 +139,24 @@ class RNFirebaseDatabaseReference {
* @param promise
*/
private void addOnceValueEventListener(final Promise promise) {
@SuppressLint("StaticFieldLeak") final DataSnapshotToMapAsyncTask asyncTask = new DataSnapshotToMapAsyncTask(
reactContext,
this
) {
@Override
protected void onPostExecute(WritableMap writableMap) {
if (this.isAvailable()) promise.resolve(writableMap);
}
};
ValueEventListener onceValueEventListener = new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
WritableMap data = RNFirebaseDatabaseUtils.snapshotToMap(dataSnapshot, null);
promise.resolve(data);
public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
asyncTask.execute(dataSnapshot, null);
}
@Override
public void onCancelled(DatabaseError error) {
public void onCancelled(@NonNull DatabaseError error) {
RNFirebaseDatabase.handlePromise(promise, error);
}
};
@ -156,7 +174,7 @@ class RNFirebaseDatabaseReference {
private void addChildOnceEventListener(final String eventName, final Promise promise) {
ChildEventListener childEventListener = new ChildEventListener() {
@Override
public void onChildAdded(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);
@ -165,7 +183,7 @@ class RNFirebaseDatabaseReference {
}
@Override
public void onChildChanged(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);
@ -174,7 +192,7 @@ class RNFirebaseDatabaseReference {
}
@Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
public void onChildRemoved(@NonNull DataSnapshot dataSnapshot) {
if ("child_removed".equals(eventName)) {
query.removeEventListener(this);
WritableMap data = RNFirebaseDatabaseUtils.snapshotToMap(dataSnapshot, null);
@ -183,7 +201,7 @@ class RNFirebaseDatabaseReference {
}
@Override
public void onChildMoved(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);
@ -192,7 +210,7 @@ class RNFirebaseDatabaseReference {
}
@Override
public void onCancelled(DatabaseError error) {
public void onCancelled(@NonNull DatabaseError error) {
query.removeEventListener(this);
RNFirebaseDatabase.handlePromise(promise, error);
}
@ -201,7 +219,6 @@ class RNFirebaseDatabaseReference {
query.addChildEventListener(childEventListener);
}
/**
* Handles a React Native JS '.on(..)' request and initializes listeners.
*
@ -229,7 +246,6 @@ class RNFirebaseDatabaseReference {
}
}
/**
* Add a native .on('child_X',.. ) event listener.
*
@ -243,35 +259,35 @@ class RNFirebaseDatabaseReference {
if (!hasEventListener(eventRegistrationKey)) {
ChildEventListener childEventListener = new ChildEventListener() {
@Override
public void onChildAdded(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(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(DataSnapshot dataSnapshot) {
public void onChildRemoved(@NonNull DataSnapshot dataSnapshot) {
if ("child_removed".equals(eventType)) {
handleDatabaseEvent("child_removed", registration, dataSnapshot, null);
}
}
@Override
public void onChildMoved(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(DatabaseError error) {
public void onCancelled(@NonNull DatabaseError error) {
removeEventListener(eventRegistrationKey);
handleDatabaseError(registration, error);
}
@ -292,12 +308,12 @@ class RNFirebaseDatabaseReference {
if (!hasEventListener(eventRegistrationKey)) {
ValueEventListener valueEventListener = new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
handleDatabaseEvent("value", registration, dataSnapshot, null);
}
@Override
public void onCancelled(DatabaseError error) {
public void onCancelled(@NonNull DatabaseError error) {
removeEventListener(eventRegistrationKey);
handleDatabaseError(registration, error);
}
@ -314,16 +330,28 @@ class RNFirebaseDatabaseReference {
* @param dataSnapshot
* @param previousChildName
*/
private void handleDatabaseEvent(String eventType, ReadableMap registration, DataSnapshot dataSnapshot, @Nullable String previousChildName) {
WritableMap event = Arguments.createMap();
WritableMap data = RNFirebaseDatabaseUtils.snapshotToMap(dataSnapshot, previousChildName);
private void handleDatabaseEvent(
final String eventType,
final ReadableMap registration,
DataSnapshot dataSnapshot,
@Nullable String previousChildName
) {
@SuppressLint("StaticFieldLeak")
DataSnapshotToMapAsyncTask asyncTask = new DataSnapshotToMapAsyncTask(reactContext, this) {
@Override
protected void onPostExecute(WritableMap data) {
if (this.isAvailable()) {
WritableMap event = Arguments.createMap();
event.putMap("data", data);
event.putString("key", key);
event.putString("eventType", eventType);
event.putMap("registration", Utils.readableMapToWritableMap(registration));
Utils.sendEvent(reactContext, "database_sync_event", event);
}
}
};
event.putMap("data", data);
event.putString("key", key);
event.putString("eventType", eventType);
event.putMap("registration", Utils.readableMapToWritableMap(registration));
Utils.sendEvent(reactContext, "database_sync_event", event);
asyncTask.execute(dataSnapshot, previousChildName);
}
/**
@ -366,11 +394,6 @@ class RNFirebaseDatabaseReference {
}
}
/* =================
* QUERY MODIFIERS
* =================
*/
/**
* @param name
* @param type
@ -393,6 +416,11 @@ class RNFirebaseDatabaseReference {
}
}
/* =================
* QUERY MODIFIERS
* =================
*/
/**
* @param name
* @param type
@ -423,12 +451,6 @@ class RNFirebaseDatabaseReference {
}
}
/* ===============
* QUERY FILTERS
* ===============
*/
/**
* @param key
* @param valueType
@ -459,6 +481,12 @@ class RNFirebaseDatabaseReference {
}
}
/* ===============
* QUERY FILTERS
* ===============
*/
/**
* @param key
* @param valueType
@ -518,4 +546,46 @@ class RNFirebaseDatabaseReference {
}
}
}
/**
* AsyncTask to convert DataSnapshot instances to WritableMap instances.
* <p>
* 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) {
referenceWeakReference = new WeakReference<>(reference);
reactContextWeakReference = new WeakReference<>(context);
}
@Override
protected final WritableMap doInBackground(Object... params) {
DataSnapshot dataSnapshot = (DataSnapshot) params[0];
@Nullable String previousChildName = (String) params[1];
try {
return RNFirebaseDatabaseUtils.snapshotToMap(dataSnapshot, previousChildName);
} catch (RuntimeException e) {
if (isAvailable()) {
reactContextWeakReference
.get()
.handleException(e);
}
throw e;
}
}
@Override
protected void onPostExecute(WritableMap writableMap) {
// do nothing as overridden on usage
}
Boolean isAvailable() {
return reactContextWeakReference.get() != null && referenceWeakReference.get() != null;
}
}
}

View File

@ -20,7 +20,10 @@ public class RNFirebaseDatabaseUtils {
* @param previousChildName
* @return
*/
public static WritableMap snapshotToMap(DataSnapshot dataSnapshot, @Nullable String previousChildName) {
public static WritableMap snapshotToMap(
DataSnapshot dataSnapshot,
@Nullable String previousChildName
) {
WritableMap result = Arguments.createMap();
WritableMap snapshot = snapshotToMap(dataSnapshot);
@ -71,7 +74,10 @@ public class RNFirebaseDatabaseUtils {
}
} else {
if (snapshot.getValue() != null) {
String type = snapshot.getValue().getClass().getName();
String type = snapshot
.getValue()
.getClass()
.getName();
switch (type) {
case "java.lang.Boolean":
case "java.lang.Long":
@ -101,7 +107,10 @@ public class RNFirebaseDatabaseUtils {
}
} else {
if (mutableData.getValue() != null) {
String type = mutableData.getValue().getClass().getName();
String type = mutableData
.getValue()
.getClass()
.getName();
switch (type) {
case "java.lang.Boolean":
case "java.lang.Long":
@ -190,7 +199,9 @@ public class RNFirebaseDatabaseUtils {
expectedKey = key;
}
Any castedChild = castValue(child);
switch (castedChild.getClass().getName()) {
switch (castedChild
.getClass()
.getName()) {
case "java.lang.Boolean":
array.pushBoolean((Boolean) castedChild);
break;
@ -211,7 +222,12 @@ public class RNFirebaseDatabaseUtils {
array.pushArray((WritableArray) castedChild);
break;
default:
Log.w(TAG, "Invalid type: " + castedChild.getClass().getName());
Log.w(
TAG,
"Invalid type: " + castedChild
.getClass()
.getName()
);
break;
}
expectedKey++;
@ -236,7 +252,9 @@ public class RNFirebaseDatabaseUtils {
expectedKey = key;
}
Any castedChild = castValue(child);
switch (castedChild.getClass().getName()) {
switch (castedChild
.getClass()
.getName()) {
case "java.lang.Boolean":
array.pushBoolean((Boolean) castedChild);
break;
@ -257,7 +275,12 @@ public class RNFirebaseDatabaseUtils {
array.pushArray((WritableArray) castedChild);
break;
default:
Log.w(TAG, "Invalid type: " + castedChild.getClass().getName());
Log.w(
TAG,
"Invalid type: " + castedChild
.getClass()
.getName()
);
break;
}
expectedKey++;
@ -275,7 +298,9 @@ public class RNFirebaseDatabaseUtils {
for (DataSnapshot child : snapshot.getChildren()) {
Any castedChild = castValue(child);
switch (castedChild.getClass().getName()) {
switch (castedChild
.getClass()
.getName()) {
case "java.lang.Boolean":
map.putBoolean(child.getKey(), (Boolean) castedChild);
break;
@ -295,7 +320,12 @@ public class RNFirebaseDatabaseUtils {
map.putArray(child.getKey(), (WritableArray) castedChild);
break;
default:
Log.w(TAG, "Invalid type: " + castedChild.getClass().getName());
Log.w(
TAG,
"Invalid type: " + castedChild
.getClass()
.getName()
);
break;
}
}
@ -312,7 +342,9 @@ public class RNFirebaseDatabaseUtils {
for (MutableData child : mutableData.getChildren()) {
Any castedChild = castValue(child);
switch (castedChild.getClass().getName()) {
switch (castedChild
.getClass()
.getName()) {
case "java.lang.Boolean":
map.putBoolean(child.getKey(), (Boolean) castedChild);
break;
@ -332,7 +364,12 @@ public class RNFirebaseDatabaseUtils {
map.putArray(child.getKey(), (WritableArray) castedChild);
break;
default:
Log.w(TAG, "Invalid type: " + castedChild.getClass().getName());
Log.w(
TAG,
"Invalid type: " + castedChild
.getClass()
.getName()
);
break;
}
}

View File

@ -19,18 +19,17 @@ import javax.annotation.Nullable;
import io.invertase.firebase.Utils;
public class RNFirebaseTransactionHandler {
private int transactionId;
private String appName;
private String dbURL;
private final ReentrantLock lock;
private final Condition condition;
private Map<String, Object> data;
private boolean signalled;
public Object value;
boolean interrupted;
boolean abort = false;
boolean timeout = false;
private int transactionId;
private String appName;
private String dbURL;
private Map<String, Object> data;
private boolean signalled;
RNFirebaseTransactionHandler(int id, String app, String url) {
appName = app;
@ -127,7 +126,11 @@ public class RNFirebaseTransactionHandler {
}
WritableMap createResultMap(@Nullable DatabaseError error, boolean committed, DataSnapshot snapshot) {
WritableMap createResultMap(
@Nullable DatabaseError error,
boolean committed,
DataSnapshot snapshot
) {
WritableMap resultMap = Arguments.createMap();
resultMap.putInt("id", transactionId);
@ -144,7 +147,10 @@ public class RNFirebaseTransactionHandler {
if (error == null && timeout) {
WritableMap timeoutError = Arguments.createMap();
timeoutError.putString("code", "DATABASE/INTERNAL-TIMEOUT");
timeoutError.putString("message", "A timeout occurred whilst waiting for RN JS thread to send transaction updates.");
timeoutError.putString(
"message",
"A timeout occurred whilst waiting for RN JS thread to send transaction updates."
);
resultMap.putMap("error", timeoutError);
}
} else {

View File

@ -2,11 +2,10 @@ package io.invertase.firebase.fabric.crashlytics;
import android.util.Log;
import com.facebook.react.bridge.ReactMethod;
import com.crashlytics.android.Crashlytics;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.crashlytics.android.Crashlytics;
import com.facebook.react.bridge.ReactMethod;
public class RNFirebaseCrashlytics extends ReactContextBaseJavaModule {
@ -25,7 +24,9 @@ public class RNFirebaseCrashlytics extends ReactContextBaseJavaModule {
@ReactMethod
public void crash() {
Crashlytics.getInstance().crash();
Crashlytics
.getInstance()
.crash();
}
@ReactMethod

View File

@ -1,7 +1,6 @@
package io.invertase.firebase.fabric.crashlytics;
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;

View File

@ -0,0 +1,49 @@
package io.invertase.firebase.firestore;
import android.os.AsyncTask;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.WritableMap;
import com.google.firebase.firestore.DocumentSnapshot;
import java.lang.ref.WeakReference;
class DocumentSnapshotSerializeAsyncTask extends AsyncTask<Object, Void, WritableMap> {
private WeakReference<ReactContext> reactContextWeakReference;
private WeakReference<RNFirebaseFirestoreDocumentReference> referenceWeakReference;
DocumentSnapshotSerializeAsyncTask(
ReactContext context,
RNFirebaseFirestoreDocumentReference reference
) {
referenceWeakReference = new WeakReference<>(reference);
reactContextWeakReference = new WeakReference<>(context);
}
@Override
protected final WritableMap doInBackground(Object... params) {
DocumentSnapshot querySnapshot = (DocumentSnapshot) params[0];
try {
return FirestoreSerialize.snapshotToWritableMap(querySnapshot);
} catch (RuntimeException e) {
if (isAvailable()) {
reactContextWeakReference
.get()
.handleException(e);
} else {
throw e;
}
return null;
}
}
@Override
protected void onPostExecute(WritableMap writableMap) {
// do nothing as overridden on usage
}
private Boolean isAvailable() {
return reactContextWeakReference.get() != null && referenceWeakReference.get() != null;
}
}

View File

@ -48,22 +48,35 @@ public class FirestoreSerialize {
static WritableMap snapshotToWritableMap(DocumentSnapshot documentSnapshot) {
WritableMap documentMap = Arguments.createMap();
documentMap.putString(KEY_PATH, documentSnapshot.getReference().getPath());
documentMap.putString(
KEY_PATH,
documentSnapshot
.getReference()
.getPath()
);
if (documentSnapshot.exists()) {
documentMap.putMap(KEY_DATA, objectMapToWritable(documentSnapshot.getData()));
}
// metadata
if (documentSnapshot.getMetadata() != null) {
WritableMap metadata = Arguments.createMap();
metadata.putBoolean("fromCache", documentSnapshot.getMetadata().isFromCache());
metadata.putBoolean("hasPendingWrites", documentSnapshot.getMetadata().hasPendingWrites());
documentMap.putMap(KEY_METADATA, metadata);
}
// metadata
WritableMap metadata = Arguments.createMap();
metadata.putBoolean(
"fromCache",
documentSnapshot
.getMetadata()
.isFromCache()
);
metadata.putBoolean(
"hasPendingWrites",
documentSnapshot
.getMetadata()
.hasPendingWrites()
);
documentMap.putMap(KEY_METADATA, metadata);
return documentMap;
}
public static WritableMap snapshotToWritableMap(QuerySnapshot querySnapshot) {
static WritableMap snapshotToWritableMap(QuerySnapshot querySnapshot) {
WritableMap queryMap = Arguments.createMap();
List<DocumentChange> documentChanges = querySnapshot.getDocumentChanges();
@ -78,12 +91,20 @@ public class FirestoreSerialize {
queryMap.putArray(KEY_DOCUMENTS, documents);
// metadata
if (querySnapshot.getMetadata() != null) {
WritableMap metadata = Arguments.createMap();
metadata.putBoolean("fromCache", querySnapshot.getMetadata().isFromCache());
metadata.putBoolean("hasPendingWrites", querySnapshot.getMetadata().hasPendingWrites());
queryMap.putMap(KEY_METADATA, metadata);
}
WritableMap metadata = Arguments.createMap();
metadata.putBoolean(
"fromCache",
querySnapshot
.getMetadata()
.isFromCache()
);
metadata.putBoolean(
"hasPendingWrites",
querySnapshot
.getMetadata()
.hasPendingWrites()
);
queryMap.putMap(KEY_METADATA, metadata);
return queryMap;
}
@ -94,7 +115,7 @@ public class FirestoreSerialize {
* @param documentChanges List<DocumentChange>
* @return WritableArray
*/
static WritableArray documentChangesToWritableArray(List<DocumentChange> documentChanges) {
private static WritableArray documentChangesToWritableArray(List<DocumentChange> documentChanges) {
WritableArray documentChangesWritable = Arguments.createArray();
for (DocumentChange documentChange : documentChanges) {
documentChangesWritable.pushMap(documentChangeToWritableMap(documentChange));
@ -108,7 +129,7 @@ public class FirestoreSerialize {
* @param documentChange DocumentChange
* @return WritableMap
*/
static WritableMap documentChangeToWritableMap(DocumentChange documentChange) {
private static WritableMap documentChangeToWritableMap(DocumentChange documentChange) {
WritableMap documentChangeMap = Arguments.createMap();
switch (documentChange.getType()) {
@ -122,8 +143,10 @@ public class FirestoreSerialize {
documentChangeMap.putString(KEY_DOC_CHANGE_TYPE, "modified");
}
documentChangeMap.putMap(KEY_DOC_CHANGE_DOCUMENT,
snapshotToWritableMap(documentChange.getDocument()));
documentChangeMap.putMap(
KEY_DOC_CHANGE_DOCUMENT,
snapshotToWritableMap(documentChange.getDocument())
);
documentChangeMap.putInt(KEY_DOC_CHANGE_NEW_INDEX, documentChange.getNewIndex());
documentChangeMap.putInt(KEY_DOC_CHANGE_OLD_INDEX, documentChange.getOldIndex());
@ -136,7 +159,7 @@ public class FirestoreSerialize {
* @param map Map<String, Object>
* @return WritableMap
*/
static WritableMap objectMapToWritable(Map<String, Object> map) {
private static WritableMap objectMapToWritable(Map<String, Object> map) {
WritableMap writableMap = Arguments.createMap();
for (Map.Entry<String, Object> entry : map.entrySet()) {
WritableMap typeMap = buildTypeMap(entry.getValue());
@ -224,7 +247,10 @@ public class FirestoreSerialize {
return typeMap;
}
static Map<String, Object> parseReadableMap(FirebaseFirestore firestore, ReadableMap readableMap) {
static Map<String, Object> parseReadableMap(
FirebaseFirestore firestore,
ReadableMap readableMap
) {
Map<String, Object> map = new HashMap<>();
if (readableMap != null) {
ReadableMapKeySetIterator iterator = readableMap.keySetIterator();
@ -290,7 +316,10 @@ public class FirestoreSerialize {
}
}
public static List<Object> parseDocumentBatches(FirebaseFirestore firestore, ReadableArray readableArray) {
static List<Object> parseDocumentBatches(
FirebaseFirestore firestore,
ReadableArray readableArray
) {
List<Object> writes = new ArrayList<>(readableArray.size());
for (int i = 0; i < readableArray.size(); i++) {
Map<String, Object> write = new HashMap<>();

View File

@ -0,0 +1,49 @@
package io.invertase.firebase.firestore;
import android.os.AsyncTask;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.WritableMap;
import com.google.firebase.firestore.QuerySnapshot;
import java.lang.ref.WeakReference;
class QuerySnapshotSerializeAsyncTask extends AsyncTask<Object, Void, WritableMap> {
private WeakReference<ReactContext> reactContextWeakReference;
private WeakReference<RNFirebaseFirestoreCollectionReference> referenceWeakReference;
QuerySnapshotSerializeAsyncTask(
ReactContext context,
RNFirebaseFirestoreCollectionReference reference
) {
referenceWeakReference = new WeakReference<>(reference);
reactContextWeakReference = new WeakReference<>(context);
}
@Override
protected final WritableMap doInBackground(Object... params) {
QuerySnapshot querySnapshot = (QuerySnapshot) params[0];
try {
return FirestoreSerialize.snapshotToWritableMap(querySnapshot);
} catch (RuntimeException e) {
if (isAvailable()) {
reactContextWeakReference
.get()
.handleException(e);
} else {
throw e;
}
return null;
}
}
@Override
protected void onPostExecute(WritableMap writableMap) {
// do nothing as overridden on usage
}
private Boolean isAvailable() {
return reactContextWeakReference.get() != null && referenceWeakReference.get() != null;
}
}

View File

@ -18,13 +18,13 @@ 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.firestore.FirebaseFirestoreSettings;
import com.google.firebase.firestore.Transaction;
import com.google.firebase.firestore.DocumentReference;
import com.google.firebase.firestore.FieldValue;
import com.google.firebase.firestore.FirebaseFirestore;
import com.google.firebase.firestore.FirebaseFirestoreException;
import com.google.firebase.firestore.FirebaseFirestoreSettings;
import com.google.firebase.firestore.SetOptions;
import com.google.firebase.firestore.Transaction;
import com.google.firebase.firestore.WriteBatch;
import java.util.HashMap;
@ -34,7 +34,6 @@ import java.util.Map;
import io.invertase.firebase.ErrorUtils;
import io.invertase.firebase.Utils;
public class RNFirebaseFirestore extends ReactContextBaseJavaModule {
private static final String TAG = "RNFirebaseFirestore";
private SparseArray<RNFirebaseFirestoreTransactionHandler> transactionHandlers = new SparseArray<>();
@ -48,20 +47,208 @@ public class RNFirebaseFirestore extends ReactContextBaseJavaModule {
* REACT NATIVE METHODS
*/
/**
* Generates a js-like error from an exception and rejects the provided promise with it.
*
* @param exception Exception Exception normally from a task result.
* @param promise Promise react native promise
*/
static void promiseRejectException(Promise promise, FirebaseFirestoreException exception) {
WritableMap jsError = getJSError(exception);
promise.reject(
jsError.getString("code"),
jsError.getString("message"),
exception
);
}
/**
* Get a database instance for a specific firebase app instance
*
* @param appName appName
* @return FirebaseFirestore
*/
static FirebaseFirestore getFirestoreForApp(String appName) {
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
return FirebaseFirestore.getInstance(firebaseApp);
}
/**
* Convert as firebase DatabaseError instance into a writable map
* with the correct web-like error codes.
*
* @param nativeException nativeException
* @return WritableMap
*/
static WritableMap getJSError(FirebaseFirestoreException nativeException) {
WritableMap errorMap = Arguments.createMap();
errorMap.putInt(
"nativeErrorCode",
nativeException
.getCode()
.value()
);
errorMap.putString("nativeErrorMessage", nativeException.getMessage());
String code;
String message;
String service = "Firestore";
// TODO: Proper error mappings
switch (nativeException.getCode()) {
case OK:
code = ErrorUtils.getCodeWithService(service, "ok");
message = ErrorUtils.getMessageWithService("Ok.", service, code);
break;
case CANCELLED:
code = ErrorUtils.getCodeWithService(service, "cancelled");
message = ErrorUtils.getMessageWithService("The operation was cancelled.", service, code);
break;
case UNKNOWN:
code = ErrorUtils.getCodeWithService(service, "unknown");
message = ErrorUtils.getMessageWithService(
"Unknown error or an error from a different error domain.",
service,
code
);
break;
case INVALID_ARGUMENT:
code = ErrorUtils.getCodeWithService(service, "invalid-argument");
message = ErrorUtils.getMessageWithService(
"Client specified an invalid argument.",
service,
code
);
break;
case DEADLINE_EXCEEDED:
code = ErrorUtils.getCodeWithService(service, "deadline-exceeded");
message = ErrorUtils.getMessageWithService(
"Deadline expired before operation could complete.",
service,
code
);
break;
case NOT_FOUND:
code = ErrorUtils.getCodeWithService(service, "not-found");
message = ErrorUtils.getMessageWithService(
"Some requested document was not found.",
service,
code
);
break;
case ALREADY_EXISTS:
code = ErrorUtils.getCodeWithService(service, "already-exists");
message = ErrorUtils.getMessageWithService(
"Some document that we attempted to create already exists.",
service,
code
);
break;
case PERMISSION_DENIED:
code = ErrorUtils.getCodeWithService(service, "permission-denied");
message = ErrorUtils.getMessageWithService(
"The caller does not have permission to execute the specified operation.",
service,
code
);
break;
case RESOURCE_EXHAUSTED:
code = ErrorUtils.getCodeWithService(service, "resource-exhausted");
message = ErrorUtils.getMessageWithService(
"Some resource has been exhausted, perhaps a per-user quota, or perhaps the entire file system is out of space.",
service,
code
);
break;
case FAILED_PRECONDITION:
code = ErrorUtils.getCodeWithService(service, "failed-precondition");
message = ErrorUtils.getMessageWithService(
"Operation was rejected because the system is not in a state required for the operation`s execution.",
service,
code
);
break;
case ABORTED:
code = ErrorUtils.getCodeWithService(service, "aborted");
message = ErrorUtils.getMessageWithService(
"The operation was aborted, typically due to a concurrency issue like transaction aborts, etc.",
service,
code
);
break;
case OUT_OF_RANGE:
code = ErrorUtils.getCodeWithService(service, "out-of-range");
message = ErrorUtils.getMessageWithService(
"Operation was attempted past the valid range.",
service,
code
);
break;
case UNIMPLEMENTED:
code = ErrorUtils.getCodeWithService(service, "unimplemented");
message = ErrorUtils.getMessageWithService(
"Operation is not implemented or not supported/enabled.",
service,
code
);
break;
case INTERNAL:
code = ErrorUtils.getCodeWithService(service, "internal");
message = ErrorUtils.getMessageWithService("Internal errors.", service, code);
break;
case UNAVAILABLE:
code = ErrorUtils.getCodeWithService(service, "unavailable");
message = ErrorUtils.getMessageWithService(
"The service is currently unavailable.",
service,
code
);
break;
case DATA_LOSS:
code = ErrorUtils.getCodeWithService(service, "data-loss");
message = ErrorUtils.getMessageWithService(
"Unrecoverable data loss or corruption.",
service,
code
);
break;
case UNAUTHENTICATED:
code = ErrorUtils.getCodeWithService(service, "unauthenticated");
message = ErrorUtils.getMessageWithService(
"The request does not have valid authentication credentials for the operation.",
service,
code
);
break;
default:
code = ErrorUtils.getCodeWithService(service, "unknown");
message = ErrorUtils.getMessageWithService("An unknown error occurred.", service, code);
}
errorMap.putString("code", code);
errorMap.putString("message", message);
return errorMap;
}
@ReactMethod
public void disableNetwork(String appName, final Promise promise) {
getFirestoreForApp(appName).disableNetwork().addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
public void onComplete(@NonNull Task<Void> task) {
if (task.isSuccessful()) {
Log.d(TAG, "disableNetwork:onComplete:success");
promise.resolve(null);
} else {
Log.e(TAG, "disableNetwork:onComplete:failure", task.getException());
RNFirebaseFirestore.promiseRejectException(promise, (FirebaseFirestoreException)task.getException());
getFirestoreForApp(appName)
.disableNetwork()
.addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
public void onComplete(@NonNull Task<Void> task) {
if (task.isSuccessful()) {
Log.d(TAG, "disableNetwork:onComplete:success");
promise.resolve(null);
} else {
Log.e(TAG, "disableNetwork:onComplete:failure", task.getException());
RNFirebaseFirestore.promiseRejectException(
promise,
(FirebaseFirestoreException) task.getException()
);
}
}
}
});
});
}
@ReactMethod
@ -75,46 +262,70 @@ public class RNFirebaseFirestore extends ReactContextBaseJavaModule {
@ReactMethod
public void enableNetwork(String appName, final Promise promise) {
getFirestoreForApp(appName).enableNetwork().addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
public void onComplete(@NonNull Task<Void> task) {
if (task.isSuccessful()) {
Log.d(TAG, "enableNetwork:onComplete:success");
promise.resolve(null);
} else {
Log.e(TAG, "enableNetwork:onComplete:failure", task.getException());
RNFirebaseFirestore.promiseRejectException(promise, (FirebaseFirestoreException)task.getException());
getFirestoreForApp(appName)
.enableNetwork()
.addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
public void onComplete(@NonNull Task<Void> task) {
if (task.isSuccessful()) {
Log.d(TAG, "enableNetwork:onComplete:success");
promise.resolve(null);
} else {
Log.e(TAG, "enableNetwork:onComplete:failure", task.getException());
RNFirebaseFirestore.promiseRejectException(
promise,
(FirebaseFirestoreException) task.getException()
);
}
}
}
});
});
}
@ReactMethod
public void collectionGet(String appName, String path, ReadableArray filters,
ReadableArray orders, ReadableMap options, ReadableMap getOptions,
final Promise promise) {
RNFirebaseFirestoreCollectionReference ref = getCollectionForAppPath(appName, path, filters, orders, options);
public void collectionGet(
String appName, String path, ReadableArray filters,
ReadableArray orders, ReadableMap options, ReadableMap getOptions,
final Promise promise
) {
RNFirebaseFirestoreCollectionReference ref = getCollectionForAppPath(
appName,
path,
filters,
orders,
options
);
ref.get(getOptions, promise);
}
@ReactMethod
public void collectionOffSnapshot(String appName, String path, ReadableArray filters,
ReadableArray orders, ReadableMap options, String listenerId) {
public void collectionOffSnapshot(
String appName, String path, ReadableArray filters,
ReadableArray orders, ReadableMap options, String listenerId
) {
RNFirebaseFirestoreCollectionReference.offSnapshot(listenerId);
}
@ReactMethod
public void collectionOnSnapshot(String appName, String path, ReadableArray filters,
ReadableArray orders, ReadableMap options, String listenerId,
ReadableMap queryListenOptions) {
RNFirebaseFirestoreCollectionReference ref = getCollectionForAppPath(appName, path, filters, orders, options);
public void collectionOnSnapshot(
String appName, String path, ReadableArray filters,
ReadableArray orders, ReadableMap options, String listenerId,
ReadableMap queryListenOptions
) {
RNFirebaseFirestoreCollectionReference ref = getCollectionForAppPath(
appName,
path,
filters,
orders,
options
);
ref.onSnapshot(listenerId, queryListenOptions);
}
@ReactMethod
public void documentBatch(final String appName, final ReadableArray writes,
final Promise promise) {
public void documentBatch(
final String appName, final ReadableArray writes,
final Promise promise
) {
FirebaseFirestore firestore = getFirestoreForApp(appName);
WriteBatch batch = firestore.batch();
final List<Object> writesArray = FirestoreSerialize.parseDocumentBatches(firestore, writes);
@ -145,18 +356,23 @@ public class RNFirebaseFirestore extends ReactContextBaseJavaModule {
}
}
batch.commit().addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
public void onComplete(@NonNull Task<Void> task) {
if (task.isSuccessful()) {
Log.d(TAG, "documentBatch:onComplete:success");
promise.resolve(null);
} else {
Log.e(TAG, "documentBatch:onComplete:failure", task.getException());
RNFirebaseFirestore.promiseRejectException(promise, (FirebaseFirestoreException) task.getException());
batch
.commit()
.addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
public void onComplete(@NonNull Task<Void> task) {
if (task.isSuccessful()) {
Log.d(TAG, "documentBatch:onComplete:success");
promise.resolve(null);
} else {
Log.e(TAG, "documentBatch:onComplete:failure", task.getException());
RNFirebaseFirestore.promiseRejectException(
promise,
(FirebaseFirestoreException) task.getException()
);
}
}
}
});
});
}
@ReactMethod
@ -166,7 +382,12 @@ public class RNFirebaseFirestore extends ReactContextBaseJavaModule {
}
@ReactMethod
public void documentGet(String appName, String path, ReadableMap getOptions, final Promise promise) {
public void documentGet(
String appName,
String path,
ReadableMap getOptions,
final Promise promise
) {
RNFirebaseFirestoreDocumentReference ref = getDocumentForAppPath(appName, path);
ref.get(getOptions, promise);
}
@ -177,18 +398,31 @@ public class RNFirebaseFirestore extends ReactContextBaseJavaModule {
}
@ReactMethod
public void documentOnSnapshot(String appName, String path, String listenerId,
ReadableMap docListenOptions) {
public void documentOnSnapshot(
String appName, String path, String listenerId,
ReadableMap docListenOptions
) {
RNFirebaseFirestoreDocumentReference ref = getDocumentForAppPath(appName, path);
ref.onSnapshot(listenerId, docListenOptions);
}
@ReactMethod
public void documentSet(String appName, String path, ReadableMap data, ReadableMap options, final Promise promise) {
public void documentSet(
String appName,
String path,
ReadableMap data,
ReadableMap options,
final Promise promise
) {
RNFirebaseFirestoreDocumentReference ref = getDocumentForAppPath(appName, path);
ref.set(data, options, promise);
}
/*
* Transaction Methods
*/
@ReactMethod
public void documentUpdate(String appName, String path, ReadableMap data, final Promise promise) {
RNFirebaseFirestoreDocumentReference ref = getDocumentForAppPath(appName, path);
@ -202,30 +436,35 @@ public class RNFirebaseFirestore extends ReactContextBaseJavaModule {
if (settings.hasKey("host")) {
firestoreSettings.setHost(settings.getString("host"));
} else {
firestoreSettings.setHost(firestore.getFirestoreSettings().getHost());
firestoreSettings.setHost(firestore
.getFirestoreSettings()
.getHost());
}
if (settings.hasKey("persistence")) {
firestoreSettings.setPersistenceEnabled(settings.getBoolean("persistence"));
} else {
firestoreSettings.setPersistenceEnabled(firestore.getFirestoreSettings().isPersistenceEnabled());
firestoreSettings.setPersistenceEnabled(firestore
.getFirestoreSettings()
.isPersistenceEnabled());
}
if (settings.hasKey("ssl")) {
firestoreSettings.setSslEnabled(settings.getBoolean("ssl"));
} else {
firestoreSettings.setSslEnabled(firestore.getFirestoreSettings().isSslEnabled());
}
if (settings.hasKey("timestampsInSnapshots")) {
// TODO: Not supported on Android yet
firestoreSettings.setSslEnabled(firestore
.getFirestoreSettings()
.isSslEnabled());
}
// if (settings.hasKey("timestampsInSnapshots")) {
// // TODO: Not supported on Android yet
// }
firestore.setFirestoreSettings(firestoreSettings.build());
promise.resolve(null);
}
/**
* Try clean up previous transactions on reload
*
*/
@Override
public void onCatalystInstanceDestroy() {
@ -239,23 +478,22 @@ public class RNFirebaseFirestore extends ReactContextBaseJavaModule {
transactionHandlers.clear();
}
/*
* Transaction Methods
*/
/**
* Calls the internal Firestore Transaction classes instance .get(ref) method and resolves with
* the DocumentSnapshot.
*
* @param appName
* @param transactionId
* @param path
* @param promise
* @param appName appName
* @param transactionId transactionId
* @param path path
* @param promise promise
*/
@ReactMethod
public void transactionGetDocument(String appName, int transactionId, String path, final Promise promise) {
public void transactionGetDocument(
String appName,
int transactionId,
String path,
final Promise promise
) {
RNFirebaseFirestoreTransactionHandler handler = transactionHandlers.get(transactionId);
if (handler == null) {
@ -269,11 +507,16 @@ public class RNFirebaseFirestore extends ReactContextBaseJavaModule {
}
}
/*
* INTERNALS/UTILS
*/
/**
* Aborts any pending signals and deletes the transaction handler.
*
* @param appName
* @param transactionId
* @param appName appName
* @param transactionId transactionId
*/
@ReactMethod
public void transactionDispose(String appName, int transactionId) {
@ -288,12 +531,16 @@ public class RNFirebaseFirestore extends ReactContextBaseJavaModule {
/**
* Signals to transactionHandler that the command buffer is ready.
*
* @param appName
* @param transactionId
* @param commandBuffer
* @param appName appName
* @param transactionId transactionId
* @param commandBuffer commandBuffer
*/
@ReactMethod
public void transactionApplyBuffer(String appName, int transactionId, ReadableArray commandBuffer) {
public void transactionApplyBuffer(
String appName,
int transactionId,
ReadableArray commandBuffer
) {
RNFirebaseFirestoreTransactionHandler handler = transactionHandlers.get(transactionId);
if (handler != null) {
@ -304,12 +551,16 @@ public class RNFirebaseFirestore extends ReactContextBaseJavaModule {
/**
* Begin a new transaction via AsyncTask 's
*
* @param appName
* @param transactionId
* @param appName appName
* @param transactionId transactionId
*/
@ReactMethod
public void transactionBegin(final String appName, int transactionId) {
final RNFirebaseFirestoreTransactionHandler transactionHandler = new RNFirebaseFirestoreTransactionHandler(appName, transactionId);
final RNFirebaseFirestoreTransactionHandler transactionHandler = new RNFirebaseFirestoreTransactionHandler(
appName,
transactionId
);
transactionHandlers.put(transactionId, transactionHandler);
AsyncTask.execute(new Runnable() {
@ -327,7 +578,11 @@ public class RNFirebaseFirestore extends ReactContextBaseJavaModule {
@Override
public void run() {
WritableMap eventMap = transactionHandler.createEventMap(null, "update");
Utils.sendEvent(getReactApplicationContext(), "firestore_transaction_event", eventMap);
Utils.sendEvent(
getReactApplicationContext(),
"firestore_transaction_event",
eventMap
);
}
});
@ -336,12 +591,18 @@ public class RNFirebaseFirestore extends ReactContextBaseJavaModule {
// exit early if aborted - has to throw an exception otherwise will just keep trying ...
if (transactionHandler.aborted) {
throw new FirebaseFirestoreException("abort", FirebaseFirestoreException.Code.ABORTED);
throw new FirebaseFirestoreException(
"abort",
FirebaseFirestoreException.Code.ABORTED
);
}
// exit early if timeout from bridge - has to throw an exception otherwise will just keep trying ...
if (transactionHandler.timeout) {
throw new FirebaseFirestoreException("timeout", FirebaseFirestoreException.Code.DEADLINE_EXCEEDED);
throw new FirebaseFirestoreException(
"timeout",
FirebaseFirestoreException.Code.DEADLINE_EXCEEDED
);
}
// process any buffered commands from JS land
@ -357,15 +618,19 @@ public class RNFirebaseFirestore extends ReactContextBaseJavaModule {
ReadableMap command = buffer.getMap(i);
String path = command.getString("path");
String type = command.getString("type");
RNFirebaseFirestoreDocumentReference documentReference = getDocumentForAppPath(appName, path);
RNFirebaseFirestoreDocumentReference documentReference = getDocumentForAppPath(
appName,
path
);
switch (type) {
case "set":
data = command.getMap("data");
ReadableMap options = command.getMap("options");
Map<String, Object> setData = FirestoreSerialize.parseReadableMap(RNFirebaseFirestore.getFirestoreForApp(appName), data);
Map<String, Object> setData = FirestoreSerialize.parseReadableMap(
RNFirebaseFirestore.getFirestoreForApp(appName),
data
);
if (options != null && options.hasKey("merge") && options.getBoolean("merge")) {
transaction.set(documentReference.getRef(), setData, SetOptions.merge());
@ -376,7 +641,11 @@ public class RNFirebaseFirestore extends ReactContextBaseJavaModule {
case "update":
data = command.getMap("data");
Map<String, Object> updateData = FirestoreSerialize.parseReadableMap(RNFirebaseFirestore.getFirestoreForApp(appName), data);
Map<String, Object> updateData = FirestoreSerialize.parseReadableMap(
RNFirebaseFirestore.getFirestoreForApp(appName),
data
);
transaction.update(documentReference.getRef(), updateData);
break;
case "delete":
@ -396,7 +665,11 @@ public class RNFirebaseFirestore extends ReactContextBaseJavaModule {
if (!transactionHandler.aborted) {
Log.d(TAG, "Transaction onSuccess!");
WritableMap eventMap = transactionHandler.createEventMap(null, "complete");
Utils.sendEvent(getReactApplicationContext(), "firestore_transaction_event", eventMap);
Utils.sendEvent(
getReactApplicationContext(),
"firestore_transaction_event",
eventMap
);
}
}
})
@ -405,8 +678,15 @@ public class RNFirebaseFirestore extends ReactContextBaseJavaModule {
public void onFailure(@NonNull Exception e) {
if (!transactionHandler.aborted) {
Log.w(TAG, "Transaction onFailure.", e);
WritableMap eventMap = transactionHandler.createEventMap((FirebaseFirestoreException) e, "error");
Utils.sendEvent(getReactApplicationContext(), "firestore_transaction_event", eventMap);
WritableMap eventMap = transactionHandler.createEventMap(
(FirebaseFirestoreException) e,
"error"
);
Utils.sendEvent(
getReactApplicationContext(),
"firestore_transaction_event",
eventMap
);
}
}
});
@ -414,158 +694,44 @@ public class RNFirebaseFirestore extends ReactContextBaseJavaModule {
});
}
/*
* INTERNALS/UTILS
*/
/**
* Generates a js-like error from an exception and rejects the provided promise with it.
*
* @param exception Exception Exception normally from a task result.
* @param promise Promise react native promise
*/
static void promiseRejectException(Promise promise, FirebaseFirestoreException exception) {
WritableMap jsError = getJSError(exception);
promise.reject(
jsError.getString("code"),
jsError.getString("message"),
exception
);
}
/**
* Get a database instance for a specific firebase app instance
*
* @param appName
* @return
*/
static FirebaseFirestore getFirestoreForApp(String appName) {
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
return FirebaseFirestore.getInstance(firebaseApp);
}
/**
* Get a collection reference for a specific app and path
*
* @param appName
* @param filters
* @param orders
* @param options
* @param appName appName
* @param filters filters
* @param orders orders
* @param options options
* @param path @return
*/
private RNFirebaseFirestoreCollectionReference getCollectionForAppPath(String appName, String path,
ReadableArray filters,
ReadableArray orders,
ReadableMap options) {
return new RNFirebaseFirestoreCollectionReference(this.getReactApplicationContext(), appName, path, filters, orders, options);
private RNFirebaseFirestoreCollectionReference getCollectionForAppPath(
String appName, String path,
ReadableArray filters,
ReadableArray orders,
ReadableMap options
) {
return new RNFirebaseFirestoreCollectionReference(
this.getReactApplicationContext(),
appName,
path,
filters,
orders,
options
);
}
/**
* Get a document reference for a specific app and path
*
* @param appName
* @param path
* @return
* @param appName appName
* @param path path
* @return RNFirebaseFirestoreDocumentReference
*/
private RNFirebaseFirestoreDocumentReference getDocumentForAppPath(String appName, String path) {
return new RNFirebaseFirestoreDocumentReference(this.getReactApplicationContext(), appName, path);
}
/**
* Convert as firebase DatabaseError instance into a writable map
* with the correct web-like error codes.
*
* @param nativeException
* @return
*/
static WritableMap getJSError(FirebaseFirestoreException nativeException) {
WritableMap errorMap = Arguments.createMap();
errorMap.putInt("nativeErrorCode", nativeException.getCode().value());
errorMap.putString("nativeErrorMessage", nativeException.getMessage());
String code;
String message;
String service = "Firestore";
// TODO: Proper error mappings
switch (nativeException.getCode()) {
case OK:
code = ErrorUtils.getCodeWithService(service, "ok");
message = ErrorUtils.getMessageWithService("Ok.", service, code);
break;
case CANCELLED:
code = ErrorUtils.getCodeWithService(service, "cancelled");
message = ErrorUtils.getMessageWithService("The operation was cancelled.", service, code);
break;
case UNKNOWN:
code = ErrorUtils.getCodeWithService(service, "unknown");
message = ErrorUtils.getMessageWithService("Unknown error or an error from a different error domain.", service, code);
break;
case INVALID_ARGUMENT:
code = ErrorUtils.getCodeWithService(service, "invalid-argument");
message = ErrorUtils.getMessageWithService("Client specified an invalid argument.", service, code);
break;
case DEADLINE_EXCEEDED:
code = ErrorUtils.getCodeWithService(service, "deadline-exceeded");
message = ErrorUtils.getMessageWithService("Deadline expired before operation could complete.", service, code);
break;
case NOT_FOUND:
code = ErrorUtils.getCodeWithService(service, "not-found");
message = ErrorUtils.getMessageWithService("Some requested document was not found.", service, code);
break;
case ALREADY_EXISTS:
code = ErrorUtils.getCodeWithService(service, "already-exists");
message = ErrorUtils.getMessageWithService("Some document that we attempted to create already exists.", service, code);
break;
case PERMISSION_DENIED:
code = ErrorUtils.getCodeWithService(service, "permission-denied");
message = ErrorUtils.getMessageWithService("The caller does not have permission to execute the specified operation.", service, code);
break;
case RESOURCE_EXHAUSTED:
code = ErrorUtils.getCodeWithService(service, "resource-exhausted");
message = ErrorUtils.getMessageWithService("Some resource has been exhausted, perhaps a per-user quota, or perhaps the entire file system is out of space.", service, code);
break;
case FAILED_PRECONDITION:
code = ErrorUtils.getCodeWithService(service, "failed-precondition");
message = ErrorUtils.getMessageWithService("Operation was rejected because the system is not in a state required for the operation`s execution.", service, code);
break;
case ABORTED:
code = ErrorUtils.getCodeWithService(service, "aborted");
message = ErrorUtils.getMessageWithService("The operation was aborted, typically due to a concurrency issue like transaction aborts, etc.", service, code);
break;
case OUT_OF_RANGE:
code = ErrorUtils.getCodeWithService(service, "out-of-range");
message = ErrorUtils.getMessageWithService("Operation was attempted past the valid range.", service, code);
break;
case UNIMPLEMENTED:
code = ErrorUtils.getCodeWithService(service, "unimplemented");
message = ErrorUtils.getMessageWithService("Operation is not implemented or not supported/enabled.", service, code);
break;
case INTERNAL:
code = ErrorUtils.getCodeWithService(service, "internal");
message = ErrorUtils.getMessageWithService("Internal errors.", service, code);
break;
case UNAVAILABLE:
code = ErrorUtils.getCodeWithService(service, "unavailable");
message = ErrorUtils.getMessageWithService("The service is currently unavailable.", service, code);
break;
case DATA_LOSS:
code = ErrorUtils.getCodeWithService(service, "data-loss");
message = ErrorUtils.getMessageWithService("Unrecoverable data loss or corruption.", service, code);
break;
case UNAUTHENTICATED:
code = ErrorUtils.getCodeWithService(service, "unauthenticated");
message = ErrorUtils.getMessageWithService("The request does not have valid authentication credentials for the operation.", service, code);
break;
default:
code = ErrorUtils.getCodeWithService(service, "unknown");
message = ErrorUtils.getMessageWithService("An unknown error occurred.", service, code);
}
errorMap.putString("code", code);
errorMap.putString("message", message);
return errorMap;
return new RNFirebaseFirestoreDocumentReference(
this.getReactApplicationContext(),
appName,
path
);
}
/**
@ -586,8 +752,18 @@ public class RNFirebaseFirestore extends ReactContextBaseJavaModule {
@Override
public Map<String, Object> getConstants() {
final Map<String, Object> constants = new HashMap<>();
constants.put("deleteFieldValue", FieldValue.delete().toString());
constants.put("serverTimestampFieldValue", FieldValue.serverTimestamp().toString());
constants.put(
"deleteFieldValue",
FieldValue
.delete()
.toString()
);
constants.put(
"serverTimestampFieldValue",
FieldValue
.serverTimestamp()
.toString()
);
return constants;
}
}

View File

@ -1,6 +1,7 @@
package io.invertase.firebase.firestore;
import android.annotation.SuppressLint;
import android.support.annotation.NonNull;
import android.util.Log;
@ -22,28 +23,32 @@ import com.google.firebase.firestore.Query;
import com.google.firebase.firestore.QuerySnapshot;
import com.google.firebase.firestore.Source;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import io.invertase.firebase.Utils;
public class RNFirebaseFirestoreCollectionReference {
class RNFirebaseFirestoreCollectionReference {
private static final String TAG = "RNFSCollectionReference";
private static Map<String, ListenerRegistration> collectionSnapshotListeners = new HashMap<>();
private final String appName;
private final String path;
private final ReadableArray filters;
private final ReadableArray orders;
private final ReadableMap options;
private final Query query;
private final String appName;
private final ReadableMap options;
private final ReadableArray orders;
private final ReadableArray filters;
private ReactContext reactContext;
RNFirebaseFirestoreCollectionReference(ReactContext reactContext, String appName, String path,
ReadableArray filters, ReadableArray orders,
ReadableMap options) {
RNFirebaseFirestoreCollectionReference(
ReactContext reactContext,
String appName,
String path,
ReadableArray filters,
ReadableArray orders,
ReadableMap options
) {
this.appName = appName;
this.path = path;
this.filters = filters;
@ -53,6 +58,13 @@ public class RNFirebaseFirestoreCollectionReference {
this.reactContext = reactContext;
}
static void offSnapshot(final String listenerId) {
ListenerRegistration listenerRegistration = collectionSnapshotListeners.remove(listenerId);
if (listenerRegistration != null) {
listenerRegistration.remove();
}
}
void get(ReadableMap getOptions, final Promise promise) {
Source source;
if (getOptions != null && getOptions.hasKey("source")) {
@ -67,29 +79,36 @@ public class RNFirebaseFirestoreCollectionReference {
} else {
source = Source.DEFAULT;
}
query.get(source).addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
@SuppressLint("StaticFieldLeak") final QuerySnapshotSerializeAsyncTask serializeAsyncTask = new QuerySnapshotSerializeAsyncTask(
reactContext, this
) {
@Override
public void onComplete(@NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
Log.d(TAG, "get:onComplete:success");
WritableMap data = FirestoreSerialize.snapshotToWritableMap(task.getResult());
promise.resolve(data);
} else {
Log.e(TAG, "get:onComplete:failure", task.getException());
RNFirebaseFirestore.promiseRejectException(promise, (FirebaseFirestoreException)task.getException());
}
protected void onPostExecute(WritableMap writableMap) {
promise.resolve(writableMap);
}
});
};
query
.get(source)
.addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
@Override
public void onComplete(@NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
Log.d(TAG, "get:onComplete:success");
serializeAsyncTask.execute(task.getResult());
} else {
Log.e(TAG, "get:onComplete:failure", task.getException());
RNFirebaseFirestore.promiseRejectException(
promise,
(FirebaseFirestoreException) task.getException()
);
}
}
});
}
public static void offSnapshot(final String listenerId) {
ListenerRegistration listenerRegistration = collectionSnapshotListeners.remove(listenerId);
if (listenerRegistration != null) {
listenerRegistration.remove();
}
}
public void onSnapshot(final String listenerId, final ReadableMap queryListenOptions) {
void onSnapshot(final String listenerId, final ReadableMap queryListenOptions) {
if (!collectionSnapshotListeners.containsKey(listenerId)) {
final EventListener<QuerySnapshot> listener = new EventListener<QuerySnapshot>() {
@Override
@ -97,7 +116,8 @@ public class RNFirebaseFirestoreCollectionReference {
if (exception == null) {
handleQuerySnapshotEvent(listenerId, querySnapshot);
} else {
ListenerRegistration listenerRegistration = collectionSnapshotListeners.remove(listenerId);
ListenerRegistration listenerRegistration = collectionSnapshotListeners.remove(
listenerId);
if (listenerRegistration != null) {
listenerRegistration.remove();
}
@ -115,7 +135,10 @@ public class RNFirebaseFirestoreCollectionReference {
metadataChanges = MetadataChanges.EXCLUDE;
}
ListenerRegistration listenerRegistration = this.query.addSnapshotListener(metadataChanges, listener);
ListenerRegistration listenerRegistration = this.query.addSnapshotListener(
metadataChanges,
listener
);
collectionSnapshotListeners.put(listenerId, listenerRegistration);
}
}
@ -170,7 +193,7 @@ public class RNFirebaseFirestoreCollectionReference {
} else {
ReadableArray fieldPathElements = fieldPathMap.getArray("elements");
String[] fieldPathArray = new String[fieldPathElements.size()];
for (int j=0; j<fieldPathElements.size(); j++) {
for (int j = 0; j < fieldPathElements.size(); j++) {
fieldPathArray[j] = fieldPathElements.getString(j);
}
FieldPath fieldPath = FieldPath.of(fieldPathArray);
@ -202,13 +225,13 @@ public class RNFirebaseFirestoreCollectionReference {
Map<String, Object> order = (Map) o;
String direction = (String) order.get("direction");
Map<String, Object> fieldPathMap = (Map) order.get("fieldPath");
String fieldPathType = (String)fieldPathMap.get("type");
String fieldPathType = (String) fieldPathMap.get("type");
if (fieldPathType.equals("string")) {
String fieldPath = (String)fieldPathMap.get("string");
String fieldPath = (String) fieldPathMap.get("string");
query = query.orderBy(fieldPath, Query.Direction.valueOf(direction));
} else {
List<String> fieldPathElements = (List)fieldPathMap.get("elements");
List<String> fieldPathElements = (List) fieldPathMap.get("elements");
FieldPath fieldPath = FieldPath.of(fieldPathElements.toArray(new String[fieldPathElements.size()]));
query = query.orderBy(fieldPath, Query.Direction.valueOf(direction));
}
@ -218,57 +241,79 @@ public class RNFirebaseFirestoreCollectionReference {
private Query applyOptions(FirebaseFirestore firestore, Query query) {
if (options.hasKey("endAt")) {
List<Object> endAtList = FirestoreSerialize.parseReadableArray(firestore, options.getArray("endAt"));
List<Object> endAtList = FirestoreSerialize.parseReadableArray(
firestore,
options.getArray("endAt")
);
query = query.endAt(endAtList.toArray());
}
if (options.hasKey("endBefore")) {
List<Object> endBeforeList = FirestoreSerialize.parseReadableArray(firestore, options.getArray("endBefore"));
List<Object> endBeforeList = FirestoreSerialize.parseReadableArray(
firestore,
options.getArray("endBefore")
);
query = query.endBefore(endBeforeList.toArray());
}
if (options.hasKey("limit")) {
int limit = options.getInt("limit");
query = query.limit(limit);
}
if (options.hasKey("offset")) {
// Android doesn't support offset
}
if (options.hasKey("selectFields")) {
// Android doesn't support selectFields
}
// if (options.hasKey("offset")) {
// Android doesn't support offset
// }
// if (options.hasKey("selectFields")) {
// Android doesn't support selectFields
// }
if (options.hasKey("startAfter")) {
List<Object> startAfterList= FirestoreSerialize.parseReadableArray(firestore, options.getArray("startAfter"));
List<Object> startAfterList = FirestoreSerialize.parseReadableArray(
firestore,
options.getArray("startAfter")
);
query = query.startAfter(startAfterList.toArray());
}
if (options.hasKey("startAt")) {
List<Object> startAtList= FirestoreSerialize.parseReadableArray(firestore, options.getArray("startAt"));
List<Object> startAtList = FirestoreSerialize.parseReadableArray(
firestore,
options.getArray("startAt")
);
query = query.startAt(startAtList.toArray());
}
return query;
}
/**
* Handles documentSnapshot events.
*
* @param listenerId
* @param querySnapshot
* @param listenerId id
* @param querySnapshot snapshot
*/
private void handleQuerySnapshotEvent(String listenerId, QuerySnapshot querySnapshot) {
WritableMap event = Arguments.createMap();
WritableMap data = FirestoreSerialize.snapshotToWritableMap(querySnapshot);
private void handleQuerySnapshotEvent(final String listenerId, QuerySnapshot querySnapshot) {
@SuppressLint("StaticFieldLeak") final QuerySnapshotSerializeAsyncTask serializeAsyncTask = new QuerySnapshotSerializeAsyncTask(
reactContext, this
) {
@Override
protected void onPostExecute(WritableMap writableMap) {
WritableMap event = Arguments.createMap();
event.putString("path", path);
event.putString("appName", appName);
event.putString("listenerId", listenerId);
event.putMap("querySnapshot", writableMap);
Utils.sendEvent(reactContext, "firestore_collection_sync_event", event);
}
};
event.putString("appName", appName);
event.putString("path", path);
event.putString("listenerId", listenerId);
event.putMap("querySnapshot", data);
Utils.sendEvent(reactContext, "firestore_collection_sync_event", event);
serializeAsyncTask.execute(querySnapshot);
}
/**
* Handles a documentSnapshot error event
*
* @param listenerId
* @param exception
* @param listenerId id
* @param exception exception
*/
private void handleQuerySnapshotError(String listenerId, FirebaseFirestoreException exception) {
WritableMap event = Arguments.createMap();

View File

@ -1,5 +1,6 @@
package io.invertase.firebase.firestore;
import android.annotation.SuppressLint;
import android.support.annotation.NonNull;
import android.util.Log;
@ -31,33 +32,48 @@ public class RNFirebaseFirestoreDocumentReference {
private final String appName;
private final String path;
private ReactContext reactContext;
private final DocumentReference ref;
private ReactContext reactContext;
RNFirebaseFirestoreDocumentReference(ReactContext reactContext, String appName, String path) {
this.appName = appName;
this.path = path;
this.appName = appName;
this.reactContext = reactContext;
this.ref = RNFirebaseFirestore.getFirestoreForApp(appName).document(path);
this.ref = RNFirebaseFirestore
.getFirestoreForApp(appName)
.document(path);
}
public void delete(final Promise promise) {
this.ref.delete().addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
public void onComplete(@NonNull Task<Void> task) {
if (task.isSuccessful()) {
Log.d(TAG, "delete:onComplete:success");
promise.resolve(null);
} else {
Log.e(TAG, "delete:onComplete:failure", task.getException());
RNFirebaseFirestore.promiseRejectException(promise, (FirebaseFirestoreException)task.getException());
static void offSnapshot(final String listenerId) {
ListenerRegistration listenerRegistration = documentSnapshotListeners.remove(listenerId);
if (listenerRegistration != null) {
listenerRegistration.remove();
}
}
void delete(final Promise promise) {
this.ref
.delete()
.addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
public void onComplete(@NonNull Task<Void> task) {
if (task.isSuccessful()) {
Log.d(TAG, "delete:onComplete:success");
promise.resolve(null);
} else {
Log.e(TAG, "delete:onComplete:failure", task.getException());
RNFirebaseFirestore.promiseRejectException(
promise,
(FirebaseFirestoreException) task.getException()
);
}
}
}
});
});
}
void get(final ReadableMap getOptions, final Promise promise) {
Source source;
if (getOptions != null && getOptions.hasKey("source")) {
String optionsSource = getOptions.getString("source");
if ("server".equals(optionsSource)) {
@ -70,45 +86,59 @@ public class RNFirebaseFirestoreDocumentReference {
} else {
source = Source.DEFAULT;
}
this.ref.get(source).addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
@SuppressLint("StaticFieldLeak") final DocumentSnapshotSerializeAsyncTask serializeAsyncTask = new DocumentSnapshotSerializeAsyncTask(
reactContext, this
) {
@Override
public void onComplete(@NonNull Task<DocumentSnapshot> task) {
if (task.isSuccessful()) {
Log.d(TAG, "get:onComplete:success");
WritableMap data = FirestoreSerialize.snapshotToWritableMap(task.getResult());
promise.resolve(data);
} else {
Log.e(TAG, "get:onComplete:failure", task.getException());
RNFirebaseFirestore.promiseRejectException(promise, (FirebaseFirestoreException)task.getException());
}
protected void onPostExecute(WritableMap writableMap) {
promise.resolve(writableMap);
}
});
};
this.ref
.get(source)
.addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
@Override
public void onComplete(@NonNull Task<DocumentSnapshot> task) {
if (task.isSuccessful()) {
Log.d(TAG, "get:onComplete:success");
serializeAsyncTask.execute(task.getResult());
} else {
Log.e(TAG, "get:onComplete:failure", task.getException());
RNFirebaseFirestore.promiseRejectException(
promise,
(FirebaseFirestoreException) task.getException()
);
}
}
});
}
public static void offSnapshot(final String listenerId) {
ListenerRegistration listenerRegistration = documentSnapshotListeners.remove(listenerId);
if (listenerRegistration != null) {
listenerRegistration.remove();
}
}
public void onSnapshot(final String listenerId, final ReadableMap docListenOptions) {
void onSnapshot(final String listenerId, final ReadableMap docListenOptions) {
if (!documentSnapshotListeners.containsKey(listenerId)) {
final EventListener<DocumentSnapshot> listener = new EventListener<DocumentSnapshot>() {
@Override
public void onEvent(DocumentSnapshot documentSnapshot, FirebaseFirestoreException exception) {
public void onEvent(
DocumentSnapshot documentSnapshot,
FirebaseFirestoreException exception
) {
if (exception == null) {
handleDocumentSnapshotEvent(listenerId, documentSnapshot);
} else {
ListenerRegistration listenerRegistration = documentSnapshotListeners.remove(listenerId);
if (listenerRegistration != null) {
listenerRegistration.remove();
}
handleDocumentSnapshotError(listenerId, exception);
}
}
};
MetadataChanges metadataChanges;
if (docListenOptions != null
&& docListenOptions.hasKey("includeMetadataChanges")
&& docListenOptions.getBoolean("includeMetadataChanges")) {
@ -116,19 +146,30 @@ public class RNFirebaseFirestoreDocumentReference {
} else {
metadataChanges = MetadataChanges.EXCLUDE;
}
ListenerRegistration listenerRegistration = this.ref.addSnapshotListener(metadataChanges, listener);
ListenerRegistration listenerRegistration = this.ref.addSnapshotListener(
metadataChanges,
listener
);
documentSnapshotListeners.put(listenerId, listenerRegistration);
}
}
public void set(final ReadableMap data, final ReadableMap options, final Promise promise) {
Map<String, Object> map = FirestoreSerialize.parseReadableMap(RNFirebaseFirestore.getFirestoreForApp(appName), data);
Task<Void> task;
Map<String, Object> map = FirestoreSerialize.parseReadableMap(
RNFirebaseFirestore.getFirestoreForApp(appName),
data
);
if (options != null && options.hasKey("merge") && options.getBoolean("merge")) {
task = this.ref.set(map, SetOptions.merge());
} else {
task = this.ref.set(map);
}
task.addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
public void onComplete(@NonNull Task<Void> task) {
@ -137,33 +178,45 @@ public class RNFirebaseFirestoreDocumentReference {
promise.resolve(null);
} else {
Log.e(TAG, "set:onComplete:failure", task.getException());
RNFirebaseFirestore.promiseRejectException(promise, (FirebaseFirestoreException)task.getException());
RNFirebaseFirestore.promiseRejectException(
promise,
(FirebaseFirestoreException) task.getException()
);
}
}
});
}
public void update(final ReadableMap data, final Promise promise) {
Map<String, Object> map = FirestoreSerialize.parseReadableMap(RNFirebaseFirestore.getFirestoreForApp(appName), data);
this.ref.update(map).addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
public void onComplete(@NonNull Task<Void> task) {
if (task.isSuccessful()) {
Log.d(TAG, "update:onComplete:success");
promise.resolve(null);
} else {
Log.e(TAG, "update:onComplete:failure", task.getException());
RNFirebaseFirestore.promiseRejectException(promise, (FirebaseFirestoreException)task.getException());
void update(final ReadableMap data, final Promise promise) {
Map<String, Object> map = FirestoreSerialize.parseReadableMap(
RNFirebaseFirestore.getFirestoreForApp(appName),
data
);
this.ref
.update(map)
.addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
public void onComplete(@NonNull Task<Void> task) {
if (task.isSuccessful()) {
Log.d(TAG, "update:onComplete:success");
promise.resolve(null);
} else {
Log.e(TAG, "update:onComplete:failure", task.getException());
RNFirebaseFirestore.promiseRejectException(
promise,
(FirebaseFirestoreException) task.getException()
);
}
}
}
});
});
}
/*
* INTERNALS/UTILS
*/
public DocumentReference getRef() {
DocumentReference getRef() {
return ref;
}
@ -174,32 +227,44 @@ public class RNFirebaseFirestoreDocumentReference {
/**
* Handles documentSnapshot events.
*
* @param listenerId
* @param documentSnapshot
* @param listenerId id
* @param documentSnapshot snapshot
*/
private void handleDocumentSnapshotEvent(String listenerId, DocumentSnapshot documentSnapshot) {
WritableMap event = Arguments.createMap();
WritableMap data = FirestoreSerialize.snapshotToWritableMap(documentSnapshot);
private void handleDocumentSnapshotEvent(
final String listenerId,
DocumentSnapshot documentSnapshot
) {
@SuppressLint("StaticFieldLeak") final DocumentSnapshotSerializeAsyncTask serializeAsyncTask = new DocumentSnapshotSerializeAsyncTask(
reactContext, this
) {
@Override
protected void onPostExecute(WritableMap writableMap) {
WritableMap event = Arguments.createMap();
event.putString("path", path);
event.putString("appName", appName);
event.putString("listenerId", listenerId);
event.putMap("documentSnapshot", writableMap);
Utils.sendEvent(reactContext, "firestore_document_sync_event", event);
}
};
event.putString("appName", appName);
event.putString("path", path);
event.putString("listenerId", listenerId);
event.putMap("documentSnapshot", data);
Utils.sendEvent(reactContext, "firestore_document_sync_event", event);
serializeAsyncTask.execute(documentSnapshot);
}
/**
* Handles a documentSnapshot error event
*
* @param listenerId
* @param exception
* @param listenerId id
* @param exception exception
*/
private void handleDocumentSnapshotError(String listenerId, FirebaseFirestoreException exception) {
private void handleDocumentSnapshotError(
String listenerId,
FirebaseFirestoreException exception
) {
WritableMap event = Arguments.createMap();
event.putString("appName", appName);
event.putString("path", path);
event.putString("appName", appName);
event.putString("listenerId", listenerId);
event.putMap("error", RNFirebaseFirestore.getJSError(exception));

View File

@ -1,7 +1,6 @@
package io.invertase.firebase.firestore;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.UIManagerModule;
@ -29,7 +28,7 @@ public class RNFirebaseFirestorePackage implements ReactPackage {
}
/**
* @param reactContext
* @param reactContext reactContext
* @return a list of view managers that should be registered with {@link UIManagerModule}
*/
@Override

View File

@ -17,17 +17,16 @@ import javax.annotation.Nullable;
class RNFirebaseFirestoreTransactionHandler {
private final ReentrantLock lock;
private final Condition condition;
boolean aborted = false;
boolean timeout = false;
private String appName;
private long timeoutAt;
private int transactionId;
private final ReentrantLock lock;
private final Condition condition;
private ReadableArray commandBuffer;
private Transaction firestoreTransaction;
boolean aborted = false;
boolean timeout = false;
RNFirebaseFirestoreTransactionHandler(String app, int id) {
appName = app;
transactionId = id;

View File

@ -41,7 +41,10 @@ public class RNFirebaseFunctions extends ReactContextBaseJavaModule {
@ReactMethod
public void httpsCallable(final String name, ReadableMap wrapper, final Promise promise) {
Object input = wrapper.toHashMap().get(DATA_KEY);
Object input = wrapper
.toHashMap()
.get(DATA_KEY);
Log.d(TAG, "function:call:input:" + name + ":" + (input != null ? input.toString() : "null"));
HttpsCallableReference httpsCallableReference = FirebaseFunctions
@ -60,10 +63,14 @@ public class RNFirebaseFunctions extends ReactContextBaseJavaModule {
TAG,
"function:call:onSuccess:" + name
);
Log.d(
TAG,
"function:call:onSuccess:result:type:" + name + ":" + (result != null ? result.getClass().getName() : "null")
"function:call:onSuccess:result:type:" + name + ":" + (result != null ? result
.getClass()
.getName() : "null")
);
Log.d(
TAG,
"function:call:onSuccess:result:data:" + name + ":" + (result != null ? result.toString() : "null")
@ -71,7 +78,6 @@ public class RNFirebaseFunctions extends ReactContextBaseJavaModule {
Utils.mapPutValue(DATA_KEY, result, map);
promise.resolve(map);
}
})
.addOnFailureListener(new OnFailureListener() {
@ -87,7 +93,9 @@ public class RNFirebaseFunctions extends ReactContextBaseJavaModule {
if (exception instanceof FirebaseFunctionsException) {
FirebaseFunctionsException ffe = (FirebaseFunctionsException) exception;
details = ffe.getDetails();
code = ffe.getCode().name();
code = ffe
.getCode()
.name();
message = ffe.getLocalizedMessage();
} else {
message = exception.getLocalizedMessage();

View File

@ -1,8 +1,6 @@
package io.invertase.firebase.instanceid;
import java.io.IOException;
import android.util.Log;
import com.facebook.react.bridge.Promise;
@ -28,10 +26,12 @@ public class RNFirebaseInstanceId extends ReactContextBaseJavaModule {
}
@ReactMethod
public void delete(Promise promise){
public void delete(Promise promise) {
try {
Log.d(TAG, "Deleting instance id");
FirebaseInstanceId.getInstance().deleteInstanceId();
FirebaseInstanceId
.getInstance()
.deleteInstanceId();
promise.resolve(null);
} catch (IOException e) {
Log.e(TAG, e.getMessage());
@ -40,15 +40,19 @@ public class RNFirebaseInstanceId extends ReactContextBaseJavaModule {
}
@ReactMethod
public void get(Promise promise){
String id = FirebaseInstanceId.getInstance().getId();
public void get(Promise promise) {
String id = FirebaseInstanceId
.getInstance()
.getId();
promise.resolve(id);
}
@ReactMethod
public void getToken(String authorizedEntity, String scope, Promise promise) {
try {
String token = FirebaseInstanceId.getInstance().getToken(authorizedEntity, scope);
String token = FirebaseInstanceId
.getInstance()
.getToken(authorizedEntity, scope);
Log.d(TAG, "Firebase token for " + authorizedEntity + ": " + token);
promise.resolve(token);
} catch (IOException e) {
@ -59,7 +63,9 @@ public class RNFirebaseInstanceId extends ReactContextBaseJavaModule {
@ReactMethod
public void deleteToken(String authorizedEntity, String scope, Promise promise) {
try {
FirebaseInstanceId.getInstance().deleteToken(authorizedEntity, scope);
FirebaseInstanceId
.getInstance()
.deleteToken(authorizedEntity, scope);
Log.d(TAG, "Firebase token deleted for " + authorizedEntity);
promise.resolve(null);
} catch (IOException e) {

View File

@ -28,7 +28,6 @@ import java.util.HashMap;
import java.util.Map;
import io.invertase.firebase.Utils;
import io.invertase.firebase.links.RNFirebaseLinks;
public class RNFirebaseInvites extends ReactContextBaseJavaModule implements ActivityEventListener, LifecycleEventListener {
private static final String TAG = "RNFirebaseInvites";
@ -58,21 +57,28 @@ public class RNFirebaseInvites extends ReactContextBaseJavaModule implements Act
}
} else {
if (getCurrentActivity() != null) {
FirebaseDynamicLinks.getInstance()
FirebaseDynamicLinks
.getInstance()
.getDynamicLink(getCurrentActivity().getIntent())
.addOnSuccessListener(new OnSuccessListener<PendingDynamicLinkData>() {
@Override
public void onSuccess(PendingDynamicLinkData pendingDynamicLinkData) {
if (pendingDynamicLinkData != null) {
FirebaseAppInvite invite = FirebaseAppInvite.getInvitation(pendingDynamicLinkData);
FirebaseAppInvite invite = FirebaseAppInvite.getInvitation(
pendingDynamicLinkData);
if (invite == null) {
promise.resolve(null);
return;
}
mInitialDeepLink = pendingDynamicLinkData.getLink().toString();
mInitialDeepLink = pendingDynamicLinkData
.getLink()
.toString();
mInitialInvitationId = invite.getInvitationId();
promise.resolve(buildInvitationMap(mInitialDeepLink, mInitialInvitationId));
promise.resolve(buildInvitationMap(
mInitialDeepLink,
mInitialInvitationId
));
} else {
promise.resolve(null);
}
@ -83,7 +89,11 @@ public class RNFirebaseInvites extends ReactContextBaseJavaModule implements Act
@Override
public void onFailure(@NonNull Exception e) {
Log.e(TAG, "getInitialInvitation: failed to resolve invitation", e);
promise.reject("invites/initial-invitation-error", e.getMessage(), e);
promise.reject(
"invites/initial-invitation-error",
e.getMessage(),
e
);
}
});
} else {
@ -96,15 +106,22 @@ public class RNFirebaseInvites extends ReactContextBaseJavaModule implements Act
@ReactMethod
public void sendInvitation(ReadableMap invitationMap, Promise promise) {
if (!invitationMap.hasKey("message")) {
promise.reject("invites/invalid-invitation", "The supplied invitation is missing a 'message' field");
promise.reject(
"invites/invalid-invitation",
"The supplied invitation is missing a 'message' field"
);
return;
}
if (!invitationMap.hasKey("title")) {
promise.reject("invites/invalid-invitation", "The supplied invitation is missing a 'title' field");
promise.reject(
"invites/invalid-invitation",
"The supplied invitation is missing a 'title' field"
);
return;
}
AppInviteInvitation.IntentBuilder ib = new AppInviteInvitation.IntentBuilder(invitationMap.getString("title"));
AppInviteInvitation.IntentBuilder ib = new AppInviteInvitation.IntentBuilder(invitationMap.getString(
"title"));
if (invitationMap.hasKey("androidMinimumVersionCode")) {
Double androidMinimumVersionCode = invitationMap.getDouble("androidMinimumVersionCode");
ib = ib.setAndroidMinimumVersionCode(androidMinimumVersionCode.intValue());
@ -121,7 +138,8 @@ public class RNFirebaseInvites extends ReactContextBaseJavaModule implements Act
if (invitationMap.hasKey("iosClientId")) {
ib = ib.setOtherPlatformsTargetApplication(
AppInviteInvitation.IntentBuilder.PlatformMode.PROJECT_PLATFORM_IOS,
invitationMap.getString("iosClientId"));
invitationMap.getString("iosClientId")
);
}
ib = ib.setMessage(invitationMap.getString("message"));
@ -155,7 +173,9 @@ public class RNFirebaseInvites extends ReactContextBaseJavaModule implements Act
this.mPromise = promise;
// Start the intent
this.getCurrentActivity().startActivityForResult(invitationIntent, REQUEST_INVITE);
this
.getCurrentActivity()
.startActivityForResult(invitationIntent, REQUEST_INVITE);
}
//////////////////////////////////////////////////////////////////////
@ -179,22 +199,33 @@ public class RNFirebaseInvites extends ReactContextBaseJavaModule implements Act
@Override
public void onNewIntent(Intent intent) {
FirebaseDynamicLinks.getInstance()
FirebaseDynamicLinks
.getInstance()
.getDynamicLink(intent)
.addOnSuccessListener(new OnSuccessListener<PendingDynamicLinkData>() {
@Override
public void onSuccess(PendingDynamicLinkData pendingDynamicLinkData) {
if (pendingDynamicLinkData != null) {
FirebaseAppInvite invite = FirebaseAppInvite.getInvitation(pendingDynamicLinkData);
FirebaseAppInvite invite = FirebaseAppInvite.getInvitation(
pendingDynamicLinkData);
if (invite == null) {
// this is a dynamic link, not an invitation
return;
}
String deepLink = pendingDynamicLinkData.getLink().toString();
String deepLink = pendingDynamicLinkData
.getLink()
.toString();
String invitationId = invite.getInvitationId();
WritableMap invitationMap = buildInvitationMap(deepLink, invitationId);
Utils.sendEvent(getReactApplicationContext(), "invites_invitation_received", invitationMap);
WritableMap invitationMap = buildInvitationMap(
deepLink,
invitationId
);
Utils.sendEvent(
getReactApplicationContext(),
"invites_invitation_received",
invitationMap
);
}
}
});

View File

@ -13,17 +13,15 @@ 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.google.android.gms.tasks.OnCompleteListener;
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.appinvite.FirebaseAppInvite;
import com.google.firebase.dynamiclinks.DynamicLink;
import com.google.firebase.dynamiclinks.FirebaseDynamicLinks;
import com.google.firebase.dynamiclinks.ShortDynamicLink;
import com.google.firebase.dynamiclinks.PendingDynamicLinkData;
import com.google.android.gms.tasks.Task;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.OnSuccessListener;
import com.google.android.gms.tasks.OnFailureListener;
import com.google.firebase.dynamiclinks.ShortDynamicLink;
import io.invertase.firebase.Utils;
@ -47,7 +45,10 @@ public class RNFirebaseLinks extends ReactContextBaseJavaModule implements Activ
public void createDynamicLink(final ReadableMap linkData, final Promise promise) {
try {
DynamicLink.Builder builder = getDynamicLinkBuilder(linkData);
String link = builder.buildDynamicLink().getUri().toString();
String link = builder
.buildDynamicLink()
.getUri()
.toString();
Log.d(TAG, "created dynamic link: " + link);
promise.resolve(link);
} catch (Exception ex) {
@ -57,7 +58,11 @@ public class RNFirebaseLinks extends ReactContextBaseJavaModule implements Activ
}
@ReactMethod
public void createShortDynamicLink(final ReadableMap linkData, final String type, final Promise promise) {
public void createShortDynamicLink(
final ReadableMap linkData,
final String type,
final Promise promise
) {
try {
DynamicLink.Builder builder = getDynamicLinkBuilder(linkData);
Task<ShortDynamicLink> shortLinkTask;
@ -70,16 +75,30 @@ public class RNFirebaseLinks extends ReactContextBaseJavaModule implements Activ
}
shortLinkTask.addOnCompleteListener(new OnCompleteListener<ShortDynamicLink>() {
@Override
public void onComplete(@NonNull Task<ShortDynamicLink> task) {
if (task.isSuccessful()) {
String shortLink = task.getResult().getShortLink().toString();
Log.d(TAG, "created short dynamic link: " + shortLink);
promise.resolve(shortLink);
} else {
Log.e(TAG, "create short dynamic link failure " + task.getException().getMessage());
promise.reject("links/failure", task.getException().getMessage(), task.getException());
}
@Override
public void onComplete(@NonNull Task<ShortDynamicLink> task) {
if (task.isSuccessful()) {
String shortLink = task
.getResult()
.getShortLink()
.toString();
Log.d(TAG, "created short dynamic link: " + shortLink);
promise.resolve(shortLink);
} else {
Log.e(
TAG,
"create short dynamic link failure " + task
.getException()
.getMessage()
);
promise.reject(
"links/failure",
task
.getException()
.getMessage(),
task.getException()
);
}
}
});
} catch (Exception ex) {
@ -94,7 +113,8 @@ public class RNFirebaseLinks extends ReactContextBaseJavaModule implements Activ
promise.resolve(mInitialLink);
} else {
if (getCurrentActivity() != null) {
FirebaseDynamicLinks.getInstance()
FirebaseDynamicLinks
.getInstance()
.getDynamicLink(getCurrentActivity().getIntent())
.addOnSuccessListener(new OnSuccessListener<PendingDynamicLinkData>() {
@Override
@ -102,7 +122,9 @@ public class RNFirebaseLinks extends ReactContextBaseJavaModule implements Activ
if (pendingDynamicLinkData != null
&& !isInvitation(pendingDynamicLinkData)) {
mInitialLink = pendingDynamicLinkData.getLink().toString();
mInitialLink = pendingDynamicLinkData
.getLink()
.toString();
}
Log.d(TAG, "getInitialLink: link is: " + mInitialLink);
mInitialLinkInitialized = true;
@ -133,15 +155,22 @@ public class RNFirebaseLinks extends ReactContextBaseJavaModule implements Activ
@Override
public void onNewIntent(Intent intent) {
FirebaseDynamicLinks.getInstance()
FirebaseDynamicLinks
.getInstance()
.getDynamicLink(intent)
.addOnSuccessListener(new OnSuccessListener<PendingDynamicLinkData>() {
@Override
public void onSuccess(PendingDynamicLinkData pendingDynamicLinkData) {
if (pendingDynamicLinkData != null
&& !isInvitation(pendingDynamicLinkData)) {
String link = pendingDynamicLinkData.getLink().toString();
Utils.sendEvent(getReactApplicationContext(), "links_link_received", link);
String link = pendingDynamicLinkData
.getLink()
.toString();
Utils.sendEvent(
getReactApplicationContext(),
"links_link_received",
link
);
}
}
});
@ -175,14 +204,18 @@ public class RNFirebaseLinks extends ReactContextBaseJavaModule implements Activ
// Looks at the internals of the link data to detect whether it's an invitation or not
private boolean isInvitation(PendingDynamicLinkData pendingDynamicLinkData) {
FirebaseAppInvite invite = FirebaseAppInvite.getInvitation(pendingDynamicLinkData);
if (invite != null && invite.getInvitationId() != null && !invite.getInvitationId().isEmpty()) {
if (invite != null && invite.getInvitationId() != null && !invite
.getInvitationId()
.isEmpty()) {
return true;
}
return false;
}
private DynamicLink.Builder getDynamicLinkBuilder(final ReadableMap linkData) {
DynamicLink.Builder builder = FirebaseDynamicLinks.getInstance().createDynamicLink();
DynamicLink.Builder builder = FirebaseDynamicLinks
.getInstance()
.createDynamicLink();
try {
builder.setLink(Uri.parse(linkData.getString("link")));
builder.setDynamicLinkDomain(linkData.getString("dynamicLinkDomain"));
@ -199,7 +232,10 @@ public class RNFirebaseLinks extends ReactContextBaseJavaModule implements Activ
return builder;
}
private void setAnalyticsParameters(final ReadableMap analyticsData, final DynamicLink.Builder builder) {
private void setAnalyticsParameters(
final ReadableMap analyticsData,
final DynamicLink.Builder builder
) {
DynamicLink.GoogleAnalyticsParameters.Builder analyticsParameters = new DynamicLink.GoogleAnalyticsParameters.Builder();
if (analyticsData.hasKey("campaign")) {
@ -220,9 +256,13 @@ public class RNFirebaseLinks extends ReactContextBaseJavaModule implements Activ
builder.setGoogleAnalyticsParameters(analyticsParameters.build());
}
private void setAndroidParameters(final ReadableMap androidData, final DynamicLink.Builder builder) {
private void setAndroidParameters(
final ReadableMap androidData,
final DynamicLink.Builder builder
) {
if (androidData.hasKey("packageName")) {
DynamicLink.AndroidParameters.Builder androidParameters = new DynamicLink.AndroidParameters.Builder(androidData.getString("packageName"));
DynamicLink.AndroidParameters.Builder androidParameters = new DynamicLink.AndroidParameters.Builder(
androidData.getString("packageName"));
if (androidData.hasKey("fallbackUrl")) {
androidParameters.setFallbackUrl(Uri.parse(androidData.getString("fallbackUrl")));
@ -261,7 +301,10 @@ public class RNFirebaseLinks extends ReactContextBaseJavaModule implements Activ
}
}
private void setITunesParameters(final ReadableMap itunesData, final DynamicLink.Builder builder) {
private void setITunesParameters(
final ReadableMap itunesData,
final DynamicLink.Builder builder
) {
DynamicLink.ItunesConnectAnalyticsParameters.Builder itunesParameters = new DynamicLink.ItunesConnectAnalyticsParameters.Builder();
if (itunesData.hasKey("affiliateToken")) {
@ -276,16 +319,23 @@ public class RNFirebaseLinks extends ReactContextBaseJavaModule implements Activ
builder.setItunesConnectAnalyticsParameters(itunesParameters.build());
}
private void setNavigationParameters(final ReadableMap navigationData, final DynamicLink.Builder builder) {
private void setNavigationParameters(
final ReadableMap navigationData,
final DynamicLink.Builder builder
) {
DynamicLink.NavigationInfoParameters.Builder navigationParameters = new DynamicLink.NavigationInfoParameters.Builder();
if (navigationData.hasKey("forcedRedirectEnabled")) {
navigationParameters.setForcedRedirectEnabled(navigationData.getBoolean("forcedRedirectEnabled"));
navigationParameters.setForcedRedirectEnabled(navigationData.getBoolean(
"forcedRedirectEnabled"));
}
builder.setNavigationInfoParameters(navigationParameters.build());
}
private void setSocialParameters(final ReadableMap socialData, final DynamicLink.Builder builder) {
private void setSocialParameters(
final ReadableMap socialData,
final DynamicLink.Builder builder
) {
DynamicLink.SocialMetaTagParameters.Builder socialParameters = new DynamicLink.SocialMetaTagParameters.Builder();
if (socialData.hasKey("descriptionText")) {

View File

@ -1,7 +1,6 @@
package io.invertase.firebase.links;
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;

View File

@ -39,9 +39,9 @@ import java.util.Map;
* com.facebook.internal is solely for the use of other packages within the Facebook SDK for
* Android. Use of any of the classes in this package is unsupported, and they may be modified or
* removed without warning at any time.
*
* <p>
* A helper class that can round trip between JSON and Bundle objects that contains the types:
* Boolean, Integer, Long, Double, String
* Boolean, Integer, Long, Double, String
* If other types are found, an IllegalArgumentException is thrown.
*/
public class BundleJSONConverter {
@ -126,13 +126,13 @@ public class BundleJSONConverter {
} else if (jsonArray.get(0) instanceof Boolean) {
boolean[] booleanArray = new boolean[jsonArray.length()];
for (int i = 0; i < jsonArray.length(); i++) {
booleanArray[i] = (Boolean)jsonArray.get(i);
booleanArray[i] = (Boolean) jsonArray.get(i);
}
bundle.putBooleanArray(key, booleanArray);
} else if (jsonArray.get(0) instanceof Double) {
double[] doubleArray = new double[jsonArray.length()];
for (int i = 0; i < jsonArray.length(); i++) {
doubleArray[i] = (Double)jsonArray.get(i);
doubleArray[i] = (Double) jsonArray.get(i);
}
bundle.putDoubleArray(key, doubleArray);
} else if (jsonArray.get(0) instanceof Long) {
@ -143,12 +143,14 @@ public class BundleJSONConverter {
bundle.putLongArray(key, longArray);
} else if (jsonArray.get(0) instanceof JSONObject) {
ArrayList<Bundle> bundleArrayList = new ArrayList<>();
for (int i =0; i < jsonArray.length(); i++) {
for (int i = 0; i < jsonArray.length(); i++) {
bundleArrayList.add(convertToBundle((JSONObject) jsonArray.get(i)));
}
bundle.putSerializable(key, bundleArrayList);
} else {
throw new IllegalArgumentException("Unexpected type in an array: " + jsonArray.get(0).getClass());
throw new IllegalArgumentException("Unexpected type in an array: " + jsonArray
.get(0)
.getClass());
}
}
@ -159,12 +161,6 @@ public class BundleJSONConverter {
});
}
public interface Setter {
public void setOnBundle(Bundle bundle, String key, Object value) throws JSONException;
public void setOnJSON(JSONObject json, String key, Object value) throws JSONException;
}
public static JSONObject convertToJSON(Bundle bundle) throws JSONException {
JSONObject json = new JSONObject();
@ -239,4 +235,10 @@ public class BundleJSONConverter {
return bundle;
}
public interface Setter {
public void setOnBundle(Bundle bundle, String key, Object value) throws JSONException;
public void setOnJSON(JSONObject json, String key, Object value) throws JSONException;
}
}

View File

@ -6,7 +6,6 @@ import com.google.firebase.messaging.RemoteMessage;
import java.util.Map;
public class MessagingSerializer {
public static WritableMap parseRemoteMessage(RemoteMessage message) {
WritableMap messageMap = Arguments.createMap();
@ -17,7 +16,9 @@ public class MessagingSerializer {
}
if (message.getData() != null) {
for (Map.Entry<String, String> e : message.getData().entrySet()) {
for (Map.Entry<String, String> e : message
.getData()
.entrySet()) {
dataMap.putString(e.getKey(), e.getValue());
}
}

View File

@ -4,7 +4,6 @@ import android.content.Intent;
import android.os.Bundle;
import com.facebook.react.HeadlessJsTaskService;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.jstasks.HeadlessJsTaskConfig;
import com.google.firebase.messaging.RemoteMessage;
@ -13,7 +12,8 @@ import javax.annotation.Nullable;
public class RNFirebaseBackgroundMessagingService extends HeadlessJsTaskService {
@Override
protected @Nullable HeadlessJsTaskConfig getTaskConfig(Intent intent) {
protected @Nullable
HeadlessJsTaskConfig getTaskConfig(Intent intent) {
Bundle extras = intent.getExtras();
if (extras != null) {
RemoteMessage message = intent.getParcelableExtra("message");

View File

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

View File

@ -5,8 +5,8 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.support.annotation.NonNull;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v4.app.NotificationManagerCompat;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
import com.facebook.react.bridge.Promise;
@ -32,12 +32,16 @@ public class RNFirebaseMessaging extends ReactContextBaseJavaModule {
LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(context);
// Subscribe to message events
localBroadcastManager.registerReceiver(new MessageReceiver(),
new IntentFilter(RNFirebaseMessagingService.MESSAGE_EVENT));
localBroadcastManager.registerReceiver(
new MessageReceiver(),
new IntentFilter(RNFirebaseMessagingService.MESSAGE_EVENT)
);
// Subscribe to token refresh events
localBroadcastManager.registerReceiver(new RefreshTokenReceiver(),
new IntentFilter(RNFirebaseInstanceIdService.TOKEN_REFRESH_EVENT));
localBroadcastManager.registerReceiver(
new RefreshTokenReceiver(),
new IntentFilter(RNFirebaseInstanceIdService.TOKEN_REFRESH_EVENT)
);
}
@Override
@ -47,7 +51,9 @@ public class RNFirebaseMessaging extends ReactContextBaseJavaModule {
@ReactMethod
public void getToken(Promise promise) {
String token = FirebaseInstanceId.getInstance().getToken();
String token = FirebaseInstanceId
.getInstance()
.getToken();
Log.d(TAG, "Firebase token: " + token);
promise.resolve(token);
}
@ -60,7 +66,9 @@ public class RNFirebaseMessaging extends ReactContextBaseJavaModule {
// Non Web SDK methods
@ReactMethod
public void hasPermission(Promise promise) {
Boolean enabled = NotificationManagerCompat.from(getReactApplicationContext()).areNotificationsEnabled();
Boolean enabled = NotificationManagerCompat
.from(getReactApplicationContext())
.areNotificationsEnabled();
promise.resolve(enabled);
}
@ -94,7 +102,9 @@ public class RNFirebaseMessaging extends ReactContextBaseJavaModule {
}
}
FirebaseMessaging.getInstance().send(mb.build());
FirebaseMessaging
.getInstance()
.send(mb.build());
// TODO: Listen to onMessageSent and onSendError for better feedback?
promise.resolve(null);
@ -102,36 +112,42 @@ public class RNFirebaseMessaging extends ReactContextBaseJavaModule {
@ReactMethod
public void subscribeToTopic(String topic, final Promise promise) {
FirebaseMessaging.getInstance().subscribeToTopic(topic).addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
public void onComplete(@NonNull Task<Void> task) {
if (task.isSuccessful()) {
Log.d(TAG, "subscribeToTopic:onComplete:success");
promise.resolve(null);
} else {
Exception exception = task.getException();
Log.e(TAG, "subscribeToTopic:onComplete:failure", exception);
promise.reject(exception);
FirebaseMessaging
.getInstance()
.subscribeToTopic(topic)
.addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
public void onComplete(@NonNull Task<Void> task) {
if (task.isSuccessful()) {
Log.d(TAG, "subscribeToTopic:onComplete:success");
promise.resolve(null);
} else {
Exception exception = task.getException();
Log.e(TAG, "subscribeToTopic:onComplete:failure", exception);
promise.reject(exception);
}
}
}
});
});
}
@ReactMethod
public void unsubscribeFromTopic(String topic, final Promise promise) {
FirebaseMessaging.getInstance().unsubscribeFromTopic(topic).addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
public void onComplete(@NonNull Task<Void> task) {
if (task.isSuccessful()) {
Log.d(TAG, "unsubscribeFromTopic:onComplete:success");
promise.resolve(null);
} else {
Exception exception = task.getException();
Log.e(TAG, "unsubscribeFromTopic:onComplete:failure", exception);
promise.reject(exception);
FirebaseMessaging
.getInstance()
.unsubscribeFromTopic(topic)
.addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
public void onComplete(@NonNull Task<Void> task) {
if (task.isSuccessful()) {
Log.d(TAG, "unsubscribeFromTopic:onComplete:success");
promise.resolve(null);
} else {
Exception exception = task.getException();
Log.e(TAG, "unsubscribeFromTopic:onComplete:failure", exception);
promise.reject(exception);
}
}
}
});
});
}
private class MessageReceiver extends BroadcastReceiver {
@ -152,7 +168,9 @@ public class RNFirebaseMessaging extends ReactContextBaseJavaModule {
@Override
public void onReceive(Context context, Intent intent) {
if (getReactApplicationContext().hasActiveCatalystInstance()) {
String token = FirebaseInstanceId.getInstance().getToken();
String token = FirebaseInstanceId
.getInstance()
.getToken();
Log.d(TAG, "Received new FCM token: " + token);
Utils.sendEvent(getReactApplicationContext(), "messaging_token_refreshed", token);

View File

@ -11,9 +11,9 @@ import com.google.firebase.messaging.RemoteMessage;
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 REMOTE_NOTIFICATION_EVENT = "notifications-remote-notification";
private static final String TAG = "RNFMessagingService";
@Override
public void onMessageReceived(RemoteMessage message) {
@ -25,7 +25,9 @@ public class RNFirebaseMessagingService extends FirebaseMessagingService {
notificationEvent.putExtra("notification", message);
// Broadcast it to the (foreground) RN Application
LocalBroadcastManager.getInstance(this).sendBroadcast(notificationEvent);
LocalBroadcastManager
.getInstance(this)
.sendBroadcast(notificationEvent);
} else {
// It's a data message
// If the app is in the foreground we send it to the Messaging module
@ -33,16 +35,27 @@ public class RNFirebaseMessagingService extends FirebaseMessagingService {
Intent messagingEvent = new Intent(MESSAGE_EVENT);
messagingEvent.putExtra("message", message);
// Broadcast it so it is only available to the RN Application
LocalBroadcastManager.getInstance(this).sendBroadcast(messagingEvent);
LocalBroadcastManager
.getInstance(this)
.sendBroadcast(messagingEvent);
} else {
try {
// If the app is in the background we send it to the Headless JS Service
Intent headlessIntent = new Intent(this.getApplicationContext(), RNFirebaseBackgroundMessagingService.class);
Intent headlessIntent = new Intent(
this.getApplicationContext(),
RNFirebaseBackgroundMessagingService.class
);
headlessIntent.putExtra("message", message);
this.getApplicationContext().startService(headlessIntent);
this
.getApplicationContext()
.startService(headlessIntent);
HeadlessJsTaskService.acquireWakeLockNow(this.getApplicationContext());
} catch (IllegalStateException ex) {
Log.e(TAG, "Background messages will only work if the message priority is set to 'high'", ex);
Log.e(
TAG,
"Background messages will only work if the message priority is set to 'high'",
ex
);
}
}
}

View File

@ -37,9 +37,11 @@ public class DisplayNotificationTask extends AsyncTask<Void, Void, Void> {
private final Promise promise;
private ReactApplicationContext reactContext;
public DisplayNotificationTask(Context context, ReactApplicationContext reactContext,
NotificationManager notificationManager,
Bundle notification, Promise promise) {
public DisplayNotificationTask(
Context context, ReactApplicationContext reactContext,
NotificationManager notificationManager,
Bundle notification, Promise promise
) {
this.context = context;
this.notification = notification;
this.notificationManager = notificationManager;
@ -53,7 +55,10 @@ public class DisplayNotificationTask extends AsyncTask<Void, Void, Void> {
Class intentClass = getMainActivityClass();
if (intentClass == null) {
if (promise != null) {
promise.reject("notification/display_notification_error", "Could not find main activity class");
promise.reject(
"notification/display_notification_error",
"Could not find main activity class"
);
}
return null;
}
@ -77,7 +82,10 @@ public class DisplayNotificationTask extends AsyncTask<Void, Void, Void> {
nb = nb.setExtras(notification.getBundle("data"));
}
if (notification.containsKey("sound")) {
Uri sound = RNFirebaseNotificationManager.getSound(context, notification.getString("sound"));
Uri sound = RNFirebaseNotificationManager.getSound(
context,
notification.getString("sound")
);
nb = nb.setSound(sound);
}
if (notification.containsKey("subtitle")) {
@ -148,7 +156,7 @@ public class DisplayNotificationTask extends AsyncTask<Void, Void, Void> {
if (defaults == 0) {
ArrayList<Integer> defaultsArray = android.getIntegerArrayList("defaults");
if(defaultsArray != null) {
if (defaultsArray != null) {
for (Integer defaultValue : defaultsArray) {
defaults |= defaultValue;
}
@ -192,7 +200,7 @@ public class DisplayNotificationTask extends AsyncTask<Void, Void, Void> {
nb = nb.setOngoing(android.getBoolean("ongoing"));
}
if (android.containsKey("onlyAlertOnce")) {
nb = nb.setOngoing(android.getBoolean("onlyAlertOnce"));
nb = nb.setOnlyAlertOnce(android.getBoolean("onlyAlertOnce"));
}
if (android.containsKey("people")) {
List<String> people = android.getStringArrayList("people");
@ -210,7 +218,11 @@ public class DisplayNotificationTask extends AsyncTask<Void, Void, Void> {
Bundle progress = android.getBundle("progress");
Double max = progress.getDouble("max");
Double progressI = progress.getDouble("progress");
nb = nb.setProgress(max.intValue(), progressI.intValue(), progress.getBoolean("indeterminate"));
nb = nb.setProgress(
max.intValue(),
progressI.intValue(),
progress.getBoolean("indeterminate")
);
}
// TODO: Public version of notification
/* if (android.containsKey("publicVersion")) {
@ -253,10 +265,12 @@ public class DisplayNotificationTask extends AsyncTask<Void, Void, Void> {
}
if (android.containsKey("vibrate")) {
ArrayList<Integer> vibrate = android.getIntegerArrayList("vibrate");
if(vibrate != null) {
if (vibrate != null) {
long[] vibrateArray = new long[vibrate.size()];
for (int i = 0; i < vibrate.size(); i++) {
vibrateArray[i] = vibrate.get(i).longValue();
vibrateArray[i] = vibrate
.get(i)
.longValue();
}
nb = nb.setVibrate(vibrateArray);
}
@ -279,16 +293,29 @@ public class DisplayNotificationTask extends AsyncTask<Void, Void, Void> {
}
}
String tag = null;
if (android.containsKey("tag")) {
tag = android.getString("tag");
}
// Create the notification intent
PendingIntent contentIntent = createIntent(intentClass, notification, android.getString("clickAction"));
PendingIntent contentIntent = createIntent(
intentClass,
notification,
android.getString("clickAction")
);
nb = nb.setContentIntent(contentIntent);
// Build the notification and send it
Notification builtNotification = nb.build();
notificationManager.notify(notificationId.hashCode(), builtNotification);
notificationManager.notify(tag, notificationId.hashCode(), builtNotification);
if (reactContext != null) {
Utils.sendEvent(reactContext, "notifications_notification_displayed", Arguments.fromBundle(notification));
Utils.sendEvent(
reactContext,
"notifications_notification_displayed",
Arguments.fromBundle(notification)
);
}
if (promise != null) {
@ -305,8 +332,13 @@ public class DisplayNotificationTask extends AsyncTask<Void, Void, Void> {
return null;
}
private NotificationCompat.Action createAction(Bundle action, Class intentClass, Bundle notification) {
boolean showUserInterface = action.containsKey("showUserInterface") && action.getBoolean("showUserInterface");
private NotificationCompat.Action createAction(
Bundle action,
Class intentClass,
Bundle notification
) {
boolean showUserInterface = action.containsKey("showUserInterface") && action.getBoolean(
"showUserInterface");
String actionKey = action.getString("action");
PendingIntent actionIntent = showUserInterface ?
createIntent(intentClass, notification, actionKey) :
@ -314,7 +346,11 @@ public class DisplayNotificationTask extends AsyncTask<Void, Void, Void> {
int icon = getIcon(action.getString("icon"));
String title = action.getString("title");
NotificationCompat.Action.Builder ab = new NotificationCompat.Action.Builder(icon, title, actionIntent);
NotificationCompat.Action.Builder ab = new NotificationCompat.Action.Builder(
icon,
title,
actionIntent
);
if (action.containsKey("allowGeneratedReplies")) {
ab = ab.setAllowGeneratedReplies(action.getBoolean("allowGeneratedReplies"));
@ -348,7 +384,12 @@ public class DisplayNotificationTask extends AsyncTask<Void, Void, Void> {
}
String notificationId = notification.getString("notificationId");
return PendingIntent.getActivity(context, notificationId.hashCode(), intent, PendingIntent.FLAG_UPDATE_CURRENT);
return PendingIntent.getActivity(
context,
notificationId.hashCode(),
intent,
PendingIntent.FLAG_UPDATE_CURRENT
);
}
private PendingIntent createBroadcastIntent(Bundle notification, String action) {
@ -360,7 +401,12 @@ public class DisplayNotificationTask extends AsyncTask<Void, Void, Void> {
intent.setAction("io.invertase.firebase.notifications.BackgroundAction");
intent.putExtra("action", action);
intent.putExtra("notification", notification);
return PendingIntent.getBroadcast(context, notificationId.hashCode(), intent, PendingIntent.FLAG_UPDATE_CURRENT);
return PendingIntent.getBroadcast(
context,
notificationId.hashCode(),
intent,
PendingIntent.FLAG_UPDATE_CURRENT
);
}
private RemoteInput createRemoteInput(Bundle remoteInput) {
@ -412,18 +458,22 @@ public class DisplayNotificationTask extends AsyncTask<Void, Void, Void> {
}
private int getIcon(String icon) {
int resourceId = RNFirebaseNotificationManager.getResourceId(context,"mipmap", icon);
int resourceId = RNFirebaseNotificationManager.getResourceId(context, "mipmap", icon);
if (resourceId == 0) {
resourceId = RNFirebaseNotificationManager.getResourceId(context,"drawable", icon);
resourceId = RNFirebaseNotificationManager.getResourceId(context, "drawable", icon);
}
return resourceId;
}
private Class getMainActivityClass() {
String packageName = context.getPackageName();
Intent launchIntent = context.getPackageManager().getLaunchIntentForPackage(packageName);
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;

View File

@ -36,12 +36,18 @@ public class RNFirebaseBackgroundNotificationActionReceiver extends BroadcastRec
if (Utils.isAppInForeground(context)) {
WritableMap notificationOpenMap = toNotificationOpenMap(intent);
ReactApplication reactApplication = (ReactApplication)context.getApplicationContext();
ReactContext reactContext = reactApplication.getReactNativeHost().getReactInstanceManager().getCurrentReactContext();
ReactApplication reactApplication = (ReactApplication) context.getApplicationContext();
ReactContext reactContext = reactApplication
.getReactNativeHost()
.getReactInstanceManager()
.getCurrentReactContext();
Utils.sendEvent(reactContext, "notifications_notification_opened", notificationOpenMap);
} else {
Intent serviceIntent = new Intent(context, RNFirebaseBackgroundNotificationActionsService.class);
Intent serviceIntent = new Intent(
context,
RNFirebaseBackgroundNotificationActionsService.class
);
serviceIntent.putExtras(intent.getExtras());
context.startService(serviceIntent);
HeadlessJsTaskService.acquireWakeLockNow(context);

View File

@ -13,7 +13,8 @@ import static io.invertase.firebase.notifications.RNFirebaseBackgroundNotificati
public class RNFirebaseBackgroundNotificationActionsService extends HeadlessJsTaskService {
@Override
protected @Nullable HeadlessJsTaskConfig getTaskConfig(Intent intent) {
protected @Nullable
HeadlessJsTaskConfig getTaskConfig(Intent intent) {
if (isBackgroundNotficationIntent(intent)) {
WritableMap notificationOpenMap = toNotificationOpenMap(intent);

View File

@ -2,7 +2,6 @@ package io.invertase.firebase.notifications;
import android.app.AlarmManager;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
import android.app.NotificationManager;
@ -10,16 +9,13 @@ import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.service.notification.StatusBarNotification;
import android.support.annotation.Nullable;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.RemoteInput;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
@ -32,9 +28,6 @@ import com.facebook.react.bridge.ReadableMap;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
@ -44,8 +37,8 @@ import io.invertase.firebase.Utils;
import io.invertase.firebase.messaging.BundleJSONConverter;
public class RNFirebaseNotificationManager {
private static final String PREFERENCES_KEY = "RNFNotifications";
public 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;
private Context context;
@ -61,31 +54,63 @@ public class RNFirebaseNotificationManager {
public RNFirebaseNotificationManager(Context context) {
this.alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
this.context = context;
this.notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
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) {
return context
.getResources()
.getIdentifier(image, type, context.getPackageName());
}
public static Uri getSound(Context context, String sound) {
if (sound == null) {
return null;
} else if (sound.contains("://")) {
return Uri.parse(sound);
} else if (sound.equalsIgnoreCase("default")) {
return RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
} else {
int soundResourceId = getResourceId(context, "raw", sound);
if (soundResourceId == 0) {
soundResourceId = getResourceId(context, "raw", sound.substring(0, sound.lastIndexOf('.')));
}
return Uri.parse("android.resource://" + context.getPackageName() + "/" + soundResourceId);
}
}
public void cancelAllNotifications(Promise promise) {
try {
Map<String, ?> notifications = preferences.getAll();
for(String notificationId : notifications.keySet()){
for (String notificationId : notifications.keySet()) {
cancelAlarm(notificationId);
}
preferences.edit().clear().apply();
preferences
.edit()
.clear()
.apply();
promise.resolve(null);
} catch (SecurityException e) {
// TODO: Identify what these situations are
// In some devices/situations cancelAllLocalNotifications can throw a SecurityException.
Log.e(TAG, e.getMessage());
promise.reject("notification/cancel_notifications_error", "Could not cancel notifications", e);
promise.reject(
"notification/cancel_notifications_error",
"Could not cancel notifications",
e
);
}
}
public void cancelNotification(String notificationId, Promise promise) {
try {
cancelAlarm(notificationId);
preferences.edit().remove(notificationId).apply();
preferences
.edit()
.remove(notificationId)
.apply();
promise.resolve(null);
} catch (SecurityException e) {
// TODO: Identify what these situations are
@ -150,10 +175,17 @@ public class RNFirebaseNotificationManager {
public void displayScheduledNotification(Bundle notification) {
// If this isn't a repeated notification, clear it from the scheduled notifications list
if (!notification.getBundle("schedule").containsKey("repeated")
|| !notification.getBundle("schedule").getBoolean("repeated")) {
if (!notification
.getBundle("schedule")
.containsKey("repeated")
|| !notification
.getBundle("schedule")
.getBoolean("repeated")) {
String notificationId = notification.getString("notificationId");
preferences.edit().remove(notificationId).apply();;
preferences
.edit()
.remove(notificationId)
.apply();
}
if (Utils.isAppInForeground(context)) {
@ -161,21 +193,23 @@ public class RNFirebaseNotificationManager {
// It is up to the JS to decide whether to display the notification
Intent scheduledNotificationEvent = new Intent(SCHEDULED_NOTIFICATION_EVENT);
scheduledNotificationEvent.putExtra("notification", notification);
LocalBroadcastManager.getInstance(context).sendBroadcast(scheduledNotificationEvent);
LocalBroadcastManager
.getInstance(context)
.sendBroadcast(scheduledNotificationEvent);
} else {
// If the app is in the background, then we display it automatically
displayNotification(notification, null);
}
}
public ArrayList<Bundle> getScheduledNotifications(){
public ArrayList<Bundle> getScheduledNotifications() {
ArrayList<Bundle> array = new ArrayList<>();
Map<String, ?> notifications = preferences.getAll();
for(String notificationId : notifications.keySet()){
for (String notificationId : notifications.keySet()) {
try {
JSONObject json = new JSONObject((String)notifications.get(notificationId));
JSONObject json = new JSONObject((String) notifications.get(notificationId));
Bundle bundle = BundleJSONConverter.convertToBundle(json);
array.add(bundle);
} catch (JSONException e) {
@ -195,10 +229,19 @@ public class RNFirebaseNotificationManager {
promise.resolve(null);
}
public void removeDeliveredNotificationsByTag(String tag, Promise promise) {
StatusBarNotification[] statusBarNotifications = notificationManager.getActiveNotifications();
for (StatusBarNotification statusBarNotification : statusBarNotifications) {
if (statusBarNotification.getTag() == tag) {
notificationManager.cancel(statusBarNotification.getTag(), statusBarNotification.getId());
}
}
promise.resolve(null);
}
public void rescheduleNotifications() {
ArrayList<Bundle> bundles = getScheduledNotifications();
for(Bundle bundle: bundles){
for (Bundle bundle : bundles) {
scheduleNotification(bundle, null);
}
}
@ -211,32 +254,23 @@ public class RNFirebaseNotificationManager {
private void cancelAlarm(String notificationId) {
Intent notificationIntent = new Intent(context, RNFirebaseNotificationReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, notificationId.hashCode(), notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
PendingIntent pendingIntent = PendingIntent.getBroadcast(
context,
notificationId.hashCode(),
notificationIntent,
PendingIntent.FLAG_UPDATE_CURRENT
);
alarmManager.cancel(pendingIntent);
}
private void displayNotification(Bundle notification, Promise promise) {
new DisplayNotificationTask(context, reactContext, notificationManager, notification, promise).execute();
}
public 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) {
if (sound == null) {
return null;
} else if (sound.contains("://")) {
return Uri.parse(sound);
} else if (sound.equalsIgnoreCase("default")) {
return RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
} else {
int soundResourceId = getResourceId(context,"raw", sound);
if (soundResourceId == 0) {
soundResourceId = getResourceId(context,"raw", sound.substring(0, sound.lastIndexOf('.')));
}
return Uri.parse("android.resource://" + context.getPackageName() + "/" + soundResourceId);
}
new DisplayNotificationTask(
context,
reactContext,
notificationManager,
notification,
promise
).execute();
}
private NotificationChannelGroup parseChannelGroupMap(ReadableMap channelGroupMap) {
@ -320,15 +354,19 @@ public class RNFirebaseNotificationManager {
String notificationId = notification.getString("notificationId");
Bundle schedule = notification.getBundle("schedule");
// fireDate is stored in the Bundle as Long after notifications are rescheduled.
// This would lead to a fireDate of 0.0 when trying to extract a Double from the bundle.
// Instead always try extract a Long
// 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).
// At the end we need Long value (timestamp) for the scheduler
Long fireDate = -1L;
try {
fireDate = (long) schedule.getDouble("fireDate", -1);
} catch (ClassCastException e) {
fireDate = schedule.getLong("fireDate", -1);
Object fireDateObject = schedule.get("fireDate");
if (fireDateObject instanceof Long) {
fireDate = (Long) fireDateObject;
} else if (fireDateObject instanceof Double) {
Double fireDateDouble = (Double) fireDateObject;
fireDate = fireDateDouble.longValue();
}
if (fireDate == -1) {
if (promise == null) {
Log.e(TAG, "Missing schedule information");
@ -342,33 +380,63 @@ public class RNFirebaseNotificationManager {
// We store them so that they can be re-scheduled when the phone restarts in RNFirebaseNotificationsRebootReceiver
try {
JSONObject json = BundleJSONConverter.convertToJSON(notification);
preferences.edit().putString(notificationId, json.toString()).apply();
preferences
.edit()
.putString(notificationId, json.toString())
.apply();
} catch (JSONException e) {
if (promise == null) {
Log.e(TAG, "Failed to store notification");
} else {
promise.reject("notification/schedule_notification_error", "Failed to store notification", e);
promise.reject(
"notification/schedule_notification_error",
"Failed to store notification",
e
);
}
return;
}
Intent notificationIntent = new Intent(context, RNFirebaseNotificationReceiver.class);
notificationIntent.putExtras(notification);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, notificationId.hashCode(),
notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
PendingIntent pendingIntent = PendingIntent.getBroadcast(
context,
notificationId.hashCode(),
notificationIntent,
PendingIntent.FLAG_UPDATE_CURRENT
);
if (schedule.containsKey("repeatInterval")) {
// If fireDate you specify is in the past, the alarm triggers immediately.
// So we need to adjust the time for correct operation.
if (fireDate < System.currentTimeMillis()) {
Log.w(TAG, "Scheduled notification date is in the past, will adjust it to be in future");
Calendar newFireDate = Calendar.getInstance();
Calendar currentFireDate = Calendar.getInstance();
currentFireDate.setTimeInMillis(fireDate);
Calendar pastFireDate = Calendar.getInstance();
pastFireDate.setTimeInMillis(fireDate);
newFireDate.add(Calendar.DATE, 1);
newFireDate.set(Calendar.HOUR_OF_DAY, currentFireDate.get(Calendar.HOUR_OF_DAY));
newFireDate.set(Calendar.MINUTE, currentFireDate.get(Calendar.MINUTE));
newFireDate.set(Calendar.SECOND, currentFireDate.get(Calendar.SECOND));
newFireDate.set(Calendar.SECOND, pastFireDate.get(Calendar.SECOND));
switch (schedule.getString("repeatInterval")) {
case "minute":
newFireDate.add(Calendar.MINUTE, 1);
break;
case "hour":
newFireDate.set(Calendar.MINUTE, pastFireDate.get(Calendar.MINUTE));
newFireDate.add(Calendar.HOUR, 1);
break;
case "day":
newFireDate.set(Calendar.MINUTE, pastFireDate.get(Calendar.MINUTE));
newFireDate.set(Calendar.HOUR_OF_DAY, pastFireDate.get(Calendar.HOUR_OF_DAY));
newFireDate.add(Calendar.DATE, 1);
break;
case "week":
newFireDate.set(Calendar.MINUTE, pastFireDate.get(Calendar.MINUTE));
newFireDate.set(Calendar.HOUR_OF_DAY, pastFireDate.get(Calendar.HOUR_OF_DAY));
newFireDate.set(Calendar.DATE, pastFireDate.get(Calendar.DATE));
newFireDate.add(Calendar.DATE, 7);
break;
}
fireDate = newFireDate.getTimeInMillis();
}
@ -401,14 +469,14 @@ public class RNFirebaseNotificationManager {
return;
}
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, fireDate.longValue(), interval, pendingIntent);
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, fireDate, interval, pendingIntent);
} else {
if (schedule.containsKey("exact")
&& schedule.getBoolean("exact")
&& Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
alarmManager.setExact(AlarmManager.RTC_WAKEUP, fireDate.longValue(), pendingIntent);
alarmManager.setExact(AlarmManager.RTC_WAKEUP, fireDate, pendingIntent);
} else {
alarmManager.set(AlarmManager.RTC_WAKEUP, fireDate.longValue(), pendingIntent);
alarmManager.set(AlarmManager.RTC_WAKEUP, fireDate, pendingIntent);
}
}

View File

@ -7,6 +7,7 @@ import android.content.Intent;
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;
@ -30,6 +31,8 @@ import io.invertase.firebase.Utils;
import io.invertase.firebase.messaging.RNFirebaseMessagingService;
import me.leolin.shortcutbadger.ShortcutBadger;
import static io.invertase.firebase.Utils.getResId;
public class RNFirebaseNotifications extends ReactContextBaseJavaModule implements ActivityEventListener {
private static final String BADGE_FILE = "BadgeCountFile";
private static final String BADGE_KEY = "BadgeCount";
@ -38,6 +41,7 @@ public class RNFirebaseNotifications extends ReactContextBaseJavaModule implemen
private SharedPreferences sharedPreferences = null;
private RNFirebaseNotificationManager notificationManager;
public RNFirebaseNotifications(ReactApplicationContext context) {
super(context);
context.addActivityEventListener(this);
@ -48,12 +52,16 @@ public class RNFirebaseNotifications extends ReactContextBaseJavaModule implemen
LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(context);
// Subscribe to remote notification events
localBroadcastManager.registerReceiver(new RemoteNotificationReceiver(),
new IntentFilter(RNFirebaseMessagingService.REMOTE_NOTIFICATION_EVENT));
localBroadcastManager.registerReceiver(
new RemoteNotificationReceiver(),
new IntentFilter(RNFirebaseMessagingService.REMOTE_NOTIFICATION_EVENT)
);
// Subscribe to scheduled notification events
localBroadcastManager.registerReceiver(new ScheduledNotificationReceiver(),
new IntentFilter(RNFirebaseNotificationManager.SCHEDULED_NOTIFICATION_EVENT));
localBroadcastManager.registerReceiver(
new ScheduledNotificationReceiver(),
new IntentFilter(RNFirebaseNotificationManager.SCHEDULED_NOTIFICATION_EVENT)
);
}
@Override
@ -112,10 +120,18 @@ public class RNFirebaseNotifications extends ReactContextBaseJavaModule implemen
notificationManager.removeDeliveredNotification(notificationId, promise);
}
@ReactMethod
public void removeDeliveredNotificationsByTag(String tag, Promise promise) {
notificationManager.removeDeliveredNotificationsByTag(tag, promise);
}
@ReactMethod
public void setBadge(int badge, Promise promise) {
// Store the badge count for later retrieval
sharedPreferences.edit().putInt(BADGE_KEY, badge).apply();
sharedPreferences
.edit()
.putInt(BADGE_KEY, badge)
.apply();
if (badge == 0) {
Log.d(TAG, "Remove badge count");
ShortcutBadger.removeCount(this.getReactApplicationContext());
@ -185,7 +201,11 @@ public class RNFirebaseNotifications extends ReactContextBaseJavaModule implemen
public void onNewIntent(Intent intent) {
WritableMap notificationOpenMap = parseIntentForNotification(intent);
if (notificationOpenMap != null) {
Utils.sendEvent(getReactApplicationContext(), "notifications_notification_opened", notificationOpenMap);
Utils.sendEvent(
getReactApplicationContext(),
"notifications_notification_opened",
notificationOpenMap
);
}
}
@ -264,9 +284,14 @@ public class RNFirebaseNotifications extends ReactContextBaseJavaModule implemen
WritableMap dataMap = Arguments.createMap();
// Cross platform notification properties
notificationMap.putString("body", notification.getBody());
String body = getNotificationBody(notification);
if (body != null) {
notificationMap.putString("body", body);
}
if (message.getData() != null) {
for (Map.Entry<String, String> e : message.getData().entrySet()) {
for (Map.Entry<String, String> e : message
.getData()
.entrySet()) {
dataMap.putString(e.getKey(), e.getValue());
}
}
@ -277,8 +302,9 @@ public class RNFirebaseNotifications extends ReactContextBaseJavaModule implemen
if (notification.getSound() != null) {
notificationMap.putString("sound", notification.getSound());
}
if (notification.getTitle() != null) {
notificationMap.putString("title", notification.getTitle());
String title = getNotificationTitle(notification);
if (title != null) {
notificationMap.putString("title", title);
}
// Android specific notification properties
@ -296,12 +322,45 @@ public class RNFirebaseNotifications extends ReactContextBaseJavaModule implemen
}
if (notification.getTag() != null) {
androidMap.putString("group", notification.getTag());
androidMap.putString("tag", notification.getTag());
}
notificationMap.putMap("android", androidMap);
return notificationMap;
}
private @Nullable
String getNotificationBody(RemoteMessage.Notification notification) {
String body = notification.getBody();
String bodyLocKey = notification.getBodyLocalizationKey();
if (bodyLocKey != null) {
String[] bodyLocArgs = notification.getBodyLocalizationArgs();
Context ctx = getReactApplicationContext();
int resId = getResId(ctx, bodyLocKey);
return ctx
.getResources()
.getString(resId, (Object[]) bodyLocArgs);
} else {
return body;
}
}
private @Nullable
String getNotificationTitle(RemoteMessage.Notification notification) {
String title = notification.getTitle();
String titleLocKey = notification.getTitleLocalizationKey();
if (titleLocKey != null) {
String[] titleLocArgs = notification.getTitleLocalizationArgs();
Context ctx = getReactApplicationContext();
int resId = getResId(ctx, titleLocKey);
return ctx
.getResources()
.getString(resId, (Object[]) titleLocArgs);
} else {
return title;
}
}
private class RemoteNotificationReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
@ -311,7 +370,11 @@ public class RNFirebaseNotifications extends ReactContextBaseJavaModule implemen
RemoteMessage message = intent.getParcelableExtra("notification");
WritableMap messageMap = parseRemoteMessage(message);
Utils.sendEvent(getReactApplicationContext(), "notifications_notification_received", messageMap);
Utils.sendEvent(
getReactApplicationContext(),
"notifications_notification_received",
messageMap
);
}
}
}
@ -325,7 +388,11 @@ public class RNFirebaseNotifications extends ReactContextBaseJavaModule implemen
Bundle notification = intent.getBundleExtra("notification");
WritableMap messageMap = parseNotificationBundle(notification);
Utils.sendEvent(getReactApplicationContext(), "notifications_notification_received", messageMap);
Utils.sendEvent(
getReactApplicationContext(),
"notifications_notification_received",
messageMap
);
}
}
}

View File

@ -3,18 +3,24 @@ package io.invertase.firebase.perf;
import android.util.Log;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.WritableMap;
import com.google.firebase.perf.FirebasePerformance;
import com.google.firebase.perf.metrics.Trace;
import com.google.firebase.perf.metrics.HttpMetric;
import java.util.HashMap;
import java.util.Map;
public class RNFirebasePerformance extends ReactContextBaseJavaModule {
private static final String TAG = "RNFirebasePerformance";
private HashMap<String, Trace> traces = new HashMap<>();
private HashMap<String, HttpMetric> httpMetrics = new HashMap<>();
public RNFirebasePerformance(ReactApplicationContext reactContext) {
super(reactContext);
@ -30,19 +36,79 @@ public class RNFirebasePerformance extends ReactContextBaseJavaModule {
}
@ReactMethod
public void setPerformanceCollectionEnabled(Boolean enabled) {
public void setPerformanceCollectionEnabled(Boolean enabled, Promise promise) {
FirebasePerformance.getInstance().setPerformanceCollectionEnabled(enabled);
promise.resolve(null);
}
/**
* Trace
*/
@ReactMethod
public void getTraceAttribute(String identifier, String attribute, Promise promise) {
promise.resolve(getOrCreateTrace(identifier).getAttribute(attribute));
}
@ReactMethod
public void start(String identifier) {
public void getTraceAttributes(String identifier, Promise promise) {
Map<String, String> attributes = getOrCreateTrace(identifier).getAttributes();
WritableMap map = Arguments.createMap();
for (Map.Entry<String, String> entry : attributes.entrySet()) {
map.putString(entry.getKey(), entry.getValue());
}
promise.resolve(map);
}
@ReactMethod
public void getTraceLongMetric(String identifier, String metricName, Promise promise) {
Integer value = Long.valueOf(getOrCreateTrace(identifier).getLongMetric(metricName)).intValue();
promise.resolve(value);
}
@ReactMethod
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) {
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();
if (attributes.containsKey(attribute)) {
promise.resolve(true);
} else {
promise.resolve(false);
}
}
@ReactMethod
public void putTraceMetric(String identifier, String metricName, Integer value, Promise promise) {
getOrCreateTrace(identifier).putMetric(metricName, value.longValue());
promise.resolve(null);
}
@ReactMethod
public void removeTraceAttribute(String identifier, String attribute, Promise promise) {
getOrCreateTrace(identifier).removeAttribute(attribute);
promise.resolve(null);
}
@ReactMethod
public void startTrace(String identifier, Promise promise) {
getOrCreateTrace(identifier).start();
promise.resolve(null);
}
@ReactMethod
public void stop(String identifier) {
public void stopTrace(String identifier, Promise promise) {
getOrCreateTrace(identifier).stop();
traces.remove(identifier);
promise.resolve(null);
}
@ReactMethod
@ -50,6 +116,86 @@ public class RNFirebasePerformance extends ReactContextBaseJavaModule {
getOrCreateTrace(identifier).incrementCounter(event);
}
/**
* Http Metric
*/
@ReactMethod
public void getHttpMetricAttribute(String url, String httpMethod, String attribute, Promise promise) {
promise.resolve(getOrCreateHttpMetric(url, httpMethod).getAttribute(attribute));
}
@ReactMethod
public void getHttpMetricAttributes(String url, String httpMethod, Promise promise) {
Map<String, String> attributes = getOrCreateHttpMetric(url, httpMethod).getAttributes();
WritableMap map = Arguments.createMap();
for (Map.Entry<String, String> entry : attributes.entrySet()) {
map.putString(entry.getKey(), entry.getValue());
}
promise.resolve(map);
}
@ReactMethod
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);
} else {
promise.resolve(false);
}
}
@ReactMethod
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) {
getOrCreateHttpMetric(url, httpMethod).setHttpResponseCode(code);
promise.resolve(null);
}
@ReactMethod
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) {
getOrCreateHttpMetric(url, httpMethod).setResponseContentType(type);
promise.resolve(null);
}
@ReactMethod
public void setHttpMetricResponsePayloadSize(String url, String httpMethod, Integer bytes, Promise promise) {
getOrCreateHttpMetric(url, httpMethod).setResponsePayloadSize(bytes.longValue());
promise.resolve(null);
}
@ReactMethod
public void startHttpMetric(String url, String httpMethod, Promise promise) {
getOrCreateHttpMetric(url, httpMethod).start();
promise.resolve(null);
}
@ReactMethod
public void stopHttpMetric(String url, String httpMethod, Promise promise) {
getOrCreateHttpMetric(url, httpMethod).stop();
httpMetrics.remove(url + httpMethod);
promise.resolve(null);
}
/**
* Private
*/
private Trace getOrCreateTrace(String identifier) {
if (traces.containsKey(identifier)) {
return traces.get(identifier);
@ -58,4 +204,39 @@ public class RNFirebasePerformance extends ReactContextBaseJavaModule {
traces.put(identifier, trace);
return trace;
}
private HttpMetric getOrCreateHttpMetric(String url, String httpMethod) {
String identifier = url + httpMethod;
if (httpMetrics.containsKey(identifier)) {
return httpMetrics.get(identifier);
}
HttpMetric httpMetric = FirebasePerformance.getInstance().newHttpMetric(url, this.mapStringToMethod(httpMethod));
httpMetrics.put(identifier, httpMetric);
return httpMetric;
}
private String mapStringToMethod(String value) {
switch (value) {
case "CONNECT":
return FirebasePerformance.HttpMethod.CONNECT;
case "DELETE":
return FirebasePerformance.HttpMethod.DELETE;
case "GET":
return FirebasePerformance.HttpMethod.GET;
case "HEAD":
return FirebasePerformance.HttpMethod.HEAD;
case "OPTIONS":
return FirebasePerformance.HttpMethod.OPTIONS;
case "PATCH":
return FirebasePerformance.HttpMethod.PATCH;
case "POST":
return FirebasePerformance.HttpMethod.POST;
case "PUT":
return FirebasePerformance.HttpMethod.PUT;
case "TRACE":
return FirebasePerformance.HttpMethod.TRACE;
}
return "";
}
}

View File

@ -41,7 +41,6 @@ import com.google.firebase.storage.OnProgressListener;
import io.invertase.firebase.Utils;
@SuppressWarnings("WeakerAccess")
public class RNFirebaseStorage extends ReactContextBaseJavaModule {
@ -111,17 +110,20 @@ public class RNFirebaseStorage extends ReactContextBaseJavaModule {
@ReactMethod
public void delete(String appName, final String path, final Promise promise) {
StorageReference reference = this.getReference(path, appName);
reference.delete().addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void aVoid) {
promise.resolve(null);
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception exception) {
promiseRejectStorageException(promise, exception);
}
});
reference
.delete()
.addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void aVoid) {
promise.resolve(null);
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception exception) {
promiseRejectStorageException(promise, exception);
}
});
}
/**
@ -162,17 +164,20 @@ public class RNFirebaseStorage extends ReactContextBaseJavaModule {
@ReactMethod
public void getMetadata(String appName, final String path, final Promise promise) {
StorageReference reference = this.getReference(path, appName);
reference.getMetadata().addOnSuccessListener(new OnSuccessListener<StorageMetadata>() {
@Override
public void onSuccess(StorageMetadata storageMetadata) {
promise.resolve(getMetadataAsMap(storageMetadata));
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception exception) {
promiseRejectStorageException(promise, exception);
}
});
reference
.getMetadata()
.addOnSuccessListener(new OnSuccessListener<StorageMetadata>() {
@Override
public void onSuccess(StorageMetadata storageMetadata) {
promise.resolve(getMetadataAsMap(storageMetadata));
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception exception) {
promiseRejectStorageException(promise, exception);
}
});
}
/**
@ -184,22 +189,30 @@ public class RNFirebaseStorage extends ReactContextBaseJavaModule {
* @url https://firebase.google.com/docs/reference/js/firebase.storage.Reference#updateMetadata
*/
@ReactMethod
public void updateMetadata(String appName, final String path, final ReadableMap metadata, final Promise promise) {
public void updateMetadata(
String appName,
final String path,
final ReadableMap metadata,
final Promise promise
) {
StorageReference reference = this.getReference(path, appName);
StorageMetadata md = buildMetadataFromMap(metadata, null);
reference.updateMetadata(md).addOnSuccessListener(new OnSuccessListener<StorageMetadata>() {
@Override
public void onSuccess(StorageMetadata storageMetadata) {
WritableMap data = getMetadataAsMap(storageMetadata);
promise.resolve(data);
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception exception) {
promiseRejectStorageException(promise, exception);
}
});
reference
.updateMetadata(md)
.addOnSuccessListener(new OnSuccessListener<StorageMetadata>() {
@Override
public void onSuccess(StorageMetadata storageMetadata) {
WritableMap data = getMetadataAsMap(storageMetadata);
promise.resolve(data);
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception exception) {
promiseRejectStorageException(promise, exception);
}
});
}
@ -212,7 +225,12 @@ public class RNFirebaseStorage extends ReactContextBaseJavaModule {
* @url https://firebase.google.com/docs/reference/js/firebase.storage.Reference#downloadFile
*/
@ReactMethod
public void downloadFile(final String appName, final String path, final String localPath, final Promise promise) {
public void downloadFile(
final String appName,
final String path,
final String localPath,
final Promise promise
) {
if (!isExternalStorageWritable()) {
promise.reject(
"storage/invalid-device-file-path",
@ -225,59 +243,70 @@ public class RNFirebaseStorage extends ReactContextBaseJavaModule {
Log.d(TAG, "downloadFile path: " + path);
StorageReference reference = this.getReference(path, appName);
reference.getStream(new StreamDownloadTask.StreamProcessor() {
@Override
public void doInBackground(StreamDownloadTask.TaskSnapshot taskSnapshot, InputStream inputStream) throws IOException {
int indexOfLastSlash = localPath.lastIndexOf("/");
String pathMinusFileName = indexOfLastSlash > 0 ? localPath.substring(0, indexOfLastSlash) + "/" : "/";
String filename = indexOfLastSlash > 0 ? localPath.substring(indexOfLastSlash + 1) : localPath;
File fileWithJustPath = new File(pathMinusFileName);
reference
.getStream(new StreamDownloadTask.StreamProcessor() {
@Override
public void doInBackground(
StreamDownloadTask.TaskSnapshot taskSnapshot,
InputStream inputStream
) throws IOException {
int indexOfLastSlash = localPath.lastIndexOf("/");
String pathMinusFileName = indexOfLastSlash > 0 ? localPath.substring(
0,
indexOfLastSlash
) + "/" : "/";
String filename = indexOfLastSlash > 0 ? localPath.substring(indexOfLastSlash + 1) : localPath;
File fileWithJustPath = new File(pathMinusFileName);
// directoriesCreated assignment for not consumed warning
Boolean directoriesCreated = fileWithJustPath.mkdirs();
File fileWithFullPath = new File(pathMinusFileName, filename);
FileOutputStream output = new FileOutputStream(fileWithFullPath);
int bufferSize = 1024;
byte[] buffer = new byte[bufferSize];
// directoriesCreated assignment for not consumed warning
Boolean directoriesCreated = fileWithJustPath.mkdirs();
File fileWithFullPath = new File(pathMinusFileName, filename);
FileOutputStream output = new FileOutputStream(fileWithFullPath);
int bufferSize = 1024;
byte[] buffer = new byte[bufferSize];
int len;
while ((len = inputStream.read(buffer)) != -1) {
output.write(buffer, 0, len);
int len;
while ((len = inputStream.read(buffer)) != -1) {
output.write(buffer, 0, len);
}
output.close();
}
output.close();
}
}).addOnProgressListener(new OnProgressListener<StreamDownloadTask.TaskSnapshot>() {
@Override
public void onProgress(StreamDownloadTask.TaskSnapshot taskSnapshot) {
Log.d(TAG, "downloadFile progress " + taskSnapshot);
WritableMap event = getDownloadTaskAsMap(taskSnapshot);
sendJSEvent(appName, STORAGE_STATE_CHANGED, path, event);
}
}).addOnPausedListener(new OnPausedListener<StreamDownloadTask.TaskSnapshot>() {
@Override
public void onPaused(StreamDownloadTask.TaskSnapshot taskSnapshot) {
Log.d(TAG, "downloadFile paused " + taskSnapshot);
WritableMap event = getDownloadTaskAsMap(taskSnapshot);
sendJSEvent(appName, STORAGE_STATE_CHANGED, path, event);
}
}).addOnSuccessListener(new OnSuccessListener<StreamDownloadTask.TaskSnapshot>() {
@Override
public void onSuccess(StreamDownloadTask.TaskSnapshot taskSnapshot) {
Log.d(TAG, "downloadFile success" + taskSnapshot);
WritableMap resp = getDownloadTaskAsMap(taskSnapshot);
sendJSEvent(appName, STORAGE_DOWNLOAD_SUCCESS, path, resp);
resp = getDownloadTaskAsMap(taskSnapshot);
promise.resolve(resp);
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception exception) {
Log.e(TAG, "downloadFile failure " + exception.getMessage());
// TODO sendJS error event
promiseRejectStorageException(promise, exception);
}
});
})
.addOnProgressListener(new OnProgressListener<StreamDownloadTask.TaskSnapshot>() {
@Override
public void onProgress(StreamDownloadTask.TaskSnapshot taskSnapshot) {
Log.d(TAG, "downloadFile progress " + taskSnapshot);
WritableMap event = getDownloadTaskAsMap(taskSnapshot);
sendJSEvent(appName, STORAGE_STATE_CHANGED, path, event);
}
})
.addOnPausedListener(new OnPausedListener<StreamDownloadTask.TaskSnapshot>() {
@Override
public void onPaused(StreamDownloadTask.TaskSnapshot taskSnapshot) {
Log.d(TAG, "downloadFile paused " + taskSnapshot);
WritableMap event = getDownloadTaskAsMap(taskSnapshot);
sendJSEvent(appName, STORAGE_STATE_CHANGED, path, event);
}
})
.addOnSuccessListener(new OnSuccessListener<StreamDownloadTask.TaskSnapshot>() {
@Override
public void onSuccess(StreamDownloadTask.TaskSnapshot taskSnapshot) {
Log.d(TAG, "downloadFile success" + taskSnapshot);
WritableMap resp = getDownloadTaskAsMap(taskSnapshot);
sendJSEvent(appName, STORAGE_DOWNLOAD_SUCCESS, path, resp);
resp = getDownloadTaskAsMap(taskSnapshot);
promise.resolve(resp);
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception exception) {
Log.e(TAG, "downloadFile failure " + exception.getMessage());
// TODO sendJS error event
promiseRejectStorageException(promise, exception);
}
});
}
/**
@ -332,7 +361,13 @@ public class RNFirebaseStorage extends ReactContextBaseJavaModule {
* @url https://firebase.google.com/docs/reference/js/firebase.storage.Reference#putFile
*/
@ReactMethod
public void putFile(final String appName, final String path, final String localPath, final ReadableMap metadata, final Promise promise) {
public void putFile(
final String appName,
final String path,
final String localPath,
final ReadableMap metadata,
final Promise promise
) {
StorageReference reference = this.getReference(path, appName);
Log.i(TAG, "putFile: " + localPath + " to " + path);
@ -436,7 +471,9 @@ public class RNFirebaseStorage extends ReactContextBaseJavaModule {
private Uri getURI(final String uri) {
Uri parsed = Uri.parse(uri);
if (parsed.getScheme() == null || parsed.getScheme().isEmpty()) {
if (parsed.getScheme() == null || parsed
.getScheme()
.isEmpty()) {
return Uri.fromFile(new File(uri));
}
return parsed;
@ -470,14 +507,18 @@ public class RNFirebaseStorage extends ReactContextBaseJavaModule {
} else if (file != null) {
String mimeType = null;
if (file.getScheme().equals(ContentResolver.SCHEME_CONTENT)) {
if (file
.getScheme()
.equals(ContentResolver.SCHEME_CONTENT)) {
ContentResolver cr = getReactApplicationContext().getContentResolver();
mimeType = cr.getType(file);
} else {
String fileExtension = MimeTypeMap.getFileExtensionFromUrl(file
.toString());
mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(
fileExtension.toLowerCase());
.toString());
mimeType = MimeTypeMap
.getSingleton()
.getMimeTypeFromExtension(
fileExtension.toLowerCase());
}
if (mimeType != null) metadataBuilder.setContentType(mimeType);
@ -498,7 +539,12 @@ public class RNFirebaseStorage extends ReactContextBaseJavaModule {
private WritableMap getDownloadTaskAsMap(final StreamDownloadTask.TaskSnapshot taskSnapshot) {
WritableMap resp = Arguments.createMap();
resp.putDouble("bytesTransferred", taskSnapshot.getBytesTransferred());
resp.putString("ref", taskSnapshot.getStorage().getPath());
resp.putString(
"ref",
taskSnapshot
.getStorage()
.getPath()
);
resp.putString("state", this.getTaskStatus(taskSnapshot.getTask()));
resp.putDouble("totalBytes", taskSnapshot.getTotalByteCount());
@ -512,29 +558,40 @@ public class RNFirebaseStorage extends ReactContextBaseJavaModule {
* @param taskSnapshot
* @return
*/
private void getUploadTaskAsMap(final UploadTask.TaskSnapshot taskSnapshot, final OnSuccessListener<WritableMap> listener) {
private void getUploadTaskAsMap(
final UploadTask.TaskSnapshot taskSnapshot,
final OnSuccessListener<WritableMap> listener
) {
if (taskSnapshot != null) {
taskSnapshot.getStorage().getDownloadUrl().addOnSuccessListener(new OnSuccessListener<Uri>() {
@Override
public void onSuccess(Uri downloadUrl) {
WritableMap resp = Arguments.createMap();
taskSnapshot
.getStorage()
.getDownloadUrl()
.addOnSuccessListener(new OnSuccessListener<Uri>() {
@Override
public void onSuccess(Uri downloadUrl) {
WritableMap resp = Arguments.createMap();
resp.putDouble("bytesTransferred", taskSnapshot.getBytesTransferred());
resp.putString("downloadURL", downloadUrl.toString());
resp.putDouble("bytesTransferred", taskSnapshot.getBytesTransferred());
resp.putString("downloadURL", downloadUrl.toString());
StorageMetadata d = taskSnapshot.getMetadata();
if (d != null) {
WritableMap metadata = getMetadataAsMap(d);
resp.putMap("metadata", metadata);
StorageMetadata d = taskSnapshot.getMetadata();
if (d != null) {
WritableMap metadata = getMetadataAsMap(d);
resp.putMap("metadata", metadata);
}
resp.putString(
"ref",
taskSnapshot
.getStorage()
.getPath()
);
resp.putString("state", RNFirebaseStorage.this.getTaskStatus(taskSnapshot.getTask()));
resp.putDouble("totalBytes", taskSnapshot.getTotalByteCount());
listener.onSuccess(resp);
}
resp.putString("ref", taskSnapshot.getStorage().getPath());
resp.putString("state", RNFirebaseStorage.this.getTaskStatus(taskSnapshot.getTask()));
resp.putDouble("totalBytes", taskSnapshot.getTotalByteCount());
listener.onSuccess(resp);
}
});
});
} else {
listener.onSuccess(Arguments.createMap());
}
@ -683,10 +740,33 @@ public class RNFirebaseStorage extends ReactContextBaseJavaModule {
public Map<String, Object> getConstants() {
final Map<String, Object> constants = new HashMap<>();
constants.put(DocumentDirectoryPath, this.getReactApplicationContext().getFilesDir().getAbsolutePath());
constants.put(TemporaryDirectoryPath, this.getReactApplicationContext().getCacheDir().getAbsolutePath());
constants.put(PicturesDirectoryPath, Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).getAbsolutePath());
constants.put(CachesDirectoryPath, this.getReactApplicationContext().getCacheDir().getAbsolutePath());
constants.put(
DocumentDirectoryPath,
this
.getReactApplicationContext()
.getFilesDir()
.getAbsolutePath()
);
constants.put(
TemporaryDirectoryPath,
this
.getReactApplicationContext()
.getCacheDir()
.getAbsolutePath()
);
constants.put(
PicturesDirectoryPath,
Environment
.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
.getAbsolutePath()
);
constants.put(
CachesDirectoryPath,
this
.getReactApplicationContext()
.getCacheDir()
.getAbsolutePath()
);
constants.put(FileTypeRegular, 0);
constants.put(FileTypeDirectory, 1);
@ -697,7 +777,9 @@ public class RNFirebaseStorage extends ReactContextBaseJavaModule {
constants.put(ExternalStorageDirectoryPath, null);
}
File externalDirectory = this.getReactApplicationContext().getExternalFilesDir(null);
File externalDirectory = this
.getReactApplicationContext()
.getExternalFilesDir(null);
if (externalDirectory != null) {
constants.put(ExternalDirectoryPath, externalDirectory.getAbsolutePath());
} else {

View File

@ -1,52 +0,0 @@
const shell = require('shelljs');
const WATCH_SRC = require('path').resolve('./lib');
const WATCH_OUT = require('path').resolve('./tests/firebase');
/*
"tests-watch-init": "wml add $(node --eval \"console.log(require('path').resolve('./lib'));\") $(node --eval \"console.log(require('path').resolve('./tests/firebase'));\")",
"tests-watch-start": "watchman watch $(node --eval \"console.log(require('path').resolve('./lib'));\") && wml start",
"tests-watch-stop": "watchman watch-del $(node --eval \"console.log(require('path').resolve('./lib'));\") && wml stop"
*/
if (process.argv.includes('watch')) {
if (!shell.which('wml')) {
shell.echo('');
shell.echo('---------------------------------------------------');
shell.echo(' Missing required npm global from library wix/wml. ');
shell.echo('---------------------------------------------------');
shell.echo('');
shell.exit(1);
}
if (!shell.which('watchman')) {
shell.echo('');
shell.echo('---------------------------------------------------');
shell.echo(' Missing required executable: watchman ');
shell.echo('---------------------------------------------------');
shell.echo('');
shell.exit(1);
}
if (process.argv.includes('init')) {
console.log(`wml add ${WATCH_SRC} ${WATCH_OUT}`);
if (shell.exec(`wml add ${WATCH_SRC} ${WATCH_OUT}`).code !== 0) {
shell.echo('Error setting up watched location via WML.');
shell.exit(1);
}
}
if (process.argv.includes('start')) {
console.log(`watchman watch ${WATCH_SRC} && wml start`);
const watcher = shell.exec(`watchman watch ${WATCH_SRC} && wml start`, { async: true });
watcher.stdout.on('data', console.log);
watcher.stderr.on('data', console.error);
}
if (process.argv.includes('stop')) {
console.log(`watchman watch-del ${WATCH_SRC} && wml stop && wml rm all`);
const watcher = shell.exec(`watchman watch-del ${WATCH_SRC} && wml stop && wml rm all`, { async: true });
watcher.stdout.on('data', console.log);
watcher.stderr.on('data', console.error);
}
}

View File

@ -99,7 +99,6 @@ dependencies {
implementation "com.google.firebase:firebase-ads:15.0.1"
implementation "com.google.firebase:firebase-auth:16.0.2"
implementation "com.google.firebase:firebase-config:16.0.0"
implementation "com.google.firebase:firebase-crash:16.0.1"
implementation "com.google.firebase:firebase-database:16.0.1"
implementation "com.google.firebase:firebase-firestore:17.0.2"
implementation "com.google.firebase:firebase-functions:16.0.1"

View File

@ -3,7 +3,7 @@ package com.testing;
import android.app.Application;
import com.facebook.react.ReactApplication;
import io.invertase.bridge.RNBridgePackage;
import io.salakar.bridge.RNBridgePackage;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.shell.MainReactPackage;
@ -14,7 +14,6 @@ import io.invertase.firebase.admob.RNFirebaseAdMobPackage;
import io.invertase.firebase.analytics.RNFirebaseAnalyticsPackage;
import io.invertase.firebase.auth.RNFirebaseAuthPackage;
import io.invertase.firebase.config.RNFirebaseRemoteConfigPackage;
import io.invertase.firebase.crash.RNFirebaseCrashPackage;
import io.invertase.firebase.database.RNFirebaseDatabasePackage;
import io.invertase.firebase.fabric.crashlytics.RNFirebaseCrashlyticsPackage;
import io.invertase.firebase.firestore.RNFirebaseFirestorePackage;
@ -48,7 +47,6 @@ public class MainApplication extends Application implements ReactApplication {
new RNFirebaseAnalyticsPackage(),
new RNFirebaseAuthPackage(),
new RNFirebaseRemoteConfigPackage(),
new RNFirebaseCrashPackage(),
new RNFirebaseCrashlyticsPackage(),
new RNFirebaseDatabasePackage(),
new RNFirebaseFirestorePackage(),

View File

@ -9,7 +9,7 @@ buildscript {
dependencies {
classpath 'com.android.tools.build:gradle:3.1.3'
classpath 'com.google.gms:google-services:4.0.1'
classpath 'com.google.firebase:firebase-plugins:1.1.1'
classpath 'com.google.firebase:firebase-plugins:1.1.5'
classpath 'io.fabric.tools:gradle:1.25.4'
}
}

View File

@ -53,7 +53,7 @@ describe('bridge', () => {
// we shim our own reloadReactNative functionality as the detox reloadReactNative built-in
// hangs often and seems unpredictable - todo: investigate & PR if solution found
// reloadReactNative is replaced on init with bridge.root automatically
it('should allow reloadReactNative usage without breaking remote debug', async () => {
xit('should allow reloadReactNative usage without breaking remote debug', async () => {
should(bridge.reload).be.a.Function();
// and check it works without breaking anything
await device.reloadReactNative();

View File

@ -1,52 +0,0 @@
describe('crash()', () => {
describe('setCrashCollectionEnabled()', () => {
it('true', async () => {
await firebase.crash().setCrashCollectionEnabled(true);
await sleep(100);
const enabled = await firebase.crash().isCrashCollectionEnabled();
should.equal(enabled, true, 'collection enabled boolean should be true');
});
it('false', async () => {
await firebase.crash().setCrashCollectionEnabled(false);
// does not exist on ios
if (device.getPlatform() === 'android') {
await sleep(150);
const enabled = await firebase.crash().isCrashCollectionEnabled();
should.equal(
enabled,
false,
'collection enabled boolean should be false'
);
}
});
});
describe('log()', () => {
it('accepts a string log', async () => {
await firebase.crash().log('hello world');
await sleep(50);
});
});
describe('logcat()', () => {
it('accepts a log level, log tag and message', async () => {
await firebase.crash().logcat(0, 'HELLO_TAG', 'hello world');
await sleep(50);
});
});
describe('report()', () => {
it('accepts an error with customisable stack size', async () => {
const error = new Error('Oh noes!');
await firebase.crash().report(error);
await firebase.crash().report(error, 5);
error.code = 'NETWORK_ERROR';
await firebase.crash().report(error);
delete error.message;
await firebase.crash().report(error);
await sleep(50);
});
});
});

View File

@ -0,0 +1,93 @@
describe('perf()', () => {
describe('HttpMetric', () => {
it('start() & stop()', async () => {
const httpMetric = firebase.perf().newHttpMetric('http://foo.com', 'GET');
await httpMetric.start();
await httpMetric.stop();
});
it('removeAttribute()', async () => {
const httpMetric = firebase.perf().newHttpMetric('http://foo.com', 'GET');
await httpMetric.start();
await httpMetric.putAttribute('foo', 'bar');
const value = await httpMetric.getAttribute('foo');
should.equal(value, 'bar');
await httpMetric.removeAttribute('foo');
const value2 = await httpMetric.getAttribute('foo');
should.equal(value2, null);
await httpMetric.stop();
});
it('getAttribute() should return null', async () => {
const httpMetric = firebase.perf().newHttpMetric('http://foo.com', 'GET');
await httpMetric.start();
const value = await httpMetric.getAttribute('foo');
should.equal(value, null);
await httpMetric.removeAttribute('foo');
await httpMetric.stop();
});
it('getAttribute() should return string value', async () => {
const httpMetric = firebase.perf().newHttpMetric('http://foo.com', 'GET');
await httpMetric.start();
await httpMetric.putAttribute('foo', 'bar');
const value = await httpMetric.getAttribute('foo');
should.equal(value, 'bar');
await httpMetric.removeAttribute('foo');
await httpMetric.stop();
});
it('putAttribute()', async () => {
const httpMetric = firebase.perf().newHttpMetric('http://foo.com', 'GET');
await httpMetric.start();
await httpMetric.putAttribute('foo', 'bar');
const value = await httpMetric.getAttribute('foo');
value.should.equal('bar');
await httpMetric.removeAttribute('foo');
await httpMetric.stop();
});
it('getAttributes()', async () => {
const httpMetric = firebase.perf().newHttpMetric('http://foo.com', 'GET');
await httpMetric.start();
await httpMetric.putAttribute('foo', 'bar');
await httpMetric.putAttribute('bar', 'baz');
const value = await httpMetric.getAttributes();
value.should.deepEqual({
foo: 'bar',
bar: 'baz',
});
await httpMetric.removeAttribute('foo');
await httpMetric.removeAttribute('bar');
await httpMetric.stop();
});
it('setHttpResponseCode()', async () => {
const httpMetric = firebase.perf().newHttpMetric('http://foo.com', 'GET');
await httpMetric.start();
await httpMetric.setHttpResponseCode(500);
await httpMetric.stop();
});
it('setRequestPayloadSize()', async () => {
const httpMetric = firebase.perf().newHttpMetric('http://foo.com', 'GET');
await httpMetric.start();
await httpMetric.setRequestPayloadSize(1234567);
await httpMetric.stop();
});
it('setResponseContentType()', async () => {
const httpMetric = firebase.perf().newHttpMetric('http://foo.com', 'GET');
await httpMetric.start();
await httpMetric.setResponseContentType('application/foobar');
await httpMetric.stop();
});
it('setResponsePayloadSize()', async () => {
const httpMetric = firebase.perf().newHttpMetric('http://foo.com', 'GET');
await httpMetric.start();
await httpMetric.setResponsePayloadSize(123456789);
await httpMetric.stop();
});
});
});

View File

@ -8,9 +8,10 @@ describe('perf()', () => {
await firebase.perf().setPerformanceCollectionEnabled(false);
});
xit('errors if not boolean', async () => {
// TODO add validations to lib
await firebase.perf().setPerformanceCollectionEnabled();
it('errors if not boolean', async () => {
(() => firebase.perf().setPerformanceCollectionEnabled()).should.throw(
'firebase.perf().setPerformanceCollectionEnabled() requires a boolean value'
);
});
});
@ -20,15 +21,27 @@ describe('perf()', () => {
trace.constructor.name.should.be.equal('Trace');
});
xit('errors if identifier not a string', async () => {
// TODO add validations to lib
try {
firebase.perf().newTrace([1, 2, 3, 4]);
} catch (e) {
return undefined;
}
it('errors if identifier not a string', async () => {
(() => firebase.perf().newTrace([1, 2, 3, 4])).should.throw(
'firebase.perf().newTrace() requires a string value'
);
});
});
throw new Error('Trace did not error on invalid identifier');
describe('newHttpMetric()', () => {
it('returns an instance of HttpMetric', async () => {
const trace = firebase.perf().newHttpMetric('http://foo.com', 'GET');
trace.constructor.name.should.be.equal('HttpMetric');
});
it('errors if url/httpMethod not a string', async () => {
(() => firebase.perf().newHttpMetric(123, [1, 2])).should.throw(
'firebase.perf().newHttpMetric() requires url and httpMethod string values'
);
});
it('errors if httpMethod not a valid type', async () => {
(() => firebase.perf().newHttpMetric('foo', 'FOO')).should.throw(); // TODO error
});
});
});

View File

@ -6,23 +6,83 @@ describe('perf()', () => {
await trace.stop();
});
describe('incrementCounter()', () => {
it('accepts a string event', async () => {
const trace = firebase.perf().newTrace('bar');
await trace.start();
await trace.incrementCounter('fooby');
await trace.incrementCounter('fooby');
await trace.incrementCounter('fooby');
await trace.incrementCounter('fooby');
await trace.stop();
});
xit('errors if event is not a string', async () => {
const trace = firebase.perf().newTrace('bar');
await trace.start();
await trace.incrementCounter([1, 2, 3, 4]);
await trace.stop();
});
it('getAttribute() should return null', async () => {
const trace = firebase.perf().newTrace('bar');
await trace.start();
const value = await trace.getAttribute('foo');
should.equal(value, null);
await trace.stop();
});
it('getAttribute() should return string value', async () => {
const trace = firebase.perf().newTrace('bar');
await trace.start();
await trace.putAttribute('foo', 'bar');
const value = await trace.getAttribute('foo');
should.equal(value, 'bar');
await trace.stop();
});
it('putAttribute()', async () => {
const trace = firebase.perf().newTrace('bar');
await trace.start();
await trace.putAttribute('foo', 'bar');
const value = await trace.getAttribute('foo');
value.should.equal('bar');
await trace.stop();
});
it('getAttributes()', async () => {
const trace = firebase.perf().newTrace('bar');
await trace.start();
await trace.putAttribute('foo', 'bar');
await trace.putAttribute('bar', 'baz');
const value = await trace.getAttributes();
value.should.deepEqual({
foo: 'bar',
bar: 'baz',
});
await trace.stop();
});
it('removeAttribute()', async () => {
const trace = firebase.perf().newTrace('bar');
await trace.start();
await trace.putAttribute('foobar', 'bar');
const value = await trace.getAttribute('foobar');
value.should.equal('bar');
await trace.removeAttribute('foobar');
const removed = await trace.getAttribute('foobar');
should.equal(removed, null);
await trace.stop();
});
it('getMetric()', async () => {
const trace = firebase.perf().newTrace('bar');
await trace.start();
const metric = await trace.getMetric('foo');
metric.should.equal(0);
await trace.stop();
});
it('putMetric()', async () => {
const trace = firebase.perf().newTrace('bar');
await trace.start();
await trace.putMetric('baz', 1);
const metric = await trace.getMetric('baz');
metric.should.equal(1);
await trace.stop();
});
it('incrementMetric()', async () => {
const trace = firebase.perf().newTrace('bar');
await trace.start();
await trace.putMetric('baz', 1);
await trace.incrementMetric('baz', 2);
const metric = await trace.getMetric('baz');
metric.should.equal(3);
await trace.stop();
});
});
});

View File

@ -21,7 +21,6 @@ target 'testing' do
pod 'Firebase/AdMob', '~> 5.3.0'
pod 'Firebase/Auth', '~> 5.3.0'
pod 'Firebase/Core', '~> 5.3.0'
pod 'Firebase/Crash', '~> 5.3.0'
pod 'Firebase/Database', '~> 5.3.0'
pod 'Firebase/Functions', '~> 5.3.0'
pod 'Firebase/DynamicLinks', '~> 5.3.0'

View File

@ -1,11 +1,11 @@
PODS:
- BoringSSL (10.0.5):
- BoringSSL/Implementation (= 10.0.5)
- BoringSSL/Interface (= 10.0.5)
- BoringSSL/Implementation (10.0.5):
- BoringSSL/Interface (= 10.0.5)
- BoringSSL/Interface (10.0.5)
- Crashlytics (3.10.4):
- BoringSSL (10.0.6):
- BoringSSL/Implementation (= 10.0.6)
- BoringSSL/Interface (= 10.0.6)
- BoringSSL/Implementation (10.0.6):
- BoringSSL/Interface (= 10.0.6)
- BoringSSL/Interface (10.0.6)
- Crashlytics (3.10.5):
- Fabric (~> 1.7.9)
- Fabric (1.7.9)
- Firebase/AdMob (5.3.0):
@ -19,9 +19,6 @@ PODS:
- FirebaseAnalytics (= 5.0.1)
- Firebase/CoreOnly (5.3.0):
- FirebaseCore (= 5.0.4)
- Firebase/Crash (5.3.0):
- Firebase/Core
- FirebaseCrash (= 3.0.0)
- Firebase/Database (5.3.0):
- Firebase/CoreOnly
- FirebaseDatabase (= 5.0.1)
@ -62,12 +59,6 @@ PODS:
- GTMSessionFetcher/Core (~> 1.1)
- FirebaseCore (5.0.4):
- "GoogleToolboxForMac/NSData+zlib (~> 2.1)"
- FirebaseCrash (3.0.0):
- FirebaseAnalytics (~> 5.0)
- FirebaseInstanceID (~> 3.0)
- GoogleToolboxForMac/Logger (~> 2.1)
- "GoogleToolboxForMac/NSData+zlib (~> 2.1)"
- Protobuf (~> 3.5)
- FirebaseDatabase (5.0.1):
- FirebaseCore (~> 5.0)
- leveldb-library (~> 1.18)
@ -131,10 +122,10 @@ PODS:
- FirebaseSwizzlingUtilities/MethodSwizzling (2.0.0):
- FirebaseCore (~> 5.0)
- Google-Mobile-Ads-SDK (7.31.0)
- GoogleAPIClientForREST (1.3.4):
- GoogleAPIClientForREST/Core (= 1.3.4)
- GoogleAPIClientForREST (1.3.5):
- GoogleAPIClientForREST/Core (= 1.3.5)
- GTMSessionFetcher (>= 1.1.7)
- GoogleAPIClientForREST/Core (1.3.4):
- GoogleAPIClientForREST/Core (1.3.5):
- GTMSessionFetcher (>= 1.1.7)
- GoogleSignIn (4.1.2):
- "GoogleToolboxForMac/NSDictionary+URLArguments (~> 2.1)"
@ -162,34 +153,34 @@ PODS:
- GoogleToolboxForMac/Defines (= 2.1.4)
- "GoogleToolboxForMac/NSDictionary+URLArguments (= 2.1.4)"
- "GoogleToolboxForMac/NSString+URLArguments (= 2.1.4)"
- gRPC (1.13.0):
- gRPC-RxLibrary (= 1.13.0)
- gRPC/Main (= 1.13.0)
- gRPC-Core (1.13.0):
- gRPC-Core/Implementation (= 1.13.0)
- gRPC-Core/Interface (= 1.13.0)
- gRPC-Core/Implementation (1.13.0):
- gRPC (1.14.0):
- gRPC-RxLibrary (= 1.14.0)
- gRPC/Main (= 1.14.0)
- gRPC-Core (1.14.0):
- gRPC-Core/Implementation (= 1.14.0)
- gRPC-Core/Interface (= 1.14.0)
- gRPC-Core/Implementation (1.14.0):
- BoringSSL (~> 10.0)
- gRPC-Core/Interface (= 1.13.0)
- gRPC-Core/Interface (= 1.14.0)
- nanopb (~> 0.3)
- gRPC-Core/Interface (1.13.0)
- gRPC-ProtoRPC (1.13.0):
- gRPC-ProtoRPC/Main (= 1.13.0)
- gRPC-ProtoRPC/Main (1.13.0):
- gRPC (= 1.13.0)
- gRPC-RxLibrary (= 1.13.0)
- gRPC-Core/Interface (1.14.0)
- gRPC-ProtoRPC (1.14.0):
- gRPC-ProtoRPC/Main (= 1.14.0)
- gRPC-ProtoRPC/Main (1.14.0):
- gRPC (= 1.14.0)
- gRPC-RxLibrary (= 1.14.0)
- Protobuf (~> 3.0)
- gRPC-RxLibrary (1.13.0)
- gRPC/Main (1.13.0):
- gRPC-Core (= 1.13.0)
- gRPC-RxLibrary (= 1.13.0)
- gRPC-RxLibrary (1.14.0)
- gRPC/Main (1.14.0):
- gRPC-Core (= 1.14.0)
- gRPC-RxLibrary (= 1.14.0)
- GTMOAuth2 (1.1.6):
- GTMSessionFetcher (~> 1.1)
- GTMSessionFetcher (1.1.15):
- GTMSessionFetcher/Full (= 1.1.15)
- GTMSessionFetcher/Core (1.1.15)
- GTMSessionFetcher/Full (1.1.15):
- GTMSessionFetcher/Core (= 1.1.15)
- GTMSessionFetcher (1.2.0):
- GTMSessionFetcher/Full (= 1.2.0)
- GTMSessionFetcher/Core (1.2.0)
- GTMSessionFetcher/Full (1.2.0):
- GTMSessionFetcher/Core (= 1.2.0)
- leveldb-library (1.20)
- nanopb (0.3.8):
- nanopb/decode (= 0.3.8)
@ -197,25 +188,25 @@ PODS:
- nanopb/decode (0.3.8)
- nanopb/encode (0.3.8)
- Protobuf (3.6.0)
- React (0.55.3):
- React/Core (= 0.55.3)
- React/Core (0.55.3):
- yoga (= 0.55.3.React)
- React/fishhook (0.55.3)
- React/RCTBlob (0.55.3):
- React (0.56.0):
- React/Core (= 0.56.0)
- React/Core (0.56.0):
- yoga (= 0.56.0.React)
- React/fishhook (0.56.0)
- React/RCTBlob (0.56.0):
- React/Core
- React/RCTNetwork (0.55.3):
- React/RCTNetwork (0.56.0):
- React/Core
- React/RCTText (0.55.3):
- React/RCTText (0.56.0):
- React/Core
- React/RCTWebSocket (0.55.3):
- React/RCTWebSocket (0.56.0):
- React/Core
- React/fishhook
- React/RCTBlob
- RNFirebase (4.2.0):
- RNFirebase (5.0.0):
- Firebase/Core
- React
- yoga (0.55.3.React)
- yoga (0.56.0.React)
DEPENDENCIES:
- Crashlytics (~> 3.10.4)
@ -223,7 +214,6 @@ DEPENDENCIES:
- Firebase/AdMob (~> 5.3.0)
- Firebase/Auth (~> 5.3.0)
- Firebase/Core (~> 5.3.0)
- Firebase/Crash (~> 5.3.0)
- Firebase/Database (~> 5.3.0)
- Firebase/DynamicLinks (~> 5.3.0)
- Firebase/Firestore (~> 5.3.0)
@ -250,7 +240,6 @@ SPEC REPOS:
- FirebaseAnalytics
- FirebaseAuth
- FirebaseCore
- FirebaseCrash
- FirebaseDatabase
- FirebaseDynamicLinks
- FirebaseFirestore
@ -285,15 +274,14 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/ReactCommon/yoga"
SPEC CHECKSUMS:
BoringSSL: cf3f1793eb6e3c445c4d150456341f149c268a35
Crashlytics: 915a7787b84f635fb2a81f92a90e265c2c413f76
BoringSSL: e10f92a27043805c01071fe815a5cd98ae8212e7
Crashlytics: 7f2e38d302d9da96475b3d64d86fb29e31a542b7
Fabric: a2917d3895e4c1569b9c3170de7320ea1b1e6661
Firebase: 68afeeb05461db02d7c9e3215cda28068670f4aa
FirebaseABTesting: 1f50b8d50f5e3469eea54e7463a7b7fe221d1f5e
FirebaseAnalytics: b3628aea54c50464c32c393fb2ea032566e7ecc2
FirebaseAuth: 463b8ce33bd5d05f706dcd4615499e3212b4132b
FirebaseCore: 62f1b792a49bb9e8b4073f24606d2c93ffc352f0
FirebaseCrash: 8900571fd763fd5bdda04522ec53da979456e3ce
FirebaseDatabase: 482bad9c2abd422bb2321194fb8c937e67426a89
FirebaseDynamicLinks: d624a7adc81a8fd70d52be5a6a47a2bc0644b923
FirebaseFirestore: 53f6fe858494c39dbfd5237655e0641152a88c89
@ -306,22 +294,22 @@ SPEC CHECKSUMS:
FirebaseStorage: 7ca4bb7b58a25fa647b04f524033fc7cb7eb272b
FirebaseSwizzlingUtilities: 6c22677c50d0b6f5f0dc637c1233f13694a3003f
Google-Mobile-Ads-SDK: 6e529e748b45507a2ca904e0b5a52669ba3920c4
GoogleAPIClientForREST: f7951c455df271bc6259b3ddb4073d0026475ccf
GoogleAPIClientForREST: b4c23f6ec9b8c29419d0377168007963d0e25551
GoogleSignIn: d9ef55b10f0aa401a5de2747f59b725e4b9732ac
GoogleToolboxForMac: 91c824d21e85b31c2aae9bb011c5027c9b4e738f
gRPC: 81b990caa01d7c32605d300c2ffd1b5ba0ef9e49
gRPC-Core: a56b086a8ef5ced6f6ba5be5bc0a63fca185015a
gRPC-ProtoRPC: 842797fbe05dfcf891afb4a5048e5ccf5f06ef86
gRPC-RxLibrary: fa6852f98d6ec0b73c0ec2f6406c26943099d501
gRPC: 9d4c549f37c7cef22478634bac7a0405b6e3dbfb
gRPC-Core: f4836515817c0eb479aeeb9cc27c91c4ba62a9f6
gRPC-ProtoRPC: 76f6053639a842e1fb45fa82aa598bf3f4d9f66f
gRPC-RxLibrary: 3c305f32155024b116b780f389323c73a28197e6
GTMOAuth2: c77fe325e4acd453837e72d91e3b5f13116857b2
GTMSessionFetcher: 5fa5b80fd20e439ef5f545fb2cb3ca6c6714caa2
GTMSessionFetcher: 0c4baf0a73acd0041bf9f71ea018deedab5ea84e
leveldb-library: 08cba283675b7ed2d99629a4bc5fd052cd2bb6a5
nanopb: 5601e6bca2dbf1ed831b519092ec110f66982ca3
Protobuf: 0fc0ad8bec688b2a3017a139953e01374fedbd5f
React: aa2040dbb6f317b95314968021bd2888816e03d5
React: 1fe0eb13d90b625d94c3b117c274dcfd2e760e11
RNFirebase: 2b25fd2e60269f26bb0a76c71dcc942b35a77df0
yoga: a23273df0088bf7f2bb7e5d7b00044ea57a2a54a
yoga: b1ce48b6cf950b98deae82838f5173ea7cf89e85
PODFILE CHECKSUM: af3286375d5c28aa5d912b64dee23c8f7c4f9282
PODFILE CHECKSUM: dd2c5548ed2d8f457d4009b6313da04af8f98f40
COCOAPODS: 1.5.3

13475
bridge/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -20,36 +20,35 @@
"ios:pod:install": "cd ios && rm -rf ReactNativeFirebaseDemo.xcworkspace && pod install && cd .."
},
"dependencies": {
"babel-preset-es2015-mod": "^6.6.0",
"babel-preset-es3": "^1.0.1",
"bridge": "^0.2.7",
"detox": "^7.3.3",
"escape-string-regexp": "^1.0.5",
"detox": "^8.1.1",
"fbjs": "^0.8.16",
"firebase-admin": "^5.12.0",
"jsonwebtoken": "^8.2.1",
"mocha": "^5.1.1",
"mocha": "^5.2.0",
"prop-types": "^15.6.1",
"react": "^16.3.2",
"react-native": "^0.55.3",
"react": "^16.4.1",
"react-native": "^0.56.0",
"should": "^13.2.1",
"should-sinon": "0.0.6",
"sinon": "^4.5.0"
"sinon": "^6.1.4"
},
"devDependencies": {
"babel-cli": "^6.24.0",
"babel-eslint": "^8.2.3",
"babel-plugin-istanbul": "^4.1.6",
"@babel/cli": "7.0.0-beta.47",
"@babel/core": "7.0.0-beta.47",
"babel-eslint": "^8.2.6",
"babel-plugin-istanbul": "^5.0.1",
"babel-plugin-module-resolver": "^3.1.1",
"babel-preset-react-native": "^4.0.0",
"eslint": "^4.19.1",
"eslint-config-airbnb": "^16.1.0",
"babel-preset-react-native": "^5.0.2",
"eslint": "^5.3.0",
"eslint-config-airbnb": "^17.0.0",
"eslint-plugin-flowtype": "^2.46.3",
"eslint-plugin-import": "^2.11.0",
"eslint-plugin-jsx-a11y": "^6.0.3",
"eslint-plugin-react": "^7.7.0",
"flow-bin": "^0.70.0",
"nyc": "^11.7.1"
"flow-bin": "^0.78.0",
"nyc": "^12.0.2",
"rimraf": "^2.6.2"
},
"nyc": {
"check-coverage": false,
@ -57,12 +56,22 @@
"statements": 95,
"functions": 95,
"branches": 95,
"include": ["**/dist/**"],
"exclude": ["node_modules", "**/dist/utils/emitter**", "**/dist/modules/admob**", "**/dist/modules/auth/phone**"],
"include": [
"**/dist/**"
],
"exclude": [
"node_modules",
"**/dist/utils/emitter**",
"**/dist/modules/admob**",
"**/dist/modules/auth/phone**"
],
"sourceMap": true,
"instrument": true,
"cwd": "..",
"reporter": ["lcov", "text-summary"]
"reporter": [
"lcov",
"text-summary"
]
},
"detox": {
"test-runner": "mocha",

View File

@ -1,188 +0,0 @@
# App Navigation
React Navigation has gone through many cycles of navigation implementations and has been a pain point for developers for a good while.
A current "go to" navigation library is called [react-navigation](https://reactnavigation.org/). It's pure JavaScript implementation
which performs well and provides a solid foundation for navigation on both Android and iOS.
In this step we'll be focusing on adding a Login & Register screen to our app.
## Installation
Simply install the dependency via NPM, no native installation is needed:
```bash
npm install --save react-navigation
```
## Navigation Stacks
Navigation on an app typically works in stacks, where a user can navigate to a new screen (pushing a new screen onto the stack), or backwards (popping
a screen off the stack).
What's great about this concept is that we can create multiple instances of a stack, for example a stack for unauthenticated users and another for
authenticated ones.
To create a new stack, we import the `StackNavigator` from `react-navigation`. In it's basic form, the first item of the `StackNavigator` object
acts as our initial screen on the stack. Lets create a new directory and component for our unauthenticated state:
```js
// src/screens/unauthenticated/index.js
import { StackNavigator } from 'react-navigation';
import Login from './Login';
import Register from './Register';
export default StackNavigator({
Login: {
screen: Login,
},
Register: {
screen: Register,
},
});
```
In both the `Login` & `Register` files, create a basic React component (change Login to Register where appropriate):
```jsx
// src/screens/unauthenticated/Login.js
// src/screens/unauthenticated/Register.js
import React, { Component } from 'react';
import { View, Text } from 'react-native';
class Login extends Component {
render() {
return (
<View>
<Text>Login</Text>
</View>
);
}
}
```
## Using the stack
StackNavigator returns a React component which can be rendered in our app. If we go back to our `src/App.js` component, we can now return
the stack:
```jsx
// src/App.js
import React, { Component } from 'react';
import UnauthenticatedStack from './screens/unauthenticated';
class App extends Component {
render() {
return <UnauthenticatedStack />;
}
}
export default App;
```
Our `UnauthenticatedStack` component will now show the `Login` component as it's the first item in the `StackNavigator`. Reload your app and you
should have your `Login` component rendering!
![Basic Navigation](assets/1-unauthenticated-nav.jpg =300x\*)
## Styling the navigator
As you can see, `react-navigation` provides basic styling to mimic the feel of Android's [Material Design](https://material.io). The
library provides a simple, React like API to style and control your app.
> If you're using iOS, the functionality will remain the same however the basic styling will represent that of the iOS interface instead!
For this example we're going to add a title to our screen and liven up the colors - there's loads more you can do with `react-navigation` though,
just check out their in-depth [documentation](https://reactnavigation.org/docs/getting-started.html).
Lets go ahead and style the screen, using a class static `navigationOptions` object which lets `react-navigation` access our screen component styling:
```jsx
// src/screens/unauthenticated/Login.js
import React, { Component } from 'react';
import { View, Text } from 'react-native';
class Login extends Component {
// Add our react-navigation static method:
static navigationOptions = {
title: 'Login',
headerStyle: {
backgroundColor: '#E6853E',
},
headerTintColor: '#fff',
};
render() {
return (
<View>
<Text>Login</Text>
</View>
);
}
}
export default Login;
```
With this basic config you'll end up with an Android looking app with minimal configuration. Whats better is that `react-navigation` will also
take care of any back buttons and screen animations when navigating through the stack, pretty nifty.
![Styled Navigation](assets/2-unauthenticated-nav.jpg =300x\*)
## Pushing to a new stack
Pushing a new screen onto the stack is a common practice on mobile apps, however requires a slightly different mindset if you're from a web development
background. The basics of a stack allow you to `push` and `pop` where screens effectively overlay each other. The user cannot change stack item
unless you give them the ability to (compared to a website where the user could manually enter a different URL). This allows for greater
control over what a user is able to push/pop to.
Each component we assign to our `StackNavigator` gets cloned by `react-navigation` with a prop called `navigation` which gives us full control over
all of the navigation functionality we'll need.
* To "push" to a new screen we call the `navigate` method with the screen name we defined as the object key within `StackNavigator`.
* To "pop", or go back to the previous screen on the stack we call the `goBack` method.
Lets add a simple button to push to the `Register` screen we defined:
```jsx
// src/screens/unauthenticated/Login.js
import React, { Component } from 'react';
import { View, Button } from 'react-native';
class Login extends Component {
static navigationOptions = {
title: 'Login',
headerStyle: {
backgroundColor: '#E6853E',
},
headerTintColor: '#fff',
};
// Call this method on the button press
_register = () => {
this.props.navigation.navigate('Register');
};
render() {
return (
<View>
<Button onPress={this._register} title="Register Now!" />
</View>
);
}
}
export default Login;
```
Go ahead and click the button, you'll be pushed to a new screen. By pressing the back arrow on the header, `react-navigation` will automatically
call the `goBack` method for us:
![Transition!](assets/3-unauthenticated-push-pop.gif =300x\*)
> To style the `Register` page, simply add it's own `navigationOptions` static config!

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 341 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 219 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 374 KiB

View File

@ -1,28 +0,0 @@
# Conclusion
Hopefully from this Codorial you have seen the power of Firebase - allowing you to concentrate on your application and not worry about other complicated
topics, such as authentication. However understanding how to integrate this into your main business logic can seem challenging at first. This Codorial
has brought together a number of separate libaries which can be applied to any application context.
## Starter Kits
Invertase has provided a couple of starter kits to get your app up and running quicker:
### React Native Firebase Starter
https://github.com/invertase/react-native-firebase-starter
The React Native Firebase Starter is a free, pre-setup React Native app which integrates the [react-native-firebase](https://github.com/invertase/react-native-firebase)
modules. It's perfect if you want to get up and running with no native module installation on both Android & iOS.
### React Native Authentication Starter Kit for Firebase
https://rnfirebase.io/kits/auth-starter
The React Native Authentication Starter Kit for Firebase is a premium starter kit for Android & iOS with a fully integrated authentication flow out of the box.
It features email/password, Facebook, Google and Phone Number login, routing with [react-navigation](https://reactnavigation.org/) and Google Analytics for Firebase.
## Hire Us
The developers behind react-native-firebase at Invertase are available for hire on your next project, big or small. If you'd like to find out more,
head over to our website and drop us a contact message: https://invertase.io/

View File

@ -1,47 +0,0 @@
{
"title": "React Native authentication with Firebase",
"description": "Create a React Native app from scratch, implementing authentication with react-native-firebase. This Codorial covers navigation, firebase, redux and more!",
"tags": ["react-native", "redux", "react-redux", "firebase", "firebase-auth", "react-native-firebase", "react-navigation"],
"steps": [
{
"title": "Getting Started",
"file": "getting-started"
},
{
"title": "Project Structure",
"file": "project-structure"
},
{
"title": "Understanding Firebase Auth",
"file": "understanding-firebase-auth"
},
{
"title": "Integrating Redux",
"file": "integrating-redux"
},
{
"title": "App Navigation",
"file": "app-navigation"
},
{
"title": "Handling Authentication State",
"file": "handling-authentication-state"
},
{
"title": "Creating a sign-in form",
"file": "creating-a-signin-form"
},
{
"title": "Facebook Login",
"file": "facebook-login"
},
{
"title": "Google Login",
"file": "google-login"
},
{
"title": "Conclusion",
"file": "conclusion"
}
]
}

View File

@ -1,163 +0,0 @@
# Creating a sign-in form
We can now get onto allowing the user to login with their email and password. First off we need to create a dummy user for testing. This can be
done via the Firebase console on the 'Authentication' tab. Lets go ahead and create one now:
![Add new user](assets/add-user.jpg)
## Handling user input
React Native provides us with a [`TextInput`](https://facebook.github.io/react-native/docs/textinput.html) component, which renders the web
equivalent of an `input` box in our app. `TextInput` components are 'uncontrolled' meaning we have to explicitly give it a value and handle
updates the user enters. We're going to do this via component state, however you could also do this via our Redux store which is an option
other React developers would go down.
```jsx
// src/screens/unauthenticated/Login.js
import React, { Component } from 'react';
import { View, TextInput } from 'react-native';
class Login extends Component {
static navigationOptions = {
title: 'Login',
headerStyle: {
backgroundColor: '#E6853E',
},
headerTintColor: '#fff',
};
constructor() {
super();
this.state = {
email: '',
password: '',
};
}
_updateEmail = email => {
this.setState({ email });
};
_updatePassword = password => {
this.setState({ password });
};
render() {
return (
<View>
<TextInput
placeholder={'Email Address'}
onChangeText={this._updateEmail}
value={this.state.email}
/>
<TextInput
placeholder={'Password'}
onChangeText={this._updatePassword}
value={this.state.password}
/>
</View>
);
}
}
export default Login;
```
If you reload your app, you will see two plain `TextInput` boxes which can accept input. As these are updated, the `onChangeText` prop is triggered
which then updates state for that specific value. The inputs then individually update whenever their `value` from state changes:
![TextInput Changes](assets/textinput-update.gif =300x\*)
> If you want to hide your users password, use the `secureTextEntry` prop.
## Communicating with Firebase
Now we've got our users input readily available in state, we can use the values to send to Firebase! First off we need a trigger to do this:
```jsx
// src/screens/unauthenticated/Login.js
import React, { Component } from 'react';
import { View, TextInput, Button } from 'react-native';
...
_signIn = () => {
};
render() {
return (
<View>
<TextInput
placeholder={'Email Address'}
onChangeText={this._updateEmail}
value={this.state.email}
/>
<TextInput
placeholder={'Password'}
onChangeText={this._updatePassword}
value={this.state.password}
/>
<Button
title={'Sign In'}
onPress={this._signIn}
/>
</View>
);
}
```
As mentioned in 'Understanding Firebase Auth', we can call the `signInAndRetrieveDataWithEmailAndPassword` method on the Firebase API within our `_signIn` method,
using the state values.
```js
// src/screens/unauthenticated/Login.js
import firebase from 'react-native-firebase';
...
_signIn = () => {
// extract the values from state
const { email, password } = this.state;
firebase.auth().signInAndRetrieveDataWithEmailAndPassword(email, password)
.catch((error) => {
console.error(error);
});
};
```
The Firebase call will catch any errors (see list [here](https://rnfirebase.io/docs/v3.2.x/auth/reference/auth#signInAndRetrieveDataWithEmailAndPassword))
which may occur, such as a bad email address or incorrect email/password combination.
You may notice we don't listen our for the success response from the call (via the `.then`). As you may remember, back in our `App` component
our listener using `onAuthStateChanged` will pick up any successful sign in that occurs - and you guessed it, update the Redux store with
our users details, which causes `App` to re-render with our new `AuthenticatedStack`!
The `Welcome` component implemented below on the `AuthenticatedStack` contains a button which calls `firebase.auth().signOut()`, which triggers the
reverse motion of showing the `UnauthenticatedStack` to the user - simple!
![Sign In](assets/signin.gif =300x\*)
> You may notice there's a delay when pressing the "Sign In" button and the `onAuthStateChanged` lister firing. An app should always give feedback
> to a user when an action is happening. This can be accomplished through state/redux using many of the React Native components available such as
> [`ActivityIndicator`](https://facebook.github.io/react-native/docs/activityindicator.html). You'll also want to handle any errors back from
> Firebase to show to your user!
## Register / Forgot Password
The logic for registering or submitting a forgot password request is exactly the same as our sign in logic. Simply follow the same pattern
as we've done above using `TextInput` components and state. Once you've got the desired information, call Firebase with the methods you need, such as
[`createUserAndRetrieveDataWithEmailAndPassword`](https://rnfirebase.io/docs/v3.2.x/auth/reference/auth#createUserAndRetrieveDataWithEmailAndPassword)
or [`sendPasswordResetEmail`](https://rnfirebase.io/docs/v3.2.x/auth/reference/auth#sendPasswordResetEmail)!
Remember to allow the user to navigate between these screens using the `navigate` method provided by `react-navigation`.
> You could also try implementing [`TabNavigation`](https://reactnavigation.org/docs/tab-based-navigation.html) to allow horizontal navigation between
> screens which is another common navigation pattern.

View File

@ -1,115 +0,0 @@
# Facebook Login
Rather than users signing in to your app with an email and password, Firebase provides the opportunity to integrate with a number of login providers
(or even your own!). It does this by creating a `credential` from an [OAuth](https://oauth.net/) request which your login provider returns, such as an
`accessToken`.
The Firebase API allows us to call `signInAndRetrieveDataWithCredential` with a generated credential. You guessed it, just as we accomplished in
'Creating a sign in form', the method triggers `onAuthStateChanged` if the request was a success - super simple!
## Installing `react-native-fbsdk`
Luckily as Facebook own React Native, they provide a handy wrapper around their own SDK to integrate with React Native, called [`react-native-fbsdk`](https://github.com/facebook/react-native-fbsdk).
```bash
npm install --save react-native-fbsdk
```
To save explaining how to install this library, refer to their [documentation](https://developers.facebook.com/docs/react-native) on how to
install the library into your React Native project on both Android & iOS.
## Creating a credential
A credential can be generated by first obtaining an `accessToken` from Facebook. This is returned once a user successfully signs in via their
Facebook app or popup (which the `react-native-fbsdk` handles).
In our `Login.js` component, go ahead and create a new method called `_facebookLogin`:
```js
// src/screens/unauthenticated/Login.js
import { AccessToken, LoginManager } from 'react-native-fbsdk'; // import AccessToken and LoginManager
...
_facebookLogin = async () => {
try {
const result = await LoginManager.logInWithReadPermissions(['public_profile', 'email']);
if (result.isCancelled) {
throw new Error('User cancelled request'); // Handle this however fits the flow of your app
}
console.log(`Login success with permissions: ${result.grantedPermissions.toString()}`);
// get the access token
const data = await AccessToken.getCurrentAccessToken();
if (!data) {
throw new Error('Something went wrong obtaining the users access token'); // Handle this however fits the flow of your app
}
// create a new firebase credential with the token
const credential = firebase.auth.FacebookAuthProvider.credential(data.accessToken);
// login with credential
await firebase.auth().signInAndRetrieveDataWithCredential(credential);
} catch (e) {
console.error(e);
}
};
```
There's quite a few steps involved here, which require asynchronous calls to both Facebook and Firebase so it's important to give feedback to your
user whilst this process is running. There's also a number of errors which can occur, caused by the user (such as rejecting the request) or
Firebase not having the Facebook provider enabled, so ensure the error is shown back to the user.
> You may notice here we make use of [`async/await`](https://ponyfoo.com/articles/understanding-javascript-async-await). This allows us to keep our code
> feeling synchronous and handle any false positive errors we want to catch without worry about promise chaining.
### Triggering the method
Quite simply, just like the `_signIn` method we call the `_facebookLogin` method with a custom `Button`:
```jsx
// src/screens/unauthenticated/Login.js
...
render() {
return (
<View>
<TextInput
placeholder={'Email Address'}
onChangeText={this._updateEmail}
value={this.state.email}
/>
<TextInput
placeholder={'Password'}
onChangeText={this._updatePassword}
value={this.state.password}
/>
<Button
title={'Sign In'}
onPress={this._signIn}
/>
<Button
title={'Sign In with Facebook'}
onPress={this._facebookLogin}
/>
</View>
);
}
```
## Updating your Facebook app ID & secret
Back in step 'Understanding Firebase Authentication', we enabled Firebase as a login provider on Facebook, however may have entered dummy values.
The `react-native-fbsdk` requires you to assign an app ID to your install as mentioned in their [quickstart guide](https://developers.facebook.com/quickstarts/?platform=android).
Ensure that the app you choose also has the credentials on the Firebase console, otherwise you'll get an error back from Firebase when attempting to
sign in with the generated credential.

View File

@ -1,42 +0,0 @@
# Getting Started
Welcome to the 'Authentication with Firebase' Codorial, using [React Native](http://facebook.github.io/react-native/) and [react-native-firebase](https://rnfirebase.io).
Over the Codorial we'll cover how to setup your application to require both email/password login and social login using Facebook,
to handling the users authenticated state using the popular [redux](https://redux.js.org/introduction) library whilst also integrating routing
using [react-navigation](https://reactnavigation.org/).
## Prerequisites
This Codorial assumes you know the basics of the following topics:
* React JS.
* ES6 JavaScript.
* Starting an app using an emulator on Android/iOS.
* Understand how to setup a new Firebase project.
* Managing your project using Android Stuido and/or XCode.
* Installation of the [react-native-firebase](https://rnfirebase.io) library (see "Creating a base project" below).
This Codorial was created with React Native version `0.53.0`.
## Creating a base project
This project will take a bare bones React Native setup and explain every step required to implement a solid authentication flow in your application.
To start we need a base project to work from.
Both options below require you to setup a new Firebase project and add the configuration file to your project - check out the [documentation](https://rnfirebase.io/docs/v3.2.x/installation/initial-setup) on how to do that if needed.
### Option 1: Using `react-native init`
You can quickly create a base React Native project using `react-native init` by following the React Native [documentation](http://facebook.github.io/react-native/docs/getting-started.html).
> Ensure you follow the "Building Projects with Native Code" tab, as the project won't work using Expo due to requiring native modules.
Once installed, you need to install the [react-native-firebase](https://rnfirebase.io/docs/v3.2.x/installation/initial-setup) library. Ensure you've
also installed the Authentication module on your platform ([Android](https://rnfirebase.io/docs/v3.2.x/auth/android) or [iOS](https://rnfirebase.io/docs/v3.2.x/auth/ios))!
### Option 2: Using [react-native-firebase-starter](https://github.com/invertase/react-native-firebase-starter)
A starter kit has been created to help you get up and running with minimal setup needed. If you're new to React Native this will be perfect starting point.
> Keep in mind every Firebase module is installed in this starter kit. You can refer to the react-native-firebase [documentation](https://rnfirebase.io/docs) if you want to remove
> any unwanted modules.

View File

@ -1,48 +0,0 @@
# Google Login
Much like Facebook loginin, Firebase provides the ability to accept and sign in with a Google credential.
## Installing `react-native-google-signin`
To sign in with Google, we recommend using `react-native-google-signin`. This provides a native way of obtaining the users
Google accounts and their required `accessToken`.
```
npm install react-native-google-signin --save
react-native link react-native-google-signin
```
## Creating a credential
To generate a new credential for the user, simply call the asynchronous `signIn` method `react-native-google-signin` provides and create a new
credential from the `firebase.auth.GoogleAuthProvider`:
```js
import { GoogleSignin } from 'react-native-google-signin';
// Calling this function will open Google for login.
export const googleLogin = async () => {
try {
// Add any configuration settings here:
await GoogleSignin.configure();
const data = await GoogleSignin.signIn();
// create a new firebase credential with the token
const credential = firebase.auth.GoogleAuthProvider.credential(
data.idToken,
data.accessToken
);
// login with credential
const currentUser = await firebase
.auth()
.signInAndRetrieveDataWithCredential(credential);
} catch (e) {
console.error(e);
}
};
```
The flow here is exactly the same as the rest of our app; logging in with the newly created credential will trigger our `onAuthStateChanged` lister to fire with
the new user, logging us into the application.

View File

@ -1,236 +0,0 @@
# Handling Authentication State
Now we've got a basic navigation stack in place along with Redux, we can combine the two together to handle the users authenticated state.
## Listen for authentication state changes
As mentioned in "Understanding Firebase Auth", we can listen for auth state changes via `onAuthStateChanged`. As our app will require authentication
to view the main content, we can conditionally render the 'unauthenticated' `StackNavigator` if the user is signed out in our `src/App.js`. Lets go
ahead and add the boilerplate code to get this in motion:
```jsx
// src/App.js
import React, { Component } from 'react';
import firebase from 'react-native-firebase';
import UnauthenticatedStack from './screens/unauthenticated';
class App extends Component {
constructor() {
super();
this.state = {
loading: true,
};
}
componentDidMount() {
// Listen for user auth state changes
firebase.auth().onAuthStateChanged(user => {
this.setState({
loading: false,
});
});
}
render() {
// Render a blank screen whilst we wait for Firebase.
// The listener generally trigger immediately so it will be too fast for the user to see
if (this.state.loading) {
return null;
}
return <UnauthenticatedStack />;
}
}
export default App;
```
## Updating Redux with the user state
Rather than passing our `user` into component `state`, we're going to add it into into Redux instead. Firebase does provide direct access to the user
via `firebase.auth().currentUser`, however as our app complexity grows we may want to integrate parts of the users data (for example the `uid`) into
other parts of our Redux store. By storing the user in Redux, it is guaranteed that the user details will keep in-sync throughout our Redux store.
### Dispatching Actions
Another common Redux concept is called 'Dispatching Actions'. An action is an event with a unique name, which our reducer can listen out for and react
to the action. Every action requires a `type` property and can pass any additional data along which the reducer needs to handle the action. Lets go ahead
and create an `actions.js` file, where we'll define our first action:
```js
// src/actions.js
// define our action type as a exportable constant
export const USER_STATE_CHANGED = 'USER_STATE_CHANGED';
// define our action function
export function userStateChanged(user) {
return {
type: USER_STATE_CHANGED, // required
user: user ? user.toJSON() : null, // the response from Firebase: if a user exists, pass the serialized data down, else send a null value.
};
}
```
To dispatch this action we need to again make use of `react-redux`. As our `App.js` has been provided the Redux store via the `Provider`
component within `index.js`, we can use a [higher order component (HOC)](https://reactjs.org/docs/higher-order-components.html) called `connect` to provide the component with access to Redux:
```jsx
// src/App.js
import { connect } from 'react-redux';
...
export default connect()(App);
```
The `connect` HOC clones the given component with a function prop called `dispatch`. The `dispatch` function then takes an action, which when called 'dispatches'
it to Redux. Lets jump back into our `App.js` and dispatch our action when `onAuthStateChanged` is triggered:
```jsx
// src/App.js
// import our userStateChanged action
import { userStateChanged } from './actions';
...
componentDidMount() {
firebase.auth().onAuthStateChanged((user) => {
// dispatch the imported action using the dispatch prop:
this.props.dispatch(userStateChanged(user));
this.setState({
loading: false,
});
});
}
```
> You may want to consider implementing [`mapDispatchToProps`](https://github.com/reactjs/react-redux/blob/master/docs/api.md) to keep the action
> usage reusable & cleaner.
Now every time `onAuthStateChanged` is triggered by Firebase, our Redux action will be dispatched regardless of whether a user is signed in our out!
### Reducing state
Back on step 'Integrating Redux' we setup a very basic Redux store. In order for us to latch onto the dispatched action we need to listen out
for the events being sent to the reducer. To do this we import the action type which we exported within `actions.js` and conditionally
return new state when that action is dispatched:
```jsx
// src/store.js
import { createStore } from 'redux';
// import the action type
import { USER_STATE_CHANGED } from './actions';
function reducer(state = {}, action) {
// When USER_STATE_CHANGED is dispatched, update the store with new state
if (action.type === USER_STATE_CHANGED) {
return {
user: action.user,
};
}
return state;
}
export default createStore(reducer);
```
You may notice here that we return a brand new object rather than modifying the existing state. This is because Redux state is
[immutable](https://facebook.github.io/immutable-js/). In order for Redux to know whether state has actually changed, it needs to compare the
previous state with a new one.
> As your Redux state grows in complexity, it may be worth breaking your store out into multiple reducers. This can easily be achieved using
> [combineReducers](https://redux.js.org/api-reference/combinereducers) from the `redux` package.
### Subscribing to Redux state
Now our action is updating the store whenever it's dispatched, we can subscribe to specific parts of the data which we need in our React
components. The power of using `react-redux` is that it allows us to subscribe to data within our store and update the component whenever that
data changes - we do this via a function known as `mapStateToProps`. This function is passed as the first argument of our `connect` HOC and gets
given the current Redux state. It returns an object, which is cloned as props into our component. Here's how it works:
```js
function mapStateToProps(state) {
return {
isUserAuthenticated: !!state.user,
};
}
export default connect(mapStateToProps)(App);
```
With this code, our `App` component will receive a prop called `isUserAuthenticated`, which in our case will be a `true` or `false` value based on
whether the `state.user` object exists or not. Every time Redux state changes, this logic is run. What's handy is that if the result of any
prop has changed, the component will be updated with the new data. If none of the props-to-be have changed, the component doesn't update.
> Keep in mind that if you return a complex `Array` or `object`, `react-redux` will only shallow compare them. Even if your state does not change
> the component will still be re-rendered with the same data which can cause performance issues in our app if not handled. Therefore it is wise
> to break components out to only subscribe to specific parts of primitive state (such as `strings`, `booleans` etc).
As our `App` component contains our routes, any change in the `isUserAuthenticated` value will cause the entire app to re-render - which in this case
is fine as we're conditionally changing navigation stacks. Lets implement that logic:
```jsx
// src/App.js
import React, { Component } from 'react';
import firebase from 'react-native-firebase';
import { connect } from 'react-redux';
import UnauthenticatedStack from './screens/unauthenticated';
import AuthenticatedStack from './screens/authenticated';
import { userStateChanged } from './actions';
class App extends Component {
constructor() {
super();
this.state = {
loading: true,
};
}
componentDidMount() {
firebase.auth().onAuthStateChanged(user => {
this.props.dispatch(userStateChanged(user));
this.setState({
loading: false,
});
});
}
render() {
// Render a blank screen whilst we wait for Firebase.
// The listener generally trigger immediately so it will be too fast for the user to see
if (this.state.loading) {
return null;
}
if (!this.props.isUserAuthenticated) {
return <UnauthenticatedStack />;
}
return <AuthenticatedStack />;
}
}
function mapStateToProps(state) {
return {
isUserAuthenticated: !!state.user,
};
}
export default connect(mapStateToProps)(App);
```
As you can see in our `render` method, if the `isUserAuthenticated` value is `false`, we render our `UnauthenticatedStack`. If it's `true` we can
render a new stack, in this case called `AuthenticatedStack` which is waiting for you to setup!

View File

@ -1,84 +0,0 @@
# Integrating Redux
Redux has become somewhat of a buzz word in the React community, and is generally used in most projects without thought. This Codorial
won't go into details on what it is as their own [documentation](https://redux.js.org/introduction/motivation) does a wonderful job at explaining
what it's for and why to use it.
_TLDR;_ Redux provides your app with a single "state" (data), which can be accessed by any component. You can subscribe to this data to cause
a component update whenever something changes, even if it's deeply nested.
Although the end product of this Codorial certainly doesn't require Redux to function, as your app grows in complexity Redux becomes more and
more important to manage your data.
## Installing Redux
Lets go ahead by installing the core Redux library and the React bindings:
```bash
npm install --save redux react-redux
```
Now within our projects `src` directory, create a `store.js` file. This file will contain all of our Redux logic, however you may want to break
this out into multiple directories as your projects grows in complexity.
```js
// src/store.js
import { createStore } from 'redux';
// Create a reducer with empty state (see below for explanation)
function reducer(state = {}, action) {
return state;
}
export default createStore(reducer);
```
By default state is `null`, however we're setting it to an empty `object` (`state = {}`) so we can attempt to access
shallow nested properties even if they don't exist.
> You may want to consider installing the [redux-logger](https://github.com/evgenyrodionov/redux-logger) library to improve
> your Redux experience.
### Reducer
A reducer is a simple JavaScript function which takes two arguments: `state` & `action`. The idea of a reducer is to take "some data" from an `action`
and return new state.
* `state` is any sort of data, which cannot be altered (immutable). A reducer must return a new value each time. More on this later.
* `action` is an object containing a `type`, and any unreduced data. More on this later.
## Integrating Redux into the app
Our Redux store is now ready to be used. `react-redux` provides us with a `Provider` component which "provides" any children
with access to the store via [context](https://reactjs.org/docs/context.html). Luckily we don't need to worry about this too much as the library
takes care of the hard work!
Back within our original bootstrap file, we'll wrap the `App` component in the `Provider` component, so our business logic has access to Redux.
```jsx
// src/index.js
import React, { Component } from 'react';
import { Provider } from 'react-redux'; // Import the Provider component
import App from './App';
import store from './store';
function bootstrap() {
// Init any external libraries here!
return class extends Component {
render() {
return (
<Provider store={store}>
<App />
</Provider>
);
}
};
}
export default bootstrap;
```
Although noting will visually change, our app now has access to the power of Redux!

View File

@ -1,113 +0,0 @@
# Project Structure
Although it may seem trivial, having a good initial project structure ensures your code will be clean and reusable.
The following step gives an opinionated guide to how this might look, which will work across both Android & iOS.
## Entry file
Every fresh React Native project a key file, an `index.js`, which currently renders a simple React component
with basic styling. Rather than keeping our business logic within this file, we're going to keep it contained in it's own
directory.
We'll achieve this by creating a `src` directory where our own code for the app will live. Create the directory with an `index.js` file, so your
project structure resembles the following:
```
- node_modules/
- android/
- ios/
- src/
-- index.js
- index.js
```
Now we can reference our bootstrap file in the `index.js` file, so both our platform share the same entry point:
```js
// index.js
import { AppRegistry } from 'react-native';
import bootstrap from './src';
AppRegistry.registerComponent('RNFirebaseStarter', () => bootstrap());
```
## Bootstrapping your project
You may have noticed before, but the `bootstrap` import is a function. This allows us to setup or initialize any external modules before our
React based application kick starts (such as [react-native-i18n](https://github.com/AlexanderZaytsev/react-native-i18n)).
Lets go ahead and setup our bootstrap file:
```js
// src/index.js
import React, { Component } from 'react';
import { View, Text } from 'react-native';
function bootstrap() {
// Init any external libraries here!
return class extends Component {
render() {
return (
<View>
<Text>Bootstrapped!</Text>
</View>
);
}
};
}
export default bootstrap;
```
Although this function simply returns a basic React component, later we'll be able to see the power of having a bootstrap file which
consumes our entire application.
Go ahead and boot up your app onto your emulator. You should simply be presented with a plain screen with the words "Bootstrapped!".
![Bootstrapped!](assets/app-bootstrapped.jpg =300x\*)
Although a good starting point, we want to separate we'll our business logic out of the bootstrap file, keeping it purely for app
initialization purposes. This can simply be done by creating a basic React component called `App.js`, which will also live in the `src` directory;
```js
// src/App.js
import React, { Component } from 'react';
import { View, Text } from 'react-native';
class App extends Component {
render() {
return (
<View>
<Text>Bootstrapped!</Text>
</View>
);
}
}
export default App;
```
Now we can reference this component within our bootstrap setup and return it from the bootstrap component:
```js
// src/index.js
import React, { Component } from 'react';
import App from './App';
function bootstrap() {
// Init any external libraries here!
return class extends Component {
render() {
return <App />;
}
};
}
export default bootstrap;
```

View File

@ -1,206 +0,0 @@
# Understanding Firebase Authentication
Before we dive into the logic of implementing authentication, it's first important to understand the Firebase API, and how it handles authentication
with the various options we have.
As we're also working in React, we'll cover how Firebase's asynchronous API fits in with Reacts lifecycle methods.
Luckily [react-native-firebase](https://rnfirebase.io) follows the Firebase web SDK API making this a breeze!
## Enabling authentication
We need to tell Firebase that we plan on using authentication and also enable a couple of the many login providers
which Firebase supports. Head over to the [Firebase console](https://console.firebase.google.com/u/0/) and select the project you're using.
Find the Authentication section and you'll be prompted with a number of options. To get started, we want to select the "SIGN-IN METHOD" tab.
You'll see we have a number of options here, however for the purposes of this Codorial we'll be using "Email/Password" and "Facebook" as our providers.
Go ahead and enable these:
![Enabled Providers](assets/auth-providers.jpg)
> If you don't have a Facebook app, simply enter dummy values. We'll cover this later on.
## Listening to the users authentication state
The Firebase API provides a simple yet powerful listener, which triggers when some event changes with the user.
This can be as obvious the user signing out or as subtle as the user validating their email address. Whatever the event, it triggers the same method: `onAuthStateChanged`.
```js
import firebase from 'react-native-firebase';
firebase.auth().onAuthStateChanged(user => {
console.log(user);
});
```
The callback for the `onAuthStateChanged` method returns a single parameter, commonly referred to as `user`.
The concept here is simple;
* the method is first called once Firebase responds, then any time user state changes thereafter.
* if a user is "signed in", our parameter will be a [`User`](https://firebase.google.com/docs/reference/js/firebase.User) `class`, containing all sorts of information we know about the user,
from their e-mail address to any social provider IDs they may have signed in through.
* if the user signed out, the parameter will be `null` value.
> The `user` class provides a `.toJSON()` method to serialize the users details if required.
### Handling authentication state when the app closes
A common question we get is how to handle the users authenticated state when the app closes/restarts so they don't have to keep logging in each
time they open the app. Luckily this is all handled through Firebase so you don't have to worry about a thing - they'll only be signed out if they
choose to, or the app is uninstalled.
## Creating a new account
Creating a new account on Firebase is very easy. Another method called `createUserAndRetrieveDataWithEmailAndPassword` is available which does exactly what it
says on the tin! This is an asynchronous promise which will throw an exception if something is wrong (such as email taken, or password too short).
Creating a user will also sign them in at the same time.
```js
import firebase from 'react-native-firebase';
firebase
.auth()
.createUserAndRetrieveDataWithEmailAndPassword(
'jim.bob@gmail.com',
'supersecret!'
)
.then(user => {
console.log('New User', user);
})
.catch(error => {
console.error('Woops, something went wrong!', error);
});
```
What's great about this is we don't need to know about the user within the `.then`, as any `onAuthStateChanged` listener would get triggered with our new
users details - how awesome is that.
## Signing into an existing account
Unsurprisingly, Firebase offers a method called `signInAndRetrieveDataWithEmailAndPassword`, which follows the exact same flow as `createUserAndRetrieveDataWithEmailAndPassword`:
```js
import firebase from 'react-native-firebase';
firebase
.auth()
.signInAndRetrieveDataWithEmailAndPassword(
'jim.bob@gmail.com',
'supersecret!'
)
.then(user => {
console.log('Existing User', user);
})
.catch(error => {
console.error('Woops, something went wrong!', error);
});
```
## Using with React
Firebase on it's own is super simple, however when using in a React environment there's some gotchas you need to be mindful of.
### Handling state changes
For any React component to update, a state or prop change needs to occur. As our Firebase auth methods are asynchronous we cannot rely on
the data being available on component mount. To solve this issue, we can make use of state:
```jsx
import React, { Component } from 'react';
import { View, Text } from 'react-native';
import firebase from 'react-native-firebase';
class App extends React.Component {
constructor() {
super();
this.state = {
loading: false,
user: null,
};
}
componentDidMount() {
firebase.auth().onAuthStateChanged(user => {
if (user) {
this.setState({
user: user.toJSON(), // serialize the user class
loading: false,
});
} else {
this.setState({
loading: false,
});
}
});
}
render() {
const { loading, user } = this.state;
// Firebase hasn't responded yet
if (loading) return null;
// Firebase has responded, but no user exists
if (!user) {
return (
<View>
<Text>Not signed in</Text>
</View>
);
}
// Firebase has responded, and a user exists
return (
<View>
<Text>User signed in! {user.email}</Text>
</View>
);
}
}
```
### Subscribing/Un-subscribing from listeners
When subscribing to a new listener, such as `onAuthStateChanged`, a new reference to it is made in memory which has no knowledge of the
React environment. If a component within your app mounts and subscribes, the method will still trigger even if your component unmounted.
If this happens and you're updating state, you'll get a yellow box warning.
To get around this, Firebase returns an unsubscribe function to every subscriber method, which when calls removes the subscription from memory.
This can be easily implemented using React lifecycle methods and class properties:
```jsx
import React, { Component } from 'react';
import { View, Text } from 'react-native';
import firebase from 'react-native-firebase';
class App extends React.Component {
constructor() {
super();
this.unsubscribe = null; // Set a empty class method
this.state = {
loading: true,
user: null,
};
}
componentDidMount() {
// Assign the class method to the unsubscriber response
this.unsubscribe = firebase.auth().onAuthStateChanged((user) => {
// handle state changes
});
}
componentWillUnmount() {
// Call the unsubscriber if it has been set
if (this.unsubscribe) {
this.unsubscribe();
}
}
```
## Further reading
The above examples just scratch the surface of whats available with Firebase auth. Firebase itself provides some in-depth documentation
on authentication and the many different implementation paths you can follow.

View File

@ -28,7 +28,6 @@
839D916F1EF3E20B0077C7C8 /* RNFirebaseAnalytics.m in Sources */ = {isa = PBXBuildFile; fileRef = 839D91561EF3E20A0077C7C8 /* RNFirebaseAnalytics.m */; };
839D91701EF3E20B0077C7C8 /* RNFirebaseAuth.m in Sources */ = {isa = PBXBuildFile; fileRef = 839D91591EF3E20A0077C7C8 /* RNFirebaseAuth.m */; };
839D91711EF3E20B0077C7C8 /* RNFirebaseRemoteConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = 839D915C1EF3E20A0077C7C8 /* RNFirebaseRemoteConfig.m */; };
839D91721EF3E20B0077C7C8 /* RNFirebaseCrash.m in Sources */ = {isa = PBXBuildFile; fileRef = 839D915F1EF3E20A0077C7C8 /* RNFirebaseCrash.m */; };
839D91731EF3E20B0077C7C8 /* RNFirebaseDatabase.m in Sources */ = {isa = PBXBuildFile; fileRef = 839D91621EF3E20A0077C7C8 /* RNFirebaseDatabase.m */; };
839D91751EF3E20B0077C7C8 /* RNFirebasePerformance.m in Sources */ = {isa = PBXBuildFile; fileRef = 839D91681EF3E20A0077C7C8 /* RNFirebasePerformance.m */; };
83AAA0792063DEC2007EC5F7 /* RNFirebaseInvites.m in Sources */ = {isa = PBXBuildFile; fileRef = 83AAA0772063DEC2007EC5F7 /* RNFirebaseInvites.m */; };
@ -93,8 +92,6 @@
839D91591EF3E20A0077C7C8 /* RNFirebaseAuth.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFirebaseAuth.m; sourceTree = "<group>"; };
839D915B1EF3E20A0077C7C8 /* RNFirebaseRemoteConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNFirebaseRemoteConfig.h; sourceTree = "<group>"; };
839D915C1EF3E20A0077C7C8 /* RNFirebaseRemoteConfig.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFirebaseRemoteConfig.m; sourceTree = "<group>"; };
839D915E1EF3E20A0077C7C8 /* RNFirebaseCrash.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNFirebaseCrash.h; sourceTree = "<group>"; };
839D915F1EF3E20A0077C7C8 /* RNFirebaseCrash.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFirebaseCrash.m; sourceTree = "<group>"; };
839D91611EF3E20A0077C7C8 /* RNFirebaseDatabase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNFirebaseDatabase.h; sourceTree = "<group>"; };
839D91621EF3E20A0077C7C8 /* RNFirebaseDatabase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFirebaseDatabase.m; sourceTree = "<group>"; };
839D91671EF3E20A0077C7C8 /* RNFirebasePerformance.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNFirebasePerformance.h; sourceTree = "<group>"; };
@ -174,7 +171,6 @@
839D91541EF3E20A0077C7C8 /* analytics */,
839D91571EF3E20A0077C7C8 /* auth */,
839D915A1EF3E20A0077C7C8 /* config */,
839D915D1EF3E20A0077C7C8 /* crash */,
839D91601EF3E20A0077C7C8 /* database */,
8376F70D1F7C141500D45A85 /* firestore */,
839D91631EF3E20A0077C7C8 /* messaging */,
@ -291,16 +287,6 @@
path = RNFirebase/config;
sourceTree = "<group>";
};
839D915D1EF3E20A0077C7C8 /* crash */ = {
isa = PBXGroup;
children = (
839D915E1EF3E20A0077C7C8 /* RNFirebaseCrash.h */,
839D915F1EF3E20A0077C7C8 /* RNFirebaseCrash.m */,
);
name = crash;
path = RNFirebase/crash;
sourceTree = "<group>";
};
839D91601EF3E20A0077C7C8 /* database */ = {
isa = PBXGroup;
children = (
@ -431,7 +417,6 @@
83AAA0792063DEC2007EC5F7 /* RNFirebaseInvites.m in Sources */,
8323CF071F6FBD870071420B /* NativeExpressComponent.m in Sources */,
83C3EEEE1FA1EACC00B64D3C /* RNFirebaseUtil.m in Sources */,
839D91721EF3E20B0077C7C8 /* RNFirebaseCrash.m in Sources */,
839D91751EF3E20B0077C7C8 /* RNFirebasePerformance.m in Sources */,
8323CF061F6FBD870071420B /* BannerComponent.m in Sources */,
839D916D1EF3E20B0077C7C8 /* RNFirebaseAdMobInterstitial.m in Sources */,
@ -535,7 +520,6 @@
"$(SRCROOT)/../../../ios/Pods/FirebaseAnalytics/Frameworks",
"$(SRCROOT)/../../../ios/Pods/FirebaseAuth/Frameworks",
"$(SRCROOT)/../../../ios/Pods/FirebaseCore/Frameworks",
"$(SRCROOT)/../../../ios/Pods/FirebaseCrash/Frameworks",
"$(SRCROOT)/../../../ios/Pods/FirebaseDatabase/Frameworks",
"$(SRCROOT)/../../../ios/Pods/FirebaseDynamicLinks/Frameworks",
"$(SRCROOT)/../../../ios/Pods/FirebaseFirestore/Frameworks",
@ -579,7 +563,6 @@
"$(SRCROOT)/../../../ios/Pods/FirebaseAnalytics/Frameworks",
"$(SRCROOT)/../../../ios/Pods/FirebaseAuth/Frameworks",
"$(SRCROOT)/../../../ios/Pods/FirebaseCore/Frameworks",
"$(SRCROOT)/../../../ios/Pods/FirebaseCrash/Frameworks",
"$(SRCROOT)/../../../ios/Pods/FirebaseDatabase/Frameworks",
"$(SRCROOT)/../../../ios/Pods/FirebaseDynamicLinks/Frameworks",
"$(SRCROOT)/../../../ios/Pods/FirebaseFirestore/Frameworks",

View File

@ -688,9 +688,23 @@ RCT_EXPORT_METHOD(checkActionCode:
actionType = @"EMAIL_SIGNIN";
break;
}
NSMutableDictionary *data = [NSMutableDictionary dictionary];
NSDictionary *result = @{@"data": @{@"email": [info dataForKey:FIRActionCodeEmailKey], @"fromEmail": [info dataForKey:FIRActionCodeFromEmailKey],}, @"actionType": actionType,};
if ([info dataForKey:FIRActionCodeEmailKey] != nil) {
[data setValue:[info dataForKey:FIRActionCodeEmailKey] forKey:@"email"];
} else {
[data setValue:[NSNull null] forKey:@"email"];
}
if ([info dataForKey:FIRActionCodeFromEmailKey] != nil) {
[data setValue:[info dataForKey:FIRActionCodeFromEmailKey] forKey:@"fromEmail"];
} else {
[data setValue:[NSNull null] forKey:@"fromEmail"];
}
NSDictionary *result = @{ @"data": data, @"actionType": actionType };
resolve(result);
}
}];

View File

@ -19,6 +19,16 @@ NSString *convertFIRRemoteConfigFetchStatusToNSString(FIRRemoteConfigFetchStatus
}
}
NSString *convertFIRRemoteConfigFetchStatusToNSStringDescription(FIRRemoteConfigFetchStatus value) {
switch (value) {
case FIRRemoteConfigFetchStatusThrottled:
return @"fetch() operation cannot be completed successfully, due to throttling.";
case FIRRemoteConfigFetchStatusNoFetchYet:
default:
return @"fetch() operation cannot be completed successfully.";
}
}
NSString *convertFIRRemoteConfigSourceToNSString(FIRRemoteConfigSource value) {
switch (value) {
case FIRRemoteConfigSourceDefault:
@ -49,9 +59,9 @@ RCT_EXPORT_METHOD(fetch:
(RCTPromiseRejectBlock) reject) {
[[FIRRemoteConfig remoteConfig] fetchWithCompletionHandler:^(FIRRemoteConfigFetchStatus status, NSError *__nullable error) {
if (error) {
reject(convertFIRRemoteConfigFetchStatusToNSString(status), error.localizedDescription, error);
reject(convertFIRRemoteConfigFetchStatusToNSString(status), convertFIRRemoteConfigFetchStatusToNSStringDescription(status), error);
} else {
resolve(convertFIRRemoteConfigFetchStatusToNSString(status));
resolve([NSNull null]);
}
}];
}
@ -63,9 +73,9 @@ RCT_EXPORT_METHOD(fetchWithExpirationDuration:
rejecter:(RCTPromiseRejectBlock)reject) {
[[FIRRemoteConfig remoteConfig] fetchWithExpirationDuration:expirationDuration.doubleValue completionHandler:^(FIRRemoteConfigFetchStatus status, NSError *__nullable error) {
if (error) {
reject(convertFIRRemoteConfigFetchStatusToNSString(status), error.localizedDescription, error);
reject(convertFIRRemoteConfigFetchStatusToNSString(status), convertFIRRemoteConfigFetchStatusToNSStringDescription(status), error);
} else {
resolve(convertFIRRemoteConfigFetchStatusToNSString(status));
resolve([NSNull null]);
}
}];
}

View File

@ -1,19 +0,0 @@
#ifndef RNFirebaseCrash_h
#define RNFirebaseCrash_h
#import <Foundation/Foundation.h>
#if __has_include(<FirebaseCrash/FIRCrashLog.h>)
#import <React/RCTBridgeModule.h>
@interface RNFirebaseCrash : NSObject <RCTBridgeModule> {
}
@end
#else
@interface RNFirebaseCrash : NSObject
@end
#endif
#endif

View File

@ -1,35 +0,0 @@
#import "RNFirebaseCrash.h"
#if __has_include(<FirebaseCrash/FIRCrashLog.h>)
#import <FirebaseCrash/FIRCrashLog.h>
@implementation RNFirebaseCrash
RCT_EXPORT_MODULE();
RCT_EXPORT_METHOD(log:(NSString *)message) {
FIRCrashLog(@"%@", message);
}
RCT_EXPORT_METHOD(logcat:(nonnull NSNumber *) level tag:(NSString *) tag message:(NSString *) message) {
FIRCrashLog(@"%@", message);
}
RCT_EXPORT_METHOD(report:(NSString *) message) {
FIRCrashLog(@"%@", message);
}
RCT_EXPORT_METHOD(setCrashCollectionEnabled:(BOOL *) enabled) {
// Not available yet
}
RCT_EXPORT_METHOD(isCrashCollectionEnabled:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
// Not available yet
resolve(@YES);
}
@end
#else
@implementation RNFirebaseCrash
@end
#endif

View File

@ -108,7 +108,22 @@ didReceiveMessage:(nonnull FIRMessagingRemoteMessage *)remoteMessage {
// ** Start React Module methods **
RCT_EXPORT_METHOD(getToken:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
resolve([[FIRInstanceID instanceID] token]);
if (initialToken) {
resolve(initialToken);
} else if ([[FIRInstanceID instanceID] token]) {
resolve([[FIRInstanceID instanceID] token]);
} else {
NSString * senderId = [[FIRApp defaultApp] options].GCMSenderID;
[[FIRMessaging messaging] retrieveFCMTokenForSenderID:senderId completion:^(NSString * _Nullable FCMToken, NSError * _Nullable error) {
if (error) {
reject(@"messaging/fcm-token-error", @"Failed to retrieve FCM token.", error);
} else if (FCMToken) {
resolve(FCMToken);
} else {
resolve([NSNull null]);
}
}];
}
}
RCT_EXPORT_METHOD(requestPermission:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {

View File

@ -10,6 +10,7 @@
}
@property NSMutableDictionary *traces;
@property NSMutableDictionary *httpMetrics;
@end

Some files were not shown because too many files have changed in this diff Show More