Merge branch 'master' of https://github.com/invertase/react-native-firebase into bridge-detox
This commit is contained in:
commit
a174c48cd5
|
@ -1,6 +1,6 @@
|
|||
<p align="center">
|
||||
<a href="https://rnfirebase.io">
|
||||
<img src="http://i.imgur.com/01XQL0x.png"><br/>
|
||||
<img src="https://i.imgur.com/eBNJlHd.png"><br/>
|
||||
</a>
|
||||
<h2 align="center">React Native Firebase</h2>
|
||||
</p>
|
||||
|
|
|
@ -1 +1,32 @@
|
|||
<svg width="183" height="197" viewBox="0 0 183 197" xmlns="http://www.w3.org/2000/svg"><title>Slice 1</title><g fill="none" fill-rule="evenodd"><path fill="#F4A93F" d="M80.95 76l19.202 33.45-24.243-.872z"/><path fill="#F4A83E" d="M90.16 82.4l14.234 27.05-29.091-.872z"/><path fill="#E88634" d="M87.489 87.038L99.94 109.45l-24.334-.243z"/><path fill="#F8CA51" d="M100.546 84.758l3.848 24.692L75 109.153z"/><path fill="#F8CA51" d="M90.16 117l14.555-7.847L75 108.875z"/><path d="M1.008 98C1.643 118.712 41.69 135 91 135v-8.002C51.309 126.8 9.675 114.642 9.008 98h-8z" fill="#E88634"/><path d="M135.523 21.782C117.27 11.976 83.14 38.515 58.484 81.218l6.93 4.001c20.016-34.275 51.364-64.252 66.11-56.508l4-6.929z" fill="#F9CB52"/><path d="M1.008 98C1.643 77.288 41.69 61 91 61v8.002C51.309 69.2 9.675 81.358 9.008 98h-8z" fill="#E88634"/><path d="M46.043 20c-17.62 10.906-11.702 53.732 12.953 96.436l6.93-4.002C46.251 77.963 35.964 35.827 50.043 26.93l-4-6.929z" fill="#F4A73E"/><path d="M46.043 176.436C28.423 165.53 34.34 122.703 58.996 80l6.93 4.001c-19.675 34.472-29.962 76.608-15.883 85.506l-4 6.929z" fill="#F9CB52"/><path d="M45 20.5c18.255-9.806 52.384 16.732 77.04 59.436l-6.931 4C95.093 49.662 63.746 19.687 49 27.43L45 20.5z" fill="#F4A73E"/><path d="M181 98c-.635-20.712-40.683-37-89.992-37v8.002C130.698 69.2 172.333 81.358 173 98h8z" fill="#E88634"/><path d="M45.241 176.501c.107.065.214.129.322.191 18.143 10.475 52.623-16.146 77.476-59.192l-6.892-4.067c-20.175 34.58-51.9 64.808-66.502 56.378a10.11 10.11 0 0 1-.437-.267L45.24 176.5z" fill="#F9CB52"/><path d="M136.036 175.938C117.78 185.738 83.653 159.2 59 116.5l-.26-.45 6.836-4.16c19.984 34.503 51.585 64.863 66.431 57.134l4.029 6.914z" fill="#F4A73E"/><path d="M181 97c.005.166.008.333.008.5 0 20.95-40.295 37.5-90 37.5v-8.002c40.012-.198 82-12.554 82-29.403 0-.2-.006-.398-.018-.595H181z" fill="#E88634"/><path d="M135.005 176.43c17.609-10.915 11.688-53.734-12.962-96.43l-.44-.76-7.022 3.835.047.082c19.952 34.558 30.52 77.214 16.45 86.298l3.927 6.975z" fill="#F4A73E"/><path d="M135.48 21.282c17.62 10.906 11.702 53.733-12.953 96.436l-6.93-4.001c19.676-34.472 29.962-76.608 15.883-85.506l4-6.929z" fill="#F9CB52"/></g></svg>
|
||||
<?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.1 KiB After Width: | Height: | Size: 2.3 KiB |
|
@ -15,8 +15,8 @@ buildscript {
|
|||
apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
compileSdkVersion 26
|
||||
buildToolsVersion "25.0.3"
|
||||
compileSdkVersion 27
|
||||
buildToolsVersion "27.0.2"
|
||||
defaultConfig {
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 26
|
||||
|
@ -82,7 +82,8 @@ rootProject.gradle.buildFinished { buildResult ->
|
|||
dependencies {
|
||||
compile fileTree(include: ['*.jar'], dir: 'libs')
|
||||
compile "com.facebook.react:react-native:+" // From node_modules
|
||||
compile 'me.leolin:ShortcutBadger:1.1.18@aar'
|
||||
compile "com.android.support:support-v4:27.0.2"
|
||||
compile 'me.leolin:ShortcutBadger:1.1.21@aar'
|
||||
compile "com.google.android.gms:play-services-base:$firebaseVersion"
|
||||
compile "com.google.firebase:firebase-core:$firebaseVersion"
|
||||
compile "com.google.firebase:firebase-config:$firebaseVersion"
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package io.invertase.firebase;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.content.Context;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.Log;
|
||||
|
||||
|
@ -522,4 +524,26 @@ public class Utils {
|
|||
}
|
||||
return deconstructedList;
|
||||
}
|
||||
|
||||
public static boolean isAppInForeground(Context context) {
|
||||
/**
|
||||
We need to check if app is in foreground otherwise the app will crash.
|
||||
http://stackoverflow.com/questions/8489993/check-android-application-is-in-foreground-or-not
|
||||
**/
|
||||
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
|
||||
List<ActivityManager.RunningAppProcessInfo> appProcesses =
|
||||
activityManager.getRunningAppProcesses();
|
||||
if (appProcesses == null) {
|
||||
return false;
|
||||
}
|
||||
final String packageName = context.getPackageName();
|
||||
for (ActivityManager.RunningAppProcessInfo appProcess : appProcesses) {
|
||||
if (appProcess.importance ==
|
||||
ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND &&
|
||||
appProcess.processName.equals(packageName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,9 +35,9 @@ import io.invertase.firebase.Utils;
|
|||
|
||||
public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
|
||||
private static final String TAG = "RNFirebaseDatabase";
|
||||
private boolean enableLogging = false;
|
||||
private static boolean enableLogging = false;
|
||||
private HashMap<String, RNFirebaseDatabaseReference> references = new HashMap<>();
|
||||
private HashMap<String, Boolean> loggingLevelSet = new HashMap<>();
|
||||
private static HashMap<String, Boolean> loggingLevelSet = new HashMap<>();
|
||||
private SparseArray<RNFirebaseTransactionHandler> transactionHandlers = new SparseArray<>();
|
||||
|
||||
RNFirebaseDatabase(ReactApplicationContext reactContext) {
|
||||
|
@ -53,16 +53,16 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
|
|||
* @param appName
|
||||
*/
|
||||
@ReactMethod
|
||||
public void goOnline(String appName) {
|
||||
getDatabaseForApp(appName).goOnline();
|
||||
public void goOnline(String appName, String dbURL) {
|
||||
getDatabaseForApp(appName, dbURL).goOnline();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param appName
|
||||
*/
|
||||
@ReactMethod
|
||||
public void goOffline(String appName) {
|
||||
getDatabaseForApp(appName).goOffline();
|
||||
public void goOffline(String appName, String dbURL) {
|
||||
getDatabaseForApp(appName, dbURL).goOffline();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -70,8 +70,8 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
|
|||
* @param state
|
||||
*/
|
||||
@ReactMethod
|
||||
public void setPersistence(String appName, Boolean state) {
|
||||
getDatabaseForApp(appName).setPersistenceEnabled(state);
|
||||
public void setPersistence(String appName, String dbURL, Boolean state) {
|
||||
getDatabaseForApp(appName, dbURL).setPersistenceEnabled(state);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -79,8 +79,8 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
|
|||
* @param size
|
||||
*/
|
||||
@ReactMethod
|
||||
public void setPersistenceCacheSizeBytes(String appName, int size) {
|
||||
getDatabaseForApp(appName).setPersistenceCacheSizeBytes((long) size);
|
||||
public void setPersistenceCacheSizeBytes(String appName, String dbURL, int size) {
|
||||
getDatabaseForApp(appName, dbURL).setPersistenceCacheSizeBytes((long) size);
|
||||
}
|
||||
|
||||
|
||||
|
@ -116,8 +116,8 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
|
|||
* @param state
|
||||
*/
|
||||
@ReactMethod
|
||||
public void keepSynced(String appName, String key, String path, ReadableArray modifiers, Boolean state) {
|
||||
getInternalReferenceForApp(appName, 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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -130,7 +130,7 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
|
|||
* @param updates
|
||||
*/
|
||||
@ReactMethod
|
||||
public void transactionTryCommit(String appName, int transactionId, ReadableMap updates) {
|
||||
public void transactionTryCommit(String appName, String dbURL, int transactionId, ReadableMap updates) {
|
||||
RNFirebaseTransactionHandler handler = transactionHandlers.get(transactionId);
|
||||
|
||||
if (handler != null) {
|
||||
|
@ -147,16 +147,16 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
|
|||
* @param applyLocally
|
||||
*/
|
||||
@ReactMethod
|
||||
public void transactionStart(final String appName, 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() {
|
||||
DatabaseReference reference = getReferenceForAppPath(appName, path);
|
||||
DatabaseReference reference = getReferenceForAppPath(appName, dbURL, path);
|
||||
|
||||
reference.runTransaction(new Transaction.Handler() {
|
||||
@Override
|
||||
public Transaction.Result doTransaction(MutableData mutableData) {
|
||||
final RNFirebaseTransactionHandler transactionHandler = new RNFirebaseTransactionHandler(transactionId, appName);
|
||||
final RNFirebaseTransactionHandler transactionHandler = new RNFirebaseTransactionHandler(transactionId, appName, dbURL);
|
||||
transactionHandlers.put(transactionId, transactionHandler);
|
||||
final WritableMap updatesMap = transactionHandler.createUpdateMap(mutableData);
|
||||
|
||||
|
@ -212,9 +212,9 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
|
|||
* @param promise
|
||||
*/
|
||||
@ReactMethod
|
||||
public void onDisconnectSet(String appName, 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, path);
|
||||
DatabaseReference ref = getReferenceForAppPath(appName, dbURL, path);
|
||||
|
||||
OnDisconnect onDisconnect = ref.onDisconnect();
|
||||
DatabaseReference.CompletionListener listener = new DatabaseReference.CompletionListener() {
|
||||
|
@ -257,8 +257,8 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
|
|||
* @param promise
|
||||
*/
|
||||
@ReactMethod
|
||||
public void onDisconnectUpdate(String appName, String path, ReadableMap props, final Promise promise) {
|
||||
DatabaseReference ref = getReferenceForAppPath(appName, path);
|
||||
public void onDisconnectUpdate(String appName, String dbURL, String path, ReadableMap props, final Promise promise) {
|
||||
DatabaseReference ref = getReferenceForAppPath(appName, dbURL, path);
|
||||
OnDisconnect ondDisconnect = ref.onDisconnect();
|
||||
|
||||
Map<String, Object> map = Utils.recursivelyDeconstructReadableMap(props);
|
||||
|
@ -279,8 +279,8 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
|
|||
* @param promise
|
||||
*/
|
||||
@ReactMethod
|
||||
public void onDisconnectRemove(String appName, String path, final Promise promise) {
|
||||
DatabaseReference ref = getReferenceForAppPath(appName, path);
|
||||
public void onDisconnectRemove(String appName, String dbURL, String path, final Promise promise) {
|
||||
DatabaseReference ref = getReferenceForAppPath(appName, dbURL, path);
|
||||
OnDisconnect onDisconnect = ref.onDisconnect();
|
||||
|
||||
onDisconnect.removeValue(new DatabaseReference.CompletionListener() {
|
||||
|
@ -299,8 +299,8 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
|
|||
* @param promise
|
||||
*/
|
||||
@ReactMethod
|
||||
public void onDisconnectCancel(String appName, String path, final Promise promise) {
|
||||
DatabaseReference ref = getReferenceForAppPath(appName, path);
|
||||
public void onDisconnectCancel(String appName, String dbURL, String path, final Promise promise) {
|
||||
DatabaseReference ref = getReferenceForAppPath(appName, dbURL, path);
|
||||
OnDisconnect onDisconnect = ref.onDisconnect();
|
||||
|
||||
onDisconnect.cancel(new DatabaseReference.CompletionListener() {
|
||||
|
@ -318,8 +318,8 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
|
|||
* @param promise
|
||||
*/
|
||||
@ReactMethod
|
||||
public void set(String appName, String path, ReadableMap props, final Promise promise) {
|
||||
DatabaseReference ref = getReferenceForAppPath(appName, path);
|
||||
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");
|
||||
|
||||
DatabaseReference.CompletionListener listener = new DatabaseReference.CompletionListener() {
|
||||
|
@ -339,8 +339,8 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
|
|||
* @param promise
|
||||
*/
|
||||
@ReactMethod
|
||||
public void setPriority(String appName, String path, ReadableMap priority, final Promise promise) {
|
||||
DatabaseReference ref = getReferenceForAppPath(appName, path);
|
||||
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");
|
||||
|
||||
DatabaseReference.CompletionListener listener = new DatabaseReference.CompletionListener() {
|
||||
|
@ -361,8 +361,8 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
|
|||
* @param promise
|
||||
*/
|
||||
@ReactMethod
|
||||
public void setWithPriority(String appName, String path, ReadableMap data, ReadableMap priority, final Promise promise) {
|
||||
DatabaseReference ref = getReferenceForAppPath(appName, path);
|
||||
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");
|
||||
|
||||
|
@ -383,8 +383,8 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
|
|||
* @param promise
|
||||
*/
|
||||
@ReactMethod
|
||||
public void update(String appName, String path, ReadableMap props, final Promise promise) {
|
||||
DatabaseReference ref = getReferenceForAppPath(appName, path);
|
||||
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);
|
||||
|
||||
DatabaseReference.CompletionListener listener = new DatabaseReference.CompletionListener() {
|
||||
|
@ -403,8 +403,8 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
|
|||
* @param promise
|
||||
*/
|
||||
@ReactMethod
|
||||
public void remove(String appName, String path, final Promise promise) {
|
||||
DatabaseReference ref = getReferenceForAppPath(appName, path);
|
||||
public void remove(String appName, String dbURL, String path, final Promise promise) {
|
||||
DatabaseReference ref = getReferenceForAppPath(appName, dbURL, path);
|
||||
|
||||
DatabaseReference.CompletionListener listener = new DatabaseReference.CompletionListener() {
|
||||
@Override
|
||||
|
@ -428,8 +428,8 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
|
|||
* @param promise
|
||||
*/
|
||||
@ReactMethod
|
||||
public void once(String appName, String key, String path, ReadableArray modifiers, String eventType, Promise promise) {
|
||||
getInternalReferenceForApp(appName, key, path, modifiers).once(eventType, 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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -439,8 +439,8 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
|
|||
* @param props ReadableMap
|
||||
*/
|
||||
@ReactMethod
|
||||
public void on(String appName, ReadableMap props) {
|
||||
getCachedInternalReferenceForApp(appName, props)
|
||||
public void on(String appName, String dbURL, ReadableMap props) {
|
||||
getCachedInternalReferenceForApp(appName, dbURL, props)
|
||||
.on(
|
||||
props.getString("eventType"),
|
||||
props.getMap("registration")
|
||||
|
@ -494,11 +494,18 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
|
|||
* Get a database instance for a specific firebase app instance
|
||||
*
|
||||
* @param appName
|
||||
* @param dbURL
|
||||
* @return
|
||||
*/
|
||||
private FirebaseDatabase getDatabaseForApp(String appName) {
|
||||
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
|
||||
FirebaseDatabase firebaseDatabase = FirebaseDatabase.getInstance(firebaseApp);
|
||||
public static FirebaseDatabase getDatabaseForApp(String appName, String dbURL) {
|
||||
FirebaseDatabase firebaseDatabase;
|
||||
if(dbURL != null && dbURL.length() > 0) {
|
||||
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)) {
|
||||
|
@ -535,8 +542,8 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
|
|||
* @param path
|
||||
* @return
|
||||
*/
|
||||
private DatabaseReference getReferenceForAppPath(String appName, String path) {
|
||||
return getDatabaseForApp(appName).getReference(path);
|
||||
private DatabaseReference getReferenceForAppPath(String appName, String dbURL, String path) {
|
||||
return getDatabaseForApp(appName, dbURL).getReference(path);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -548,10 +555,11 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
|
|||
* @param modifiers
|
||||
* @return
|
||||
*/
|
||||
private RNFirebaseDatabaseReference getInternalReferenceForApp(String appName, String key, String path, ReadableArray modifiers) {
|
||||
private RNFirebaseDatabaseReference getInternalReferenceForApp(String appName, String dbURL, String key, String path, ReadableArray modifiers) {
|
||||
return new RNFirebaseDatabaseReference(
|
||||
getReactApplicationContext(),
|
||||
appName,
|
||||
dbURL,
|
||||
key,
|
||||
path,
|
||||
modifiers
|
||||
|
@ -565,7 +573,7 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
|
|||
* @param props
|
||||
* @return
|
||||
*/
|
||||
private RNFirebaseDatabaseReference getCachedInternalReferenceForApp(String appName, 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");
|
||||
|
@ -573,7 +581,7 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
|
|||
RNFirebaseDatabaseReference existingRef = references.get(key);
|
||||
|
||||
if (existingRef == null) {
|
||||
existingRef = getInternalReferenceForApp(appName, key, path, modifiers);
|
||||
existingRef = getInternalReferenceForApp(appName, dbURL, key, path, modifiers);
|
||||
references.put(key, existingRef);
|
||||
}
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ class RNFirebaseDatabaseReference {
|
|||
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<>();
|
||||
|
@ -43,10 +44,11 @@ class RNFirebaseDatabaseReference {
|
|||
* @param refPath
|
||||
* @param modifiersArray
|
||||
*/
|
||||
RNFirebaseDatabaseReference(ReactContext context, String app, 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;
|
||||
dbURL = url;
|
||||
reactContext = context;
|
||||
buildDatabaseQueryAtPathAndModifiers(refPath, modifiersArray);
|
||||
}
|
||||
|
@ -346,9 +348,7 @@ class RNFirebaseDatabaseReference {
|
|||
* @return
|
||||
*/
|
||||
private void buildDatabaseQueryAtPathAndModifiers(String path, ReadableArray modifiers) {
|
||||
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
|
||||
FirebaseDatabase firebaseDatabase = FirebaseDatabase.getInstance(firebaseApp);
|
||||
|
||||
FirebaseDatabase firebaseDatabase = RNFirebaseDatabase.getDatabaseForApp(appName, dbURL);
|
||||
query = firebaseDatabase.getReference(path);
|
||||
List<Object> modifiersList = Utils.recursivelyDeconstructReadableArray(modifiers);
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ 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;
|
||||
|
@ -31,8 +32,9 @@ public class RNFirebaseTransactionHandler {
|
|||
boolean abort = false;
|
||||
boolean timeout = false;
|
||||
|
||||
RNFirebaseTransactionHandler(int id, String app) {
|
||||
RNFirebaseTransactionHandler(int id, String app, String url) {
|
||||
appName = app;
|
||||
dbURL = url;
|
||||
transactionId = id;
|
||||
lock = new ReentrantLock();
|
||||
condition = lock.newCondition();
|
||||
|
@ -107,6 +109,7 @@ public class RNFirebaseTransactionHandler {
|
|||
|
||||
// all events get distributed js side based on app name
|
||||
updatesMap.putString("appName", appName);
|
||||
updatesMap.putString("dbURL", dbURL);
|
||||
|
||||
if (!updatesData.hasChildren()) {
|
||||
Utils.mapPutValue("value", updatesData.getValue(), updatesMap);
|
||||
|
@ -129,6 +132,7 @@ public class RNFirebaseTransactionHandler {
|
|||
|
||||
resultMap.putInt("id", transactionId);
|
||||
resultMap.putString("appName", appName);
|
||||
resultMap.putString("dbURL", dbURL);
|
||||
|
||||
resultMap.putBoolean("timeout", timeout);
|
||||
resultMap.putBoolean("committed", committed);
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
package io.invertase.firebase.instanceid;
|
||||
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
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.google.firebase.iid.FirebaseInstanceId;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class RNFirebaseInstanceId extends ReactContextBaseJavaModule {
|
||||
|
||||
private static final String TAG = "RNFirebaseInstanceId";
|
||||
|
||||
public RNFirebaseInstanceId(ReactApplicationContext reactContext) {
|
||||
super(reactContext);
|
||||
Log.d(TAG, "New instance");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return TAG;
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void delete(Promise promise){
|
||||
try {
|
||||
Log.d(TAG, "Deleting instance id");
|
||||
FirebaseInstanceId.getInstance().deleteInstanceId();
|
||||
promise.resolve(null);
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, e.getMessage());
|
||||
promise.reject("instance_id_error", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void get(Promise promise){
|
||||
String id = FirebaseInstanceId.getInstance().getId();
|
||||
promise.resolve(id);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package io.invertase.firebase.instanceid;
|
||||
|
||||
import com.facebook.react.ReactPackage;
|
||||
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 RNFirebaseInstanceIdPackage implements ReactPackage {
|
||||
public RNFirebaseInstanceIdPackage() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 RNFirebaseInstanceId(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();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,236 @@
|
|||
package io.invertase.firebase.invites;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.util.Log;
|
||||
|
||||
import com.facebook.react.bridge.ActivityEventListener;
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
import com.facebook.react.bridge.LifecycleEventListener;
|
||||
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.ReadableMap;
|
||||
import com.facebook.react.bridge.ReadableMapKeySetIterator;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
import com.google.android.gms.appinvite.AppInviteInvitation;
|
||||
import com.google.android.gms.tasks.OnFailureListener;
|
||||
import com.google.android.gms.tasks.OnSuccessListener;
|
||||
import com.google.firebase.appinvite.FirebaseAppInvite;
|
||||
import com.google.firebase.dynamiclinks.FirebaseDynamicLinks;
|
||||
import com.google.firebase.dynamiclinks.PendingDynamicLinkData;
|
||||
|
||||
import java.util.Arrays;
|
||||
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";
|
||||
private static final int REQUEST_INVITE = 81283;
|
||||
private boolean mInitialInvitationInitialized = false;
|
||||
private String mInitialDeepLink = null;
|
||||
private String mInitialInvitationId = null;
|
||||
private Promise mPromise = null;
|
||||
|
||||
public RNFirebaseInvites(ReactApplicationContext context) {
|
||||
super(context);
|
||||
getReactApplicationContext().addActivityEventListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "RNFirebaseInvites";
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void getInitialInvitation(final Promise promise) {
|
||||
if (mInitialInvitationInitialized) {
|
||||
if (mInitialDeepLink != null || mInitialInvitationId != null) {
|
||||
promise.resolve(buildInvitationMap(mInitialDeepLink, mInitialInvitationId));
|
||||
} else {
|
||||
promise.resolve(null);
|
||||
}
|
||||
} else {
|
||||
if (getCurrentActivity() != null) {
|
||||
FirebaseDynamicLinks.getInstance()
|
||||
.getDynamicLink(getCurrentActivity().getIntent())
|
||||
.addOnSuccessListener(new OnSuccessListener<PendingDynamicLinkData>() {
|
||||
@Override
|
||||
public void onSuccess(PendingDynamicLinkData pendingDynamicLinkData) {
|
||||
if (pendingDynamicLinkData != null) {
|
||||
FirebaseAppInvite invite = FirebaseAppInvite.getInvitation(pendingDynamicLinkData);
|
||||
if (invite == null) {
|
||||
promise.resolve(null);
|
||||
return;
|
||||
}
|
||||
|
||||
mInitialDeepLink = pendingDynamicLinkData.getLink().toString();
|
||||
mInitialInvitationId = invite.getInvitationId();
|
||||
promise.resolve(buildInvitationMap(mInitialDeepLink, mInitialInvitationId));
|
||||
} else {
|
||||
promise.resolve(null);
|
||||
}
|
||||
mInitialInvitationInitialized = true;
|
||||
}
|
||||
})
|
||||
.addOnFailureListener(new OnFailureListener() {
|
||||
@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);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
Log.d(TAG, "getInitialInvitation: activity is null");
|
||||
promise.resolve(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@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");
|
||||
return;
|
||||
}
|
||||
if (!invitationMap.hasKey("title")) {
|
||||
promise.reject("invites/invalid-invitation", "The supplied invitation is missing a 'title' field");
|
||||
return;
|
||||
}
|
||||
|
||||
AppInviteInvitation.IntentBuilder ib = new AppInviteInvitation.IntentBuilder(invitationMap.getString("title"));
|
||||
if (invitationMap.hasKey("androidMinimumVersionCode")) {
|
||||
Double androidMinimumVersionCode = invitationMap.getDouble("androidMinimumVersionCode");
|
||||
ib = ib.setAndroidMinimumVersionCode(androidMinimumVersionCode.intValue());
|
||||
}
|
||||
if (invitationMap.hasKey("callToActionText")) {
|
||||
ib = ib.setCallToActionText(invitationMap.getString("callToActionText"));
|
||||
}
|
||||
if (invitationMap.hasKey("customImage")) {
|
||||
ib = ib.setCustomImage(Uri.parse(invitationMap.getString("customImage")));
|
||||
}
|
||||
if (invitationMap.hasKey("deepLink")) {
|
||||
ib = ib.setDeepLink(Uri.parse(invitationMap.getString("deepLink")));
|
||||
}
|
||||
if (invitationMap.hasKey("iosClientId")) {
|
||||
ib = ib.setOtherPlatformsTargetApplication(
|
||||
AppInviteInvitation.IntentBuilder.PlatformMode.PROJECT_PLATFORM_IOS,
|
||||
invitationMap.getString("iosClientId"));
|
||||
}
|
||||
ib = ib.setMessage(invitationMap.getString("message"));
|
||||
|
||||
// Android specific properties
|
||||
if (invitationMap.hasKey("android")) {
|
||||
ReadableMap androidMap = invitationMap.getMap("android");
|
||||
|
||||
if (androidMap.hasKey("additionalReferralParameters")) {
|
||||
Map<String, String> arpMap = new HashMap<>();
|
||||
ReadableMap arpReadableMap = androidMap.getMap("additionalReferralParameters");
|
||||
ReadableMapKeySetIterator iterator = arpReadableMap.keySetIterator();
|
||||
while (iterator.hasNextKey()) {
|
||||
String key = iterator.nextKey();
|
||||
arpMap.put(key, arpReadableMap.getString(key));
|
||||
}
|
||||
ib = ib.setAdditionalReferralParameters(arpMap);
|
||||
}
|
||||
if (androidMap.hasKey("emailHtmlContent")) {
|
||||
ib = ib.setEmailHtmlContent(invitationMap.getString("emailHtmlContent"));
|
||||
}
|
||||
if (androidMap.hasKey("emailSubject")) {
|
||||
ib = ib.setEmailSubject(invitationMap.getString("emailSubject"));
|
||||
}
|
||||
if (androidMap.hasKey("googleAnalyticsTrackingId")) {
|
||||
ib = ib.setGoogleAnalyticsTrackingId(invitationMap.getString("googleAnalyticsTrackingId"));
|
||||
}
|
||||
}
|
||||
|
||||
Intent invitationIntent = ib.build();
|
||||
// Save the promise for later
|
||||
this.mPromise = promise;
|
||||
|
||||
// Start the intent
|
||||
this.getCurrentActivity().startActivityForResult(invitationIntent, REQUEST_INVITE);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Start ActivityEventListener methods
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
@Override
|
||||
public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
|
||||
if (requestCode == REQUEST_INVITE) {
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
String[] ids = AppInviteInvitation.getInvitationIds(resultCode, data);
|
||||
mPromise.resolve(Arguments.fromList(Arrays.asList(ids)));
|
||||
} else if (resultCode == Activity.RESULT_CANCELED) {
|
||||
mPromise.reject("invites/invitation-cancelled", "Invitation cancelled");
|
||||
} else {
|
||||
mPromise.reject("invites/invitation-error", "Invitation failed to send");
|
||||
}
|
||||
// Clear the promise
|
||||
mPromise = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNewIntent(Intent intent) {
|
||||
FirebaseDynamicLinks.getInstance()
|
||||
.getDynamicLink(intent)
|
||||
.addOnSuccessListener(new OnSuccessListener<PendingDynamicLinkData>() {
|
||||
@Override
|
||||
public void onSuccess(PendingDynamicLinkData pendingDynamicLinkData) {
|
||||
if (pendingDynamicLinkData != null) {
|
||||
FirebaseAppInvite invite = FirebaseAppInvite.getInvitation(pendingDynamicLinkData);
|
||||
if (invite == null) {
|
||||
// this is a dynamic link, not an invitation
|
||||
return;
|
||||
}
|
||||
|
||||
String deepLink = pendingDynamicLinkData.getLink().toString();
|
||||
String invitationId = invite.getInvitationId();
|
||||
WritableMap invitationMap = buildInvitationMap(deepLink, invitationId);
|
||||
Utils.sendEvent(getReactApplicationContext(), "invites_invitation_received", invitationMap);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// End ActivityEventListener methods
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Start LifecycleEventListener methods
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
@Override
|
||||
public void onHostResume() {
|
||||
// Not required for this module
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHostPause() {
|
||||
// Not required for this module
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHostDestroy() {
|
||||
mInitialDeepLink = null;
|
||||
mInitialInvitationId = null;
|
||||
mInitialInvitationInitialized = false;
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// End LifecycleEventListener methods
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
private WritableMap buildInvitationMap(String deepLink, String invitationId) {
|
||||
WritableMap invitationMap = Arguments.createMap();
|
||||
invitationMap.putString("deepLink", deepLink);
|
||||
invitationMap.putString("invitationId", invitationId);
|
||||
|
||||
return invitationMap;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package io.invertase.firebase.invites;
|
||||
|
||||
import com.facebook.react.ReactPackage;
|
||||
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 RNFirebaseInvitesPackage implements ReactPackage {
|
||||
public RNFirebaseInvitesPackage() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 RNFirebaseInvites(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();
|
||||
}
|
||||
}
|
|
@ -6,8 +6,6 @@ import android.net.Uri;
|
|||
import android.support.annotation.NonNull;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.facebook.react.bridge.ActivityEventListener;
|
||||
import com.facebook.react.bridge.LifecycleEventListener;
|
||||
import com.facebook.react.bridge.Promise;
|
||||
|
@ -16,6 +14,7 @@ import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
|||
import com.facebook.react.bridge.ReactMethod;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
|
||||
import com.google.firebase.appinvite.FirebaseAppInvite;
|
||||
import com.google.firebase.dynamiclinks.DynamicLink;
|
||||
import com.google.firebase.dynamiclinks.FirebaseDynamicLinks;
|
||||
import com.google.firebase.dynamiclinks.ShortDynamicLink;
|
||||
|
@ -33,14 +32,6 @@ public class RNFirebaseLinks extends ReactContextBaseJavaModule implements Activ
|
|||
private String mInitialLink = null;
|
||||
private boolean mInitialLinkInitialized = false;
|
||||
|
||||
private interface ResolveHandler {
|
||||
void onResolved(String url);
|
||||
}
|
||||
|
||||
private interface ErrorHandler {
|
||||
void onError(Exception e);
|
||||
}
|
||||
|
||||
public RNFirebaseLinks(ReactApplicationContext reactContext) {
|
||||
super(reactContext);
|
||||
getReactApplicationContext().addActivityEventListener(this);
|
||||
|
@ -52,28 +43,49 @@ public class RNFirebaseLinks extends ReactContextBaseJavaModule implements Activ
|
|||
return "RNFirebaseLinks";
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void createDynamicLink(final ReadableMap linkData, final Promise promise) {
|
||||
try {
|
||||
DynamicLink.Builder builder = getDynamicLinkBuilder(linkData);
|
||||
String link = builder.buildDynamicLink().getUri().toString();
|
||||
Log.d(TAG, "created dynamic link: " + link);
|
||||
promise.resolve(link);
|
||||
} catch (Exception ex) {
|
||||
Log.e(TAG, "create dynamic link failure " + ex.getMessage());
|
||||
promise.reject("links/failure", ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void resolveLink(Intent intent, final ResolveHandler resolveHandler, final ErrorHandler errorHandler) {
|
||||
FirebaseDynamicLinks.getInstance()
|
||||
.getDynamicLink(intent)
|
||||
.addOnSuccessListener(new OnSuccessListener<PendingDynamicLinkData>() {
|
||||
@Override
|
||||
public void onSuccess(PendingDynamicLinkData pendingDynamicLinkData) {
|
||||
if (pendingDynamicLinkData != null) {
|
||||
Uri deepLinkUri = pendingDynamicLinkData.getLink();
|
||||
String url = deepLinkUri.toString();
|
||||
resolveHandler.onResolved(url);
|
||||
} else {
|
||||
resolveHandler.onResolved(null);
|
||||
}
|
||||
@ReactMethod
|
||||
public void createShortDynamicLink(final ReadableMap linkData, final String type, final Promise promise) {
|
||||
try {
|
||||
DynamicLink.Builder builder = getDynamicLinkBuilder(linkData);
|
||||
Task<ShortDynamicLink> shortLinkTask;
|
||||
if ("SHORT".equals(type)) {
|
||||
shortLinkTask = builder.buildShortDynamicLink(ShortDynamicLink.Suffix.SHORT);
|
||||
} else if ("UNGUESSABLE".equals(type)) {
|
||||
shortLinkTask = builder.buildShortDynamicLink(ShortDynamicLink.Suffix.UNGUESSABLE);
|
||||
} else {
|
||||
shortLinkTask = builder.buildShortDynamicLink();
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
})
|
||||
.addOnFailureListener(new OnFailureListener() {
|
||||
@Override
|
||||
public void onFailure(@NonNull Exception e) {
|
||||
errorHandler.onError(e);
|
||||
}
|
||||
});
|
||||
} catch (Exception ex) {
|
||||
Log.e(TAG, "create short dynamic link failure " + ex.getMessage());
|
||||
promise.reject("links/failure", ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
|
@ -81,26 +93,29 @@ public class RNFirebaseLinks extends ReactContextBaseJavaModule implements Activ
|
|||
if (mInitialLinkInitialized) {
|
||||
promise.resolve(mInitialLink);
|
||||
} else {
|
||||
Activity activity = getCurrentActivity();
|
||||
if (activity != null) {
|
||||
resolveLink(activity.getIntent(), new ResolveHandler() {
|
||||
@Override
|
||||
public void onResolved(String url) {
|
||||
if (url != null) {
|
||||
mInitialLink = url;
|
||||
Log.d(TAG, "getInitialLink received a new dynamic link from pendingDynamicLinkData");
|
||||
if (getCurrentActivity() != null) {
|
||||
FirebaseDynamicLinks.getInstance()
|
||||
.getDynamicLink(getCurrentActivity().getIntent())
|
||||
.addOnSuccessListener(new OnSuccessListener<PendingDynamicLinkData>() {
|
||||
@Override
|
||||
public void onSuccess(PendingDynamicLinkData pendingDynamicLinkData) {
|
||||
if (pendingDynamicLinkData != null
|
||||
&& !isInvitation(pendingDynamicLinkData)) {
|
||||
|
||||
mInitialLink = pendingDynamicLinkData.getLink().toString();
|
||||
}
|
||||
Log.d(TAG, "getInitialLink: link is: " + mInitialLink);
|
||||
mInitialLinkInitialized = true;
|
||||
promise.resolve(mInitialLink);
|
||||
}
|
||||
Log.d(TAG, "initial link is: " + mInitialLink);
|
||||
promise.resolve(mInitialLink);
|
||||
mInitialLinkInitialized = true;
|
||||
}
|
||||
}, new ErrorHandler() {
|
||||
@Override
|
||||
public void onError(Exception e) {
|
||||
Log.e(TAG, "getInitialLink: failed to resolve link", e);
|
||||
promise.reject("links/getDynamicLink", e.getMessage(), e);
|
||||
}
|
||||
});
|
||||
})
|
||||
.addOnFailureListener(new OnFailureListener() {
|
||||
@Override
|
||||
public void onFailure(@NonNull Exception e) {
|
||||
Log.e(TAG, "getInitialLink: failed to resolve link", e);
|
||||
promise.reject("link/initial-link-error", e.getMessage(), e);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
Log.d(TAG, "getInitialLink: activity is null");
|
||||
promise.resolve(null);
|
||||
|
@ -108,6 +123,9 @@ public class RNFirebaseLinks extends ReactContextBaseJavaModule implements Activ
|
|||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Start ActivityEventListener methods
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
@Override
|
||||
public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
|
||||
// Not required for this module
|
||||
|
@ -115,22 +133,26 @@ public class RNFirebaseLinks extends ReactContextBaseJavaModule implements Activ
|
|||
|
||||
@Override
|
||||
public void onNewIntent(Intent intent) {
|
||||
resolveLink(intent, new ResolveHandler() {
|
||||
@Override
|
||||
public void onResolved(String url) {
|
||||
if (url != null) {
|
||||
Log.d(TAG, "handleLink: sending link: " + url);
|
||||
Utils.sendEvent(getReactApplicationContext(), "dynamic_link_received", url);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, new ErrorHandler() {
|
||||
@Override
|
||||
public void onError(Exception e) {
|
||||
Log.e(TAG, "handleLink: failed to resolve link", e);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// End ActivityEventListener methods
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Start LifecycleEventListener methods
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
@Override
|
||||
public void onHostResume() {
|
||||
// Not required for this module
|
||||
|
@ -146,139 +168,131 @@ public class RNFirebaseLinks extends ReactContextBaseJavaModule implements Activ
|
|||
mInitialLink = null;
|
||||
mInitialLinkInitialized = false;
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// End LifecycleEventListener methods
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
@ReactMethod
|
||||
public void createDynamicLink(final ReadableMap parameters, final Promise promise) {
|
||||
try {
|
||||
Map<String, Object> metaData = Utils.recursivelyDeconstructReadableMap(parameters);
|
||||
|
||||
DynamicLink.Builder builder = getDynamicLinkBuilderFromMap(metaData);
|
||||
Uri link = builder.buildDynamicLink().getUri();
|
||||
|
||||
Log.d(TAG, "created dynamic link: " + link.toString());
|
||||
promise.resolve(link.toString());
|
||||
} catch (Exception ex) {
|
||||
Log.e(TAG, "create dynamic link failure " + ex.getMessage());
|
||||
promise.reject("links/failure", ex.getMessage(), ex);
|
||||
}
|
||||
// Looks at the internals of the link data to detect whether it's an invitation or not
|
||||
private boolean isInvitation(PendingDynamicLinkData pendingDynamicLinkData) {
|
||||
return FirebaseAppInvite.getInvitation(pendingDynamicLinkData) != null;
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void createShortDynamicLink(final ReadableMap parameters, final Promise promise) {
|
||||
private DynamicLink.Builder getDynamicLinkBuilder(final ReadableMap linkData) {
|
||||
DynamicLink.Builder builder = FirebaseDynamicLinks.getInstance().createDynamicLink();
|
||||
try {
|
||||
Map<String, Object> metaData = Utils.recursivelyDeconstructReadableMap(parameters);
|
||||
|
||||
DynamicLink.Builder builder = getDynamicLinkBuilderFromMap(metaData);
|
||||
|
||||
Task<ShortDynamicLink> shortLinkTask = getShortDynamicLinkTask(builder, metaData)
|
||||
.addOnCompleteListener(new OnCompleteListener<ShortDynamicLink>() {
|
||||
@Override
|
||||
public void onComplete(@NonNull Task<ShortDynamicLink> task) {
|
||||
if (task.isSuccessful()) {
|
||||
Uri shortLink = task.getResult().getShortLink();
|
||||
Log.d(TAG, "created short dynamic link: " + shortLink.toString());
|
||||
promise.resolve(shortLink.toString());
|
||||
} else {
|
||||
Log.e(TAG, "create short dynamic link failure " + task.getException().getMessage());
|
||||
promise.reject("links/failure", task.getException().getMessage(), task.getException());
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (Exception ex) {
|
||||
Log.e(TAG, "create short dynamic link failure " + ex.getMessage());
|
||||
promise.reject("links/failure", ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
private DynamicLink.Builder getDynamicLinkBuilderFromMap(final Map<String, Object> metaData) {
|
||||
DynamicLink.Builder parametersBuilder = FirebaseDynamicLinks.getInstance().createDynamicLink();
|
||||
try {
|
||||
parametersBuilder.setLink(Uri.parse((String) metaData.get("link")));
|
||||
parametersBuilder.setDynamicLinkDomain((String) metaData.get("dynamicLinkDomain"));
|
||||
setAndroidParameters(metaData, parametersBuilder);
|
||||
setIosParameters(metaData, parametersBuilder);
|
||||
setSocialMetaTagParameters(metaData, parametersBuilder);
|
||||
builder.setLink(Uri.parse(linkData.getString("link")));
|
||||
builder.setDynamicLinkDomain(linkData.getString("dynamicLinkDomain"));
|
||||
setAnalyticsParameters(linkData.getMap("analytics"), builder);
|
||||
setAndroidParameters(linkData.getMap("android"), builder);
|
||||
setIosParameters(linkData.getMap("ios"), builder);
|
||||
setITunesParameters(linkData.getMap("itunes"), builder);
|
||||
setNavigationParameters(linkData.getMap("navigation"), builder);
|
||||
setSocialParameters(linkData.getMap("social"), builder);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "error while building parameters " + e.getMessage());
|
||||
throw e;
|
||||
}
|
||||
return parametersBuilder;
|
||||
return builder;
|
||||
}
|
||||
|
||||
private Task<ShortDynamicLink> getShortDynamicLinkTask(final DynamicLink.Builder builder, final Map<String, Object> metaData) {
|
||||
Map<String, Object> suffix = (Map<String, Object>) metaData.get("suffix");
|
||||
if (suffix != null) {
|
||||
String option = (String) suffix.get("option");
|
||||
if ("SHORT".equals(option)) {
|
||||
return builder.buildShortDynamicLink(ShortDynamicLink.Suffix.SHORT);
|
||||
} else if ("UNGUESSABLE".equals(option)) {
|
||||
return builder.buildShortDynamicLink(ShortDynamicLink.Suffix.UNGUESSABLE);
|
||||
}
|
||||
private void setAnalyticsParameters(final ReadableMap analyticsData, final DynamicLink.Builder builder) {
|
||||
DynamicLink.GoogleAnalyticsParameters.Builder analyticsParameters = new DynamicLink.GoogleAnalyticsParameters.Builder();
|
||||
|
||||
if (analyticsData.hasKey("campaign")) {
|
||||
analyticsParameters.setCampaign(analyticsData.getString("campaign"));
|
||||
}
|
||||
return builder.buildShortDynamicLink();
|
||||
if (analyticsData.hasKey("content")) {
|
||||
analyticsParameters.setContent(analyticsData.getString("content"));
|
||||
}
|
||||
if (analyticsData.hasKey("medium")) {
|
||||
analyticsParameters.setMedium(analyticsData.getString("medium"));
|
||||
}
|
||||
if (analyticsData.hasKey("source")) {
|
||||
analyticsParameters.setSource(analyticsData.getString("source"));
|
||||
}
|
||||
if (analyticsData.hasKey("term")) {
|
||||
analyticsParameters.setTerm(analyticsData.getString("term"));
|
||||
}
|
||||
builder.setGoogleAnalyticsParameters(analyticsParameters.build());
|
||||
}
|
||||
|
||||
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"));
|
||||
|
||||
private void setAndroidParameters(final Map<String, Object> metaData, final DynamicLink.Builder parametersBuilder) {
|
||||
Map<String, Object> androidParameters = (Map<String, Object>) metaData.get("androidInfo");
|
||||
if (androidParameters != null) {
|
||||
DynamicLink.AndroidParameters.Builder androidParametersBuilder =
|
||||
new DynamicLink.AndroidParameters.Builder((String) androidParameters.get("androidPackageName"));
|
||||
|
||||
if (androidParameters.containsKey("androidFallbackLink")) {
|
||||
androidParametersBuilder.setFallbackUrl(Uri.parse((String) androidParameters.get("androidFallbackLink")));
|
||||
if (androidData.hasKey("fallbackUrl")) {
|
||||
androidParameters.setFallbackUrl(Uri.parse(androidData.getString("fallbackUrl")));
|
||||
}
|
||||
if (androidParameters.containsKey("androidMinPackageVersionCode")) {
|
||||
androidParametersBuilder.setMinimumVersion(Integer.parseInt((String) androidParameters.get("androidMinPackageVersionCode")));
|
||||
if (androidData.hasKey("minimumVersion")) {
|
||||
androidParameters.setMinimumVersion(Integer.parseInt(androidData.getString("minimumVersion")));
|
||||
}
|
||||
parametersBuilder.setAndroidParameters(androidParametersBuilder.build());
|
||||
builder.setAndroidParameters(androidParameters.build());
|
||||
}
|
||||
}
|
||||
|
||||
private void setIosParameters(final Map<String, Object> metaData, final DynamicLink.Builder parametersBuilder) {
|
||||
Map<String, Object> iosParameters = (Map<String, Object>) metaData.get("iosInfo");
|
||||
if (iosParameters != null) {
|
||||
DynamicLink.IosParameters.Builder iosParametersBuilder =
|
||||
new DynamicLink.IosParameters.Builder((String) iosParameters.get("iosBundleId"));
|
||||
private void setIosParameters(final ReadableMap iosData, final DynamicLink.Builder builder) {
|
||||
if (iosData.hasKey("bundleId")) {
|
||||
DynamicLink.IosParameters.Builder iosParameters =
|
||||
new DynamicLink.IosParameters.Builder(iosData.getString("bundleId"));
|
||||
|
||||
if (iosParameters.containsKey("iosAppStoreId")) {
|
||||
iosParametersBuilder.setAppStoreId((String) iosParameters.get("iosAppStoreId"));
|
||||
if (iosData.hasKey("appStoreId")) {
|
||||
iosParameters.setAppStoreId(iosData.getString("appStoreId"));
|
||||
}
|
||||
if (iosParameters.containsKey("iosCustomScheme")) {
|
||||
iosParametersBuilder.setCustomScheme((String) iosParameters.get("iosCustomScheme"));
|
||||
if (iosData.hasKey("customScheme")) {
|
||||
iosParameters.setCustomScheme(iosData.getString("customScheme"));
|
||||
}
|
||||
if (iosParameters.containsKey("iosFallbackLink")) {
|
||||
iosParametersBuilder.setFallbackUrl(Uri.parse((String) iosParameters.get("iosFallbackLink")));
|
||||
if (iosData.hasKey("fallbackUrl")) {
|
||||
iosParameters.setFallbackUrl(Uri.parse(iosData.getString("fallbackUrl")));
|
||||
}
|
||||
if (iosParameters.containsKey("iosIpadBundleId")) {
|
||||
iosParametersBuilder.setIpadBundleId((String) iosParameters.get("iosIpadBundleId"));
|
||||
if (iosData.hasKey("iPadBundleId")) {
|
||||
iosParameters.setIpadBundleId(iosData.getString("iPadBundleId"));
|
||||
}
|
||||
if (iosParameters.containsKey("iosIpadFallbackLink")) {
|
||||
iosParametersBuilder.setIpadFallbackUrl(Uri.parse((String) iosParameters.get("iosIpadFallbackLink")));
|
||||
if (iosData.hasKey("iPadFallbackUrl")) {
|
||||
iosParameters.setIpadFallbackUrl(Uri.parse(iosData.getString("iPadFallbackUrl")));
|
||||
}
|
||||
if (iosParameters.containsKey("iosMinPackageVersionCode")) {
|
||||
iosParametersBuilder.setMinimumVersion((String) iosParameters.get("iosMinPackageVersionCode"));
|
||||
if (iosData.hasKey("minimumVersion")) {
|
||||
iosParameters.setMinimumVersion(iosData.getString("minimumVersion"));
|
||||
}
|
||||
parametersBuilder.setIosParameters(iosParametersBuilder.build());
|
||||
builder.setIosParameters(iosParameters.build());
|
||||
}
|
||||
}
|
||||
|
||||
private void setSocialMetaTagParameters(final Map<String, Object> metaData, final DynamicLink.Builder parametersBuilder) {
|
||||
Map<String, Object> socialMetaTagParameters = (Map<String, Object>) metaData.get("socialMetaTagInfo");
|
||||
if (socialMetaTagParameters != null) {
|
||||
DynamicLink.SocialMetaTagParameters.Builder socialMetaTagParametersBuilder =
|
||||
new DynamicLink.SocialMetaTagParameters.Builder();
|
||||
private void setITunesParameters(final ReadableMap itunesData, final DynamicLink.Builder builder) {
|
||||
DynamicLink.ItunesConnectAnalyticsParameters.Builder itunesParameters = new DynamicLink.ItunesConnectAnalyticsParameters.Builder();
|
||||
|
||||
if (socialMetaTagParameters.containsKey("socialDescription")) {
|
||||
socialMetaTagParametersBuilder.setDescription((String) socialMetaTagParameters.get("socialDescription"));
|
||||
}
|
||||
if (socialMetaTagParameters.containsKey("socialImageLink")) {
|
||||
socialMetaTagParametersBuilder.setImageUrl(Uri.parse((String) socialMetaTagParameters.get("socialImageLink")));
|
||||
}
|
||||
if (socialMetaTagParameters.containsKey("socialTitle")) {
|
||||
socialMetaTagParametersBuilder.setTitle((String) socialMetaTagParameters.get("socialTitle"));
|
||||
}
|
||||
parametersBuilder.setSocialMetaTagParameters(socialMetaTagParametersBuilder.build());
|
||||
if (itunesData.hasKey("affiliateToken")) {
|
||||
itunesParameters.setAffiliateToken(itunesData.getString("affiliateToken"));
|
||||
}
|
||||
if (itunesData.hasKey("campaignToken")) {
|
||||
itunesParameters.setCampaignToken(itunesData.getString("campaignToken"));
|
||||
}
|
||||
if (itunesData.hasKey("providerToken")) {
|
||||
itunesParameters.setProviderToken(itunesData.getString("providerToken"));
|
||||
}
|
||||
builder.setItunesConnectAnalyticsParameters(itunesParameters.build());
|
||||
}
|
||||
|
||||
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"));
|
||||
}
|
||||
builder.setNavigationInfoParameters(navigationParameters.build());
|
||||
}
|
||||
|
||||
private void setSocialParameters(final ReadableMap socialData, final DynamicLink.Builder builder) {
|
||||
DynamicLink.SocialMetaTagParameters.Builder socialParameters = new DynamicLink.SocialMetaTagParameters.Builder();
|
||||
|
||||
if (socialData.hasKey("descriptionText")) {
|
||||
socialParameters.setDescription(socialData.getString("descriptionText"));
|
||||
}
|
||||
if (socialData.hasKey("imageUrl")) {
|
||||
socialParameters.setImageUrl(Uri.parse(socialData.getString("imageUrl")));
|
||||
}
|
||||
if (socialData.hasKey("title")) {
|
||||
socialParameters.setTitle(socialData.getString("title"));
|
||||
}
|
||||
builder.setSocialMetaTagParameters(socialParameters.build());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,43 +0,0 @@
|
|||
package io.invertase.firebase.messaging;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.util.Log;
|
||||
|
||||
import me.leolin.shortcutbadger.ShortcutBadger;
|
||||
|
||||
public class BadgeHelper {
|
||||
|
||||
private static final String TAG = "BadgeHelper";
|
||||
private static final String PREFERENCES_FILE = "BadgeCountFile";
|
||||
private static final String BADGE_COUNT_KEY = "BadgeCount";
|
||||
|
||||
private Context mContext;
|
||||
private SharedPreferences sharedPreferences = null;
|
||||
|
||||
public BadgeHelper(Context context) {
|
||||
mContext = context;
|
||||
sharedPreferences = (SharedPreferences) mContext.getSharedPreferences(PREFERENCES_FILE, Context.MODE_PRIVATE);
|
||||
}
|
||||
|
||||
public int getBadgeCount() {
|
||||
return sharedPreferences.getInt(BADGE_COUNT_KEY, 0);
|
||||
}
|
||||
|
||||
public void setBadgeCount(int badgeCount) {
|
||||
storeBadgeCount(badgeCount);
|
||||
if (badgeCount == 0) {
|
||||
ShortcutBadger.removeCount(mContext);
|
||||
Log.d(TAG, "Remove count");
|
||||
} else {
|
||||
ShortcutBadger.applyCount(mContext, badgeCount);
|
||||
Log.d(TAG, "Apply count: " + badgeCount);
|
||||
}
|
||||
}
|
||||
|
||||
private void storeBadgeCount(int badgeCount) {
|
||||
SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||
editor.putInt(BADGE_COUNT_KEY, badgeCount);
|
||||
editor.apply();
|
||||
}
|
||||
}
|
|
@ -110,23 +110,28 @@ public class BundleJSONConverter {
|
|||
SETTERS.put(JSONArray.class, new Setter() {
|
||||
public void setOnBundle(Bundle bundle, String key, Object value) throws JSONException {
|
||||
JSONArray jsonArray = (JSONArray) value;
|
||||
ArrayList<String> stringArrayList = new ArrayList<String>();
|
||||
// Empty list, can't even figure out the type, assume an ArrayList<String>
|
||||
if (jsonArray.length() == 0) {
|
||||
bundle.putStringArrayList(key, stringArrayList);
|
||||
bundle.putStringArrayList(key, new ArrayList<String>());
|
||||
return;
|
||||
}
|
||||
|
||||
// Only strings are supported for now
|
||||
for (int i = 0; i < jsonArray.length(); i++) {
|
||||
Object current = jsonArray.get(i);
|
||||
if (current instanceof String) {
|
||||
stringArrayList.add((String) current);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unexpected type in an array: " + current.getClass());
|
||||
if (jsonArray.get(0) instanceof String) {
|
||||
ArrayList<String> stringArrayList = new ArrayList<String>();
|
||||
for (int i = 0; i < jsonArray.length(); i++) {
|
||||
stringArrayList.add((String) jsonArray.get(i));
|
||||
}
|
||||
bundle.putStringArrayList(key, stringArrayList);
|
||||
} else if (jsonArray.get(0) instanceof JSONObject) {
|
||||
ArrayList<Bundle> bundleArrayList = new ArrayList<>();
|
||||
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());
|
||||
}
|
||||
bundle.putStringArrayList(key, stringArrayList);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -152,13 +157,18 @@ public class BundleJSONConverter {
|
|||
continue;
|
||||
}
|
||||
|
||||
// Special case List<String> as getClass would not work, since List is an interface
|
||||
// Special case List<?> as getClass would not work, since List is an interface
|
||||
if (value instanceof List<?>) {
|
||||
JSONArray jsonArray = new JSONArray();
|
||||
@SuppressWarnings("unchecked")
|
||||
List<String> listValue = (List<String>) value;
|
||||
for (String stringValue : listValue) {
|
||||
jsonArray.put(stringValue);
|
||||
List<Object> listValue = (List<Object>) value;
|
||||
for (Object objValue : listValue) {
|
||||
if (objValue instanceof String) {
|
||||
jsonArray.put(objValue);
|
||||
} else if (objValue instanceof Bundle) {
|
||||
jsonArray.put(convertToJSON((Bundle) objValue));
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unsupported type: " + objValue.getClass());
|
||||
}
|
||||
}
|
||||
json.put(key, jsonArray);
|
||||
continue;
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
package io.invertase.firebase.messaging;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.firebase.iid.FirebaseInstanceId;
|
||||
import com.google.firebase.iid.FirebaseInstanceIdService;
|
||||
|
||||
public class InstanceIdService extends FirebaseInstanceIdService {
|
||||
|
||||
private static final String TAG = "InstanceIdService";
|
||||
|
||||
/**
|
||||
* Called if InstanceID token is updated. This may occur if the security of
|
||||
* the previous token had been compromised. This call is initiated by the
|
||||
* InstanceID provider.
|
||||
*/
|
||||
@Override
|
||||
public void onTokenRefresh() {
|
||||
// Get updated InstanceID token.
|
||||
String refreshedToken = FirebaseInstanceId.getInstance().getToken();
|
||||
Log.d(TAG, "Refreshed token: " + refreshedToken);
|
||||
|
||||
// Broadcast refreshed token
|
||||
Intent i = new Intent("io.invertase.firebase.messaging.FCMRefreshToken");
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString("token", refreshedToken);
|
||||
i.putExtras(bundle);
|
||||
sendBroadcast(i);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
package io.invertase.firebase.messaging;
|
||||
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
import com.google.firebase.messaging.RemoteMessage;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
public class MessagingSerializer {
|
||||
public static WritableMap parseRemoteMessage(RemoteMessage message) {
|
||||
WritableMap messageMap = Arguments.createMap();
|
||||
WritableMap dataMap = Arguments.createMap();
|
||||
|
||||
if (message.getCollapseKey() != null) {
|
||||
messageMap.putString("collapseKey", message.getCollapseKey());
|
||||
}
|
||||
|
||||
if (message.getData() != null) {
|
||||
for (Map.Entry<String, String> e : message.getData().entrySet()) {
|
||||
dataMap.putString(e.getKey(), e.getValue());
|
||||
}
|
||||
}
|
||||
messageMap.putMap("data", dataMap);
|
||||
|
||||
if (message.getFrom() != null) {
|
||||
messageMap.putString("from", message.getFrom());
|
||||
}
|
||||
if (message.getMessageId() != null) {
|
||||
messageMap.putString("messageId", message.getMessageId());
|
||||
}
|
||||
if (message.getMessageType() != null) {
|
||||
messageMap.putString("messageType", message.getMessageType());
|
||||
}
|
||||
messageMap.putDouble("sentTime", message.getSentTime());
|
||||
if (message.getTo() != null) {
|
||||
messageMap.putString("to", message.getTo());
|
||||
}
|
||||
messageMap.putDouble("ttl", message.getTtl());
|
||||
|
||||
return messageMap;
|
||||
}
|
||||
}
|
|
@ -1,64 +0,0 @@
|
|||
package io.invertase.firebase.messaging;
|
||||
|
||||
import java.util.Map;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.firebase.messaging.FirebaseMessagingService;
|
||||
import com.google.firebase.messaging.RemoteMessage;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
public class MessagingService extends FirebaseMessagingService {
|
||||
|
||||
private static final String TAG = "MessagingService";
|
||||
|
||||
@Override
|
||||
public void onMessageReceived(RemoteMessage remoteMessage) {
|
||||
Log.d(TAG, "Remote message received");
|
||||
Intent i = new Intent("io.invertase.firebase.messaging.ReceiveNotification");
|
||||
i.putExtra("data", remoteMessage);
|
||||
handleBadge(remoteMessage);
|
||||
buildLocalNotification(remoteMessage);
|
||||
sendOrderedBroadcast(i, null);
|
||||
}
|
||||
|
||||
private void handleBadge(RemoteMessage remoteMessage) {
|
||||
BadgeHelper badgeHelper = new BadgeHelper(this);
|
||||
if (remoteMessage.getData() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Map data = remoteMessage.getData();
|
||||
if (data.get("badge") == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
int badgeCount = Integer.parseInt((String)data.get("badge"));
|
||||
badgeHelper.setBadgeCount(badgeCount);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Badge count needs to be an integer", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void buildLocalNotification(RemoteMessage remoteMessage) {
|
||||
if(remoteMessage.getData() == null){
|
||||
return;
|
||||
}
|
||||
Map<String, String> data = remoteMessage.getData();
|
||||
String customNotification = data.get("custom_notification");
|
||||
if(customNotification != null){
|
||||
try {
|
||||
Bundle bundle = BundleJSONConverter.convertToBundle(new JSONObject(customNotification));
|
||||
RNFirebaseLocalMessagingHelper helper = new RNFirebaseLocalMessagingHelper(this.getApplication());
|
||||
helper.sendNotification(bundle);
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package io.invertase.firebase.messaging;
|
||||
|
||||
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;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class RNFirebaseBackgroundMessagingService extends HeadlessJsTaskService {
|
||||
@Override
|
||||
protected @Nullable HeadlessJsTaskConfig getTaskConfig(Intent intent) {
|
||||
Bundle extras = intent.getExtras();
|
||||
if (extras != null) {
|
||||
RemoteMessage message = intent.getParcelableExtra("message");
|
||||
WritableMap messageMap = MessagingSerializer.parseRemoteMessage(message);
|
||||
return new HeadlessJsTaskConfig(
|
||||
"RNFirebaseBackgroundMessage",
|
||||
messageMap,
|
||||
60000,
|
||||
false
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package io.invertase.firebase.messaging;
|
||||
|
||||
|
||||
import android.content.Intent;
|
||||
import android.support.v4.content.LocalBroadcastManager;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.firebase.iid.FirebaseInstanceIdService;
|
||||
|
||||
public class RNFirebaseInstanceIdService extends FirebaseInstanceIdService {
|
||||
private static final String TAG = "RNFInstanceIdService";
|
||||
public static final String TOKEN_REFRESH_EVENT = "messaging-token-refresh";
|
||||
|
||||
@Override
|
||||
public void onTokenRefresh() {
|
||||
Log.d(TAG, "onTokenRefresh event received");
|
||||
|
||||
// Build an Intent to pass the token to the RN Application
|
||||
Intent tokenRefreshEvent = new Intent(TOKEN_REFRESH_EVENT);
|
||||
|
||||
// Broadcast it so it is only available to the RN Application
|
||||
LocalBroadcastManager.getInstance(this).sendBroadcast(tokenRefreshEvent);
|
||||
}
|
||||
}
|
|
@ -1,336 +0,0 @@
|
|||
package io.invertase.firebase.messaging;
|
||||
|
||||
import android.app.AlarmManager;
|
||||
import android.app.Application;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.res.Resources;
|
||||
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.support.v4.app.NotificationCompat;
|
||||
import android.util.Log;
|
||||
import android.content.SharedPreferences;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.net.HttpURLConnection;
|
||||
|
||||
public class RNFirebaseLocalMessagingHelper {
|
||||
private static final long DEFAULT_VIBRATION = 300L;
|
||||
private static final String TAG = RNFirebaseLocalMessagingHelper.class.getSimpleName();
|
||||
private final static String PREFERENCES_KEY = "ReactNativeSystemNotification";
|
||||
private static boolean mIsForeground = false; //this is a hack
|
||||
|
||||
private Context mContext;
|
||||
private SharedPreferences sharedPreferences = null;
|
||||
|
||||
public RNFirebaseLocalMessagingHelper(Application context) {
|
||||
mContext = context;
|
||||
sharedPreferences = mContext.getSharedPreferences(PREFERENCES_KEY, Context.MODE_PRIVATE);
|
||||
}
|
||||
|
||||
public void sendNotification(Bundle bundle) {
|
||||
try {
|
||||
Class intentClass = getMainActivityClass();
|
||||
if (intentClass == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (bundle.getString("body") == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Resources res = mContext.getResources();
|
||||
String packageName = mContext.getPackageName();
|
||||
|
||||
String title = bundle.getString("title");
|
||||
if (title == null) {
|
||||
ApplicationInfo appInfo = mContext.getApplicationInfo();
|
||||
title = mContext.getPackageManager().getApplicationLabel(appInfo).toString();
|
||||
}
|
||||
|
||||
NotificationCompat.Builder notification = new NotificationCompat.Builder(mContext)
|
||||
.setContentTitle(title)
|
||||
.setContentText(bundle.getString("body"))
|
||||
.setTicker(bundle.getString("ticker"))
|
||||
.setVisibility(NotificationCompat.VISIBILITY_PRIVATE)
|
||||
.setAutoCancel(bundle.getBoolean("auto_cancel", true))
|
||||
.setNumber(bundle.getInt("number"))
|
||||
.setSubText(bundle.getString("sub_text"))
|
||||
.setGroup(bundle.getString("group"))
|
||||
.setVibrate(new long[]{0, DEFAULT_VIBRATION})
|
||||
.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION))
|
||||
.setExtras(bundle.getBundle("data"));
|
||||
|
||||
//priority
|
||||
String priority = bundle.getString("priority", "");
|
||||
switch(priority) {
|
||||
case "min":
|
||||
notification.setPriority(NotificationCompat.PRIORITY_MIN);
|
||||
break;
|
||||
case "high":
|
||||
notification.setPriority(NotificationCompat.PRIORITY_HIGH);
|
||||
break;
|
||||
case "max":
|
||||
notification.setPriority(NotificationCompat.PRIORITY_MAX);
|
||||
break;
|
||||
default:
|
||||
notification.setPriority(NotificationCompat.PRIORITY_DEFAULT);
|
||||
}
|
||||
|
||||
//icon
|
||||
String smallIcon = bundle.getString("icon", "ic_launcher");
|
||||
int smallIconResId = res.getIdentifier(smallIcon, "mipmap", packageName);
|
||||
notification.setSmallIcon(smallIconResId);
|
||||
|
||||
//large icon
|
||||
String largeIcon = bundle.getString("large_icon");
|
||||
if(largeIcon != null && android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP){
|
||||
if (largeIcon.startsWith("http://") || largeIcon.startsWith("https://")) {
|
||||
Bitmap bitmap = getBitmapFromURL(largeIcon);
|
||||
notification.setLargeIcon(bitmap);
|
||||
} else {
|
||||
int largeIconResId = res.getIdentifier(largeIcon, "mipmap", packageName);
|
||||
Bitmap largeIconBitmap = BitmapFactory.decodeResource(res, largeIconResId);
|
||||
|
||||
if (largeIconResId != 0) {
|
||||
notification.setLargeIcon(largeIconBitmap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//big text
|
||||
String bigText = bundle.getString("big_text");
|
||||
if(bigText != null){
|
||||
notification.setStyle(new NotificationCompat.BigTextStyle().bigText(bigText));
|
||||
}
|
||||
|
||||
//sound
|
||||
String soundName = bundle.getString("sound", "default");
|
||||
if (!soundName.equalsIgnoreCase("default")) {
|
||||
int soundResourceId = res.getIdentifier(soundName, "raw", packageName);
|
||||
if(soundResourceId == 0){
|
||||
soundName = soundName.substring(0, soundName.lastIndexOf('.'));
|
||||
soundResourceId = res.getIdentifier(soundName, "raw", packageName);
|
||||
}
|
||||
notification.setSound(Uri.parse("android.resource://" + packageName + "/" + soundResourceId));
|
||||
}
|
||||
|
||||
//color
|
||||
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
notification.setCategory(NotificationCompat.CATEGORY_CALL);
|
||||
|
||||
String color = bundle.getString("color");
|
||||
if (color != null) {
|
||||
notification.setColor(Color.parseColor(color));
|
||||
}
|
||||
}
|
||||
|
||||
//vibrate
|
||||
if(bundle.containsKey("vibrate")){
|
||||
long vibrate = bundle.getLong("vibrate", Math.round(bundle.getDouble("vibrate", bundle.getInt("vibrate"))));
|
||||
if(vibrate > 0){
|
||||
notification.setVibrate(new long[]{0, vibrate});
|
||||
}else{
|
||||
notification.setVibrate(null);
|
||||
}
|
||||
}
|
||||
|
||||
//lights
|
||||
if (bundle.getBoolean("lights")) {
|
||||
notification.setDefaults(NotificationCompat.DEFAULT_LIGHTS);
|
||||
}
|
||||
|
||||
Log.d(TAG, "broadcast intent before showing notification");
|
||||
Intent i = new Intent("io.invertase.firebase.messaging.ReceiveLocalNotification");
|
||||
i.putExtras(bundle);
|
||||
mContext.sendOrderedBroadcast(i, null);
|
||||
|
||||
if(!mIsForeground || bundle.getBoolean("show_in_foreground")){
|
||||
Intent intent = new Intent(mContext, intentClass);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||
intent.putExtras(bundle);
|
||||
intent.setAction(bundle.getString("click_action"));
|
||||
|
||||
int notificationID = bundle.containsKey("id") ? bundle.getString("id", "").hashCode() : (int) System.currentTimeMillis();
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(mContext, notificationID, intent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
|
||||
NotificationManager notificationManager =
|
||||
(NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
|
||||
notification.setContentIntent(pendingIntent);
|
||||
|
||||
Notification info = notification.build();
|
||||
|
||||
if (bundle.containsKey("tag")) {
|
||||
String tag = bundle.getString("tag");
|
||||
notificationManager.notify(tag, notificationID, info);
|
||||
} else {
|
||||
notificationManager.notify(notificationID, info);
|
||||
}
|
||||
}
|
||||
//clear out one time scheduled notification once fired
|
||||
if(!bundle.containsKey("repeat_interval") && bundle.containsKey("fire_date")) {
|
||||
SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||
editor.remove(bundle.getString("id"));
|
||||
editor.apply();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "failed to send local notification", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void sendNotificationScheduled(Bundle bundle) {
|
||||
Class intentClass = getMainActivityClass();
|
||||
if (intentClass == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String notificationId = bundle.getString("id");
|
||||
if(notificationId == null){
|
||||
Log.e(TAG, "failed to schedule notification because id is missing");
|
||||
return;
|
||||
}
|
||||
|
||||
Long fireDate = Math.round(bundle.getDouble("fire_date"));
|
||||
|
||||
if (fireDate == 0) {
|
||||
Log.e(TAG, "failed to schedule notification because fire date is missing");
|
||||
return;
|
||||
}
|
||||
|
||||
Intent notificationIntent = new Intent(mContext, RNFirebaseLocalMessagingPublisher.class);
|
||||
notificationIntent.putExtras(bundle);
|
||||
PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, notificationId.hashCode(), notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
|
||||
Long interval = null;
|
||||
switch (bundle.getString("repeat_interval", "")) {
|
||||
case "minute":
|
||||
interval = (long) 60000;
|
||||
break;
|
||||
case "hour":
|
||||
interval = AlarmManager.INTERVAL_HOUR;
|
||||
break;
|
||||
case "day":
|
||||
interval = AlarmManager.INTERVAL_DAY;
|
||||
break;
|
||||
case "week":
|
||||
interval = AlarmManager.INTERVAL_DAY * 7;
|
||||
break;
|
||||
}
|
||||
|
||||
if(interval != null){
|
||||
getAlarmManager().setRepeating(AlarmManager.RTC_WAKEUP, fireDate, interval, pendingIntent);
|
||||
} else if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){
|
||||
getAlarmManager().setExact(AlarmManager.RTC_WAKEUP, fireDate, pendingIntent);
|
||||
}else {
|
||||
getAlarmManager().set(AlarmManager.RTC_WAKEUP, fireDate, pendingIntent);
|
||||
}
|
||||
|
||||
//store intent
|
||||
SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||
try {
|
||||
JSONObject json = BundleJSONConverter.convertToJSON(bundle);
|
||||
editor.putString(notificationId, json.toString());
|
||||
editor.apply();
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void cancelLocalNotification(String notificationId) {
|
||||
cancelAlarm(notificationId);
|
||||
SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||
editor.remove(notificationId);
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
public void cancelAllLocalNotifications() {
|
||||
java.util.Map<String, ?> keyMap = sharedPreferences.getAll();
|
||||
SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||
for(java.util.Map.Entry<String, ?> entry:keyMap.entrySet()){
|
||||
cancelAlarm(entry.getKey());
|
||||
}
|
||||
editor.clear();
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
public void removeDeliveredNotification(String notificationId){
|
||||
NotificationManager notificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
notificationManager.cancel(notificationId.hashCode());
|
||||
}
|
||||
|
||||
public void removeAllDeliveredNotifications(){
|
||||
NotificationManager notificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
notificationManager.cancelAll();
|
||||
}
|
||||
|
||||
public ArrayList<Bundle> getScheduledLocalNotifications(){
|
||||
ArrayList<Bundle> array = new ArrayList<Bundle>();
|
||||
java.util.Map<String, ?> keyMap = sharedPreferences.getAll();
|
||||
for(java.util.Map.Entry<String, ?> entry:keyMap.entrySet()){
|
||||
try {
|
||||
JSONObject json = new JSONObject((String)entry.getValue());
|
||||
Bundle bundle = BundleJSONConverter.convertToBundle(json);
|
||||
array.add(bundle);
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
public void setApplicationForeground(boolean foreground){
|
||||
mIsForeground = foreground;
|
||||
}
|
||||
|
||||
private Class getMainActivityClass() {
|
||||
String packageName = mContext.getPackageName();
|
||||
Intent launchIntent = mContext.getPackageManager().getLaunchIntentForPackage(packageName);
|
||||
String className = launchIntent.getComponent().getClassName();
|
||||
try {
|
||||
return Class.forName(className);
|
||||
} catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private AlarmManager getAlarmManager() {
|
||||
return (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
|
||||
}
|
||||
|
||||
private void cancelAlarm(String notificationId) {
|
||||
Intent notificationIntent = new Intent(mContext, RNFirebaseLocalMessagingPublisher.class);
|
||||
PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, notificationId.hashCode(), notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
getAlarmManager().cancel(pendingIntent);
|
||||
}
|
||||
|
||||
private Bitmap getBitmapFromURL(String strURL) {
|
||||
try {
|
||||
URL url = new URL(strURL);
|
||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||
connection.setDoInput(true);
|
||||
connection.connect();
|
||||
InputStream input = connection.getInputStream();
|
||||
return BitmapFactory.decodeStream(input);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
package io.invertase.firebase.messaging;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
public class RNFirebaseLocalMessagingPublisher extends BroadcastReceiver {
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
new RNFirebaseLocalMessagingHelper((Application) context.getApplicationContext()).sendNotification(intent.getExtras());
|
||||
}
|
||||
}
|
|
@ -1,51 +1,39 @@
|
|||
package io.invertase.firebase.messaging;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Application;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.content.LocalBroadcastManager;
|
||||
import android.util.Log;
|
||||
|
||||
import com.facebook.react.bridge.ActivityEventListener;
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
import com.facebook.react.bridge.LifecycleEventListener;
|
||||
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.ReadableMap;
|
||||
import com.facebook.react.bridge.ReadableMapKeySetIterator;
|
||||
import com.facebook.react.bridge.WritableArray;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
import com.google.firebase.iid.FirebaseInstanceId;
|
||||
import com.google.firebase.messaging.FirebaseMessaging;
|
||||
import com.google.firebase.messaging.RemoteMessage;
|
||||
import com.google.firebase.messaging.RemoteMessage.Notification;
|
||||
|
||||
import io.invertase.firebase.Utils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
public class RNFirebaseMessaging extends ReactContextBaseJavaModule {
|
||||
private static final String TAG = "RNFirebaseMessaging";
|
||||
|
||||
public class RNFirebaseMessaging extends ReactContextBaseJavaModule implements LifecycleEventListener, ActivityEventListener {
|
||||
private final static String TAG = RNFirebaseMessaging.class.getCanonicalName();
|
||||
private RNFirebaseLocalMessagingHelper mRNFirebaseLocalMessagingHelper;
|
||||
private BadgeHelper mBadgeHelper;
|
||||
public RNFirebaseMessaging(ReactApplicationContext context) {
|
||||
super(context);
|
||||
LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(context);
|
||||
|
||||
public RNFirebaseMessaging(ReactApplicationContext reactContext) {
|
||||
super(reactContext);
|
||||
mRNFirebaseLocalMessagingHelper = new RNFirebaseLocalMessagingHelper((Application) reactContext.getApplicationContext());
|
||||
mBadgeHelper = new BadgeHelper(reactContext.getApplicationContext());
|
||||
getReactApplicationContext().addLifecycleEventListener(this);
|
||||
getReactApplicationContext().addActivityEventListener(this);
|
||||
registerTokenRefreshHandler();
|
||||
registerMessageHandler();
|
||||
registerLocalMessageHandler();
|
||||
// Subscribe to message events
|
||||
localBroadcastManager.registerReceiver(new MessageReceiver(),
|
||||
new IntentFilter(RNFirebaseMessagingService.MESSAGE_EVENT));
|
||||
|
||||
// Subscribe to token refresh events
|
||||
localBroadcastManager.registerReceiver(new RefreshTokenReceiver(),
|
||||
new IntentFilter(RNFirebaseInstanceIdService.TOKEN_REFRESH_EVENT));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -54,73 +42,57 @@ public class RNFirebaseMessaging extends ReactContextBaseJavaModule implements L
|
|||
}
|
||||
|
||||
@ReactMethod
|
||||
public void getInitialNotification(Promise promise) {
|
||||
Activity activity = getCurrentActivity();
|
||||
if (activity == null) {
|
||||
promise.resolve(null);
|
||||
public void getToken(Promise promise) {
|
||||
String token = FirebaseInstanceId.getInstance().getToken();
|
||||
Log.d(TAG, "Firebase token: " + token);
|
||||
promise.resolve(token);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void requestPermission(Promise promise) {
|
||||
promise.resolve(null);
|
||||
}
|
||||
|
||||
// Non Web SDK methods
|
||||
@ReactMethod
|
||||
public void hasPermission(Promise promise) {
|
||||
promise.resolve(true);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void sendMessage(ReadableMap messageMap, Promise promise) {
|
||||
if (!messageMap.hasKey("to")) {
|
||||
promise.reject("messaging/invalid-message", "The supplied message is missing a 'to' field");
|
||||
return;
|
||||
}
|
||||
promise.resolve(parseIntent(getCurrentActivity().getIntent()));
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void requestPermissions() {
|
||||
}
|
||||
RemoteMessage.Builder mb = new RemoteMessage.Builder(messageMap.getString("to"));
|
||||
|
||||
@ReactMethod
|
||||
public void getToken(Promise promise) {
|
||||
Log.d(TAG, "Firebase token: " + FirebaseInstanceId.getInstance().getToken());
|
||||
promise.resolve(FirebaseInstanceId.getInstance().getToken());
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void deleteInstanceId(Promise promise){
|
||||
try {
|
||||
Log.d(TAG, "Deleting instance id");
|
||||
FirebaseInstanceId.getInstance().deleteInstanceId();
|
||||
promise.resolve(null);
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, e.getMessage());
|
||||
promise.reject(null, e.getMessage());
|
||||
if (messageMap.hasKey("collapseKey")) {
|
||||
mb = mb.setCollapseKey(messageMap.getString("collapseKey"));
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void createLocalNotification(ReadableMap details) {
|
||||
Bundle bundle = Arguments.toBundle(details);
|
||||
mRNFirebaseLocalMessagingHelper.sendNotification(bundle);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void scheduleLocalNotification(ReadableMap details) {
|
||||
Bundle bundle = Arguments.toBundle(details);
|
||||
mRNFirebaseLocalMessagingHelper.sendNotificationScheduled(bundle);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void cancelLocalNotification(String notificationID) {
|
||||
mRNFirebaseLocalMessagingHelper.cancelLocalNotification(notificationID);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void cancelAllLocalNotifications() {
|
||||
try {
|
||||
mRNFirebaseLocalMessagingHelper.cancelAllLocalNotifications();
|
||||
} catch (SecurityException e) {
|
||||
// In some devices/situations cancelAllLocalNotifications will throw a SecurityException
|
||||
// We can safely ignore this error for now as the UX impact of this not working is minor.
|
||||
Log.w(TAG, e.getMessage());
|
||||
if (messageMap.hasKey("messageId")) {
|
||||
mb = mb.setMessageId(messageMap.getString("messageId"));
|
||||
}
|
||||
if (messageMap.hasKey("messageType")) {
|
||||
mb = mb.setMessageType(messageMap.getString("messageType"));
|
||||
}
|
||||
if (messageMap.hasKey("ttl")) {
|
||||
mb = mb.setTtl(messageMap.getInt("ttl"));
|
||||
}
|
||||
if (messageMap.hasKey("data")) {
|
||||
ReadableMap dataMap = messageMap.getMap("data");
|
||||
ReadableMapKeySetIterator iterator = dataMap.keySetIterator();
|
||||
while (iterator.hasNextKey()) {
|
||||
String key = iterator.nextKey();
|
||||
mb = mb.addData(key, dataMap.getString(key));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void removeDeliveredNotification(String notificationID) {
|
||||
mRNFirebaseLocalMessagingHelper.removeDeliveredNotification(notificationID);
|
||||
}
|
||||
FirebaseMessaging.getInstance().send(mb.build());
|
||||
|
||||
@ReactMethod
|
||||
public void removeAllDeliveredNotifications() {
|
||||
mRNFirebaseLocalMessagingHelper.removeAllDeliveredNotifications();
|
||||
// TODO: Listen to onMessageSent and onSendError for better feedback?
|
||||
promise.resolve(null);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
|
@ -133,159 +105,29 @@ public class RNFirebaseMessaging extends ReactContextBaseJavaModule implements L
|
|||
FirebaseMessaging.getInstance().unsubscribeFromTopic(topic);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void getScheduledLocalNotifications(Promise promise) {
|
||||
ArrayList<Bundle> bundles = mRNFirebaseLocalMessagingHelper.getScheduledLocalNotifications();
|
||||
WritableArray array = Arguments.createArray();
|
||||
for (Bundle bundle : bundles) {
|
||||
array.pushMap(Arguments.fromBundle(bundle));
|
||||
}
|
||||
promise.resolve(array);
|
||||
}
|
||||
private class MessageReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (getReactApplicationContext().hasActiveCatalystInstance()) {
|
||||
Log.d(TAG, "Received new message");
|
||||
|
||||
@ReactMethod
|
||||
public void setBadgeNumber(int badgeNumber) {
|
||||
mBadgeHelper.setBadgeCount(badgeNumber);
|
||||
}
|
||||
RemoteMessage message = intent.getParcelableExtra("message");
|
||||
WritableMap messageMap = MessagingSerializer.parseRemoteMessage(message);
|
||||
|
||||
@ReactMethod
|
||||
public void getBadgeNumber(Promise promise) {
|
||||
promise.resolve(mBadgeHelper.getBadgeCount());
|
||||
}
|
||||
|
||||
private void registerTokenRefreshHandler() {
|
||||
IntentFilter intentFilter = new IntentFilter("io.invertase.firebase.messaging.FCMRefreshToken");
|
||||
getReactApplicationContext().registerReceiver(new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (getReactApplicationContext().hasActiveCatalystInstance()) {
|
||||
String token = intent.getStringExtra("token");
|
||||
Utils.sendEvent(getReactApplicationContext(), "messaging_token_refreshed", token);
|
||||
}
|
||||
Utils.sendEvent(getReactApplicationContext(), "messaging_message_received", messageMap);
|
||||
}
|
||||
}, intentFilter);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void send(ReadableMap remoteMessage) {
|
||||
FirebaseMessaging fm = FirebaseMessaging.getInstance();
|
||||
RemoteMessage.Builder message = new RemoteMessage.Builder(remoteMessage.getString("sender"));
|
||||
|
||||
message.setTtl(remoteMessage.getInt("ttl"));
|
||||
message.setMessageId(remoteMessage.getString("id"));
|
||||
message.setMessageType(remoteMessage.getString("type"));
|
||||
|
||||
if (remoteMessage.hasKey("collapseKey")) {
|
||||
message.setCollapseKey(remoteMessage.getString("collapseKey"));
|
||||
}
|
||||
}
|
||||
|
||||
// get data keys and values and add to builder
|
||||
// js side ensures all data values are strings
|
||||
// so no need to check types
|
||||
ReadableMap data = remoteMessage.getMap("data");
|
||||
ReadableMapKeySetIterator iterator = data.keySetIterator();
|
||||
while (iterator.hasNextKey()) {
|
||||
String key = iterator.nextKey();
|
||||
String value = data.getString(key);
|
||||
message.addData(key, value);
|
||||
private class RefreshTokenReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (getReactApplicationContext().hasActiveCatalystInstance()) {
|
||||
String token = FirebaseInstanceId.getInstance().getToken();
|
||||
Log.d(TAG, "Received new FCM token: " + token);
|
||||
|
||||
Utils.sendEvent(getReactApplicationContext(), "messaging_token_refreshed", token);
|
||||
}
|
||||
}
|
||||
|
||||
fm.send(message.build());
|
||||
}
|
||||
|
||||
private void registerMessageHandler() {
|
||||
IntentFilter intentFilter = new IntentFilter("io.invertase.firebase.messaging.ReceiveNotification");
|
||||
|
||||
getReactApplicationContext().registerReceiver(new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (getReactApplicationContext().hasActiveCatalystInstance()) {
|
||||
RemoteMessage message = intent.getParcelableExtra("data");
|
||||
WritableMap params = Arguments.createMap();
|
||||
WritableMap fcmData = Arguments.createMap();
|
||||
|
||||
if (message.getNotification() != null) {
|
||||
Notification notification = message.getNotification();
|
||||
fcmData.putString("title", notification.getTitle());
|
||||
fcmData.putString("body", notification.getBody());
|
||||
fcmData.putString("color", notification.getColor());
|
||||
fcmData.putString("icon", notification.getIcon());
|
||||
fcmData.putString("tag", notification.getTag());
|
||||
fcmData.putString("action", notification.getClickAction());
|
||||
}
|
||||
params.putMap("fcm", fcmData);
|
||||
|
||||
if (message.getData() != null) {
|
||||
Map<String, String> data = message.getData();
|
||||
Set<String> keysIterator = data.keySet();
|
||||
for (String key : keysIterator) {
|
||||
params.putString(key, data.get(key));
|
||||
}
|
||||
}
|
||||
Utils.sendEvent(getReactApplicationContext(), "messaging_notification_received", params);
|
||||
|
||||
}
|
||||
}
|
||||
}, intentFilter);
|
||||
}
|
||||
|
||||
private void registerLocalMessageHandler() {
|
||||
IntentFilter intentFilter = new IntentFilter("io.invertase.firebase.messaging.ReceiveLocalNotification");
|
||||
|
||||
getReactApplicationContext().registerReceiver(new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (getReactApplicationContext().hasActiveCatalystInstance()) {
|
||||
Utils.sendEvent(getReactApplicationContext(), "messaging_notification_received", Arguments.fromBundle(intent.getExtras()));
|
||||
}
|
||||
}
|
||||
}, intentFilter);
|
||||
}
|
||||
|
||||
private WritableMap parseIntent(Intent intent) {
|
||||
WritableMap params;
|
||||
Bundle extras = intent.getExtras();
|
||||
if (extras != null) {
|
||||
try {
|
||||
params = Arguments.fromBundle(extras);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, e.getMessage());
|
||||
params = Arguments.createMap();
|
||||
}
|
||||
} else {
|
||||
params = Arguments.createMap();
|
||||
}
|
||||
WritableMap fcm = Arguments.createMap();
|
||||
fcm.putString("action", intent.getAction());
|
||||
params.putMap("fcm", fcm);
|
||||
|
||||
params.putBoolean("opened_from_tray", true);
|
||||
return params;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHostResume() {
|
||||
mRNFirebaseLocalMessagingHelper.setApplicationForeground(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHostPause() {
|
||||
mRNFirebaseLocalMessagingHelper.setApplicationForeground(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHostDestroy() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
|
||||
// todo hmm?
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNewIntent(Intent intent) {
|
||||
// todo hmm?
|
||||
Utils.sendEvent(getReactApplicationContext(), "messaging_notification_received", parseIntent(intent));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package io.invertase.firebase.messaging;
|
||||
|
||||
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;
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
package io.invertase.firebase.messaging;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.support.v4.content.LocalBroadcastManager;
|
||||
import android.util.Log;
|
||||
|
||||
import com.facebook.react.HeadlessJsTaskService;
|
||||
import com.google.firebase.messaging.FirebaseMessagingService;
|
||||
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";
|
||||
|
||||
@Override
|
||||
public void onMessageReceived(RemoteMessage message) {
|
||||
Log.d(TAG, "onMessageReceived event received");
|
||||
|
||||
if (message.getNotification() != null) {
|
||||
// It's a notification, pass to the Notifications module
|
||||
Intent notificationEvent = new Intent(REMOTE_NOTIFICATION_EVENT);
|
||||
notificationEvent.putExtra("notification", message);
|
||||
|
||||
// Broadcast it to the (foreground) RN Application
|
||||
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
|
||||
if (Utils.isAppInForeground(this.getApplicationContext())) {
|
||||
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);
|
||||
} else {
|
||||
// If the app is in the background we send it to the Headless JS Service
|
||||
Intent headlessIntent = new Intent(this.getApplicationContext(), RNFirebaseBackgroundMessagingService.class);
|
||||
headlessIntent.putExtra("message", message);
|
||||
this.getApplicationContext().startService(headlessIntent);
|
||||
HeadlessJsTaskService.acquireWakeLockNow(this.getApplicationContext());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
package io.invertase.firebase.messaging;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* Set alarms for scheduled notification after system reboot.
|
||||
*/
|
||||
public class RNFirebaseSystemBootEventReceiver extends BroadcastReceiver {
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
Log.i("FCMSystemBootReceiver", "Received reboot event");
|
||||
RNFirebaseLocalMessagingHelper helper = new RNFirebaseLocalMessagingHelper((Application) context.getApplicationContext());
|
||||
ArrayList<Bundle> bundles = helper.getScheduledLocalNotifications();
|
||||
for(Bundle bundle: bundles){
|
||||
helper.sendNotificationScheduled(bundle);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,673 @@
|
|||
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;
|
||||
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.os.Parcelable;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.support.v4.app.RemoteInput;
|
||||
import android.support.v4.content.LocalBroadcastManager;
|
||||
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.ReadableArray;
|
||||
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.List;
|
||||
import java.util.Map;
|
||||
|
||||
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 TAG = "RNFNotificationManager";
|
||||
private AlarmManager alarmManager;
|
||||
private Context context;
|
||||
private ReactApplicationContext reactContext;
|
||||
private NotificationManager notificationManager;
|
||||
private SharedPreferences preferences;
|
||||
|
||||
public RNFirebaseNotificationManager(ReactApplicationContext reactContext) {
|
||||
this(reactContext.getApplicationContext());
|
||||
this.reactContext = reactContext;
|
||||
}
|
||||
|
||||
public RNFirebaseNotificationManager(Context context) {
|
||||
this.alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
|
||||
this.context = context;
|
||||
this.notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
this.preferences = context.getSharedPreferences(PREFERENCES_KEY, Context.MODE_PRIVATE);
|
||||
}
|
||||
|
||||
public void cancelAllNotifications() {
|
||||
try {
|
||||
Map<String, ?> notifications = preferences.getAll();
|
||||
|
||||
for(String notificationId : notifications.keySet()){
|
||||
cancelAlarm(notificationId);
|
||||
}
|
||||
preferences.edit().clear().apply();
|
||||
} catch (SecurityException e) {
|
||||
// TODO: Identify what these situations are
|
||||
// In some devices/situations cancelAllLocalNotifications can throw a SecurityException.
|
||||
Log.e(TAG, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void cancelNotification(String notificationId) {
|
||||
cancelAlarm(notificationId);
|
||||
preferences.edit().remove(notificationId).apply();
|
||||
}
|
||||
|
||||
public void createChannel(ReadableMap channelMap) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
NotificationChannel channel = parseChannelMap(channelMap);
|
||||
notificationManager.createNotificationChannel(channel);
|
||||
}
|
||||
}
|
||||
|
||||
public void createChannelGroup(ReadableMap channelGroupMap) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
NotificationChannelGroup channelGroup = parseChannelGroupMap(channelGroupMap);
|
||||
notificationManager.createNotificationChannelGroup(channelGroup);
|
||||
}
|
||||
}
|
||||
|
||||
public void createChannelGroups(ReadableArray channelGroupsArray) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
List<NotificationChannelGroup> channelGroups = new ArrayList<>();
|
||||
for (int i = 0; i < channelGroupsArray.size(); i++) {
|
||||
NotificationChannelGroup channelGroup = parseChannelGroupMap(channelGroupsArray.getMap(i));
|
||||
channelGroups.add(channelGroup);
|
||||
}
|
||||
notificationManager.createNotificationChannelGroups(channelGroups);
|
||||
}
|
||||
}
|
||||
|
||||
public void createChannels(ReadableArray channelsArray) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
List<NotificationChannel> channels = new ArrayList<>();
|
||||
for (int i = 0; i < channelsArray.size(); i++) {
|
||||
NotificationChannel channel = parseChannelMap(channelsArray.getMap(i));
|
||||
channels.add(channel);
|
||||
}
|
||||
notificationManager.createNotificationChannels(channels);
|
||||
}
|
||||
}
|
||||
|
||||
public void displayNotification(ReadableMap notification, Promise promise) {
|
||||
Bundle notificationBundle = Arguments.toBundle(notification);
|
||||
displayNotification(notificationBundle, promise);
|
||||
}
|
||||
|
||||
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")) {
|
||||
String notificationId = notification.getString("notificationId");
|
||||
preferences.edit().remove(notificationId).apply();;
|
||||
}
|
||||
|
||||
if (Utils.isAppInForeground(context)) {
|
||||
// If the app is in the foregound, broadcast the notification to the RN Application
|
||||
// It is up to the JS to decide whether to display the notification
|
||||
Intent scheduledNotificationEvent = new Intent(SCHEDULED_NOTIFICATION_EVENT);
|
||||
scheduledNotificationEvent.putExtra("notification", notification);
|
||||
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(){
|
||||
ArrayList<Bundle> array = new ArrayList<>();
|
||||
|
||||
Map<String, ?> notifications = preferences.getAll();
|
||||
|
||||
for(String notificationId : notifications.keySet()){
|
||||
try {
|
||||
JSONObject json = new JSONObject((String)notifications.get(notificationId));
|
||||
Bundle bundle = BundleJSONConverter.convertToBundle(json);
|
||||
array.add(bundle);
|
||||
} catch (JSONException e) {
|
||||
Log.e(TAG, e.getMessage());
|
||||
}
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
public void removeAllDeliveredNotifications() {
|
||||
notificationManager.cancelAll();
|
||||
}
|
||||
|
||||
public void removeDeliveredNotification(String notificationId) {
|
||||
notificationManager.cancel(notificationId.hashCode());
|
||||
}
|
||||
|
||||
|
||||
public void rescheduleNotifications() {
|
||||
ArrayList<Bundle> bundles = getScheduledNotifications();
|
||||
for(Bundle bundle: bundles){
|
||||
scheduleNotification(bundle, null);
|
||||
}
|
||||
}
|
||||
|
||||
public void scheduleNotification(ReadableMap notification, Promise promise) {
|
||||
Bundle notificationBundle = Arguments.toBundle(notification);
|
||||
|
||||
scheduleNotification(notificationBundle, promise);
|
||||
}
|
||||
|
||||
private void cancelAlarm(String notificationId) {
|
||||
Intent notificationIntent = new Intent(context, RNFirebaseNotificationManager.class);
|
||||
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, notificationId.hashCode(), notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
alarmManager.cancel(pendingIntent);
|
||||
}
|
||||
|
||||
private void displayNotification(Bundle notification, Promise promise) {
|
||||
try {
|
||||
Class intentClass = getMainActivityClass();
|
||||
if (intentClass == null) {
|
||||
if (promise != null) {
|
||||
promise.reject("notification/display_notification_error", "Could not find main activity class");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
Bundle android = notification.getBundle("android");
|
||||
|
||||
String channelId = android.getString("channelId");
|
||||
String notificationId = notification.getString("notificationId");
|
||||
|
||||
NotificationCompat.Builder nb;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
nb = new NotificationCompat.Builder(context, channelId);
|
||||
} else {
|
||||
nb = new NotificationCompat.Builder(context);
|
||||
}
|
||||
|
||||
if (notification.containsKey("body")) {
|
||||
nb = nb.setContentText(notification.getString("body"));
|
||||
}
|
||||
if (notification.containsKey("data")) {
|
||||
nb = nb.setExtras(notification.getBundle("data"));
|
||||
}
|
||||
if (notification.containsKey("sound")) {
|
||||
Uri sound = getSound(notification.getString("sound"));
|
||||
nb = nb.setSound(sound);
|
||||
}
|
||||
if (notification.containsKey("subtitle")) {
|
||||
nb = nb.setSubText(notification.getString("subtitle"));
|
||||
}
|
||||
if (notification.containsKey("title")) {
|
||||
nb = nb.setContentTitle(notification.getString("title"));
|
||||
}
|
||||
|
||||
if (android.containsKey("autoCancel")) {
|
||||
nb = nb.setAutoCancel(android.getBoolean("autoCancel"));
|
||||
}
|
||||
if (android.containsKey("badgeIconType")) {
|
||||
Double badgeIconType = android.getDouble("badgeIconType");
|
||||
nb = nb.setBadgeIconType(badgeIconType.intValue());
|
||||
}
|
||||
if (android.containsKey("category")) {
|
||||
nb = nb.setCategory(android.getString("category"));
|
||||
}
|
||||
if (android.containsKey("color")) {
|
||||
String color = android.getString("color");
|
||||
nb = nb.setColor(Color.parseColor(color));
|
||||
}
|
||||
if (android.containsKey("colorized")) {
|
||||
nb = nb.setColorized(android.getBoolean("colorized"));
|
||||
}
|
||||
if (android.containsKey("contentInfo")) {
|
||||
nb = nb.setContentInfo(android.getString("contentInfo"));
|
||||
}
|
||||
if (notification.containsKey("defaults")) {
|
||||
double[] defaultsArray = android.getDoubleArray("defaults");
|
||||
int defaults = 0;
|
||||
for (Double d : defaultsArray) {
|
||||
defaults |= d.intValue();
|
||||
}
|
||||
nb = nb.setDefaults(defaults);
|
||||
}
|
||||
if (android.containsKey("group")) {
|
||||
nb = nb.setGroup(android.getString("group"));
|
||||
}
|
||||
if (android.containsKey("groupAlertBehaviour")) {
|
||||
Double groupAlertBehaviour = android.getDouble("groupAlertBehaviour");
|
||||
nb = nb.setGroupAlertBehavior(groupAlertBehaviour.intValue());
|
||||
}
|
||||
if (android.containsKey("groupSummary")) {
|
||||
nb = nb.setGroupSummary(android.getBoolean("groupSummary"));
|
||||
}
|
||||
if (android.containsKey("largeIcon")) {
|
||||
Bitmap largeIcon = getBitmap(android.getString("largeIcon"));
|
||||
if (largeIcon != null) {
|
||||
nb = nb.setLargeIcon(largeIcon);
|
||||
}
|
||||
}
|
||||
if (android.containsKey("lights")) {
|
||||
Bundle lights = android.getBundle("lights");
|
||||
Double argb = lights.getDouble("argb");
|
||||
Double onMs = lights.getDouble("onMs");
|
||||
Double offMs = lights.getDouble("offMs");
|
||||
nb = nb.setLights(argb.intValue(), onMs.intValue(), offMs.intValue());
|
||||
}
|
||||
if (android.containsKey("localOnly")) {
|
||||
nb = nb.setLocalOnly(android.getBoolean("localOnly"));
|
||||
}
|
||||
|
||||
if (android.containsKey("number")) {
|
||||
Double number = android.getDouble("number");
|
||||
nb = nb.setNumber(number.intValue());
|
||||
}
|
||||
if (android.containsKey("ongoing")) {
|
||||
nb = nb.setOngoing(android.getBoolean("ongoing"));
|
||||
}
|
||||
if (android.containsKey("onlyAlertOnce")) {
|
||||
nb = nb.setOngoing(android.getBoolean("onlyAlertOnce"));
|
||||
}
|
||||
if (android.containsKey("people")) {
|
||||
String[] people = android.getStringArray("people");
|
||||
if (people != null) {
|
||||
for (String person : people) {
|
||||
nb = nb.addPerson(person);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (android.containsKey("priority")) {
|
||||
Double priority = android.getDouble("priority");
|
||||
nb = nb.setPriority(priority.intValue());
|
||||
}
|
||||
if (android.containsKey("progress")) {
|
||||
Bundle progress = android.getBundle("lights");
|
||||
Double max = progress.getDouble("max");
|
||||
Double progressI = progress.getDouble("progress");
|
||||
nb = nb.setProgress(max.intValue(), progressI.intValue(), progress.getBoolean("indeterminate"));
|
||||
}
|
||||
// TODO: Public version of notification
|
||||
/* if (android.containsKey("publicVersion")) {
|
||||
nb = nb.setPublicVersion();
|
||||
} */
|
||||
if (android.containsKey("remoteInputHistory")) {
|
||||
nb = nb.setRemoteInputHistory(android.getStringArray("remoteInputHistory"));
|
||||
}
|
||||
if (android.containsKey("shortcutId")) {
|
||||
nb = nb.setShortcutId(android.getString("shortcutId"));
|
||||
}
|
||||
if (android.containsKey("showWhen")) {
|
||||
nb = nb.setShowWhen(android.getBoolean("showWhen"));
|
||||
}
|
||||
if (android.containsKey("smallIcon")) {
|
||||
Bundle smallIcon = android.getBundle("smallIcon");
|
||||
int smallIconResourceId = getIcon(smallIcon.getString("icon"));
|
||||
|
||||
if (smallIconResourceId != 0) {
|
||||
if (smallIcon.containsKey("level")) {
|
||||
Double level = smallIcon.getDouble("level");
|
||||
nb = nb.setSmallIcon(smallIconResourceId, level.intValue());
|
||||
} else {
|
||||
nb = nb.setSmallIcon(smallIconResourceId);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (android.containsKey("sortKey")) {
|
||||
nb = nb.setSortKey(android.getString("sortKey"));
|
||||
}
|
||||
if (android.containsKey("ticker")) {
|
||||
nb = nb.setTicker(android.getString("ticker"));
|
||||
}
|
||||
if (android.containsKey("timeoutAfter")) {
|
||||
Double timeoutAfter = android.getDouble("timeoutAfter");
|
||||
nb = nb.setTimeoutAfter(timeoutAfter.longValue());
|
||||
}
|
||||
if (android.containsKey("usesChronometer")) {
|
||||
nb = nb.setUsesChronometer(android.getBoolean("usesChronometer"));
|
||||
}
|
||||
if (android.containsKey("vibrate")) {
|
||||
double[] vibrate = android.getDoubleArray("vibrate");
|
||||
long[] vibrateArray = new long[vibrate.length];
|
||||
for (int i = 0; i < vibrate.length; i++) {
|
||||
vibrateArray[i] = ((Double)vibrate[i]).longValue();
|
||||
}
|
||||
nb = nb.setVibrate(vibrateArray);
|
||||
}
|
||||
if (android.containsKey("visibility")) {
|
||||
Double visibility = android.getDouble("visibility");
|
||||
nb = nb.setVisibility(visibility.intValue());
|
||||
}
|
||||
if (android.containsKey("when")) {
|
||||
Double when = android.getDouble("when");
|
||||
nb = nb.setWhen(when.longValue());
|
||||
}
|
||||
|
||||
// TODO: Big text / Big picture
|
||||
/* String bigText = bundle.getString("big_text");
|
||||
if(bigText != null){
|
||||
notification.setStyle(new NotificationCompat.BigTextStyle().bigText(bigText));
|
||||
}
|
||||
String picture = bundle.getString("picture");
|
||||
if(picture!=null){
|
||||
NotificationCompat.BigPictureStyle bigPicture = new NotificationCompat.BigPictureStyle();
|
||||
|
||||
Bitmap pictureBitmap = getBitmap(picture);
|
||||
if (pictureBitmap != null) {
|
||||
bigPicture.bigPicture(pictureBitmap);
|
||||
}
|
||||
bigPicture.setBigContentTitle(title);
|
||||
bigPicture.setSummaryText(bundle.getString("body"));
|
||||
|
||||
notification.setStyle(bigPicture);
|
||||
} */
|
||||
// Build any actions
|
||||
if (android.containsKey("actions")) {
|
||||
List<Bundle> actions = (List) android.getSerializable("actions");
|
||||
for (Bundle a : actions) {
|
||||
NotificationCompat.Action action = createAction(a, intentClass, notification);
|
||||
nb = nb.addAction(action);
|
||||
}
|
||||
}
|
||||
|
||||
// Create the notification intent
|
||||
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);
|
||||
|
||||
if (reactContext != null) {
|
||||
Utils.sendEvent(reactContext, "notifications_notification_displayed", Arguments.fromBundle(notification));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (promise == null) {
|
||||
Log.e(TAG, "Failed to send notification", e);
|
||||
} else {
|
||||
promise.reject("notification/display_notification_error", "Could not send notification", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private NotificationCompat.Action createAction(Bundle action, Class intentClass, Bundle notification) {
|
||||
String actionKey = action.getString("action");
|
||||
PendingIntent actionIntent = createIntent(intentClass, notification, actionKey);
|
||||
|
||||
int icon = getIcon(action.getString("icon"));
|
||||
String title = action.getString("title");
|
||||
|
||||
NotificationCompat.Action.Builder ab = new NotificationCompat.Action.Builder(icon, title, actionIntent);
|
||||
|
||||
if (action.containsKey("allowGeneratedReplies")) {
|
||||
ab = ab.setAllowGeneratedReplies(action.getBoolean("allowGeneratedReplies"));
|
||||
}
|
||||
if (action.containsKey("remoteInputs")) {
|
||||
List<Bundle> remoteInputs = (List) action.getSerializable("remoteInputs");
|
||||
for (Bundle ri : remoteInputs) {
|
||||
RemoteInput remoteInput = createRemoteInput(ri);
|
||||
ab = ab.addRemoteInput(remoteInput);
|
||||
}
|
||||
}
|
||||
// TODO: SemanticAction and ShowsUserInterface only available on v28?
|
||||
// if (action.containsKey("semanticAction")) {
|
||||
// Double semanticAction = action.getDouble("semanticAction");
|
||||
// ab = ab.setSemanticAction(semanticAction.intValue());
|
||||
// }
|
||||
// if (action.containsKey("showsUserInterface")) {
|
||||
// ab = ab.setShowsUserInterface(action.getBoolean("showsUserInterface"));
|
||||
// }
|
||||
|
||||
return ab.build();
|
||||
}
|
||||
|
||||
private PendingIntent createIntent(Class intentClass, Bundle notification, String action) {
|
||||
Intent intent = new Intent(context, intentClass);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||
intent.putExtras(notification);
|
||||
|
||||
if (action != null) {
|
||||
intent.setAction(action);
|
||||
}
|
||||
|
||||
String notificationId = notification.getString("notificationId");
|
||||
|
||||
return PendingIntent.getActivity(context, notificationId.hashCode(), intent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
}
|
||||
|
||||
private RemoteInput createRemoteInput(Bundle remoteInput) {
|
||||
String resultKey = remoteInput.getString("resultKey");
|
||||
|
||||
RemoteInput.Builder rb = new RemoteInput.Builder(resultKey);
|
||||
|
||||
if (remoteInput.containsKey("allowedDataTypes")) {
|
||||
List<Bundle> allowedDataTypes = (List) remoteInput.getSerializable("allowedDataTypes");
|
||||
for (Bundle adt : allowedDataTypes) {
|
||||
rb.setAllowDataType(adt.getString("mimeType"), adt.getBoolean("allow"));
|
||||
}
|
||||
}
|
||||
if (remoteInput.containsKey("allowFreeFormInput")) {
|
||||
rb.setAllowFreeFormInput(remoteInput.getBoolean("allowFreeFormInput"));
|
||||
}
|
||||
if (remoteInput.containsKey("choices")) {
|
||||
rb.setChoices(remoteInput.getStringArray("choices"));
|
||||
}
|
||||
if (remoteInput.containsKey("label")) {
|
||||
rb.setLabel(remoteInput.getString("label"));
|
||||
}
|
||||
|
||||
return rb.build();
|
||||
}
|
||||
|
||||
private Bitmap getBitmap(String image) {
|
||||
if (image.startsWith("http://") || image.startsWith("https://")) {
|
||||
return getBitmapFromUrl(image);
|
||||
} else {
|
||||
int largeIconResId = getResourceId("mipmap", image);
|
||||
return BitmapFactory.decodeResource(context.getResources(), largeIconResId);
|
||||
}
|
||||
}
|
||||
|
||||
private Bitmap getBitmapFromUrl(String imageUrl) {
|
||||
try {
|
||||
HttpURLConnection connection = (HttpURLConnection) new URL(imageUrl).openConnection();
|
||||
connection.setDoInput(true);
|
||||
connection.connect();
|
||||
return BitmapFactory.decodeStream(connection.getInputStream());
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Failed to get bitmap for url: " + imageUrl, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private int getIcon(String icon) {
|
||||
int smallIconResourceId = getResourceId("mipmap", icon);
|
||||
if (smallIconResourceId == 0) {
|
||||
smallIconResourceId = getResourceId("drawable", icon);
|
||||
}
|
||||
return smallIconResourceId;
|
||||
}
|
||||
|
||||
private Class getMainActivityClass() {
|
||||
String packageName = context.getPackageName();
|
||||
Intent launchIntent = context.getPackageManager().getLaunchIntentForPackage(packageName);
|
||||
try {
|
||||
return Class.forName(launchIntent.getComponent().getClassName());
|
||||
} catch (ClassNotFoundException e) {
|
||||
Log.e(TAG, "Failed to get main activity class", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private int getResourceId(String type, String image) {
|
||||
return context.getResources().getIdentifier(image, type, context.getPackageName());
|
||||
}
|
||||
|
||||
private Uri getSound(String sound) {
|
||||
if (sound.equalsIgnoreCase("default")) {
|
||||
return RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
|
||||
} else {
|
||||
int soundResourceId = getResourceId("raw", sound);
|
||||
if (soundResourceId == 0) {
|
||||
soundResourceId = getResourceId("raw", sound.substring(0, sound.lastIndexOf('.')));
|
||||
}
|
||||
return Uri.parse("android.resource://" + context.getPackageName() + "/" + soundResourceId);
|
||||
}
|
||||
}
|
||||
|
||||
private NotificationChannelGroup parseChannelGroupMap(ReadableMap channelGroupMap) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
String groupId = channelGroupMap.getString("groupId");
|
||||
String name = channelGroupMap.getString("name");
|
||||
|
||||
return new NotificationChannelGroup(groupId, name);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private NotificationChannel parseChannelMap(ReadableMap channelMap) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
String channelId = channelMap.getString("channelId");
|
||||
String name = channelMap.getString("name");
|
||||
int importance = channelMap.getInt("importance");
|
||||
|
||||
NotificationChannel channel = new NotificationChannel(channelId, name, importance);
|
||||
if (channelMap.hasKey("bypassDnd")) {
|
||||
channel.setBypassDnd(channelMap.getBoolean("bypassDnd"));
|
||||
}
|
||||
if (channelMap.hasKey("description")) {
|
||||
channel.setDescription(channelMap.getString("description"));
|
||||
}
|
||||
if (channelMap.hasKey("group")) {
|
||||
channel.setGroup(channelMap.getString("group"));
|
||||
}
|
||||
if (channelMap.hasKey("lightColor")) {
|
||||
String lightColor = channelMap.getString("lightColor");
|
||||
channel.setLightColor(Color.parseColor(lightColor));
|
||||
}
|
||||
if (channelMap.hasKey("lockScreenVisibility")) {
|
||||
channel.setLockscreenVisibility(channelMap.getInt("lockScreenVisibility"));
|
||||
}
|
||||
if (channelMap.hasKey("showBadge")) {
|
||||
channel.setShowBadge(channelMap.getBoolean("showBadge"));
|
||||
}
|
||||
if (channelMap.hasKey("sound")) {
|
||||
Uri sound = getSound(channelMap.getString("sound"));
|
||||
channel.setSound(sound, null);
|
||||
}
|
||||
if (channelMap.hasKey("vibrationPattern")) {
|
||||
ReadableArray vibrationArray = channelMap.getArray("vibrationPattern");
|
||||
long[] vibration = new long[]{};
|
||||
for (int i = 0; i < vibrationArray.size(); i++) {
|
||||
vibration[i] = (long) vibrationArray.getDouble(i);
|
||||
}
|
||||
channel.setVibrationPattern(vibration);
|
||||
}
|
||||
return channel;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void scheduleNotification(Bundle notification, Promise promise) {
|
||||
if (!notification.containsKey("notificationId")) {
|
||||
if (promise == null) {
|
||||
Log.e(TAG, "Missing notificationId");
|
||||
} else {
|
||||
promise.reject("notification/schedule_notification_error", "Missing notificationId");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!notification.containsKey("schedule")) {
|
||||
if (promise == null) {
|
||||
Log.e(TAG, "Missing schedule information");
|
||||
} else {
|
||||
promise.reject("notification/schedule_notification_error", "Missing schedule information");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
String notificationId = notification.getString("notificationId");
|
||||
Bundle schedule = notification.getBundle("schedule");
|
||||
|
||||
Double fireDate = schedule.getDouble("fireDate");
|
||||
|
||||
// Scheduled alarms are cleared on restart
|
||||
// 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();
|
||||
} catch (JSONException 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);
|
||||
|
||||
if (schedule.containsKey("repeatInterval")) {
|
||||
Long interval = null;
|
||||
switch (schedule.getString("repeatInterval")) {
|
||||
case "minute":
|
||||
interval = 60000L;
|
||||
break;
|
||||
case "hour":
|
||||
interval = AlarmManager.INTERVAL_HOUR;
|
||||
break;
|
||||
case "day":
|
||||
interval = AlarmManager.INTERVAL_DAY;
|
||||
break;
|
||||
case "week":
|
||||
interval = AlarmManager.INTERVAL_DAY * 7;
|
||||
break;
|
||||
default:
|
||||
Log.e(TAG, "Invalid interval: " + schedule.getString("interval"));
|
||||
break;
|
||||
}
|
||||
|
||||
if (interval == null) {
|
||||
promise.reject("notification/schedule_notification_error", "Invalid interval");
|
||||
return;
|
||||
}
|
||||
|
||||
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, fireDate.longValue(), 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);
|
||||
} else {
|
||||
alarmManager.set(AlarmManager.RTC_WAKEUP, fireDate.longValue(), pendingIntent);
|
||||
}
|
||||
}
|
||||
|
||||
promise.resolve(null);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package io.invertase.firebase.notifications;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
/*
|
||||
* This is invoked by the Alarm Manager when it is time to display a scheduled notification.
|
||||
*/
|
||||
public class RNFirebaseNotificationReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
new RNFirebaseNotificationManager(context).displayScheduledNotification(intent.getExtras());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,319 @@
|
|||
package io.invertase.firebase.notifications;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.RemoteInput;
|
||||
import android.support.v4.content.LocalBroadcastManager;
|
||||
import android.util.Log;
|
||||
|
||||
import com.facebook.react.bridge.ActivityEventListener;
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
import com.facebook.react.bridge.Promise;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
import com.facebook.react.bridge.WritableArray;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
import com.google.firebase.messaging.RemoteMessage;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
|
||||
import io.invertase.firebase.Utils;
|
||||
import io.invertase.firebase.messaging.RNFirebaseMessagingService;
|
||||
import me.leolin.shortcutbadger.ShortcutBadger;
|
||||
|
||||
public class RNFirebaseNotifications extends ReactContextBaseJavaModule implements ActivityEventListener {
|
||||
private static final String BADGE_FILE = "BadgeCountFile";
|
||||
private static final String BADGE_KEY = "BadgeCount";
|
||||
private static final String TAG = "RNFirebaseNotifications";
|
||||
|
||||
private SharedPreferences sharedPreferences = null;
|
||||
|
||||
private RNFirebaseNotificationManager notificationManager;
|
||||
public RNFirebaseNotifications(ReactApplicationContext context) {
|
||||
super(context);
|
||||
context.addActivityEventListener(this);
|
||||
|
||||
notificationManager = new RNFirebaseNotificationManager(context);
|
||||
sharedPreferences = context.getSharedPreferences(BADGE_FILE, Context.MODE_PRIVATE);
|
||||
|
||||
LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(context);
|
||||
|
||||
// Subscribe to remote notification events
|
||||
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));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "RNFirebaseNotifications";
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void cancelAllNotifications() {
|
||||
notificationManager.cancelAllNotifications();
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void cancelNotification(String notificationId) {
|
||||
notificationManager.cancelNotification(notificationId);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void displayNotification(ReadableMap notification, Promise promise) {
|
||||
notificationManager.displayNotification(notification, promise);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void getBadge(Promise promise) {
|
||||
int badge = sharedPreferences.getInt(BADGE_KEY, 0);
|
||||
Log.d(TAG, "Got badge count: " + badge);
|
||||
promise.resolve(badge);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void getInitialNotification(Promise promise) {
|
||||
WritableMap notificationOpenMap = null;
|
||||
if (getCurrentActivity() != null) {
|
||||
notificationOpenMap = parseIntentForNotification(getCurrentActivity().getIntent());
|
||||
}
|
||||
promise.resolve(notificationOpenMap);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void getScheduledNotifications(Promise promise) {
|
||||
ArrayList<Bundle> bundles = notificationManager.getScheduledNotifications();
|
||||
WritableArray array = Arguments.createArray();
|
||||
for (Bundle bundle : bundles) {
|
||||
array.pushMap(parseNotificationBundle(bundle));
|
||||
}
|
||||
promise.resolve(array);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void removeAllDeliveredNotifications() {
|
||||
notificationManager.removeAllDeliveredNotifications();
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void removeDeliveredNotification(String notificationId) {
|
||||
notificationManager.removeDeliveredNotification(notificationId);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void setBadge(int badge) {
|
||||
// Store the badge count for later retrieval
|
||||
sharedPreferences.edit().putInt(BADGE_KEY, badge).apply();
|
||||
if (badge == 0) {
|
||||
Log.d(TAG, "Remove badge count");
|
||||
ShortcutBadger.removeCount(this.getReactApplicationContext());
|
||||
} else {
|
||||
Log.d(TAG, "Apply badge count: " + badge);
|
||||
ShortcutBadger.applyCount(this.getReactApplicationContext(), badge);
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void scheduleNotification(ReadableMap notification, Promise promise) {
|
||||
notificationManager.scheduleNotification(notification, promise);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Start Android specific methods
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
@ReactMethod
|
||||
public void createChannel(ReadableMap channelMap, Promise promise) {
|
||||
notificationManager.createChannel(channelMap);
|
||||
promise.resolve(null);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void createChannelGroup(ReadableMap channelGroupMap, Promise promise) {
|
||||
notificationManager.createChannelGroup(channelGroupMap);
|
||||
promise.resolve(null);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void createChannelGroup(ReadableArray channelGroupsArray, Promise promise) {
|
||||
notificationManager.createChannelGroups(channelGroupsArray);
|
||||
promise.resolve(null);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void createChannels(ReadableArray channelsArray, Promise promise) {
|
||||
notificationManager.createChannels(channelsArray);
|
||||
promise.resolve(null);
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// End Android specific methods
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Start ActivityEventListener methods
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
@Override
|
||||
public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
|
||||
// FCM functionality does not need this function
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNewIntent(Intent intent) {
|
||||
WritableMap notificationOpenMap = parseIntentForNotification(intent);
|
||||
if (notificationOpenMap != null) {
|
||||
Utils.sendEvent(getReactApplicationContext(), "notifications_notification_opened", notificationOpenMap);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// End ActivityEventListener methods
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
private WritableMap parseIntentForNotification(Intent intent) {
|
||||
WritableMap notificationOpenMap = parseIntentForRemoteNotification(intent);
|
||||
if (notificationOpenMap == null) {
|
||||
notificationOpenMap = parseIntentForLocalNotification(intent);
|
||||
}
|
||||
return notificationOpenMap;
|
||||
}
|
||||
|
||||
private WritableMap parseIntentForLocalNotification(Intent intent) {
|
||||
if (intent.getExtras() == null || !intent.hasExtra("notificationId")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
WritableMap notificationMap = Arguments.makeNativeMap(intent.getExtras());
|
||||
WritableMap notificationOpenMap = Arguments.createMap();
|
||||
notificationOpenMap.putString("action", intent.getAction());
|
||||
notificationOpenMap.putMap("notification", notificationMap);
|
||||
|
||||
// Check for remote input results
|
||||
Bundle remoteInput = RemoteInput.getResultsFromIntent(intent);
|
||||
if (remoteInput != null) {
|
||||
notificationOpenMap.putMap("results", Arguments.makeNativeMap(remoteInput));
|
||||
}
|
||||
|
||||
return notificationOpenMap;
|
||||
}
|
||||
|
||||
private WritableMap parseIntentForRemoteNotification(Intent intent) {
|
||||
// Check if FCM data exists
|
||||
if (intent.getExtras() == null || !intent.hasExtra("google.message_id")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Bundle extras = intent.getExtras();
|
||||
|
||||
WritableMap notificationMap = Arguments.createMap();
|
||||
WritableMap dataMap = Arguments.createMap();
|
||||
|
||||
for (String key : extras.keySet()) {
|
||||
if (key.equals("google.message_id")) {
|
||||
notificationMap.putString("notificationId", extras.getString(key));
|
||||
} else if (key.equals("collapse_key")
|
||||
|| key.equals("from")
|
||||
|| key.equals("google.sent_time")
|
||||
|| key.equals("google.ttl")
|
||||
|| key.equals("_fbSourceApplicationHasBeenSet")) {
|
||||
// ignore known unneeded fields
|
||||
} else {
|
||||
dataMap.putString(key, extras.getString(key));
|
||||
}
|
||||
}
|
||||
notificationMap.putMap("data", dataMap);
|
||||
|
||||
WritableMap notificationOpenMap = Arguments.createMap();
|
||||
notificationOpenMap.putString("action", intent.getAction());
|
||||
notificationOpenMap.putMap("notification", notificationMap);
|
||||
|
||||
return notificationOpenMap;
|
||||
}
|
||||
|
||||
private WritableMap parseNotificationBundle(Bundle notification) {
|
||||
return Arguments.makeNativeMap(notification);
|
||||
}
|
||||
|
||||
private WritableMap parseRemoteMessage(RemoteMessage message) {
|
||||
RemoteMessage.Notification notification = message.getNotification();
|
||||
|
||||
WritableMap notificationMap = Arguments.createMap();
|
||||
WritableMap dataMap = Arguments.createMap();
|
||||
|
||||
// Cross platform notification properties
|
||||
notificationMap.putString("body", notification.getBody());
|
||||
if (message.getData() != null) {
|
||||
for (Map.Entry<String, String> e : message.getData().entrySet()) {
|
||||
dataMap.putString(e.getKey(), e.getValue());
|
||||
}
|
||||
}
|
||||
notificationMap.putMap("data", dataMap);
|
||||
if (message.getMessageId() != null) {
|
||||
notificationMap.putString("notificationId", message.getMessageId());
|
||||
}
|
||||
if (notification.getSound() != null) {
|
||||
notificationMap.putString("sound", notification.getSound());
|
||||
}
|
||||
if (notification.getTitle() != null) {
|
||||
notificationMap.putString("title", notification.getTitle());
|
||||
}
|
||||
|
||||
// Android specific notification properties
|
||||
WritableMap androidMap = Arguments.createMap();
|
||||
if (notification.getClickAction() != null) {
|
||||
androidMap.putString("clickAction", notification.getClickAction());
|
||||
}
|
||||
if (notification.getColor() != null) {
|
||||
androidMap.putString("color", notification.getColor());
|
||||
}
|
||||
if (notification.getIcon() != null) {
|
||||
WritableMap iconMap = Arguments.createMap();
|
||||
iconMap.putString("icon", notification.getIcon());
|
||||
androidMap.putMap("smallIcon", iconMap);
|
||||
}
|
||||
if (notification.getTag() != null) {
|
||||
androidMap.putString("group", notification.getTag());
|
||||
}
|
||||
notificationMap.putMap("android", androidMap);
|
||||
|
||||
return notificationMap;
|
||||
}
|
||||
|
||||
private class RemoteNotificationReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (getReactApplicationContext().hasActiveCatalystInstance()) {
|
||||
Log.d(TAG, "Received new remote notification");
|
||||
|
||||
RemoteMessage message = intent.getParcelableExtra("notification");
|
||||
WritableMap messageMap = parseRemoteMessage(message);
|
||||
|
||||
Utils.sendEvent(getReactApplicationContext(), "notifications_notification_received", messageMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class ScheduledNotificationReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (getReactApplicationContext().hasActiveCatalystInstance()) {
|
||||
Log.d(TAG, "Received new scheduled notification");
|
||||
|
||||
Bundle notification = intent.getBundleExtra("notification");
|
||||
WritableMap messageMap = parseNotificationBundle(notification);
|
||||
|
||||
Utils.sendEvent(getReactApplicationContext(), "notifications_notification_received", messageMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package io.invertase.firebase.notifications;
|
||||
|
||||
import com.facebook.react.ReactPackage;
|
||||
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;
|
||||
|
||||
public class RNFirebaseNotificationsPackage implements ReactPackage {
|
||||
public RNFirebaseNotificationsPackage() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 RNFirebaseNotifications(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();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package io.invertase.firebase.notifications;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.util.Log;
|
||||
|
||||
/*
|
||||
* This is invoked when the phone restarts to ensure that all notifications are rescheduled
|
||||
* correctly, as Android removes all scheduled alarms when the phone shuts down.
|
||||
*/
|
||||
public class RNFirebaseNotificationsRebootReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
Log.i("RNFNotifRebootReceiver", "Received reboot event");
|
||||
new RNFirebaseNotificationManager(context).rescheduleNotifications();
|
||||
}
|
||||
}
|
|
@ -17,6 +17,9 @@
|
|||
8376F7141F7C149100D45A85 /* RNFirebaseFirestoreDocumentReference.m in Sources */ = {isa = PBXBuildFile; fileRef = 8376F70E1F7C149000D45A85 /* RNFirebaseFirestoreDocumentReference.m */; };
|
||||
8376F7151F7C149100D45A85 /* RNFirebaseFirestore.m in Sources */ = {isa = PBXBuildFile; fileRef = 8376F7101F7C149000D45A85 /* RNFirebaseFirestore.m */; };
|
||||
8376F7161F7C149100D45A85 /* RNFirebaseFirestoreCollectionReference.m in Sources */ = {isa = PBXBuildFile; fileRef = 8376F7111F7C149000D45A85 /* RNFirebaseFirestoreCollectionReference.m */; };
|
||||
838E36FE201B9169004DCD3A /* RNFirebaseMessaging.m in Sources */ = {isa = PBXBuildFile; fileRef = 838E36FD201B9169004DCD3A /* RNFirebaseMessaging.m */; };
|
||||
838E372320231DF0004DCD3A /* RNFirebaseInstanceId.m in Sources */ = {isa = PBXBuildFile; fileRef = 838E372220231DF0004DCD3A /* RNFirebaseInstanceId.m */; };
|
||||
838E372720231E15004DCD3A /* RNFirebaseNotifications.m in Sources */ = {isa = PBXBuildFile; fileRef = 838E372520231E15004DCD3A /* RNFirebaseNotifications.m */; };
|
||||
839D916C1EF3E20B0077C7C8 /* RNFirebaseAdMob.m in Sources */ = {isa = PBXBuildFile; fileRef = 839D914F1EF3E20A0077C7C8 /* RNFirebaseAdMob.m */; };
|
||||
839D916D1EF3E20B0077C7C8 /* RNFirebaseAdMobInterstitial.m in Sources */ = {isa = PBXBuildFile; fileRef = 839D91511EF3E20A0077C7C8 /* RNFirebaseAdMobInterstitial.m */; };
|
||||
839D916E1EF3E20B0077C7C8 /* RNFirebaseAdMobRewardedVideo.m in Sources */ = {isa = PBXBuildFile; fileRef = 839D91531EF3E20A0077C7C8 /* RNFirebaseAdMobRewardedVideo.m */; };
|
||||
|
@ -25,8 +28,8 @@
|
|||
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 */; };
|
||||
839D91741EF3E20B0077C7C8 /* RNFirebaseMessaging.m in Sources */ = {isa = PBXBuildFile; fileRef = 839D91651EF3E20A0077C7C8 /* RNFirebaseMessaging.m */; };
|
||||
839D91751EF3E20B0077C7C8 /* RNFirebasePerformance.m in Sources */ = {isa = PBXBuildFile; fileRef = 839D91681EF3E20A0077C7C8 /* RNFirebasePerformance.m */; };
|
||||
83AAA0792063DEC2007EC5F7 /* RNFirebaseInvites.m in Sources */ = {isa = PBXBuildFile; fileRef = 83AAA0772063DEC2007EC5F7 /* RNFirebaseInvites.m */; };
|
||||
83C3EEEE1FA1EACC00B64D3C /* RNFirebaseUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = 83C3EEEC1FA1EACC00B64D3C /* RNFirebaseUtil.m */; };
|
||||
BA84AE571FA9E59800E79390 /* RNFirebaseStorage.m in Sources */ = {isa = PBXBuildFile; fileRef = BA84AE561FA9E59800E79390 /* RNFirebaseStorage.m */; };
|
||||
D950369E1D19C77400F7094D /* RNFirebase.m in Sources */ = {isa = PBXBuildFile; fileRef = D950369D1D19C77400F7094D /* RNFirebase.m */; };
|
||||
|
@ -66,6 +69,12 @@
|
|||
8376F7111F7C149000D45A85 /* RNFirebaseFirestoreCollectionReference.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFirebaseFirestoreCollectionReference.m; sourceTree = "<group>"; };
|
||||
8376F7121F7C149000D45A85 /* RNFirebaseFirestoreDocumentReference.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNFirebaseFirestoreDocumentReference.h; sourceTree = "<group>"; };
|
||||
8376F7131F7C149000D45A85 /* RNFirebaseFirestoreCollectionReference.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNFirebaseFirestoreCollectionReference.h; sourceTree = "<group>"; };
|
||||
838E36FC201B9169004DCD3A /* RNFirebaseMessaging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNFirebaseMessaging.h; sourceTree = "<group>"; };
|
||||
838E36FD201B9169004DCD3A /* RNFirebaseMessaging.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFirebaseMessaging.m; sourceTree = "<group>"; };
|
||||
838E372120231DF0004DCD3A /* RNFirebaseInstanceId.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNFirebaseInstanceId.h; sourceTree = "<group>"; };
|
||||
838E372220231DF0004DCD3A /* RNFirebaseInstanceId.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFirebaseInstanceId.m; sourceTree = "<group>"; };
|
||||
838E372520231E15004DCD3A /* RNFirebaseNotifications.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFirebaseNotifications.m; sourceTree = "<group>"; };
|
||||
838E372620231E15004DCD3A /* RNFirebaseNotifications.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNFirebaseNotifications.h; sourceTree = "<group>"; };
|
||||
839D914E1EF3E20A0077C7C8 /* RNFirebaseAdMob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNFirebaseAdMob.h; sourceTree = "<group>"; };
|
||||
839D914F1EF3E20A0077C7C8 /* RNFirebaseAdMob.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFirebaseAdMob.m; sourceTree = "<group>"; };
|
||||
839D91501EF3E20A0077C7C8 /* RNFirebaseAdMobInterstitial.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNFirebaseAdMobInterstitial.h; sourceTree = "<group>"; };
|
||||
|
@ -82,11 +91,11 @@
|
|||
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>"; };
|
||||
839D91641EF3E20A0077C7C8 /* RNFirebaseMessaging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNFirebaseMessaging.h; sourceTree = "<group>"; };
|
||||
839D91651EF3E20A0077C7C8 /* RNFirebaseMessaging.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFirebaseMessaging.m; sourceTree = "<group>"; };
|
||||
839D91671EF3E20A0077C7C8 /* RNFirebasePerformance.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNFirebasePerformance.h; sourceTree = "<group>"; };
|
||||
839D91681EF3E20A0077C7C8 /* RNFirebasePerformance.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFirebasePerformance.m; sourceTree = "<group>"; };
|
||||
839D91771EF3E22F0077C7C8 /* RNFirebaseEvents.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNFirebaseEvents.h; path = RNFirebase/RNFirebaseEvents.h; sourceTree = "<group>"; };
|
||||
83AAA0772063DEC2007EC5F7 /* RNFirebaseInvites.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFirebaseInvites.m; sourceTree = "<group>"; };
|
||||
83AAA0782063DEC2007EC5F7 /* RNFirebaseInvites.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNFirebaseInvites.h; sourceTree = "<group>"; };
|
||||
83C3EEEC1FA1EACC00B64D3C /* RNFirebaseUtil.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNFirebaseUtil.m; path = RNFirebase/RNFirebaseUtil.m; sourceTree = "<group>"; };
|
||||
83C3EEED1FA1EACC00B64D3C /* RNFirebaseUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNFirebaseUtil.h; path = RNFirebase/RNFirebaseUtil.h; sourceTree = "<group>"; };
|
||||
BA84AE551FA9E59800E79390 /* RNFirebaseStorage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNFirebaseStorage.h; sourceTree = "<group>"; };
|
||||
|
@ -127,6 +136,9 @@
|
|||
58B511D21A9E6C8500147676 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
83AAA0762063DEC2007EC5F7 /* invites */,
|
||||
838E372420231E15004DCD3A /* notifications */,
|
||||
838E372020231DF0004DCD3A /* instanceid */,
|
||||
8336930F1FD80DE800AA806B /* fabric */,
|
||||
BA84AE541FA9E59800E79390 /* storage */,
|
||||
17AF4F681F59CDBF00C02336 /* links */,
|
||||
|
@ -179,6 +191,26 @@
|
|||
path = RNFirebase/firestore;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
838E372020231DF0004DCD3A /* instanceid */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
838E372120231DF0004DCD3A /* RNFirebaseInstanceId.h */,
|
||||
838E372220231DF0004DCD3A /* RNFirebaseInstanceId.m */,
|
||||
);
|
||||
name = instanceid;
|
||||
path = RNFirebase/instanceid;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
838E372420231E15004DCD3A /* notifications */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
838E372520231E15004DCD3A /* RNFirebaseNotifications.m */,
|
||||
838E372620231E15004DCD3A /* RNFirebaseNotifications.h */,
|
||||
);
|
||||
name = notifications;
|
||||
path = RNFirebase/notifications;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
839D914D1EF3E20A0077C7C8 /* admob */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -256,8 +288,8 @@
|
|||
839D91631EF3E20A0077C7C8 /* messaging */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
839D91641EF3E20A0077C7C8 /* RNFirebaseMessaging.h */,
|
||||
839D91651EF3E20A0077C7C8 /* RNFirebaseMessaging.m */,
|
||||
838E36FC201B9169004DCD3A /* RNFirebaseMessaging.h */,
|
||||
838E36FD201B9169004DCD3A /* RNFirebaseMessaging.m */,
|
||||
);
|
||||
name = messaging;
|
||||
path = RNFirebase/messaging;
|
||||
|
@ -273,6 +305,16 @@
|
|||
path = RNFirebase/perf;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
83AAA0762063DEC2007EC5F7 /* invites */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
83AAA0772063DEC2007EC5F7 /* RNFirebaseInvites.m */,
|
||||
83AAA0782063DEC2007EC5F7 /* RNFirebaseInvites.h */,
|
||||
);
|
||||
name = invites;
|
||||
path = RNFirebase/invites;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
BA84AE541FA9E59800E79390 /* storage */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -339,10 +381,12 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
838E372320231DF0004DCD3A /* RNFirebaseInstanceId.m in Sources */,
|
||||
839D916E1EF3E20B0077C7C8 /* RNFirebaseAdMobRewardedVideo.m in Sources */,
|
||||
839D916C1EF3E20B0077C7C8 /* RNFirebaseAdMob.m in Sources */,
|
||||
17AF4F6B1F59CDBF00C02336 /* RNFirebaseLinks.m in Sources */,
|
||||
8376F7161F7C149100D45A85 /* RNFirebaseFirestoreCollectionReference.m in Sources */,
|
||||
838E36FE201B9169004DCD3A /* RNFirebaseMessaging.m in Sources */,
|
||||
8376F7151F7C149100D45A85 /* RNFirebaseFirestore.m in Sources */,
|
||||
839D91701EF3E20B0077C7C8 /* RNFirebaseAuth.m in Sources */,
|
||||
8323CF091F6FBD870071420B /* RNFirebaseAdMobNativeExpressManager.m in Sources */,
|
||||
|
@ -352,11 +396,12 @@
|
|||
833693131FD824EF00AA806B /* RNFirebaseCrashlytics.m in Sources */,
|
||||
D950369E1D19C77400F7094D /* RNFirebase.m in Sources */,
|
||||
839D91731EF3E20B0077C7C8 /* RNFirebaseDatabase.m in Sources */,
|
||||
838E372720231E15004DCD3A /* RNFirebaseNotifications.m in Sources */,
|
||||
BA84AE571FA9E59800E79390 /* RNFirebaseStorage.m in Sources */,
|
||||
83AAA0792063DEC2007EC5F7 /* RNFirebaseInvites.m in Sources */,
|
||||
8323CF071F6FBD870071420B /* NativeExpressComponent.m in Sources */,
|
||||
83C3EEEE1FA1EACC00B64D3C /* RNFirebaseUtil.m in Sources */,
|
||||
839D91721EF3E20B0077C7C8 /* RNFirebaseCrash.m in Sources */,
|
||||
839D91741EF3E20B0077C7C8 /* RNFirebaseMessaging.m in Sources */,
|
||||
839D91751EF3E20B0077C7C8 /* RNFirebasePerformance.m in Sources */,
|
||||
8323CF061F6FBD870071420B /* BannerComponent.m in Sources */,
|
||||
839D916D1EF3E20B0077C7C8 /* RNFirebaseAdMobInterstitial.m in Sources */,
|
||||
|
|
|
@ -33,14 +33,22 @@ static NSString *const STORAGE_DOWNLOAD_SUCCESS = @"download_success";
|
|||
static NSString *const STORAGE_DOWNLOAD_FAILURE = @"download_failure";
|
||||
|
||||
// Messaging
|
||||
static NSString *const MESSAGING_MESSAGE_RECEIVED = @"messaging_message_received";
|
||||
static NSString *const MESSAGING_TOKEN_REFRESHED = @"messaging_token_refreshed";
|
||||
static NSString *const MESSAGING_NOTIFICATION_RECEIVED = @"messaging_notification_received";
|
||||
|
||||
// Notifications
|
||||
static NSString *const NOTIFICATIONS_NOTIFICATION_DISPLAYED = @"notifications_notification_displayed";
|
||||
static NSString *const NOTIFICATIONS_NOTIFICATION_OPENED = @"notifications_notification_opened";
|
||||
static NSString *const NOTIFICATIONS_NOTIFICATION_RECEIVED = @"notifications_notification_received";
|
||||
|
||||
// AdMob
|
||||
static NSString *const ADMOB_INTERSTITIAL_EVENT = @"interstitial_event";
|
||||
static NSString *const ADMOB_REWARDED_VIDEO_EVENT = @"rewarded_video_event";
|
||||
|
||||
// Links
|
||||
static NSString *const LINKS_DYNAMIC_LINK_RECEIVED = @"dynamic_link_received";
|
||||
static NSString *const LINKS_LINK_RECEIVED = @"links_link_received";
|
||||
|
||||
// Invites
|
||||
static NSString *const INVITES_INVITATION_RECEIVED = @"invites_invitation_received";
|
||||
|
||||
#endif
|
||||
|
|
|
@ -10,8 +10,8 @@
|
|||
+ (FIRApp *)getApp:(NSString *)appDisplayName;
|
||||
+ (NSString *)getAppName:(NSString *)appDisplayName;
|
||||
+ (NSString *)getAppDisplayName:(NSString *)appName;
|
||||
+ (void)sendJSEvent:(RCTEventEmitter *)emitter name:(NSString *)name body:(NSDictionary *)body;
|
||||
+ (void)sendJSEventWithAppName:(RCTEventEmitter *)emitter app:(FIRApp *)app name:(NSString *)name body:(NSDictionary *)body;
|
||||
+ (void)sendJSEvent:(RCTEventEmitter *)emitter name:(NSString *)name body:(id)body;
|
||||
+ (void)sendJSEventWithAppName:(RCTEventEmitter *)emitter app:(FIRApp *)app name:(NSString *)name body:(id)body;
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ static NSString *const DEFAULT_APP_NAME = @"__FIRAPP_DEFAULT";
|
|||
return appName;
|
||||
}
|
||||
|
||||
+ (void)sendJSEvent:(RCTEventEmitter *)emitter name:(NSString *)name body:(NSDictionary *)body {
|
||||
+ (void)sendJSEvent:(RCTEventEmitter *)emitter name:(NSString *)name body:(id)body {
|
||||
@try {
|
||||
// TODO: Temporary fix for https://github.com/invertase/react-native-firebase/issues/233
|
||||
// until a better solution comes around
|
||||
|
@ -36,7 +36,7 @@ static NSString *const DEFAULT_APP_NAME = @"__FIRAPP_DEFAULT";
|
|||
}
|
||||
}
|
||||
|
||||
+ (void)sendJSEventWithAppName:(RCTEventEmitter *)emitter app:(FIRApp *)app name:(NSString *)name body:(NSDictionary *)body {
|
||||
+ (void)sendJSEventWithAppName:(RCTEventEmitter *)emitter app:(FIRApp *)app name:(NSString *)name body:(id)body {
|
||||
// Add the appName to the body
|
||||
NSMutableDictionary *newBody = [body mutableCopy];
|
||||
newBody[@"appName"] = [RNFirebaseUtil getAppDisplayName:app.name];
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
+ (void)handlePromise:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject databaseError:(NSError *)databaseError;
|
||||
|
||||
+ (FIRDatabase *)getDatabaseForApp:(NSString *)appDisplayName;
|
||||
+ (FIRDatabase *)getDatabaseForApp:(NSString *)appDisplayName URL:(NSString *)url;
|
||||
|
||||
+ (NSDictionary *)getJSError:(NSError *)nativeError;
|
||||
|
||||
|
|
|
@ -22,22 +22,26 @@ RCT_EXPORT_MODULE();
|
|||
return self;
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(goOnline:(NSString *)appDisplayName) {
|
||||
[[RNFirebaseDatabase getDatabaseForApp:appDisplayName] goOnline];
|
||||
RCT_EXPORT_METHOD(goOnline:(NSString *)appDisplayName
|
||||
dbURL:(NSString *)dbURL) {
|
||||
[[RNFirebaseDatabase getDatabaseForApp:appDisplayName URL:dbURL] goOnline];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(goOffline:(NSString *)appDisplayName) {
|
||||
[[RNFirebaseDatabase getDatabaseForApp:appDisplayName] goOffline];
|
||||
RCT_EXPORT_METHOD(goOffline:(NSString *)appDisplayName
|
||||
dbURL:(NSString *)dbURL) {
|
||||
[[RNFirebaseDatabase getDatabaseForApp:appDisplayName URL:dbURL] goOffline];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(setPersistence:(NSString *)appDisplayName
|
||||
dbURL:(NSString *)dbURL
|
||||
state:(BOOL)state) {
|
||||
[RNFirebaseDatabase getDatabaseForApp:appDisplayName].persistenceEnabled = state;
|
||||
[RNFirebaseDatabase getDatabaseForApp:appDisplayName URL:dbURL].persistenceEnabled = state;
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(setPersistenceCacheSizeBytes:(NSString *)appDisplayName
|
||||
dbURL:(NSString *)dbURL
|
||||
size:(NSInteger *)size) {
|
||||
[RNFirebaseDatabase getDatabaseForApp:appDisplayName].persistenceCacheSizeBytes = (NSUInteger)size;
|
||||
[RNFirebaseDatabase getDatabaseForApp:appDisplayName URL:dbURL].persistenceCacheSizeBytes = (NSUInteger)size;
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(enableLogging:(BOOL)enabled) {
|
||||
|
@ -45,15 +49,17 @@ RCT_EXPORT_METHOD(enableLogging:(BOOL)enabled) {
|
|||
}
|
||||
|
||||
RCT_EXPORT_METHOD(keepSynced:(NSString *)appDisplayName
|
||||
dbURL:(NSString *)dbURL
|
||||
key:(NSString *)key
|
||||
path:(NSString *)path
|
||||
modifiers:(NSArray *)modifiers
|
||||
state:(BOOL)state) {
|
||||
FIRDatabaseQuery *query = [self getInternalReferenceForApp:appDisplayName key:key path:path modifiers:modifiers].query;
|
||||
FIRDatabaseQuery *query = [self getInternalReferenceForApp:appDisplayName dbURL:dbURL key:key path:path modifiers:modifiers].query;
|
||||
[query keepSynced:state];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(transactionTryCommit:(NSString *)appDisplayName
|
||||
dbURL:(NSString *)dbURL
|
||||
transactionId:(nonnull NSNumber *)transactionId
|
||||
updates:(NSDictionary *)updates) {
|
||||
__block NSMutableDictionary *transactionState;
|
||||
|
@ -83,6 +89,7 @@ RCT_EXPORT_METHOD(transactionTryCommit:(NSString *)appDisplayName
|
|||
|
||||
|
||||
RCT_EXPORT_METHOD(transactionStart:(NSString *)appDisplayName
|
||||
dbURL:(NSString *)dbURL
|
||||
path:(NSString *)path
|
||||
transactionId:(nonnull NSNumber *)transactionId
|
||||
applyLocally:(BOOL)applyLocally) {
|
||||
|
@ -90,12 +97,12 @@ RCT_EXPORT_METHOD(transactionStart:(NSString *)appDisplayName
|
|||
NSMutableDictionary *transactionState = [NSMutableDictionary new];
|
||||
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
|
||||
transactionState[@"semaphore"] = sema;
|
||||
FIRDatabaseReference *ref = [self getReferenceForAppPath:appDisplayName path:path];
|
||||
FIRDatabaseReference *ref = [self getReferenceForAppPath:appDisplayName dbURL:dbURL path:path];
|
||||
|
||||
[ref runTransactionBlock:^FIRTransactionResult *_Nonnull (FIRMutableData *_Nonnull currentData) {
|
||||
dispatch_barrier_async(_transactionQueue, ^{
|
||||
[_transactions setValue:transactionState forKey:[transactionId stringValue]];
|
||||
NSDictionary *updateMap = [self createTransactionUpdateMap:appDisplayName transactionId:transactionId updatesData:currentData];
|
||||
NSDictionary *updateMap = [self createTransactionUpdateMap:appDisplayName dbURL:dbURL transactionId:transactionId updatesData:currentData];
|
||||
[RNFirebaseUtil sendJSEvent:self name:DATABASE_TRANSACTION_EVENT body:updateMap];
|
||||
});
|
||||
|
||||
|
@ -120,123 +127,134 @@ RCT_EXPORT_METHOD(transactionStart:(NSString *)appDisplayName
|
|||
return [FIRTransactionResult successWithValue:currentData];
|
||||
}
|
||||
} andCompletionBlock:^(NSError *_Nullable databaseError, BOOL committed, FIRDataSnapshot *_Nullable snapshot) {
|
||||
NSDictionary *resultMap = [self createTransactionResultMap:appDisplayName transactionId:transactionId error:databaseError committed:committed snapshot:snapshot];
|
||||
NSDictionary *resultMap = [self createTransactionResultMap:appDisplayName dbURL:dbURL transactionId:transactionId error:databaseError committed:committed snapshot:snapshot];
|
||||
[RNFirebaseUtil sendJSEvent:self name:DATABASE_TRANSACTION_EVENT body:resultMap];
|
||||
} withLocalEvents:applyLocally];
|
||||
});
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(onDisconnectSet:(NSString *)appDisplayName
|
||||
dbURL:(NSString *)dbURL
|
||||
path:(NSString *)path
|
||||
props:(NSDictionary *)props
|
||||
resolver:(RCTPromiseResolveBlock)resolve
|
||||
rejecter:(RCTPromiseRejectBlock)reject) {
|
||||
FIRDatabaseReference *ref = [self getReferenceForAppPath:appDisplayName path:path];
|
||||
FIRDatabaseReference *ref = [self getReferenceForAppPath:appDisplayName dbURL:dbURL path:path];
|
||||
[ref onDisconnectSetValue:props[@"value"] withCompletionBlock:^(NSError *_Nullable error, FIRDatabaseReference *_Nonnull _ref) {
|
||||
[RNFirebaseDatabase handlePromise:resolve rejecter:reject databaseError:error];
|
||||
}];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(onDisconnectUpdate:(NSString *)appDisplayName
|
||||
dbURL:(NSString *)dbURL
|
||||
path:(NSString *)path
|
||||
props:(NSDictionary *)props
|
||||
resolver:(RCTPromiseResolveBlock)resolve
|
||||
rejecter:(RCTPromiseRejectBlock)reject) {
|
||||
FIRDatabaseReference *ref = [self getReferenceForAppPath:appDisplayName path:path];
|
||||
FIRDatabaseReference *ref = [self getReferenceForAppPath:appDisplayName dbURL:dbURL path:path];
|
||||
[ref onDisconnectUpdateChildValues:props withCompletionBlock:^(NSError *_Nullable error, FIRDatabaseReference *_Nonnull _ref) {
|
||||
[RNFirebaseDatabase handlePromise:resolve rejecter:reject databaseError:error];
|
||||
}];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(onDisconnectRemove:(NSString *)appDisplayName
|
||||
dbURL:(NSString *)dbURL
|
||||
path:(NSString *)path
|
||||
resolver:(RCTPromiseResolveBlock)resolve
|
||||
rejecter:(RCTPromiseRejectBlock)reject) {
|
||||
FIRDatabaseReference *ref = [self getReferenceForAppPath:appDisplayName path:path];
|
||||
FIRDatabaseReference *ref = [self getReferenceForAppPath:appDisplayName dbURL:dbURL path:path];
|
||||
[ref onDisconnectRemoveValueWithCompletionBlock:^(NSError *_Nullable error, FIRDatabaseReference *_Nonnull _ref) {
|
||||
[RNFirebaseDatabase handlePromise:resolve rejecter:reject databaseError:error];
|
||||
}];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(onDisconnectCancel:(NSString *)appDisplayName
|
||||
dbURL:(NSString *)dbURL
|
||||
path:(NSString *)path
|
||||
resolver:(RCTPromiseResolveBlock)resolve
|
||||
rejecter:(RCTPromiseRejectBlock)reject) {
|
||||
FIRDatabaseReference *ref = [self getReferenceForAppPath:appDisplayName path:path];
|
||||
FIRDatabaseReference *ref = [self getReferenceForAppPath:appDisplayName dbURL:dbURL path:path];
|
||||
[ref cancelDisconnectOperationsWithCompletionBlock:^(NSError *_Nullable error, FIRDatabaseReference *_Nonnull _ref) {
|
||||
[RNFirebaseDatabase handlePromise:resolve rejecter:reject databaseError:error];
|
||||
}];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(set:(NSString *)appDisplayName
|
||||
dbURL:(NSString *)dbURL
|
||||
path:(NSString *)path
|
||||
props:(NSDictionary *)props
|
||||
resolver:(RCTPromiseResolveBlock)resolve
|
||||
rejecter:(RCTPromiseRejectBlock)reject) {
|
||||
FIRDatabaseReference *ref = [self getReferenceForAppPath:appDisplayName path:path];
|
||||
FIRDatabaseReference *ref = [self getReferenceForAppPath:appDisplayName dbURL:dbURL path:path];
|
||||
[ref setValue:[props valueForKey:@"value"] withCompletionBlock:^(NSError *_Nullable error, FIRDatabaseReference *_Nonnull _ref) {
|
||||
[RNFirebaseDatabase handlePromise:resolve rejecter:reject databaseError:error];
|
||||
}];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(setPriority:(NSString *)appDisplayName
|
||||
dbURL:(NSString *)dbURL
|
||||
path:(NSString *)path
|
||||
priority:(NSDictionary *)priority
|
||||
resolver:(RCTPromiseResolveBlock)resolve
|
||||
rejecter:(RCTPromiseRejectBlock)reject) {
|
||||
FIRDatabaseReference *ref = [self getReferenceForAppPath:appDisplayName path:path];
|
||||
FIRDatabaseReference *ref = [self getReferenceForAppPath:appDisplayName dbURL:dbURL path:path];
|
||||
[ref setPriority:[priority valueForKey:@"value"] withCompletionBlock:^(NSError *_Nullable error, FIRDatabaseReference *_Nonnull ref) {
|
||||
[RNFirebaseDatabase handlePromise:resolve rejecter:reject databaseError:error];
|
||||
}];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(setWithPriority:(NSString *)appDisplayName
|
||||
dbURL:(NSString *)dbURL
|
||||
path:(NSString *)path
|
||||
data:(NSDictionary *)data
|
||||
priority:(NSDictionary *)priority
|
||||
resolver:(RCTPromiseResolveBlock)resolve
|
||||
rejecter:(RCTPromiseRejectBlock)reject) {
|
||||
FIRDatabaseReference *ref = [self getReferenceForAppPath:appDisplayName path:path];
|
||||
FIRDatabaseReference *ref = [self getReferenceForAppPath:appDisplayName dbURL:dbURL path:path];
|
||||
[ref setValue:[data valueForKey:@"value"] andPriority:[priority valueForKey:@"value"] withCompletionBlock:^(NSError *_Nullable error, FIRDatabaseReference *_Nonnull ref) {
|
||||
[RNFirebaseDatabase handlePromise:resolve rejecter:reject databaseError:error];
|
||||
}];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(update:(NSString *)appDisplayName
|
||||
dbURL:(NSString *)dbURL
|
||||
path:(NSString *)path
|
||||
props:(NSDictionary *)props
|
||||
resolver:(RCTPromiseResolveBlock)resolve
|
||||
rejecter:(RCTPromiseRejectBlock)reject) {
|
||||
FIRDatabaseReference *ref = [self getReferenceForAppPath:appDisplayName path:path];
|
||||
FIRDatabaseReference *ref = [self getReferenceForAppPath:appDisplayName dbURL:dbURL path:path];
|
||||
[ref updateChildValues:props withCompletionBlock:^(NSError *_Nullable error, FIRDatabaseReference *_Nonnull _ref) {
|
||||
[RNFirebaseDatabase handlePromise:resolve rejecter:reject databaseError:error];
|
||||
}];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(remove:(NSString *)appDisplayName
|
||||
dbURL:(NSString *)dbURL
|
||||
path:(NSString *)path
|
||||
resolver:(RCTPromiseResolveBlock)resolve
|
||||
rejecter:(RCTPromiseRejectBlock)reject) {
|
||||
FIRDatabaseReference *ref = [self getReferenceForAppPath:appDisplayName path:path];
|
||||
FIRDatabaseReference *ref = [self getReferenceForAppPath:appDisplayName dbURL:dbURL path:path];
|
||||
[ref removeValueWithCompletionBlock:^(NSError *_Nullable error, FIRDatabaseReference *_Nonnull _ref) {
|
||||
[RNFirebaseDatabase handlePromise:resolve rejecter:reject databaseError:error];
|
||||
}];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(once:(NSString *)appDisplayName
|
||||
dbURL:(NSString *)dbURL
|
||||
key:(NSString *)key
|
||||
path:(NSString *)path
|
||||
modifiers:(NSArray *)modifiers
|
||||
eventName:(NSString *)eventName
|
||||
resolver:(RCTPromiseResolveBlock)resolve
|
||||
rejecter:(RCTPromiseRejectBlock)reject) {
|
||||
RNFirebaseDatabaseReference *ref = [self getInternalReferenceForApp:appDisplayName key:key path:path modifiers:modifiers];
|
||||
RNFirebaseDatabaseReference *ref = [self getInternalReferenceForApp:appDisplayName dbURL:dbURL key:key path:path modifiers:modifiers];
|
||||
[ref once:eventName resolver:resolve rejecter:reject];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(on:(NSString *)appDisplayName
|
||||
dbURL:(NSString *)dbURL
|
||||
props:(NSDictionary *)props) {
|
||||
RNFirebaseDatabaseReference *ref = [self getCachedInternalReferenceForApp:appDisplayName props:props];
|
||||
RNFirebaseDatabaseReference *ref = [self getCachedInternalReferenceForApp:appDisplayName dbURL:dbURL props:props];
|
||||
[ref on:props[@"eventType"] registration:props[@"registration"]];
|
||||
}
|
||||
|
||||
|
@ -271,23 +289,31 @@ RCT_EXPORT_METHOD(off:(NSString *)key
|
|||
return [FIRDatabase databaseForApp:app];
|
||||
}
|
||||
|
||||
- (FIRDatabaseReference *)getReferenceForAppPath:(NSString *)appDisplayName path:(NSString *)path {
|
||||
return [[RNFirebaseDatabase getDatabaseForApp:appDisplayName] referenceWithPath:path];
|
||||
+ (FIRDatabase *)getDatabaseForApp:(NSString *)appDisplayName URL:(NSString *)url {
|
||||
if (url == nil) {
|
||||
return [self getDatabaseForApp:appDisplayName];
|
||||
}
|
||||
FIRApp *app = [RNFirebaseUtil getApp:appDisplayName];
|
||||
return [FIRDatabase databaseForApp:app URL:url];
|
||||
}
|
||||
|
||||
- (RNFirebaseDatabaseReference *)getInternalReferenceForApp:(NSString *)appDisplayName key:(NSString *)key path:(NSString *)path modifiers:(NSArray *)modifiers {
|
||||
return [[RNFirebaseDatabaseReference alloc] initWithPathAndModifiers:self appDisplayName:appDisplayName key:key refPath:path modifiers:modifiers];
|
||||
- (FIRDatabaseReference *)getReferenceForAppPath:(NSString *)appDisplayName dbURL:(NSString *)dbURL path:(NSString *)path {
|
||||
return [[RNFirebaseDatabase getDatabaseForApp:appDisplayName URL:dbURL] referenceWithPath:path];
|
||||
}
|
||||
|
||||
- (RNFirebaseDatabaseReference *)getCachedInternalReferenceForApp:(NSString *)appDisplayName props:(NSDictionary *)props {
|
||||
- (RNFirebaseDatabaseReference *)getInternalReferenceForApp:(NSString *)appDisplayName dbURL:(NSString *)dbURL key:(NSString *)key path:(NSString *)path modifiers:(NSArray *)modifiers {
|
||||
return [[RNFirebaseDatabaseReference alloc] initWithPathAndModifiers:self appDisplayName:appDisplayName dbURL:dbURL key:key refPath:path modifiers:modifiers];
|
||||
}
|
||||
|
||||
- (RNFirebaseDatabaseReference *)getCachedInternalReferenceForApp:(NSString *)appDisplayName dbURL:(NSString *)dbURL props:(NSDictionary *)props {
|
||||
NSString *key = props[@"key"];
|
||||
NSString *path = props[@"path"];
|
||||
NSDictionary *modifiers = props[@"modifiers"];
|
||||
NSArray *modifiers = props[@"modifiers"];
|
||||
|
||||
RNFirebaseDatabaseReference *ref = _dbReferences[key];
|
||||
|
||||
if (ref == nil) {
|
||||
ref = [[RNFirebaseDatabaseReference alloc] initWithPathAndModifiers:self appDisplayName:appDisplayName key:key refPath:path modifiers:modifiers];
|
||||
ref = [[RNFirebaseDatabaseReference alloc] initWithPathAndModifiers:self appDisplayName:appDisplayName dbURL:dbURL key:key refPath:path modifiers:modifiers];
|
||||
_dbReferences[key] = ref;
|
||||
}
|
||||
return ref;
|
||||
|
@ -375,20 +401,22 @@ RCT_EXPORT_METHOD(off:(NSString *)key
|
|||
return errorMap;
|
||||
}
|
||||
|
||||
- (NSDictionary *)createTransactionUpdateMap:(NSString *)appDisplayName transactionId:(NSNumber *)transactionId updatesData:(FIRMutableData *)updatesData {
|
||||
- (NSDictionary *)createTransactionUpdateMap:(NSString *)appDisplayName dbURL:(NSString *)dbURL transactionId:(NSNumber *)transactionId updatesData:(FIRMutableData *)updatesData {
|
||||
NSMutableDictionary *updatesMap = [[NSMutableDictionary alloc] init];
|
||||
[updatesMap setValue:transactionId forKey:@"id"];
|
||||
[updatesMap setValue:@"update" forKey:@"type"];
|
||||
[updatesMap setValue:appDisplayName forKey:@"appName"];
|
||||
[updatesMap setValue:dbURL forKey:@"dbURL"];
|
||||
[updatesMap setValue:updatesData.value forKey:@"value"];
|
||||
|
||||
return updatesMap;
|
||||
}
|
||||
|
||||
- (NSDictionary *)createTransactionResultMap:(NSString *)appDisplayName transactionId:(NSNumber *)transactionId error:(NSError *)error committed:(BOOL)committed snapshot:(FIRDataSnapshot *)snapshot {
|
||||
- (NSDictionary *)createTransactionResultMap:(NSString *)appDisplayName dbURL:(NSString *)dbURL transactionId:(NSNumber *)transactionId error:(NSError *)error committed:(BOOL)committed snapshot:(FIRDataSnapshot *)snapshot {
|
||||
NSMutableDictionary *resultMap = [[NSMutableDictionary alloc] init];
|
||||
[resultMap setValue:transactionId forKey:@"id"];
|
||||
[resultMap setValue:appDisplayName forKey:@"appName"];
|
||||
[resultMap setValue:dbURL forKey:@"dbURL"];
|
||||
// TODO: no timeout on iOS
|
||||
[resultMap setValue:@(committed) forKey:@"committed"];
|
||||
// TODO: no interrupted on iOS
|
||||
|
|
|
@ -13,11 +13,12 @@
|
|||
@property RCTEventEmitter *emitter;
|
||||
@property FIRDatabaseQuery *query;
|
||||
@property NSString *appDisplayName;
|
||||
@property NSString *dbURL;
|
||||
@property NSString *key;
|
||||
@property NSString *path;
|
||||
@property NSMutableDictionary *listeners;
|
||||
|
||||
- (id)initWithPathAndModifiers:(RCTEventEmitter *)emitter appDisplayName:(NSString *)appDisplayName key:(NSString *)key refPath:(NSString *)refPath modifiers:(NSArray *)modifiers;
|
||||
- (id)initWithPathAndModifiers:(RCTEventEmitter *)emitter appDisplayName:(NSString *)appDisplayName dbURL:(NSString *)dbURL key:(NSString *)key refPath:(NSString *)refPath modifiers:(NSArray *)modifiers;
|
||||
- (void)on:(NSString *) eventName registration:(NSDictionary *) registration;
|
||||
- (void)once:(NSString *) eventType resolver:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject;
|
||||
- (void)removeEventListener:(NSString *)eventRegistrationKey;
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
- (id)initWithPathAndModifiers:(RCTEventEmitter *)emitter
|
||||
appDisplayName:(NSString *)appDisplayName
|
||||
dbURL:(NSString *)dbURL
|
||||
key:(NSString *)key
|
||||
refPath:(NSString *)refPath
|
||||
modifiers:(NSArray *)modifiers {
|
||||
|
@ -13,6 +14,7 @@
|
|||
if (self) {
|
||||
_emitter = emitter;
|
||||
_appDisplayName = appDisplayName;
|
||||
_dbURL = dbURL;
|
||||
_key = key;
|
||||
_path = refPath;
|
||||
_listeners = [[NSMutableDictionary alloc] init];
|
||||
|
@ -123,7 +125,7 @@
|
|||
|
||||
- (FIRDatabaseQuery *)buildQueryAtPathWithModifiers:(NSString *)path
|
||||
modifiers:(NSArray *)modifiers {
|
||||
FIRDatabase *firebaseDatabase = [RNFirebaseDatabase getDatabaseForApp:_appDisplayName];
|
||||
FIRDatabase *firebaseDatabase = [RNFirebaseDatabase getDatabaseForApp:_appDisplayName URL:_dbURL];
|
||||
FIRDatabaseQuery *query = [[firebaseDatabase reference] child:path];
|
||||
|
||||
for (NSDictionary *modifier in modifiers) {
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
#ifndef RNFirebaseInstanceId_h
|
||||
#define RNFirebaseInstanceId_h
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#if __has_include(<FirebaseInstanceID/FirebaseInstanceID.h>)
|
||||
#import <React/RCTBridgeModule.h>
|
||||
|
||||
@interface RNFirebaseInstanceId : NSObject <RCTBridgeModule> {
|
||||
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#else
|
||||
@interface RNFirebaseInstanceId : NSObject
|
||||
@end
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -0,0 +1,35 @@
|
|||
#import "RNFirebaseInstanceId.h"
|
||||
|
||||
#if __has_include(<FirebaseInstanceID/FIRInstanceID.h>)
|
||||
#import <FirebaseInstanceID/FIRInstanceID.h>
|
||||
|
||||
@implementation RNFirebaseInstanceId
|
||||
RCT_EXPORT_MODULE();
|
||||
|
||||
RCT_EXPORT_METHOD(delete:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
|
||||
[[FIRInstanceID instanceID] deleteIDWithHandler:^(NSError * _Nullable error) {
|
||||
if (error) {
|
||||
reject(@"instance_id_error", @"Failed to delete instance id", error);
|
||||
} else {
|
||||
resolve(nil);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(get:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
|
||||
[[FIRInstanceID instanceID] getIDWithHandler:^(NSString * _Nullable identity, NSError * _Nullable error) {
|
||||
if (error) {
|
||||
reject(@"instance_id_error", @"Failed to get instance id", error);
|
||||
} else {
|
||||
resolve(identity);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#else
|
||||
@implementation RNFirebaseInstanceId
|
||||
@end
|
||||
#endif
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
#ifndef RNFirebaseInvites_h
|
||||
#define RNFirebaseInvites_h
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#if __has_include(<FirebaseInvites/FirebaseInvites.h>)
|
||||
#import <FirebaseInvites/FirebaseInvites.h>
|
||||
#import <React/RCTBridgeModule.h>
|
||||
#import <React/RCTEventEmitter.h>
|
||||
|
||||
@interface RNFirebaseInvites : RCTEventEmitter<RCTBridgeModule, FIRInviteDelegate>
|
||||
|
||||
+ (_Nonnull instancetype)instance;
|
||||
|
||||
@property _Nullable RCTPromiseRejectBlock invitationsRejecter;
|
||||
@property _Nullable RCTPromiseResolveBlock invitationsResolver;
|
||||
|
||||
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options;
|
||||
- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray *))restorationHandler;
|
||||
|
||||
@end
|
||||
|
||||
#else
|
||||
@interface RNFirebaseInvites : NSObject
|
||||
@end
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -0,0 +1,186 @@
|
|||
#import "RNFirebaseInvites.h"
|
||||
|
||||
#if __has_include(<FirebaseInvites/FirebaseInvites.h>)
|
||||
#import "RNFirebaseEvents.h"
|
||||
#import "RNFirebaseLinks.h"
|
||||
#import "RNFirebaseUtil.h"
|
||||
#import <FirebaseInvites/FirebaseInvites.h>
|
||||
|
||||
#import <React/RCTEventDispatcher.h>
|
||||
#import <React/RCTConvert.h>
|
||||
#import <React/RCTUtils.h>
|
||||
|
||||
@implementation RNFirebaseInvites
|
||||
|
||||
static RNFirebaseInvites *theRNFirebaseInvites = nil;
|
||||
|
||||
+ (nonnull instancetype)instance {
|
||||
return theRNFirebaseInvites;
|
||||
}
|
||||
|
||||
RCT_EXPORT_MODULE()
|
||||
|
||||
- (id)init {
|
||||
self = [super init];
|
||||
if (self != nil) {
|
||||
NSLog(@"Setting up RNFirebaseInvites instance");
|
||||
// Set static instance for use from AppDelegate
|
||||
theRNFirebaseInvites = self;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
// *******************************************************
|
||||
// ** Start AppDelegate methods
|
||||
// *******************************************************
|
||||
|
||||
- (BOOL)application:(UIApplication *)app
|
||||
openURL:(NSURL *)url
|
||||
options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
|
||||
return [self handleUrl:url];
|
||||
}
|
||||
|
||||
- (BOOL)application:(UIApplication *)application
|
||||
continueUserActivity:(NSUserActivity *)userActivity
|
||||
restorationHandler:(void (^)(NSArray *))restorationHandler {
|
||||
if ([userActivity.activityType isEqualToString:NSUserActivityTypeBrowsingWeb]) {
|
||||
return [self handleUrl:userActivity.webpageURL];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
// *******************************************************
|
||||
// ** Finish AppDelegate methods
|
||||
// *******************************************************
|
||||
|
||||
// *******************************************************
|
||||
// ** Start FIRInviteDelegate methods
|
||||
// *******************************************************
|
||||
|
||||
// Listen for invitation response
|
||||
- (void)inviteFinishedWithInvitations:(NSArray *)invitationIds error:(NSError *)error {
|
||||
if (error) {
|
||||
if (error.code == -402) {
|
||||
_invitationsRejecter(@"invites/invitation-cancelled", @"Invitation cancelled", nil);
|
||||
} else if (error.code == -404) {
|
||||
_invitationsRejecter(@"invites/invitation-error", @"User must be signed in with GoogleSignIn", nil);
|
||||
} else {
|
||||
_invitationsRejecter(@"invites/invitation-error", @"Invitation failed to send", error);
|
||||
}
|
||||
} else {
|
||||
_invitationsResolver(invitationIds);
|
||||
}
|
||||
_invitationsRejecter = nil;
|
||||
_invitationsResolver = nil;
|
||||
}
|
||||
|
||||
// *******************************************************
|
||||
// ** Finish FIRInviteDelegate methods
|
||||
// *******************************************************
|
||||
|
||||
// ** Start React Module methods **
|
||||
RCT_EXPORT_METHOD(getInitialInvitation:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
|
||||
NSURL* url = nil;
|
||||
if (self.bridge.launchOptions[UIApplicationLaunchOptionsURLKey]) {
|
||||
url = (NSURL*)self.bridge.launchOptions[UIApplicationLaunchOptionsURLKey];
|
||||
} else if (self.bridge.launchOptions[UIApplicationLaunchOptionsUserActivityDictionaryKey]) {
|
||||
NSDictionary *dictionary = self.bridge.launchOptions[UIApplicationLaunchOptionsUserActivityDictionaryKey];
|
||||
if ([dictionary[UIApplicationLaunchOptionsUserActivityTypeKey] isEqual:NSUserActivityTypeBrowsingWeb]) {
|
||||
NSUserActivity* userActivity = (NSUserActivity*) dictionary[@"UIApplicationLaunchOptionsUserActivityKey"];
|
||||
url = userActivity.webpageURL;
|
||||
}
|
||||
}
|
||||
|
||||
if (url) {
|
||||
[FIRInvites handleUniversalLink:url completion:^(FIRReceivedInvite * _Nullable receivedInvite, NSError * _Nullable error) {
|
||||
if (error) {
|
||||
NSLog(@"Failed to handle universal link: %@", [error localizedDescription]);
|
||||
reject(@"invites/initial-invitation-error", @"Failed to handle invitation", error);
|
||||
} else if (receivedInvite && receivedInvite.inviteId) {
|
||||
resolve(@{
|
||||
@"deepLink": receivedInvite.deepLink,
|
||||
@"invitationId": receivedInvite.inviteId,
|
||||
});
|
||||
} else {
|
||||
resolve(nil);
|
||||
}
|
||||
}];
|
||||
} else {
|
||||
resolve(nil);
|
||||
}
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(sendInvitation:(NSDictionary *) invitation
|
||||
resolve:(RCTPromiseResolveBlock) resolve
|
||||
reject:(RCTPromiseRejectBlock) reject) {
|
||||
if (!invitation[@"message"]) {
|
||||
reject(@"invites/invalid-invitation", @"The supplied invitation is missing a 'message' field", nil);
|
||||
}
|
||||
if (!invitation[@"title"]) {
|
||||
reject(@"invites/invalid-invitation", @"The supplied invitation is missing a 'title' field", nil);
|
||||
}
|
||||
id<FIRInviteBuilder> inviteDialog = [FIRInvites inviteDialog];
|
||||
[inviteDialog setInviteDelegate: self];
|
||||
[inviteDialog setMessage:invitation[@"message"]];
|
||||
[inviteDialog setTitle:invitation[@"title"]];
|
||||
|
||||
if (invitation[@"androidClientId"]) {
|
||||
FIRInvitesTargetApplication *targetApplication = [[FIRInvitesTargetApplication alloc] init];
|
||||
targetApplication.androidClientID = invitation[@"androidClientId"];
|
||||
[inviteDialog setOtherPlatformsTargetApplication:targetApplication];
|
||||
}
|
||||
if (invitation[@"androidMinimumVersionCode"]) {
|
||||
[inviteDialog setAndroidMinimumVersionCode:invitation[@"androidMinimumVersionCode"]];
|
||||
}
|
||||
if (invitation[@"callToActionText"]) {
|
||||
[inviteDialog setCallToActionText:invitation[@"callToActionText"]];
|
||||
}
|
||||
if (invitation[@"customImage"]) {
|
||||
[inviteDialog setCustomImage:invitation[@"customImage"]];
|
||||
}
|
||||
if (invitation[@"deepLink"]) {
|
||||
[inviteDialog setDeepLink:invitation[@"deepLink"]];
|
||||
}
|
||||
|
||||
// Save the promise details for later
|
||||
_invitationsRejecter = reject;
|
||||
_invitationsResolver = resolve;
|
||||
|
||||
// Open the invitation dialog
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[inviteDialog open];
|
||||
});
|
||||
}
|
||||
|
||||
// ** Start internals **
|
||||
- (BOOL)handleUrl:(NSURL *)url {
|
||||
return [FIRInvites handleUniversalLink:url completion:^(FIRReceivedInvite * _Nullable receivedInvite, NSError * _Nullable error) {
|
||||
if (error) {
|
||||
NSLog(@"Failed to handle invitation: %@", [error localizedDescription]);
|
||||
} else if (receivedInvite && receivedInvite.inviteId) {
|
||||
[RNFirebaseUtil sendJSEvent:self name:INVITES_INVITATION_RECEIVED body:@{
|
||||
@"deepLink": receivedInvite.deepLink,
|
||||
@"invitationId": receivedInvite.inviteId,
|
||||
}];
|
||||
} else {
|
||||
[[RNFirebaseLinks instance] sendLink:receivedInvite.deepLink];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
- (NSArray<NSString *> *)supportedEvents {
|
||||
return @[INVITES_INVITATION_RECEIVED];
|
||||
}
|
||||
|
||||
+ (BOOL)requiresMainQueueSetup
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#else
|
||||
@implementation RNFirebaseInvites
|
||||
@end
|
||||
#endif
|
||||
|
|
@ -7,21 +7,13 @@
|
|||
#import <React/RCTBridgeModule.h>
|
||||
#import <React/RCTEventEmitter.h>
|
||||
|
||||
@interface RNFirebaseLinks : RCTEventEmitter<RCTBridgeModule> {
|
||||
|
||||
}
|
||||
+ (BOOL)application:(UIApplication *)app
|
||||
openURL:(NSURL *)url
|
||||
options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options;
|
||||
@interface RNFirebaseLinks : RCTEventEmitter<RCTBridgeModule>
|
||||
|
||||
+ (BOOL)application:(UIApplication *)application
|
||||
openURL:(NSURL *)url
|
||||
sourceApplication:(NSString *)sourceApplication
|
||||
annotation:(id)annotation;
|
||||
+ (_Nonnull instancetype)instance;
|
||||
|
||||
+ (BOOL)application:(UIApplication *)application
|
||||
continueUserActivity:(NSUserActivity *)userActivity
|
||||
restorationHandler:(void (^)(NSArray *))restorationHandler;
|
||||
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options;
|
||||
- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray *))restorationHandler;
|
||||
- (void)sendLink:(NSString *)link;
|
||||
|
||||
@end
|
||||
|
||||
|
@ -31,3 +23,4 @@ continueUserActivity:(NSUserActivity *)userActivity
|
|||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -3,147 +3,86 @@
|
|||
#if __has_include(<FirebaseDynamicLinks/FirebaseDynamicLinks.h>)
|
||||
#import <Firebase.h>
|
||||
#import "RNFirebaseEvents.h"
|
||||
|
||||
|
||||
static void sendDynamicLink(NSURL *url, id sender) {
|
||||
if (url) {
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:LINKS_DYNAMIC_LINK_RECEIVED
|
||||
object:sender
|
||||
userInfo:@{@"url": url.absoluteString}];
|
||||
NSLog(@"sendDynamicLink Success: %@", url.absoluteString);
|
||||
}
|
||||
}
|
||||
#import "RNFirebaseUtil.h"
|
||||
|
||||
@implementation RNFirebaseLinks
|
||||
|
||||
static RNFirebaseLinks *theRNFirebaseLinks = nil;
|
||||
|
||||
+ (nonnull instancetype)instance {
|
||||
return theRNFirebaseLinks;
|
||||
}
|
||||
|
||||
RCT_EXPORT_MODULE();
|
||||
|
||||
- (id)init {
|
||||
self = [super init];
|
||||
if (self != nil) {
|
||||
NSLog(@"Setting up RNFirebaseLinks instance");
|
||||
[self initialiseLinks];
|
||||
// Set static instance for use from AppDelegate
|
||||
theRNFirebaseLinks = self;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)initialiseLinks {
|
||||
// Set up internal listener to send notification over bridge
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(sendDynamicLinkEvent:)
|
||||
name:LINKS_DYNAMIC_LINK_RECEIVED
|
||||
object:nil];
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
}
|
||||
|
||||
+ (BOOL)application:(UIApplication *)app
|
||||
// *******************************************************
|
||||
// ** Start AppDelegate methods
|
||||
// *******************************************************
|
||||
|
||||
- (BOOL)application:(UIApplication *)app
|
||||
openURL:(NSURL *)url
|
||||
options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
|
||||
return [self handleLinkFromCustomSchemeURL:url];
|
||||
}
|
||||
|
||||
+ (BOOL)application:(UIApplication *)application
|
||||
openURL:(NSURL *)url
|
||||
sourceApplication:(NSString *)sourceApplication
|
||||
annotation:(id)annotation {
|
||||
return [self handleLinkFromCustomSchemeURL:url];
|
||||
}
|
||||
|
||||
+ (BOOL)handleLinkFromCustomSchemeURL:(NSURL *)url {
|
||||
FIRDynamicLink *dynamicLink =
|
||||
[[FIRDynamicLinks dynamicLinks] dynamicLinkFromCustomSchemeURL:url];
|
||||
FIRDynamicLink *dynamicLink = [[FIRDynamicLinks dynamicLinks] dynamicLinkFromCustomSchemeURL:url];
|
||||
if (dynamicLink && dynamicLink.url) {
|
||||
NSURL* dynamicLinkUrl = dynamicLink.url;
|
||||
sendDynamicLink(dynamicLinkUrl, self);
|
||||
NSURL* url = dynamicLink.url;
|
||||
[RNFirebaseUtil sendJSEvent:self name:LINKS_LINK_RECEIVED body:url.absoluteString];
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
+ (BOOL)application:(UIApplication *)application
|
||||
- (BOOL)application:(UIApplication *)application
|
||||
continueUserActivity:(NSUserActivity *)userActivity
|
||||
restorationHandler:(void (^)(NSArray *))restorationHandler {
|
||||
BOOL handled = [[FIRDynamicLinks dynamicLinks]
|
||||
handleUniversalLink:userActivity.webpageURL
|
||||
completion:^(FIRDynamicLink * _Nullable dynamicLink, NSError * _Nullable error) {
|
||||
if (error != nil){
|
||||
NSLog(@"Failed to handle universal link: %@", [error localizedDescription]);
|
||||
}
|
||||
else {
|
||||
if ([userActivity.activityType isEqualToString:NSUserActivityTypeBrowsingWeb]) {
|
||||
NSURL* url = dynamicLink ? dynamicLink.url : userActivity.webpageURL;
|
||||
sendDynamicLink(url, self);
|
||||
}
|
||||
}
|
||||
}];
|
||||
return handled;
|
||||
}
|
||||
|
||||
- (NSArray<NSString *> *)supportedEvents {
|
||||
return @[LINKS_DYNAMIC_LINK_RECEIVED];
|
||||
}
|
||||
|
||||
- (void)sendDynamicLinkEvent:(NSNotification *)notification {
|
||||
[self sendEventWithName:LINKS_DYNAMIC_LINK_RECEIVED body:notification.userInfo[@"url"]];
|
||||
}
|
||||
|
||||
-(void)handleInitialLinkFromCustomSchemeURL:(NSURL*)url resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject {
|
||||
FIRDynamicLink *dynamicLink =
|
||||
[[FIRDynamicLinks dynamicLinks] dynamicLinkFromCustomSchemeURL:url];
|
||||
NSString* urlString = dynamicLink ? dynamicLink.url.absoluteString : (id)kCFNull;
|
||||
NSLog(@"initial link is: %@", urlString);
|
||||
resolve(urlString);
|
||||
}
|
||||
|
||||
-(void)handleInitialLinkFromUniversalLinkURL:(NSDictionary *)userActivityDictionary resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject {
|
||||
NSUserActivity* userActivity = (NSUserActivity*) userActivityDictionary[@"UIApplicationLaunchOptionsUserActivityKey"];
|
||||
if ([userActivityDictionary[UIApplicationLaunchOptionsUserActivityTypeKey] isEqual:NSUserActivityTypeBrowsingWeb])
|
||||
{
|
||||
[[FIRDynamicLinks dynamicLinks]
|
||||
handleUniversalLink:userActivity.webpageURL
|
||||
completion:^(FIRDynamicLink * _Nullable dynamicLink, NSError * _Nullable error) {
|
||||
if (error != nil){
|
||||
NSLog(@"Failed to handle universal link: %@", [error localizedDescription]);
|
||||
reject(@"links/failure", @"Failed to handle universal link", error);
|
||||
}
|
||||
else {
|
||||
NSString* urlString = dynamicLink ? dynamicLink.url.absoluteString : userActivity.webpageURL.absoluteString;
|
||||
NSLog(@"initial link is: %@", urlString);
|
||||
resolve(urlString);
|
||||
}
|
||||
}];
|
||||
}
|
||||
else {
|
||||
NSLog(@"no initial link");
|
||||
resolve((id)kCFNull);
|
||||
if ([userActivity.activityType isEqualToString:NSUserActivityTypeBrowsingWeb]) {
|
||||
return [[FIRDynamicLinks dynamicLinks]
|
||||
handleUniversalLink:userActivity.webpageURL
|
||||
completion:^(FIRDynamicLink * _Nullable dynamicLink, NSError * _Nullable error) {
|
||||
if (error != nil){
|
||||
NSLog(@"Failed to handle universal link: %@", [error localizedDescription]);
|
||||
} else {
|
||||
NSURL* url = dynamicLink ? dynamicLink.url : userActivity.webpageURL;
|
||||
[RNFirebaseUtil sendJSEvent:self name:LINKS_LINK_RECEIVED body:url.absoluteString];
|
||||
}
|
||||
}];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
// *******************************************************
|
||||
// ** Finish AppDelegate methods
|
||||
// *******************************************************
|
||||
|
||||
- (void)sendLink:(NSString *)link {
|
||||
[RNFirebaseUtil sendJSEvent:self name:LINKS_LINK_RECEIVED body:link];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(getInitialLink:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
|
||||
if (self.bridge.launchOptions[UIApplicationLaunchOptionsURLKey]) {
|
||||
NSURL* url = (NSURL*)self.bridge.launchOptions[UIApplicationLaunchOptionsURLKey];
|
||||
[self handleInitialLinkFromCustomSchemeURL:url resolver:resolve rejecter:reject];
|
||||
|
||||
} else {
|
||||
NSDictionary *userActivityDictionary =
|
||||
self.bridge.launchOptions[UIApplicationLaunchOptionsUserActivityDictionaryKey];
|
||||
[self handleInitialLinkFromUniversalLinkURL:userActivityDictionary resolver:resolve rejecter:reject];
|
||||
}
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(createDynamicLink: (NSDictionary *) metadata resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
|
||||
// ** Start React Module methods **
|
||||
RCT_EXPORT_METHOD(createDynamicLink:(NSDictionary *)linkData
|
||||
resolver:(RCTPromiseResolveBlock)resolve
|
||||
rejecter:(RCTPromiseRejectBlock)reject) {
|
||||
@try {
|
||||
FIRDynamicLinkComponents *components = [self getDynamicLinkComponentsFromMetadata:metadata];
|
||||
FIRDynamicLinkComponents *dynamicLink = [self buildDynamicLink:linkData];
|
||||
|
||||
if (components == nil) {
|
||||
if (dynamicLink == nil) {
|
||||
reject(@"links/failure", @"Failed to create Dynamic Link", nil);
|
||||
} else {
|
||||
NSURL *longLink = components.url;
|
||||
NSLog(@"created long dynamic link: %@", longLink.absoluteString);
|
||||
resolve(longLink.absoluteString);
|
||||
NSString *longLink = dynamicLink.url.absoluteString;
|
||||
NSLog(@"created long dynamic link: %@", longLink);
|
||||
resolve(longLink);
|
||||
}
|
||||
}
|
||||
@catch(NSException * e) {
|
||||
|
@ -152,21 +91,29 @@ RCT_EXPORT_METHOD(createDynamicLink: (NSDictionary *) metadata resolver:(RCTProm
|
|||
}
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(createShortDynamicLink: (NSDictionary *) metadata resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
|
||||
RCT_EXPORT_METHOD(createShortDynamicLink:(NSDictionary *)linkData
|
||||
type:(NSString *)type
|
||||
resolver:(RCTPromiseResolveBlock)resolve
|
||||
rejecter:(RCTPromiseRejectBlock)reject) {
|
||||
@try {
|
||||
FIRDynamicLinkComponents *components = [self getDynamicLinkComponentsFromMetadata:metadata];
|
||||
[self setSuffixParameters:metadata components:components];
|
||||
[components shortenWithCompletion:^(NSURL *_Nullable shortURL,
|
||||
NSArray *_Nullable warnings,
|
||||
NSError *_Nullable error) {
|
||||
FIRDynamicLinkComponents *components = [self buildDynamicLink:linkData];
|
||||
if (type) {
|
||||
FIRDynamicLinkComponentsOptions *options = [FIRDynamicLinkComponentsOptions options];
|
||||
if ([type isEqual: @"SHORT"]) {
|
||||
options.pathLength = FIRShortDynamicLinkPathLengthShort;
|
||||
} else if ([type isEqual: @"UNGUESSABLE"]) {
|
||||
options.pathLength = FIRShortDynamicLinkPathLengthUnguessable;
|
||||
}
|
||||
components.options = options;
|
||||
}
|
||||
[components shortenWithCompletion:^(NSURL *_Nullable shortURL, NSArray *_Nullable warnings, NSError *_Nullable error) {
|
||||
if (error) {
|
||||
NSLog(@"create short dynamic link failure %@", [error localizedDescription]);
|
||||
reject(@"links/failure", @"Failed to create Short Dynamic Link", error);
|
||||
}
|
||||
else {
|
||||
NSURL *shortLink = shortURL;
|
||||
NSLog(@"created short dynamic link: %@", shortLink.absoluteString);
|
||||
resolve(shortLink.absoluteString);
|
||||
} else {
|
||||
NSString *shortLink = shortURL.absoluteString;
|
||||
NSLog(@"created short dynamic link: %@", shortLink);
|
||||
resolve(shortLink);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
@ -176,15 +123,43 @@ RCT_EXPORT_METHOD(createShortDynamicLink: (NSDictionary *) metadata resolver:(RC
|
|||
}
|
||||
}
|
||||
|
||||
- (FIRDynamicLinkComponents *)getDynamicLinkComponentsFromMetadata:(NSDictionary *)metadata {
|
||||
RCT_EXPORT_METHOD(getInitialLink:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
|
||||
if (self.bridge.launchOptions[UIApplicationLaunchOptionsURLKey]) {
|
||||
NSURL* url = (NSURL*)self.bridge.launchOptions[UIApplicationLaunchOptionsURLKey];
|
||||
FIRDynamicLink *dynamicLink = [[FIRDynamicLinks dynamicLinks] dynamicLinkFromCustomSchemeURL:url];
|
||||
resolve(dynamicLink ? dynamicLink.url.absoluteString : nil);
|
||||
} else if (self.bridge.launchOptions[UIApplicationLaunchOptionsUserActivityDictionaryKey]
|
||||
&& [self.bridge.launchOptions[UIApplicationLaunchOptionsUserActivityDictionaryKey][UIApplicationLaunchOptionsUserActivityTypeKey] isEqualToString:NSUserActivityTypeBrowsingWeb]) {
|
||||
NSDictionary *dictionary = self.bridge.launchOptions[UIApplicationLaunchOptionsUserActivityDictionaryKey];
|
||||
NSUserActivity* userActivity = (NSUserActivity*) dictionary[@"UIApplicationLaunchOptionsUserActivityKey"];
|
||||
[[FIRDynamicLinks dynamicLinks] handleUniversalLink:userActivity.webpageURL
|
||||
completion:^(FIRDynamicLink * _Nullable dynamicLink, NSError * _Nullable error) {
|
||||
if (error != nil){
|
||||
NSLog(@"Failed to handle universal link: %@", [error localizedDescription]);
|
||||
reject(@"links/failure", @"Failed to handle universal link", error);
|
||||
} else {
|
||||
NSString* urlString = dynamicLink ? dynamicLink.url.absoluteString : userActivity.webpageURL.absoluteString;
|
||||
NSLog(@"initial link is: %@", urlString);
|
||||
resolve(urlString);
|
||||
}
|
||||
}];
|
||||
} else {
|
||||
resolve(nil);
|
||||
}
|
||||
}
|
||||
|
||||
// ** Start internals **
|
||||
- (FIRDynamicLinkComponents *)buildDynamicLink:(NSDictionary *)linkData {
|
||||
@try {
|
||||
NSURL *link = [NSURL URLWithString:metadata[@"link"]];
|
||||
FIRDynamicLinkComponents *components =
|
||||
[FIRDynamicLinkComponents componentsWithLink:link domain:metadata[@"dynamicLinkDomain"]];
|
||||
NSURL *link = [NSURL URLWithString:linkData[@"link"]];
|
||||
FIRDynamicLinkComponents *components = [FIRDynamicLinkComponents componentsWithLink:link domain:linkData[@"dynamicLinkDomain"]];
|
||||
|
||||
[self setAndroidParameters:metadata components:components];
|
||||
[self setIosParameters:metadata components:components];
|
||||
[self setSocialMetaTagParameters:metadata components:components];
|
||||
[self setAnalyticsParameters:linkData[@"analytics"] components:components];
|
||||
[self setAndroidParameters:linkData[@"android"] components:components];
|
||||
[self setIosParameters:linkData[@"ios"] components:components];
|
||||
[self setITunesParameters:linkData[@"itunes"] components:components];
|
||||
[self setNavigationParameters:linkData[@"navigation"] components:components];
|
||||
[self setSocialParameters:linkData[@"social"] components:components];
|
||||
|
||||
return components;
|
||||
}
|
||||
|
@ -194,86 +169,113 @@ RCT_EXPORT_METHOD(createShortDynamicLink: (NSDictionary *) metadata resolver:(RC
|
|||
}
|
||||
}
|
||||
|
||||
- (void)setAndroidParameters:(NSDictionary *)metadata
|
||||
- (void)setAnalyticsParameters:(NSDictionary *)analyticsData
|
||||
components:(FIRDynamicLinkComponents *)components {
|
||||
FIRDynamicLinkGoogleAnalyticsParameters *analyticsParams = [FIRDynamicLinkGoogleAnalyticsParameters parameters];
|
||||
|
||||
if (analyticsData[@"campaign"]) {
|
||||
analyticsParams.campaign = analyticsData[@"campaign"];
|
||||
}
|
||||
if (analyticsData[@"content"]) {
|
||||
analyticsParams.content = analyticsData[@"content"];
|
||||
}
|
||||
if (analyticsData[@"medium"]) {
|
||||
analyticsParams.medium = analyticsData[@"medium"];
|
||||
}
|
||||
if (analyticsData[@"source"]) {
|
||||
analyticsParams.source = analyticsData[@"source"];
|
||||
}
|
||||
if (analyticsData[@"term"]) {
|
||||
analyticsParams.term = analyticsData[@"term"];
|
||||
}
|
||||
components.analyticsParameters = analyticsParams;
|
||||
}
|
||||
|
||||
- (void)setAndroidParameters:(NSDictionary *)androidData
|
||||
components:(FIRDynamicLinkComponents *)components {
|
||||
NSDictionary *androidParametersDict = metadata[@"androidInfo"];
|
||||
if (androidParametersDict) {
|
||||
FIRDynamicLinkAndroidParameters *androidParams = [FIRDynamicLinkAndroidParameters
|
||||
parametersWithPackageName: androidParametersDict[@"androidPackageName"]];
|
||||
if (androidData[@"packageName"]) {
|
||||
FIRDynamicLinkAndroidParameters *androidParams = [FIRDynamicLinkAndroidParameters parametersWithPackageName: androidData[@"packageName"]];
|
||||
|
||||
if (androidParametersDict[@"androidFallbackLink"]) {
|
||||
androidParams.fallbackURL = [NSURL URLWithString:androidParametersDict[@"androidFallbackLink"]];
|
||||
if (androidData[@"fallbackUrl"]) {
|
||||
androidParams.fallbackURL = [NSURL URLWithString:androidData[@"fallbackUrl"]];
|
||||
}
|
||||
if (androidParametersDict[@"androidMinPackageVersionCode"]) {
|
||||
androidParams.minimumVersion = [androidParametersDict[@"androidMinPackageVersionCode"] integerValue];
|
||||
if (androidData[@"minimumVersion"]) {
|
||||
androidParams.minimumVersion = [androidData[@"minimumVersion"] integerValue];
|
||||
}
|
||||
components.androidParameters = androidParams;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setIosParameters:(NSDictionary *)metadata
|
||||
- (void)setIosParameters:(NSDictionary *)iosData
|
||||
components:(FIRDynamicLinkComponents *)components {
|
||||
NSDictionary *iosParametersDict = metadata[@"iosInfo"];
|
||||
if (iosParametersDict) {
|
||||
FIRDynamicLinkIOSParameters *iOSParams = [FIRDynamicLinkIOSParameters
|
||||
parametersWithBundleID:iosParametersDict[@"iosBundleId"]];
|
||||
if (iosParametersDict[@"iosAppStoreId"]) {
|
||||
iOSParams.appStoreID = iosParametersDict[@"iosAppStoreId"];
|
||||
if (iosData[@"bundleId"]) {
|
||||
FIRDynamicLinkIOSParameters *iOSParams = [FIRDynamicLinkIOSParameters parametersWithBundleID:iosData[@"bundleId"]];
|
||||
if (iosData[@"appStoreId"]) {
|
||||
iOSParams.appStoreID = iosData[@"appStoreId"];
|
||||
}
|
||||
if (iosParametersDict[@"iosCustomScheme"]) {
|
||||
iOSParams.customScheme = iosParametersDict[@"iosCustomScheme"];
|
||||
if (iosData[@"customScheme"]) {
|
||||
iOSParams.customScheme = iosData[@"customScheme"];
|
||||
}
|
||||
if (iosParametersDict[@"iosFallbackLink"]) {
|
||||
iOSParams.fallbackURL = [NSURL URLWithString:iosParametersDict[@"iosFallbackLink"]];
|
||||
if (iosData[@"fallbackUrl"]) {
|
||||
iOSParams.fallbackURL = [NSURL URLWithString:iosData[@"fallbackUrl"]];
|
||||
}
|
||||
if (iosParametersDict[@"iosIpadBundleId"]) {
|
||||
iOSParams.iPadBundleID = iosParametersDict[@"iosIpadBundleId"];
|
||||
if (iosData[@"iPadBundleId"]) {
|
||||
iOSParams.iPadBundleID = iosData[@"iPadBundleId"];
|
||||
}
|
||||
if (iosParametersDict[@"iosIpadFallbackLink"]) {
|
||||
iOSParams.iPadFallbackURL = [NSURL URLWithString:iosParametersDict[@"iosIpadFallbackLink"]];
|
||||
if (iosData[@"iPadFallbackUrl"]) {
|
||||
iOSParams.iPadFallbackURL = [NSURL URLWithString:iosData[@"iPadFallbackUrl"]];
|
||||
}
|
||||
if (iosParametersDict[@"iosMinPackageVersionCode"]) {
|
||||
iOSParams.minimumAppVersion = iosParametersDict[@"iosMinPackageVersionCode"];
|
||||
if (iosData[@"minimumVersion"]) {
|
||||
iOSParams.minimumAppVersion = iosData[@"minimumVersion"];
|
||||
}
|
||||
components.iOSParameters = iOSParams;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setSocialMetaTagParameters:(NSDictionary *)metadata
|
||||
components:(FIRDynamicLinkComponents *)components {
|
||||
NSDictionary *socialParamsDict = metadata[@"socialMetaTagInfo"];
|
||||
if (socialParamsDict) {
|
||||
FIRDynamicLinkSocialMetaTagParameters *socialParams = [FIRDynamicLinkSocialMetaTagParameters parameters];
|
||||
if (socialParamsDict[@"socialTitle"]) {
|
||||
socialParams.title = socialParamsDict[@"socialTitle"];
|
||||
}
|
||||
if (socialParamsDict[@"socialDescription"]) {
|
||||
socialParams.descriptionText = socialParamsDict[@"socialDescription"];
|
||||
}
|
||||
if (socialParamsDict[@"socialImageLink"]) {
|
||||
socialParams.imageURL = [NSURL URLWithString:socialParamsDict[@"socialImageLink"]];
|
||||
}
|
||||
components.socialMetaTagParameters = socialParams;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setSuffixParameters:(NSDictionary *)metadata
|
||||
- (void)setITunesParameters:(NSDictionary *)itunesData
|
||||
components:(FIRDynamicLinkComponents *)components {
|
||||
NSDictionary *suffixParametersDict = metadata[@"suffix"];
|
||||
if (suffixParametersDict) {
|
||||
FIRDynamicLinkComponentsOptions *options = [FIRDynamicLinkComponentsOptions options];
|
||||
if ([suffixParametersDict[@"option"] isEqual: @"SHORT"]) {
|
||||
options.pathLength = FIRShortDynamicLinkPathLengthShort;
|
||||
}
|
||||
else if ([suffixParametersDict[@"option"] isEqual: @"UNGUESSABLE"]) {
|
||||
options.pathLength = FIRShortDynamicLinkPathLengthUnguessable;
|
||||
}
|
||||
components.options = options;
|
||||
FIRDynamicLinkItunesConnectAnalyticsParameters *itunesParams = [FIRDynamicLinkItunesConnectAnalyticsParameters parameters];
|
||||
if (itunesData[@"affiliateToken"]) {
|
||||
itunesParams.affiliateToken = itunesData[@"affiliateToken"];
|
||||
}
|
||||
if (itunesData[@"campaignToken"]) {
|
||||
itunesParams.campaignToken = itunesData[@"campaignToken"];
|
||||
}
|
||||
if (itunesData[@"providerToken"]) {
|
||||
itunesParams.providerToken = itunesData[@"providerToken"];
|
||||
}
|
||||
components.iTunesConnectParameters = itunesParams;
|
||||
}
|
||||
|
||||
+ (BOOL)requiresMainQueueSetup
|
||||
{
|
||||
- (void)setNavigationParameters:(NSDictionary *)navigationData
|
||||
components:(FIRDynamicLinkComponents *)components {
|
||||
FIRDynamicLinkNavigationInfoParameters *navigationParams = [FIRDynamicLinkNavigationInfoParameters parameters];
|
||||
if (navigationData[@"forcedRedirectEnabled"]) {
|
||||
navigationParams.forcedRedirectEnabled = navigationData[@"forcedRedirectEnabled"];
|
||||
}
|
||||
components.navigationInfoParameters = navigationParams;
|
||||
}
|
||||
|
||||
- (void)setSocialParameters:(NSDictionary *)socialData
|
||||
components:(FIRDynamicLinkComponents *)components {
|
||||
FIRDynamicLinkSocialMetaTagParameters *socialParams = [FIRDynamicLinkSocialMetaTagParameters parameters];
|
||||
if (socialData[@"descriptionText"]) {
|
||||
socialParams.descriptionText = socialData[@"descriptionText"];
|
||||
}
|
||||
if (socialData[@"imageUrl"]) {
|
||||
socialParams.imageURL = [NSURL URLWithString:socialData[@"imageUrl"]];
|
||||
}
|
||||
if (socialData[@"title"]) {
|
||||
socialParams.title = socialData[@"title"];
|
||||
}
|
||||
components.socialMetaTagParameters = socialParams;
|
||||
}
|
||||
|
||||
- (NSArray<NSString *> *)supportedEvents {
|
||||
return @[LINKS_LINK_RECEIVED];
|
||||
}
|
||||
|
||||
+ (BOOL)requiresMainQueueSetup {
|
||||
return YES;
|
||||
}
|
||||
|
||||
|
@ -284,4 +286,3 @@ RCT_EXPORT_METHOD(createShortDynamicLink: (NSDictionary *) metadata resolver:(RC
|
|||
@end
|
||||
#endif
|
||||
|
||||
|
||||
|
|
|
@ -7,22 +7,16 @@
|
|||
#import <React/RCTBridgeModule.h>
|
||||
#import <React/RCTEventEmitter.h>
|
||||
|
||||
@import UserNotifications;
|
||||
|
||||
@interface RNFirebaseMessaging : RCTEventEmitter<RCTBridgeModule, FIRMessagingDelegate>
|
||||
|
||||
typedef void (^RCTRemoteNotificationCallback)(UIBackgroundFetchResult result);
|
||||
typedef void (^RCTWillPresentNotificationCallback)(UNNotificationPresentationOptions result);
|
||||
typedef void (^RCTNotificationResponseCallback)();
|
||||
+ (_Nonnull instancetype)instance;
|
||||
|
||||
@property (nonatomic, assign) bool connectedToFCM;
|
||||
@property _Nullable RCTPromiseRejectBlock permissionRejecter;
|
||||
@property _Nullable RCTPromiseResolveBlock permissionResolver;
|
||||
|
||||
#if !TARGET_OS_TV
|
||||
+ (void)didReceiveRemoteNotification:(nonnull NSDictionary *)userInfo;
|
||||
+ (void)didReceiveRemoteNotification:(nonnull NSDictionary *)userInfo fetchCompletionHandler:(nonnull RCTRemoteNotificationCallback)completionHandler;
|
||||
+ (void)didReceiveLocalNotification:(nonnull UILocalNotification *)notification;
|
||||
+ (void)didReceiveNotificationResponse:(nonnull UNNotificationResponse *)response withCompletionHandler:(nonnull RCTNotificationResponseCallback)completionHandler;
|
||||
+ (void)willPresentNotification:(nonnull UNNotification *)notification withCompletionHandler:(nonnull RCTWillPresentNotificationCallback)completionHandler;
|
||||
- (void)didReceiveRemoteNotification:(nonnull NSDictionary *)userInfo;
|
||||
- (void)didRegisterUserNotificationSettings:(nonnull UIUserNotificationSettings *)notificationSettings;
|
||||
#endif
|
||||
|
||||
@end
|
||||
|
@ -33,3 +27,4 @@ typedef void (^RCTNotificationResponseCallback)();
|
|||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#import "RNFirebaseMessaging.h"
|
||||
|
||||
@import UserNotifications;
|
||||
#if __has_include(<FirebaseMessaging/FirebaseMessaging.h>)
|
||||
@import UserNotifications;
|
||||
#import "RNFirebaseEvents.h"
|
||||
#import "RNFirebaseUtil.h"
|
||||
#import <FirebaseMessaging/FirebaseMessaging.h>
|
||||
|
@ -11,297 +11,130 @@
|
|||
#import <React/RCTConvert.h>
|
||||
#import <React/RCTUtils.h>
|
||||
|
||||
@implementation RCTConvert (NSCalendarUnit)
|
||||
|
||||
RCT_ENUM_CONVERTER(NSCalendarUnit,
|
||||
(@{
|
||||
@"year": @(NSCalendarUnitYear),
|
||||
@"month": @(NSCalendarUnitMonth),
|
||||
@"week": @(NSCalendarUnitWeekOfYear),
|
||||
@"day": @(NSCalendarUnitDay),
|
||||
@"hour": @(NSCalendarUnitHour),
|
||||
@"minute": @(NSCalendarUnitMinute)
|
||||
}),
|
||||
0,
|
||||
integerValue)
|
||||
@end
|
||||
|
||||
|
||||
@implementation RCTConvert (UNNotificationRequest)
|
||||
|
||||
+ (UNNotificationRequest *)UNNotificationRequest:(id)json
|
||||
{
|
||||
NSDictionary<NSString *, id> *details = [self NSDictionary:json];
|
||||
UNMutableNotificationContent *content = [UNMutableNotificationContent new];
|
||||
content.title =[RCTConvert NSString:details[@"title"]];
|
||||
content.body =[RCTConvert NSString:details[@"body"]];
|
||||
NSString* sound = [RCTConvert NSString:details[@"sound"]];
|
||||
if(sound != nil){
|
||||
content.sound = [UNNotificationSound soundNamed:sound];
|
||||
}else{
|
||||
content.sound = [UNNotificationSound defaultSound];
|
||||
}
|
||||
content.categoryIdentifier = [RCTConvert NSString:details[@"click_action"]];
|
||||
content.userInfo = details;
|
||||
content.badge = [RCTConvert NSNumber:details[@"badge"]];
|
||||
|
||||
if([details objectForKey:@"show_in_foreground"] != nil) {
|
||||
if([(NSNumber *)details[@"show_in_foreground"] boolValue] == YES) {
|
||||
[content setValue:@YES forKeyPath:@"shouldAlwaysAlertWhileAppIsForeground"];
|
||||
}
|
||||
}
|
||||
|
||||
NSDate *fireDate = [RCTConvert NSDate:details[@"fire_date"]];
|
||||
|
||||
if(fireDate == nil){
|
||||
return [UNNotificationRequest requestWithIdentifier:[RCTConvert NSString:details[@"id"]] content:content trigger:nil];
|
||||
}
|
||||
|
||||
NSCalendarUnit interval = [RCTConvert NSCalendarUnit:details[@"repeat_interval"]];
|
||||
NSCalendarUnit unitFlags;
|
||||
switch (interval) {
|
||||
case NSCalendarUnitMinute: {
|
||||
unitFlags = NSCalendarUnitSecond;
|
||||
break;
|
||||
}
|
||||
case NSCalendarUnitHour: {
|
||||
unitFlags = NSCalendarUnitMinute | NSCalendarUnitSecond;
|
||||
break;
|
||||
}
|
||||
case NSCalendarUnitDay: {
|
||||
unitFlags = NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond;
|
||||
break;
|
||||
}
|
||||
case NSCalendarUnitWeekOfYear: {
|
||||
unitFlags = NSCalendarUnitWeekday | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond;
|
||||
break;
|
||||
}
|
||||
case NSCalendarUnitMonth:{
|
||||
unitFlags = NSCalendarUnitDay | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond;
|
||||
}
|
||||
case NSCalendarUnitYear:{
|
||||
unitFlags = NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond;
|
||||
}
|
||||
default:
|
||||
unitFlags = NSCalendarUnitDay | NSCalendarUnitMonth | NSCalendarUnitYear | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond;
|
||||
break;
|
||||
}
|
||||
NSDateComponents *components = [[NSCalendar currentCalendar] components:unitFlags fromDate:fireDate];
|
||||
UNCalendarNotificationTrigger *trigger = [UNCalendarNotificationTrigger triggerWithDateMatchingComponents:components repeats:interval != 0];
|
||||
return [UNNotificationRequest requestWithIdentifier:[RCTConvert NSString:details[@"id"]] content:content trigger:trigger];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTConvert (UILocalNotification)
|
||||
|
||||
+ (UILocalNotification *)UILocalNotification:(id)json
|
||||
{
|
||||
NSDictionary<NSString *, id> *details = [self NSDictionary:json];
|
||||
UILocalNotification *notification = [UILocalNotification new];
|
||||
notification.fireDate = [RCTConvert NSDate:details[@"fire_date"]] ?: [NSDate date];
|
||||
if([notification respondsToSelector:@selector(setAlertTitle:)]){
|
||||
[notification setAlertTitle:[RCTConvert NSString:details[@"title"]]];
|
||||
}
|
||||
notification.alertBody = [RCTConvert NSString:details[@"body"]];
|
||||
notification.alertAction = [RCTConvert NSString:details[@"alert_action"]];
|
||||
notification.soundName = [RCTConvert NSString:details[@"sound"]] ?: UILocalNotificationDefaultSoundName;
|
||||
notification.userInfo = details;
|
||||
notification.category = [RCTConvert NSString:details[@"click_action"]];
|
||||
notification.repeatInterval = [RCTConvert NSCalendarUnit:details[@"repeat_interval"]];
|
||||
notification.applicationIconBadgeNumber = [RCTConvert NSInteger:details[@"badge"]];
|
||||
return notification;
|
||||
}
|
||||
|
||||
RCT_ENUM_CONVERTER(UIBackgroundFetchResult, (@{
|
||||
@"UIBackgroundFetchResultNewData": @(UIBackgroundFetchResultNewData),
|
||||
@"UIBackgroundFetchResultNoData": @(UIBackgroundFetchResultNoData),
|
||||
@"UIBackgroundFetchResultFailed": @(UIBackgroundFetchResultFailed),
|
||||
}), UIBackgroundFetchResultNoData, integerValue)
|
||||
|
||||
RCT_ENUM_CONVERTER(UNNotificationPresentationOptions, (@{
|
||||
@"UNNotificationPresentationOptionAll": @(UNNotificationPresentationOptionAlert | UNNotificationPresentationOptionBadge | UNNotificationPresentationOptionSound),
|
||||
@"UNNotificationPresentationOptionNone": @(UNNotificationPresentationOptionNone)}), UIBackgroundFetchResultNoData, integerValue)
|
||||
|
||||
@end
|
||||
|
||||
@interface RNFirebaseMessaging ()
|
||||
@property (nonatomic, strong) NSMutableDictionary *notificationCallbacks;
|
||||
@end
|
||||
#if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
|
||||
@import UserNotifications;
|
||||
#endif
|
||||
|
||||
@implementation RNFirebaseMessaging
|
||||
|
||||
static RNFirebaseMessaging *theRNFirebaseMessaging = nil;
|
||||
|
||||
+ (nonnull instancetype)instance {
|
||||
return theRNFirebaseMessaging;
|
||||
}
|
||||
|
||||
RCT_EXPORT_MODULE()
|
||||
|
||||
+ (void)didReceiveRemoteNotification:(nonnull NSDictionary *)userInfo {
|
||||
NSMutableDictionary* data = [[NSMutableDictionary alloc] initWithDictionary: userInfo];
|
||||
[data setValue:@"remote_notification" forKey:@"_notificationType"];
|
||||
[data setValue:@(RCTSharedApplication().applicationState == UIApplicationStateInactive) forKey:@"opened_from_tray"];
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:MESSAGING_NOTIFICATION_RECEIVED object:self userInfo:@{@"data": data}];
|
||||
}
|
||||
|
||||
+ (void)didReceiveRemoteNotification:(nonnull NSDictionary *)userInfo fetchCompletionHandler:(nonnull RCTRemoteNotificationCallback)completionHandler {
|
||||
NSMutableDictionary* data = [[NSMutableDictionary alloc] initWithDictionary: userInfo];
|
||||
[data setValue:@"remote_notification" forKey:@"_notificationType"];
|
||||
[data setValue:@(RCTSharedApplication().applicationState == UIApplicationStateInactive) forKey:@"opened_from_tray"];
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:MESSAGING_NOTIFICATION_RECEIVED object:self userInfo:@{@"data": data, @"completionHandler": completionHandler}];
|
||||
}
|
||||
|
||||
+ (void)didReceiveLocalNotification:(UILocalNotification *)notification {
|
||||
NSMutableDictionary* data = [[NSMutableDictionary alloc] initWithDictionary: notification.userInfo];
|
||||
[data setValue:@"local_notification" forKey:@"_notificationType"];
|
||||
[data setValue:@(RCTSharedApplication().applicationState == UIApplicationStateInactive) forKey:@"opened_from_tray"];
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:MESSAGING_NOTIFICATION_RECEIVED object:self userInfo:@{@"data": data}];
|
||||
}
|
||||
|
||||
+ (void)willPresentNotification:(UNNotification *)notification withCompletionHandler:(nonnull RCTWillPresentNotificationCallback)completionHandler
|
||||
{
|
||||
NSMutableDictionary* data = [[NSMutableDictionary alloc] initWithDictionary: notification.request.content.userInfo];
|
||||
[data setValue:@"will_present_notification" forKey:@"_notificationType"];
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:MESSAGING_NOTIFICATION_RECEIVED object:self userInfo:@{@"data": data, @"completionHandler": completionHandler}];
|
||||
}
|
||||
|
||||
+ (void)didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(nonnull RCTNotificationResponseCallback)completionHandler
|
||||
{
|
||||
NSMutableDictionary* data = [[NSMutableDictionary alloc] initWithDictionary: response.notification.request.content.userInfo];
|
||||
[data setValue:@"notification_response" forKey:@"_notificationType"];
|
||||
[data setValue:@YES forKey:@"opened_from_tray"];
|
||||
if (response.actionIdentifier) {
|
||||
[data setValue:response.actionIdentifier forKey:@"_actionIdentifier"];
|
||||
}
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:MESSAGING_NOTIFICATION_RECEIVED object:self userInfo:@{@"data": data, @"completionHandler": completionHandler}];
|
||||
}
|
||||
|
||||
- (id)init {
|
||||
self = [super init];
|
||||
if (self != nil) {
|
||||
NSLog(@"Setting up RNFirebaseMessaging instance");
|
||||
[self initialiseMessaging];
|
||||
[self configure];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)initialiseMessaging {
|
||||
- (void)configure {
|
||||
// Set as delegate for FIRMessaging
|
||||
[FIRMessaging messaging].delegate = self;
|
||||
|
||||
// Establish Firebase managed data channel
|
||||
[FIRMessaging messaging].shouldEstablishDirectChannel = YES;
|
||||
|
||||
// Set up listeners for data messages
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(sendDataMessageFailure:)
|
||||
name:FIRMessagingSendErrorNotification
|
||||
object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(sendDataMessageSuccess:)
|
||||
name:FIRMessagingSendSuccessNotification
|
||||
object:nil];
|
||||
|
||||
// Set up internal listener to send notification over bridge
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(handleNotificationReceived:)
|
||||
name:MESSAGING_NOTIFICATION_RECEIVED
|
||||
object:nil];
|
||||
|
||||
// Set this as a delegate for FIRMessaging
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[FIRMessaging messaging].delegate = self;
|
||||
});
|
||||
|
||||
// Set static instance for use from AppDelegate
|
||||
theRNFirebaseMessaging = self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
}
|
||||
// *******************************************************
|
||||
// ** Start AppDelegate methods
|
||||
// ** iOS 8/9 Only
|
||||
// *******************************************************
|
||||
|
||||
- (void)handleNotificationReceived:(NSNotification *)notification {
|
||||
id completionHandler = notification.userInfo[@"completionHandler"];
|
||||
NSMutableDictionary* data = notification.userInfo[@"data"];
|
||||
if(completionHandler != nil){
|
||||
NSString *completionHandlerId = [[NSUUID UUID] UUIDString];
|
||||
if (!self.notificationCallbacks) {
|
||||
// Lazy initialization
|
||||
self.notificationCallbacks = [NSMutableDictionary dictionary];
|
||||
// Listen for permission response
|
||||
- (void) didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings {
|
||||
if (notificationSettings.types == UIUserNotificationTypeNone) {
|
||||
if (_permissionRejecter) {
|
||||
_permissionRejecter(@"messaging/permission_error", @"Failed to grant permission", nil);
|
||||
}
|
||||
self.notificationCallbacks[completionHandlerId] = completionHandler;
|
||||
data[@"_completionHandlerId"] = completionHandlerId;
|
||||
} else if (_permissionResolver) {
|
||||
_permissionResolver(nil);
|
||||
}
|
||||
|
||||
[RNFirebaseUtil sendJSEvent:self name:MESSAGING_NOTIFICATION_RECEIVED body:data];
|
||||
_permissionRejecter = nil;
|
||||
_permissionResolver = nil;
|
||||
}
|
||||
|
||||
|
||||
- (void)sendDataMessageFailure:(NSNotification *)notification {
|
||||
NSString *messageID = (NSString *)notification.userInfo[@"messageID"];
|
||||
NSLog(@"sendDataMessageFailure: %@", messageID);
|
||||
// Listen for FCM data messages that arrive as a remote notification
|
||||
- (void)didReceiveRemoteNotification:(nonnull NSDictionary *)userInfo {
|
||||
NSDictionary *message = [self parseUserInfo:userInfo];
|
||||
[RNFirebaseUtil sendJSEvent:self name:MESSAGING_MESSAGE_RECEIVED body:message];
|
||||
}
|
||||
|
||||
- (void)sendDataMessageSuccess:(NSNotification *)notification {
|
||||
NSString *messageID = (NSString *)notification.userInfo[@"messageID"];
|
||||
NSLog(@"sendDataMessageSuccess: %@", messageID);
|
||||
}
|
||||
// *******************************************************
|
||||
// ** Finish AppDelegate methods
|
||||
// *******************************************************
|
||||
|
||||
// ** Start FIRMessagingDelegate methods **
|
||||
// Handle data messages in the background
|
||||
- (void)applicationReceivedRemoteMessage:(FIRMessagingRemoteMessage *)remoteMessage {
|
||||
[RNFirebaseUtil sendJSEvent:self name:MESSAGING_NOTIFICATION_RECEIVED body:[remoteMessage appData]];
|
||||
}
|
||||
|
||||
// Listen for token refreshes
|
||||
- (void)messaging:(nonnull FIRMessaging *)messaging didRefreshRegistrationToken:(nonnull NSString *)fcmToken {
|
||||
NSLog(@"FCM registration token: %@", fcmToken);
|
||||
// *******************************************************
|
||||
// ** Start FIRMessagingDelegate methods
|
||||
// ** iOS 8+
|
||||
// *******************************************************
|
||||
|
||||
// Listen for FCM tokens
|
||||
- (void)messaging:(FIRMessaging *)messaging didReceiveRegistrationToken:(NSString *)fcmToken {
|
||||
NSLog(@"Received new FCM token: %@", fcmToken);
|
||||
[RNFirebaseUtil sendJSEvent:self name:MESSAGING_TOKEN_REFRESHED body:fcmToken];
|
||||
}
|
||||
// ** End FIRMessagingDelegate methods **
|
||||
|
||||
// Listen for data messages in the foreground
|
||||
- (void)applicationReceivedRemoteMessage:(nonnull FIRMessagingRemoteMessage *)remoteMessage {
|
||||
NSDictionary *message = [self parseFIRMessagingRemoteMessage:remoteMessage];
|
||||
[RNFirebaseUtil sendJSEvent:self name:MESSAGING_MESSAGE_RECEIVED body:message];
|
||||
}
|
||||
|
||||
// Receive data messages on iOS 10+ directly from FCM (bypassing APNs) when the app is in the foreground.
|
||||
// To enable direct data messages, you can set [Messaging messaging].shouldEstablishDirectChannel to YES.
|
||||
- (void)messaging:(nonnull FIRMessaging *)messaging
|
||||
didReceiveMessage:(nonnull FIRMessagingRemoteMessage *)remoteMessage {
|
||||
NSDictionary *message = [self parseFIRMessagingRemoteMessage:remoteMessage];
|
||||
[RNFirebaseUtil sendJSEvent:self name:MESSAGING_MESSAGE_RECEIVED body:message];
|
||||
}
|
||||
|
||||
// *******************************************************
|
||||
// ** Finish FIRMessagingDelegate methods
|
||||
// *******************************************************
|
||||
|
||||
// ** Start React Module methods **
|
||||
RCT_EXPORT_METHOD(getInitialNotification:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject){
|
||||
UILocalNotification *localUserInfo = [self bridge].launchOptions[UIApplicationLaunchOptionsLocalNotificationKey];
|
||||
if (localUserInfo) {
|
||||
resolve([[localUserInfo userInfo] copy]);
|
||||
} else {
|
||||
resolve([[self bridge].launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey] copy]);
|
||||
}
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(getToken:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
|
||||
resolve([FIRMessaging messaging].FCMToken);
|
||||
resolve([[FIRInstanceID instanceID] token]);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(deleteInstanceId:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
|
||||
[[FIRInstanceID instanceID] deleteIDWithHandler:^(NSError * _Nullable error) {
|
||||
if (!error) {
|
||||
resolve(nil);
|
||||
} else {
|
||||
reject(@"instance_id_error", @"Failed to delete instance id", error);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(requestPermissions:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
|
||||
RCT_EXPORT_METHOD(requestPermission:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
|
||||
if (RCTRunningInAppExtension()) {
|
||||
reject(@"messaging/request-permission-unavailable", @"requestPermission is not supported in App Extensions", nil);
|
||||
return;
|
||||
}
|
||||
|
||||
if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_9_x_Max) {
|
||||
UIUserNotificationType allNotificationTypes =
|
||||
(UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge);
|
||||
UIUserNotificationSettings *settings =
|
||||
[UIUserNotificationSettings settingsForTypes:allNotificationTypes categories:nil];
|
||||
[RCTSharedApplication() registerUserNotificationSettings:settings];
|
||||
// Unfortunately on iOS 9 or below, there's no way to tell whether the user accepted or
|
||||
// rejected the permissions popup
|
||||
resolve(@{@"status":@"unknown"});
|
||||
UIUserNotificationType types = (UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge);
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[RCTSharedApplication() registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:types categories:nil]];
|
||||
// We set the promise for usage by the AppDelegate callback which listens
|
||||
// for the result of the permission request
|
||||
_permissionRejecter = reject;
|
||||
_permissionResolver = resolve;
|
||||
});
|
||||
} else {
|
||||
// iOS 10 or later
|
||||
#if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
|
||||
// For iOS 10 display notification (sent via APNS)
|
||||
[UNUserNotificationCenter currentNotificationCenter].delegate = self;
|
||||
UNAuthorizationOptions authOptions =
|
||||
UNAuthorizationOptionAlert
|
||||
| UNAuthorizationOptionSound
|
||||
| UNAuthorizationOptionBadge;
|
||||
[[UNUserNotificationCenter currentNotificationCenter] requestAuthorizationWithOptions:authOptions completionHandler:^(BOOL granted, NSError * _Nullable error) {
|
||||
resolve(@{@"granted":@(granted)});
|
||||
}];
|
||||
#endif
|
||||
#if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
|
||||
// For iOS 10 display notification (sent via APNS)
|
||||
UNAuthorizationOptions authOptions = UNAuthorizationOptionAlert | UNAuthorizationOptionSound | UNAuthorizationOptionBadge;
|
||||
[[UNUserNotificationCenter currentNotificationCenter] requestAuthorizationWithOptions:authOptions completionHandler:^(BOOL granted, NSError * _Nullable error) {
|
||||
if (granted) {
|
||||
resolve(nil);
|
||||
} else {
|
||||
reject(@"messaging/permission_error", @"Failed to grant permission", error);
|
||||
}
|
||||
}];
|
||||
#endif
|
||||
}
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
|
@ -309,6 +142,39 @@ RCT_EXPORT_METHOD(requestPermissions:(RCTPromiseResolveBlock)resolve rejecter:(R
|
|||
});
|
||||
}
|
||||
|
||||
// Non Web SDK methods
|
||||
RCT_EXPORT_METHOD(hasPermission: (RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
|
||||
if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_9_x_Max) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
resolve(@([RCTSharedApplication() currentUserNotificationSettings].types != UIUserNotificationTypeNone));
|
||||
});
|
||||
} else {
|
||||
#if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
|
||||
[[UNUserNotificationCenter currentNotificationCenter] getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
|
||||
resolve(@(settings.alertSetting == UNNotificationSettingEnabled));
|
||||
}];
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
RCT_EXPORT_METHOD(sendMessage: (NSDictionary *) message
|
||||
resolve:(RCTPromiseResolveBlock) resolve
|
||||
reject:(RCTPromiseRejectBlock) reject) {
|
||||
if (!message[@"to"]) {
|
||||
reject(@"messaging/invalid-message", @"The supplied message is missing a 'to' field", nil);
|
||||
}
|
||||
NSString *to = message[@"to"];
|
||||
NSString *messageId = message[@"messageId"];
|
||||
NSNumber *ttl = message[@"ttl"];
|
||||
NSDictionary *data = message[@"data"];
|
||||
|
||||
[[FIRMessaging messaging] sendMessage:data to:to withMessageID:messageId timeToLive:[ttl intValue]];
|
||||
|
||||
// TODO: Listen for send success / errors
|
||||
resolve(nil);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(subscribeToTopic: (NSString*) topic) {
|
||||
[[FIRMessaging messaging] subscribeToTopic:topic];
|
||||
}
|
||||
|
@ -317,144 +183,61 @@ RCT_EXPORT_METHOD(unsubscribeFromTopic: (NSString*) topic) {
|
|||
[[FIRMessaging messaging] unsubscribeFromTopic:topic];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(createLocalNotification:(id)data resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
|
||||
if([UNUserNotificationCenter currentNotificationCenter] != nil){
|
||||
UNNotificationRequest* request = [RCTConvert UNNotificationRequest:data];
|
||||
[[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
|
||||
if (!error) {
|
||||
resolve(nil);
|
||||
}else{
|
||||
reject(@"notification_error", @"Failed to present local notificaton", error);
|
||||
}
|
||||
}];
|
||||
}else{
|
||||
UILocalNotification* notif = [RCTConvert UILocalNotification:data];
|
||||
[RCTSharedApplication() presentLocalNotificationNow:notif];
|
||||
resolve(nil);
|
||||
}
|
||||
}
|
||||
// ** Start internals **
|
||||
|
||||
RCT_EXPORT_METHOD(scheduleLocalNotification:(id)data resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
|
||||
if([UNUserNotificationCenter currentNotificationCenter] != nil){
|
||||
UNNotificationRequest* request = [RCTConvert UNNotificationRequest:data];
|
||||
[[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
|
||||
if (!error) {
|
||||
resolve(nil);
|
||||
}else{
|
||||
reject(@"notification_error", @"Failed to present local notificaton", error);
|
||||
}
|
||||
}];
|
||||
}else{
|
||||
UILocalNotification* notif = [RCTConvert UILocalNotification:data];
|
||||
[RCTSharedApplication() scheduleLocalNotification:notif];
|
||||
resolve(nil);
|
||||
}
|
||||
}
|
||||
- (NSDictionary*)parseFIRMessagingRemoteMessage:(FIRMessagingRemoteMessage *)remoteMessage {
|
||||
NSDictionary *appData = remoteMessage.appData;
|
||||
|
||||
RCT_EXPORT_METHOD(removeDeliveredNotification:(NSString*) notificationId) {
|
||||
if([UNUserNotificationCenter currentNotificationCenter] != nil){
|
||||
[[UNUserNotificationCenter currentNotificationCenter] removeDeliveredNotificationsWithIdentifiers:@[notificationId]];
|
||||
}
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(removeAllDeliveredNotifications) {
|
||||
if([UNUserNotificationCenter currentNotificationCenter] != nil){
|
||||
[[UNUserNotificationCenter currentNotificationCenter] removeAllDeliveredNotifications];
|
||||
} else {
|
||||
[RCTSharedApplication() setApplicationIconBadgeNumber: 0];
|
||||
}
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(cancelAllLocalNotifications) {
|
||||
if([UNUserNotificationCenter currentNotificationCenter] != nil){
|
||||
[[UNUserNotificationCenter currentNotificationCenter] removeAllPendingNotificationRequests];
|
||||
} else {
|
||||
[RCTSharedApplication() cancelAllLocalNotifications];
|
||||
}
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(cancelLocalNotification:(NSString*) notificationId) {
|
||||
if([UNUserNotificationCenter currentNotificationCenter] != nil){
|
||||
[[UNUserNotificationCenter currentNotificationCenter] removePendingNotificationRequestsWithIdentifiers:@[notificationId]];
|
||||
} else {
|
||||
for (UILocalNotification *notification in RCTSharedApplication().scheduledLocalNotifications) {
|
||||
NSDictionary<NSString *, id> *notificationInfo = notification.userInfo;
|
||||
if([notificationId isEqualToString:[notificationInfo valueForKey:@"id"]]){
|
||||
[RCTSharedApplication() cancelLocalNotification:notification];
|
||||
}
|
||||
NSMutableDictionary *message = [[NSMutableDictionary alloc] init];
|
||||
NSMutableDictionary *data = [[NSMutableDictionary alloc] init];
|
||||
for (id k1 in appData) {
|
||||
if ([k1 isEqualToString:@"collapse_key"]) {
|
||||
message[@"collapseKey"] = appData[@"collapse_key"];
|
||||
} else if ([k1 isEqualToString:@"from"]) {
|
||||
message[@"from"] = appData[k1];
|
||||
} else if ([k1 isEqualToString:@"notification"]) {
|
||||
// Ignore for messages
|
||||
} else {
|
||||
// Assume custom data key
|
||||
data[k1] = appData[k1];
|
||||
}
|
||||
}
|
||||
message[@"data"] = data;
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(getScheduledLocalNotifications:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
|
||||
if([UNUserNotificationCenter currentNotificationCenter] != nil){
|
||||
[[UNUserNotificationCenter currentNotificationCenter] getPendingNotificationRequestsWithCompletionHandler:^(NSArray<UNNotificationRequest *> * _Nonnull requests) {
|
||||
NSMutableArray* list = [[NSMutableArray alloc] init];
|
||||
for(UNNotificationRequest * notif in requests){
|
||||
UNMutableNotificationContent *content = notif.content;
|
||||
[list addObject:content.userInfo];
|
||||
}
|
||||
resolve(list);
|
||||
}];
|
||||
} else {
|
||||
NSMutableArray* list = [[NSMutableArray alloc] init];
|
||||
for(UILocalNotification * notif in [RCTSharedApplication() scheduledLocalNotifications]){
|
||||
[list addObject:notif.userInfo];
|
||||
- (NSDictionary*)parseUserInfo:(NSDictionary *)userInfo {
|
||||
NSMutableDictionary *message = [[NSMutableDictionary alloc] init];
|
||||
NSMutableDictionary *data = [[NSMutableDictionary alloc] init];
|
||||
|
||||
for (id k1 in userInfo) {
|
||||
if ([k1 isEqualToString:@"aps"]) {
|
||||
// Ignore notification section
|
||||
} else if ([k1 isEqualToString:@"gcm.message_id"]) {
|
||||
message[@"messageId"] = userInfo[k1];
|
||||
} else if ([k1 isEqualToString:@"google.c.a.ts"]) {
|
||||
message[@"sentTime"] = userInfo[k1];
|
||||
} else if ([k1 isEqualToString:@"gcm.n.e"]
|
||||
|| [k1 isEqualToString:@"gcm.notification.sound2"]
|
||||
|| [k1 isEqualToString:@"google.c.a.c_id"]
|
||||
|| [k1 isEqualToString:@"google.c.a.c_l"]
|
||||
|| [k1 isEqualToString:@"google.c.a.e"]
|
||||
|| [k1 isEqualToString:@"google.c.a.udt"]) {
|
||||
// Ignore known keys
|
||||
} else {
|
||||
// Assume custom data
|
||||
data[k1] = userInfo[k1];
|
||||
}
|
||||
resolve(list);
|
||||
}
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(setBadgeNumber: (NSInteger*) number) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[RCTSharedApplication() setApplicationIconBadgeNumber:number];
|
||||
});
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(getBadgeNumber: (RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
|
||||
resolve(@([RCTSharedApplication() applicationIconBadgeNumber]));
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(send:(NSDictionary *)remoteMessage) {
|
||||
int64_t ttl = @([[remoteMessage valueForKey:@"ttl"] intValue]).doubleValue;
|
||||
NSString * mId = [remoteMessage valueForKey:@"id"];
|
||||
NSString * receiver = [remoteMessage valueForKey:@"sender"];
|
||||
NSDictionary * data = [remoteMessage valueForKey:@"data"];
|
||||
[[FIRMessaging messaging] sendMessage:data to:receiver withMessageID:mId timeToLive:ttl];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(finishRemoteNotification: (NSString *)completionHandlerId fetchResult:(UIBackgroundFetchResult)result) {
|
||||
RCTRemoteNotificationCallback completionHandler = self.notificationCallbacks[completionHandlerId];
|
||||
if (!completionHandler) {
|
||||
RCTLogError(@"There is no completion handler with completionHandlerId: %@", completionHandlerId);
|
||||
return;
|
||||
}
|
||||
completionHandler(result);
|
||||
[self.notificationCallbacks removeObjectForKey:completionHandlerId];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(finishWillPresentNotification: (NSString *)completionHandlerId fetchResult:(UNNotificationPresentationOptions)result) {
|
||||
RCTWillPresentNotificationCallback completionHandler = self.notificationCallbacks[completionHandlerId];
|
||||
if (!completionHandler) {
|
||||
RCTLogError(@"There is no completion handler with completionHandlerId: %@", completionHandlerId);
|
||||
return;
|
||||
}
|
||||
completionHandler(result);
|
||||
[self.notificationCallbacks removeObjectForKey:completionHandlerId];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(finishNotificationResponse: (NSString *)completionHandlerId) {
|
||||
RCTNotificationResponseCallback completionHandler = self.notificationCallbacks[completionHandlerId];
|
||||
if (!completionHandler) {
|
||||
RCTLogError(@"There is no completion handler with completionHandlerId: %@", completionHandlerId);
|
||||
return;
|
||||
}
|
||||
completionHandler();
|
||||
[self.notificationCallbacks removeObjectForKey:completionHandlerId];
|
||||
|
||||
message[@"data"] = data;
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
- (NSArray<NSString *> *)supportedEvents {
|
||||
return @[MESSAGING_TOKEN_REFRESHED, MESSAGING_NOTIFICATION_RECEIVED];
|
||||
return @[MESSAGING_MESSAGE_RECEIVED, MESSAGING_TOKEN_REFRESHED];
|
||||
}
|
||||
|
||||
+ (BOOL)requiresMainQueueSetup
|
||||
|
@ -468,3 +251,4 @@ RCT_EXPORT_METHOD(finishNotificationResponse: (NSString *)completionHandlerId) {
|
|||
@implementation RNFirebaseMessaging
|
||||
@end
|
||||
#endif
|
||||
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
#ifndef RNFirebaseNotifications_h
|
||||
#define RNFirebaseNotifications_h
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#if __has_include(<FirebaseMessaging/FirebaseMessaging.h>)
|
||||
#import <React/RCTBridgeModule.h>
|
||||
#import <React/RCTEventEmitter.h>
|
||||
|
||||
@interface RNFirebaseNotifications : RCTEventEmitter<RCTBridgeModule>
|
||||
|
||||
+ (void)configure;
|
||||
+ (_Nonnull instancetype)instance;
|
||||
|
||||
#if !TARGET_OS_TV
|
||||
- (void)didReceiveLocalNotification:(nonnull UILocalNotification *)notification;
|
||||
- (void)didReceiveRemoteNotification:(nonnull NSDictionary *)userInfo fetchCompletionHandler:(void (^_Nonnull)(UIBackgroundFetchResult))completionHandler;
|
||||
#endif
|
||||
|
||||
@end
|
||||
|
||||
#else
|
||||
@interface RNFirebaseNotifications : NSObject
|
||||
@end
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,725 @@
|
|||
#import "RNFirebaseNotifications.h"
|
||||
|
||||
#if __has_include(<FirebaseMessaging/FIRMessaging.h>)
|
||||
#import "RNFirebaseEvents.h"
|
||||
#import "RNFirebaseMessaging.h"
|
||||
#import "RNFirebaseUtil.h"
|
||||
#import <React/RCTUtils.h>
|
||||
|
||||
// For iOS 10 we need to implement UNUserNotificationCenterDelegate to receive display
|
||||
// notifications via APNS
|
||||
#if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
|
||||
@import UserNotifications;
|
||||
@interface RNFirebaseNotifications () <UNUserNotificationCenterDelegate>
|
||||
#else
|
||||
@interface RNFirebaseNotifications ()
|
||||
#endif
|
||||
@end
|
||||
|
||||
@implementation RNFirebaseNotifications
|
||||
|
||||
static RNFirebaseNotifications *theRNFirebaseNotifications = nil;
|
||||
// PRE-BRIDGE-EVENTS: Consider enabling this to allow events built up before the bridge is built to be sent to the JS side
|
||||
// static NSMutableArray *pendingEvents = nil;
|
||||
static NSDictionary *initialNotification = nil;
|
||||
|
||||
+ (nonnull instancetype)instance {
|
||||
return theRNFirebaseNotifications;
|
||||
}
|
||||
|
||||
+ (void)configure {
|
||||
// PRE-BRIDGE-EVENTS: Consider enabling this to allow events built up before the bridge is built to be sent to the JS side
|
||||
// pendingEvents = [[NSMutableArray alloc] init];
|
||||
theRNFirebaseNotifications = [[RNFirebaseNotifications alloc] init];
|
||||
}
|
||||
|
||||
RCT_EXPORT_MODULE();
|
||||
|
||||
- (id)init {
|
||||
self = [super init];
|
||||
if (self != nil) {
|
||||
NSLog(@"Setting up RNFirebaseNotifications instance");
|
||||
[self initialise];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)initialise {
|
||||
// If we're on iOS 10 then we need to set this as a delegate for the UNUserNotificationCenter
|
||||
#if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
|
||||
[UNUserNotificationCenter currentNotificationCenter].delegate = self;
|
||||
#endif
|
||||
|
||||
// Set static instance for use from AppDelegate
|
||||
theRNFirebaseNotifications = self;
|
||||
}
|
||||
|
||||
// PRE-BRIDGE-EVENTS: Consider enabling this to allow events built up before the bridge is built to be sent to the JS side
|
||||
// The bridge is initialised after the module is created
|
||||
// When the bridge is set, check if we have any pending events to send, and send them
|
||||
/* - (void)setValue:(nullable id)value forKey:(NSString *)key {
|
||||
[super setValue:value forKey:key];
|
||||
if ([key isEqualToString:@"bridge"] && value) {
|
||||
for (NSDictionary* event in pendingEvents) {
|
||||
[RNFirebaseUtil sendJSEvent:self name:event[@"name"] body:event[@"body"]];
|
||||
}
|
||||
[pendingEvents removeAllObjects];
|
||||
}
|
||||
} */
|
||||
|
||||
// *******************************************************
|
||||
// ** Start AppDelegate methods
|
||||
// ** iOS 8/9 Only
|
||||
// *******************************************************
|
||||
- (void)didReceiveLocalNotification:(nonnull UILocalNotification *)localNotification {
|
||||
if ([self isIOS89]) {
|
||||
NSString *event;
|
||||
if (RCTSharedApplication().applicationState == UIApplicationStateBackground) {
|
||||
event = NOTIFICATIONS_NOTIFICATION_DISPLAYED;
|
||||
} else if (RCTSharedApplication().applicationState == UIApplicationStateInactive) {
|
||||
event = NOTIFICATIONS_NOTIFICATION_OPENED;
|
||||
} else {
|
||||
event = NOTIFICATIONS_NOTIFICATION_RECEIVED;
|
||||
}
|
||||
|
||||
NSDictionary *notification = [self parseUILocalNotification:localNotification];
|
||||
if (event == NOTIFICATIONS_NOTIFICATION_OPENED) {
|
||||
notification = @{
|
||||
@"action": UNNotificationDefaultActionIdentifier,
|
||||
@"notification": notification
|
||||
};
|
||||
}
|
||||
[self sendJSEvent:self name:event body:notification];
|
||||
}
|
||||
}
|
||||
|
||||
// Listen for background messages
|
||||
- (void)didReceiveRemoteNotification:(NSDictionary *)userInfo
|
||||
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
|
||||
// FCM Data messages come through here if they specify content-available=true
|
||||
// Pass them over to the RNFirebaseMessaging handler instead
|
||||
if (userInfo[@"aps"] && ((NSDictionary*)userInfo[@"aps"]).count == 1 && userInfo[@"aps"][@"content-available"]) {
|
||||
[[RNFirebaseMessaging instance] didReceiveRemoteNotification:userInfo];
|
||||
return;
|
||||
}
|
||||
|
||||
NSString *event;
|
||||
if (RCTSharedApplication().applicationState == UIApplicationStateBackground) {
|
||||
event = NOTIFICATIONS_NOTIFICATION_DISPLAYED;
|
||||
} else if ([self isIOS89]) {
|
||||
if (RCTSharedApplication().applicationState == UIApplicationStateInactive) {
|
||||
event = NOTIFICATIONS_NOTIFICATION_OPENED;
|
||||
} else {
|
||||
event = NOTIFICATIONS_NOTIFICATION_RECEIVED;
|
||||
}
|
||||
} else {
|
||||
// On IOS 10:
|
||||
// - foreground notifications also go through willPresentNotification
|
||||
// - background notification presses also go through didReceiveNotificationResponse
|
||||
// This prevents duplicate messages from hitting the JS app
|
||||
return;
|
||||
}
|
||||
|
||||
NSDictionary *notification = [self parseUserInfo:userInfo];
|
||||
// For onOpened events, we set the default action name as iOS 8/9 has no concept of actions
|
||||
if (event == NOTIFICATIONS_NOTIFICATION_OPENED) {
|
||||
notification = @{
|
||||
@"action": UNNotificationDefaultActionIdentifier,
|
||||
@"notification": notification
|
||||
};
|
||||
}
|
||||
|
||||
[self sendJSEvent:self name:event body:notification];
|
||||
completionHandler(UIBackgroundFetchResultNoData);
|
||||
}
|
||||
|
||||
// *******************************************************
|
||||
// ** Finish AppDelegate methods
|
||||
// *******************************************************
|
||||
|
||||
// *******************************************************
|
||||
// ** Start UNUserNotificationCenterDelegate methods
|
||||
// ** iOS 10+
|
||||
// *******************************************************
|
||||
|
||||
#if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
|
||||
// Handle incoming notification messages while app is in the foreground.
|
||||
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
|
||||
willPresentNotification:(UNNotification *)notification
|
||||
withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler {
|
||||
|
||||
UNNotificationTrigger *trigger = notification.request.trigger;
|
||||
BOOL isFcm = trigger && [notification.request.trigger class] == [UNPushNotificationTrigger class];
|
||||
BOOL isScheduled = trigger && [notification.request.trigger class] == [UNCalendarNotificationTrigger class];
|
||||
|
||||
NSString *event;
|
||||
UNNotificationPresentationOptions options;
|
||||
NSDictionary *message = [self parseUNNotification:notification];
|
||||
|
||||
if (isFcm || isScheduled) {
|
||||
// If app is in the background
|
||||
if (RCTSharedApplication().applicationState == UIApplicationStateBackground
|
||||
|| RCTSharedApplication().applicationState == UIApplicationStateInactive) {
|
||||
// display the notification
|
||||
options = UNNotificationPresentationOptionAlert | UNNotificationPresentationOptionBadge | UNNotificationPresentationOptionSound;
|
||||
// notification_displayed
|
||||
event = NOTIFICATIONS_NOTIFICATION_DISPLAYED;
|
||||
} else {
|
||||
// don't show notification
|
||||
options = UNNotificationPresentationOptionNone;
|
||||
// notification_received
|
||||
event = NOTIFICATIONS_NOTIFICATION_RECEIVED;
|
||||
}
|
||||
} else {
|
||||
// Triggered by `notifications().displayNotification(notification)`
|
||||
// Display the notification
|
||||
options = UNNotificationPresentationOptionAlert | UNNotificationPresentationOptionBadge | UNNotificationPresentationOptionSound;
|
||||
// notification_displayed
|
||||
event = NOTIFICATIONS_NOTIFICATION_DISPLAYED;
|
||||
}
|
||||
|
||||
[self sendJSEvent:self name:event body:message];
|
||||
completionHandler(options);
|
||||
}
|
||||
|
||||
// Handle notification messages after display notification is tapped by the user.
|
||||
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
|
||||
didReceiveNotificationResponse:(UNNotificationResponse *)response
|
||||
#if defined(__IPHONE_11_0)
|
||||
withCompletionHandler:(void(^)(void))completionHandler {
|
||||
#else
|
||||
withCompletionHandler:(void(^)())completionHandler {
|
||||
#endif
|
||||
NSDictionary *message = [self parseUNNotificationResponse:response];
|
||||
|
||||
[self sendJSEvent:self name:NOTIFICATIONS_NOTIFICATION_OPENED body:message];
|
||||
completionHandler();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// *******************************************************
|
||||
// ** Finish UNUserNotificationCenterDelegate methods
|
||||
// *******************************************************
|
||||
|
||||
RCT_EXPORT_METHOD(cancelAllNotifications) {
|
||||
if ([self isIOS89]) {
|
||||
[RCTSharedApplication() cancelAllLocalNotifications];
|
||||
} else {
|
||||
#if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
|
||||
UNUserNotificationCenter *notificationCenter = [UNUserNotificationCenter currentNotificationCenter];
|
||||
if (notificationCenter != nil) {
|
||||
[[UNUserNotificationCenter currentNotificationCenter] removeAllPendingNotificationRequests];
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(cancelNotification:(NSString*) notificationId) {
|
||||
if ([self isIOS89]) {
|
||||
for (UILocalNotification *notification in RCTSharedApplication().scheduledLocalNotifications) {
|
||||
NSDictionary *notificationInfo = notification.userInfo;
|
||||
if ([notificationId isEqualToString:[notificationInfo valueForKey:@"notificationId"]]) {
|
||||
[RCTSharedApplication() cancelLocalNotification:notification];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
#if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
|
||||
UNUserNotificationCenter *notificationCenter = [UNUserNotificationCenter currentNotificationCenter];
|
||||
if (notificationCenter != nil) {
|
||||
[[UNUserNotificationCenter currentNotificationCenter] removePendingNotificationRequestsWithIdentifiers:@[notificationId]];
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(displayNotification:(NSDictionary*) notification
|
||||
resolver:(RCTPromiseResolveBlock)resolve
|
||||
rejecter:(RCTPromiseRejectBlock)reject) {
|
||||
if ([self isIOS89]) {
|
||||
UILocalNotification* notif = [self buildUILocalNotification:notification withSchedule:false];
|
||||
[RCTSharedApplication() presentLocalNotificationNow:notif];
|
||||
resolve(nil);
|
||||
} else {
|
||||
#if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
|
||||
UNNotificationRequest* request = [self buildUNNotificationRequest:notification withSchedule:false];
|
||||
[[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
|
||||
if (!error) {
|
||||
resolve(nil);
|
||||
} else{
|
||||
reject(@"notifications/display_notification_error", @"Failed to display notificaton", error);
|
||||
}
|
||||
}];
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(getBadge: (RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
resolve(@([RCTSharedApplication() applicationIconBadgeNumber]));
|
||||
});
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(getInitialNotification:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
|
||||
if ([self isIOS89]) {
|
||||
// With iOS 8/9 we rely on the launch options
|
||||
UILocalNotification *localNotification = [self bridge].launchOptions[UIApplicationLaunchOptionsLocalNotificationKey];
|
||||
NSDictionary *notification;
|
||||
if (localNotification) {
|
||||
notification = [self parseUILocalNotification:localNotification];
|
||||
} else {
|
||||
NSDictionary *remoteNotification = [self bridge].launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey];
|
||||
if (remoteNotification) {
|
||||
notification = [self parseUserInfo:remoteNotification];
|
||||
}
|
||||
}
|
||||
if (notification) {
|
||||
resolve(@{@"action": UNNotificationDefaultActionIdentifier, @"notification": notification});
|
||||
} else {
|
||||
resolve(nil);
|
||||
}
|
||||
} else {
|
||||
// With iOS 10+ we are able to intercept the didReceiveNotificationResponse message
|
||||
// to get both the notification and the action
|
||||
resolve(initialNotification);
|
||||
}
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(getScheduledNotifications:(RCTPromiseResolveBlock)resolve
|
||||
rejecter:(RCTPromiseRejectBlock)reject) {
|
||||
if ([self isIOS89]) {
|
||||
NSMutableArray* notifications = [[NSMutableArray alloc] init];
|
||||
for (UILocalNotification *notif in [RCTSharedApplication() scheduledLocalNotifications]){
|
||||
NSDictionary *notification = [self parseUILocalNotification:notif];
|
||||
[notifications addObject:notification];
|
||||
}
|
||||
resolve(notifications);
|
||||
} else {
|
||||
#if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
|
||||
[[UNUserNotificationCenter currentNotificationCenter] getPendingNotificationRequestsWithCompletionHandler:^(NSArray<UNNotificationRequest *> * _Nonnull requests) {
|
||||
NSMutableArray* notifications = [[NSMutableArray alloc] init];
|
||||
for (UNNotificationRequest *notif in requests){
|
||||
NSDictionary *notification = [self parseUNNotificationRequest:notif];
|
||||
[notifications addObject:notification];
|
||||
}
|
||||
resolve(notifications);
|
||||
}];
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(removeAllDeliveredNotifications) {
|
||||
if ([self isIOS89]) {
|
||||
// No such functionality on iOS 8/9
|
||||
} else {
|
||||
#if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
|
||||
UNUserNotificationCenter *notificationCenter = [UNUserNotificationCenter currentNotificationCenter];
|
||||
if (notificationCenter != nil) {
|
||||
[[UNUserNotificationCenter currentNotificationCenter] removeAllDeliveredNotifications];
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(removeDeliveredNotification:(NSString*) notificationId) {
|
||||
if ([self isIOS89]) {
|
||||
// No such functionality on iOS 8/9
|
||||
} else {
|
||||
#if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
|
||||
UNUserNotificationCenter *notificationCenter = [UNUserNotificationCenter currentNotificationCenter];
|
||||
if (notificationCenter != nil) {
|
||||
[[UNUserNotificationCenter currentNotificationCenter] removeDeliveredNotificationsWithIdentifiers:@[notificationId]];
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(scheduleNotification:(NSDictionary*) notification
|
||||
resolver:(RCTPromiseResolveBlock)resolve
|
||||
rejecter:(RCTPromiseRejectBlock)reject) {
|
||||
if ([self isIOS89]) {
|
||||
UILocalNotification* notif = [self buildUILocalNotification:notification withSchedule:true];
|
||||
[RCTSharedApplication() scheduleLocalNotification:notif];
|
||||
resolve(nil);
|
||||
} else {
|
||||
#if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
|
||||
UNNotificationRequest* request = [self buildUNNotificationRequest:notification withSchedule:true];
|
||||
[[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
|
||||
if (!error) {
|
||||
resolve(nil);
|
||||
} else{
|
||||
reject(@"notification/schedule_notification_error", @"Failed to schedule notificaton", error);
|
||||
}
|
||||
}];
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(setBadge: (NSInteger) number) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[RCTSharedApplication() setApplicationIconBadgeNumber:number];
|
||||
});
|
||||
}
|
||||
|
||||
// Because of the time delay between the app starting and the bridge being initialised
|
||||
// we create a temporary instance of RNFirebaseNotifications.
|
||||
// With this temporary instance, we cache any events to be sent as soon as the bridge is set on the module
|
||||
- (void)sendJSEvent:(RCTEventEmitter *)emitter name:(NSString *)name body:(id)body {
|
||||
if (emitter.bridge) {
|
||||
[RNFirebaseUtil sendJSEvent:emitter name:name body:body];
|
||||
} else {
|
||||
if ([name isEqualToString:NOTIFICATIONS_NOTIFICATION_OPENED] && !initialNotification) {
|
||||
initialNotification = body;
|
||||
}
|
||||
// PRE-BRIDGE-EVENTS: Consider enabling this to allow events built up before the bridge is built to be sent to the JS side
|
||||
// [pendingEvents addObject:@{@"name":name, @"body":body}];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)isIOS89 {
|
||||
return floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_9_x_Max;
|
||||
}
|
||||
|
||||
- (UILocalNotification*) buildUILocalNotification:(NSDictionary *) notification
|
||||
withSchedule:(BOOL) withSchedule {
|
||||
UILocalNotification *localNotification = [[UILocalNotification alloc] init];
|
||||
if (notification[@"body"]) {
|
||||
localNotification.alertBody = notification[@"body"];
|
||||
}
|
||||
if (notification[@"data"]) {
|
||||
localNotification.userInfo = notification[@"data"];
|
||||
}
|
||||
if (notification[@"sound"]) {
|
||||
localNotification.soundName = notification[@"sound"];
|
||||
}
|
||||
if (notification[@"title"]) {
|
||||
localNotification.alertTitle = notification[@"title"];
|
||||
}
|
||||
if (notification[@"ios"]) {
|
||||
NSDictionary *ios = notification[@"ios"];
|
||||
if (ios[@"alertAction"]) {
|
||||
localNotification.alertAction = ios[@"alertAction"];
|
||||
}
|
||||
if (ios[@"badge"]) {
|
||||
NSNumber *badge = ios[@"badge"];
|
||||
localNotification.applicationIconBadgeNumber = badge.integerValue;
|
||||
}
|
||||
if (ios[@"category"]) {
|
||||
localNotification.category = ios[@"category"];
|
||||
}
|
||||
if (ios[@"hasAction"]) {
|
||||
localNotification.hasAction = ios[@"hasAction"];
|
||||
}
|
||||
if (ios[@"launchImage"]) {
|
||||
localNotification.alertLaunchImage = ios[@"launchImage"];
|
||||
}
|
||||
}
|
||||
if (withSchedule) {
|
||||
NSDictionary *schedule = notification[@"schedule"];
|
||||
NSNumber *fireDateNumber = schedule[@"fireDate"];
|
||||
NSDate *fireDate = [NSDate dateWithTimeIntervalSince1970:([fireDateNumber doubleValue] / 1000.0)];
|
||||
localNotification.fireDate = fireDate;
|
||||
|
||||
NSString *interval = schedule[@"repeatInterval"];
|
||||
if (interval) {
|
||||
if ([interval isEqualToString:@"minute"]) {
|
||||
localNotification.repeatInterval = NSCalendarUnitMinute;
|
||||
} else if ([interval isEqualToString:@"hour"]) {
|
||||
localNotification.repeatInterval = NSCalendarUnitHour;
|
||||
} else if ([interval isEqualToString:@"day"]) {
|
||||
localNotification.repeatInterval = NSCalendarUnitDay;
|
||||
} else if ([interval isEqualToString:@"week"]) {
|
||||
localNotification.repeatInterval = NSCalendarUnitWeekday;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return localNotification;
|
||||
}
|
||||
|
||||
- (UNNotificationRequest*) buildUNNotificationRequest:(NSDictionary *) notification
|
||||
withSchedule:(BOOL) withSchedule {
|
||||
UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
|
||||
if (notification[@"body"]) {
|
||||
content.body = notification[@"body"];
|
||||
}
|
||||
if (notification[@"data"]) {
|
||||
content.userInfo = notification[@"data"];
|
||||
}
|
||||
if (notification[@"sound"]) {
|
||||
content.sound = notification[@"sound"];
|
||||
}
|
||||
if (notification[@"subtitle"]) {
|
||||
content.subtitle = notification[@"subtitle"];
|
||||
}
|
||||
if (notification[@"title"]) {
|
||||
content.title = notification[@"title"];
|
||||
}
|
||||
if (notification[@"ios"]) {
|
||||
NSDictionary *ios = notification[@"ios"];
|
||||
if (ios[@"attachments"]) {
|
||||
NSMutableArray *attachments = [[NSMutableArray alloc] init];
|
||||
for (NSDictionary *a in ios[@"attachments"]) {
|
||||
NSString *identifier = a[@"identifier"];
|
||||
NSURL *url = [NSURL URLWithString:a[@"url"]];
|
||||
NSMutableDictionary *attachmentOptions = nil;
|
||||
|
||||
if (a[@"options"]) {
|
||||
NSDictionary *options = a[@"options"];
|
||||
attachmentOptions = [[NSMutableDictionary alloc] init];
|
||||
|
||||
for (id key in options) {
|
||||
if ([key isEqualToString:@"typeHint"]) {
|
||||
attachmentOptions[UNNotificationAttachmentOptionsTypeHintKey] = options[key];
|
||||
} else if ([key isEqualToString:@"thumbnailHidden"]) {
|
||||
attachmentOptions[UNNotificationAttachmentOptionsThumbnailHiddenKey] = options[key];
|
||||
} else if ([key isEqualToString:@"thumbnailClippingRect"]) {
|
||||
attachmentOptions[UNNotificationAttachmentOptionsThumbnailClippingRectKey] = options[key];
|
||||
} else if ([key isEqualToString:@"thumbnailTime"]) {
|
||||
attachmentOptions[UNNotificationAttachmentOptionsThumbnailTimeKey] = options[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NSError *error;
|
||||
UNNotificationAttachment *attachment = [UNNotificationAttachment attachmentWithIdentifier:identifier URL:url options:attachmentOptions error:&error];
|
||||
if (attachment) {
|
||||
[attachments addObject:attachment];
|
||||
} else {
|
||||
NSLog(@"Failed to create attachment: %@", error);
|
||||
}
|
||||
}
|
||||
content.attachments = attachments;
|
||||
}
|
||||
|
||||
if (ios[@"badge"]) {
|
||||
content.badge = ios[@"badge"];
|
||||
}
|
||||
if (ios[@"category"]) {
|
||||
content.categoryIdentifier = ios[@"category"];
|
||||
}
|
||||
if (ios[@"launchImage"]) {
|
||||
content.launchImageName = ios[@"launchImage"];
|
||||
}
|
||||
if (ios[@"threadIdentifier"]) {
|
||||
content.threadIdentifier = ios[@"threadIdentifier"];
|
||||
}
|
||||
}
|
||||
|
||||
if (withSchedule) {
|
||||
NSDictionary *schedule = notification[@"schedule"];
|
||||
NSNumber *fireDateNumber = schedule[@"fireDate"];
|
||||
NSString *interval = schedule[@"repeatInterval"];
|
||||
NSDate *fireDate = [NSDate dateWithTimeIntervalSince1970:([fireDateNumber doubleValue] / 1000.0)];
|
||||
|
||||
NSCalendarUnit calendarUnit;
|
||||
if (interval) {
|
||||
if ([interval isEqualToString:@"minute"]) {
|
||||
calendarUnit = NSCalendarUnitSecond;
|
||||
} else if ([interval isEqualToString:@"hour"]) {
|
||||
calendarUnit = NSCalendarUnitMinute | NSCalendarUnitSecond;
|
||||
} else if ([interval isEqualToString:@"day"]) {
|
||||
calendarUnit = NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond;
|
||||
} else if ([interval isEqualToString:@"week"]) {
|
||||
calendarUnit = NSCalendarUnitWeekday | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond;
|
||||
}
|
||||
} else {
|
||||
// Needs to match exactly to the secpmd
|
||||
calendarUnit = NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond;
|
||||
}
|
||||
|
||||
NSDateComponents *components = [[NSCalendar currentCalendar] components:calendarUnit fromDate:fireDate];
|
||||
UNCalendarNotificationTrigger *trigger = [UNCalendarNotificationTrigger triggerWithDateMatchingComponents:components repeats:interval];
|
||||
return [UNNotificationRequest requestWithIdentifier:notification[@"notificationId"] content:content trigger:trigger];
|
||||
} else {
|
||||
return [UNNotificationRequest requestWithIdentifier:notification[@"notificationId"] content:content trigger:nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSDictionary*) parseUILocalNotification:(UILocalNotification *) localNotification {
|
||||
NSMutableDictionary *notification = [[NSMutableDictionary alloc] init];
|
||||
|
||||
if (localNotification.alertBody) {
|
||||
notification[@"body"] = localNotification.alertBody;
|
||||
}
|
||||
if (localNotification.userInfo) {
|
||||
notification[@"data"] = localNotification.userInfo;
|
||||
}
|
||||
if (localNotification.soundName) {
|
||||
notification[@"sound"] = localNotification.soundName;
|
||||
}
|
||||
if (localNotification.alertTitle) {
|
||||
notification[@"title"] = localNotification.alertTitle;
|
||||
}
|
||||
|
||||
NSMutableDictionary *ios = [[NSMutableDictionary alloc] init];
|
||||
if (localNotification.alertAction) {
|
||||
ios[@"alertAction"] = localNotification.alertAction;
|
||||
}
|
||||
if (localNotification.applicationIconBadgeNumber) {
|
||||
ios[@"badge"] = @(localNotification.applicationIconBadgeNumber);
|
||||
}
|
||||
if (localNotification.category) {
|
||||
ios[@"category"] = localNotification.category;
|
||||
}
|
||||
if (localNotification.hasAction) {
|
||||
ios[@"hasAction"] = @(localNotification.hasAction);
|
||||
}
|
||||
if (localNotification.alertLaunchImage) {
|
||||
ios[@"launchImage"] = localNotification.alertLaunchImage;
|
||||
}
|
||||
notification[@"ios"] = ios;
|
||||
|
||||
return notification;
|
||||
}
|
||||
|
||||
- (NSDictionary*)parseUNNotificationResponse:(UNNotificationResponse *)response {
|
||||
NSMutableDictionary *notificationResponse = [[NSMutableDictionary alloc] init];
|
||||
NSDictionary *notification = [self parseUNNotification:response.notification];
|
||||
notificationResponse[@"notification"] = notification;
|
||||
notificationResponse[@"action"] = response.actionIdentifier;
|
||||
|
||||
return notificationResponse;
|
||||
}
|
||||
|
||||
- (NSDictionary*)parseUNNotification:(UNNotification *)notification {
|
||||
return [self parseUNNotificationRequest:notification.request];
|
||||
}
|
||||
|
||||
- (NSDictionary*) parseUNNotificationRequest:(UNNotificationRequest *) notificationRequest {
|
||||
NSMutableDictionary *notification = [[NSMutableDictionary alloc] init];
|
||||
|
||||
notification[@"notificationId"] = notificationRequest.identifier;
|
||||
|
||||
if (notificationRequest.content.body) {
|
||||
notification[@"body"] = notificationRequest.content.body;
|
||||
}
|
||||
if (notificationRequest.content.userInfo) {
|
||||
NSMutableDictionary *data = [[NSMutableDictionary alloc] init];
|
||||
for (id k in notificationRequest.content.userInfo) {
|
||||
if ([k isEqualToString:@"aps"]
|
||||
|| [k isEqualToString:@"gcm.message_id"]) {
|
||||
// ignore as these are handled by the OS
|
||||
} else {
|
||||
data[k] = notificationRequest.content.userInfo[k];
|
||||
}
|
||||
}
|
||||
notification[@"data"] = data;
|
||||
}
|
||||
if (notificationRequest.content.sound) {
|
||||
notification[@"sound"] = notificationRequest.content.sound;
|
||||
}
|
||||
if (notificationRequest.content.subtitle) {
|
||||
notification[@"subtitle"] = notificationRequest.content.subtitle;
|
||||
}
|
||||
if (notificationRequest.content.title) {
|
||||
notification[@"title"] = notificationRequest.content.title;
|
||||
}
|
||||
|
||||
NSMutableDictionary *ios = [[NSMutableDictionary alloc] init];
|
||||
|
||||
if (notificationRequest.content.attachments) {
|
||||
NSMutableArray *attachments = [[NSMutableArray alloc] init];
|
||||
for (UNNotificationAttachment *a in notificationRequest.content.attachments) {
|
||||
NSMutableDictionary *attachment = [[NSMutableDictionary alloc] init];
|
||||
attachment[@"identifier"] = a.identifier;
|
||||
attachment[@"type"] = a.type;
|
||||
attachment[@"url"] = [a.URL absoluteString];
|
||||
[attachments addObject:attachment];
|
||||
}
|
||||
ios[@"attachments"] = attachments;
|
||||
}
|
||||
|
||||
if (notificationRequest.content.badge) {
|
||||
ios[@"badge"] = notificationRequest.content.badge;
|
||||
}
|
||||
if (notificationRequest.content.categoryIdentifier) {
|
||||
ios[@"category"] = notificationRequest.content.categoryIdentifier;
|
||||
}
|
||||
if (notificationRequest.content.launchImageName) {
|
||||
ios[@"launchImage"] = notificationRequest.content.launchImageName;
|
||||
}
|
||||
if (notificationRequest.content.threadIdentifier) {
|
||||
ios[@"threadIdentifier"] = notificationRequest.content.threadIdentifier;
|
||||
}
|
||||
notification[@"ios"] = ios;
|
||||
|
||||
return notification;
|
||||
}
|
||||
|
||||
- (NSDictionary*)parseUserInfo:(NSDictionary *)userInfo {
|
||||
|
||||
NSMutableDictionary *notification = [[NSMutableDictionary alloc] init];
|
||||
NSMutableDictionary *data = [[NSMutableDictionary alloc] init];
|
||||
NSMutableDictionary *ios = [[NSMutableDictionary alloc] init];
|
||||
|
||||
for (id k1 in userInfo) {
|
||||
if ([k1 isEqualToString:@"aps"]) {
|
||||
NSDictionary *aps = userInfo[k1];
|
||||
for (id k2 in aps) {
|
||||
if ([k2 isEqualToString:@"alert"]) {
|
||||
NSDictionary *alert = aps[k2];
|
||||
for (id k3 in alert) {
|
||||
if ([k3 isEqualToString:@"body"]) {
|
||||
notification[@"body"] = alert[k3];
|
||||
} else if ([k3 isEqualToString:@"subtitle"]) {
|
||||
notification[@"subtitle"] = alert[k3];
|
||||
} else if ([k3 isEqualToString:@"title"]) {
|
||||
notification[@"title"] = alert[k3];
|
||||
} else if ([k3 isEqualToString:@"loc-args"]
|
||||
|| [k3 isEqualToString:@"loc-key"]
|
||||
|| [k3 isEqualToString:@"title-loc-args"]
|
||||
|| [k3 isEqualToString:@"title-loc-key"]) {
|
||||
// Ignore known keys
|
||||
} else {
|
||||
NSLog(@"Unknown alert key: %@", k2);
|
||||
}
|
||||
}
|
||||
} else if ([k2 isEqualToString:@"badge"]) {
|
||||
ios[@"badge"] = aps[k2];
|
||||
} else if ([k2 isEqualToString:@"category"]) {
|
||||
ios[@"category"] = aps[k2];
|
||||
} else if ([k2 isEqualToString:@"sound"]) {
|
||||
notification[@"sound"] = aps[k2];
|
||||
} else {
|
||||
NSLog(@"Unknown aps key: %@", k2);
|
||||
}
|
||||
}
|
||||
} else if ([k1 isEqualToString:@"gcm.message_id"]) {
|
||||
notification[@"notificationId"] = userInfo[k1];
|
||||
} else if ([k1 isEqualToString:@"gcm.n.e"]
|
||||
|| [k1 isEqualToString:@"gcm.notification.sound2"]
|
||||
|| [k1 isEqualToString:@"google.c.a.c_id"]
|
||||
|| [k1 isEqualToString:@"google.c.a.c_l"]
|
||||
|| [k1 isEqualToString:@"google.c.a.e"]
|
||||
|| [k1 isEqualToString:@"google.c.a.udt"]
|
||||
|| [k1 isEqualToString:@"google.c.a.ts"]) {
|
||||
// Ignore known keys
|
||||
} else {
|
||||
// Assume custom data
|
||||
data[k1] = userInfo[k1];
|
||||
}
|
||||
}
|
||||
|
||||
notification[@"data"] = data;
|
||||
notification[@"ios"] = ios;
|
||||
|
||||
return notification;
|
||||
}
|
||||
|
||||
- (NSArray<NSString *> *)supportedEvents {
|
||||
return @[NOTIFICATIONS_NOTIFICATION_DISPLAYED, NOTIFICATIONS_NOTIFICATION_OPENED, NOTIFICATIONS_NOTIFICATION_RECEIVED];
|
||||
}
|
||||
|
||||
+ (BOOL)requiresMainQueueSetup
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#else
|
||||
@implementation RNFirebaseNotifications
|
||||
@end
|
||||
#endif
|
|
@ -954,7 +954,7 @@ declare module "react-native-firebase" {
|
|||
* IOS
|
||||
* Requests app notification permissions in an Alert dialog.
|
||||
*/
|
||||
requestPermissions(): void
|
||||
requestPermissions(): Promise<{ granted: boolean }>;
|
||||
|
||||
/**
|
||||
* Sets the badge number on the iOS app icon.
|
||||
|
@ -971,7 +971,7 @@ declare module "react-native-firebase" {
|
|||
* @param senderId
|
||||
* @param payload
|
||||
*/
|
||||
send(senderId: string, payload: RemoteMessage): any
|
||||
sendMessage(senderId: string, payload: RemoteMessage): any
|
||||
|
||||
NOTIFICATION_TYPE: Object
|
||||
REMOTE_NOTIFICATION_RESULT: Object
|
||||
|
|
16
lib/index.js
16
lib/index.js
|
@ -24,7 +24,7 @@ export type {
|
|||
} from './modules/auth/types';
|
||||
export type {
|
||||
default as ConfirmationResult,
|
||||
} from './modules/auth/ConfirmationResult';
|
||||
} from './modules/auth/phone/ConfirmationResult';
|
||||
export type { default as User } from './modules/auth/User';
|
||||
|
||||
/*
|
||||
|
@ -64,3 +64,17 @@ export type {
|
|||
default as QuerySnapshot,
|
||||
} from './modules/firestore/QuerySnapshot';
|
||||
export type { default as WriteBatch } from './modules/firestore/WriteBatch';
|
||||
|
||||
/*
|
||||
* Export Messaging types
|
||||
*/
|
||||
export type {
|
||||
default as RemoteMessage,
|
||||
} from './modules/messaging/RemoteMessage';
|
||||
|
||||
/*
|
||||
* Export Notifications types
|
||||
*/
|
||||
export type {
|
||||
default as Notification,
|
||||
} from './modules/notifications/Notification';
|
||||
|
|
|
@ -41,6 +41,7 @@ export default class AdMob extends ModuleBase {
|
|||
events: NATIVE_EVENTS,
|
||||
moduleName: MODULE_NAME,
|
||||
multiApp: false,
|
||||
hasShards: false,
|
||||
namespace: NAMESPACE,
|
||||
});
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@ export default class Analytics extends ModuleBase {
|
|||
super(app, {
|
||||
moduleName: MODULE_NAME,
|
||||
multiApp: false,
|
||||
hasShards: false,
|
||||
namespace: NAMESPACE,
|
||||
});
|
||||
}
|
||||
|
@ -122,7 +123,12 @@ export default class Analytics extends ModuleBase {
|
|||
* Sets the user ID property.
|
||||
* @param id
|
||||
*/
|
||||
setUserId(id: string): void {
|
||||
setUserId(id: string | null): void {
|
||||
if (id !== null && !isString(id)) {
|
||||
throw new Error(
|
||||
'analytics.setUserId(): The supplied userId must be a string value or null.'
|
||||
);
|
||||
}
|
||||
getNativeModule(this).setUserId(id);
|
||||
}
|
||||
|
||||
|
@ -131,17 +137,28 @@ export default class Analytics extends ModuleBase {
|
|||
* @param name
|
||||
* @param value
|
||||
*/
|
||||
setUserProperty(name: string, value: string): void {
|
||||
setUserProperty(name: string, value: string | null): void {
|
||||
if (value !== null && !isString(value)) {
|
||||
throw new Error(
|
||||
'analytics.setUserProperty(): The supplied property must be a string value or null.'
|
||||
);
|
||||
}
|
||||
getNativeModule(this).setUserProperty(name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a user property to a given value.
|
||||
* Sets multiple user properties to the supplied values.
|
||||
* @RNFirebaseSpecific
|
||||
* @param object
|
||||
*/
|
||||
setUserProperties(object: Object): void {
|
||||
Object.keys(object).forEach(property => {
|
||||
const value = object[property];
|
||||
if (value !== null && !isString(value)) {
|
||||
throw new Error(
|
||||
`analytics.setUserProperties(): The property with name '${property}' must be a string value or null.`
|
||||
);
|
||||
}
|
||||
getNativeModule(this).setUserProperty(property, object[property]);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -8,7 +8,8 @@ import { getAppEventName, SharedEventEmitter } from '../../utils/events';
|
|||
import { getLogger } from '../../utils/log';
|
||||
import { getNativeModule } from '../../utils/native';
|
||||
import INTERNALS from '../../utils/internals';
|
||||
import ConfirmationResult from './ConfirmationResult';
|
||||
import ConfirmationResult from './phone/ConfirmationResult';
|
||||
import PhoneAuthListener from './phone/PhoneAuthListener';
|
||||
|
||||
// providers
|
||||
import EmailAuthProvider from './providers/EmailAuthProvider';
|
||||
|
@ -19,8 +20,6 @@ import OAuthProvider from './providers/OAuthProvider';
|
|||
import TwitterAuthProvider from './providers/TwitterAuthProvider';
|
||||
import FacebookAuthProvider from './providers/FacebookAuthProvider';
|
||||
|
||||
import PhoneAuthListener from './PhoneAuthListener';
|
||||
|
||||
import type {
|
||||
ActionCodeInfo,
|
||||
ActionCodeSettings,
|
||||
|
@ -54,6 +53,7 @@ export default class Auth extends ModuleBase {
|
|||
events: NATIVE_EVENTS,
|
||||
moduleName: MODULE_NAME,
|
||||
multiApp: true,
|
||||
hasShards: false,
|
||||
namespace: NAMESPACE,
|
||||
});
|
||||
this._user = null;
|
||||
|
@ -251,7 +251,7 @@ export default class Auth extends ModuleBase {
|
|||
createUserAndRetrieveDataWithEmailAndPassword(
|
||||
email: string,
|
||||
password: string
|
||||
): Promise<User> {
|
||||
): Promise<UserCredential> {
|
||||
return getNativeModule(this)
|
||||
.createUserAndRetrieveDataWithEmailAndPassword(email, password)
|
||||
.then(userCredential => this._setUserCredential(userCredential));
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
* @flow
|
||||
* ConfirmationResult representation wrapper
|
||||
*/
|
||||
import { getNativeModule } from '../../utils/native';
|
||||
import type Auth from './';
|
||||
import type User from './User';
|
||||
import { getNativeModule } from '../../../utils/native';
|
||||
import type Auth from '../';
|
||||
import type User from '../User';
|
||||
|
||||
export default class ConfirmationResult {
|
||||
_auth: Auth;
|
|
@ -1,6 +1,6 @@
|
|||
// @flow
|
||||
import INTERNALS from '../../utils/internals';
|
||||
import { SharedEventEmitter } from '../../utils/events';
|
||||
import INTERNALS from '../../../utils/internals';
|
||||
import { SharedEventEmitter } from '../../../utils/events';
|
||||
import {
|
||||
generatePushID,
|
||||
isFunction,
|
||||
|
@ -8,10 +8,10 @@ import {
|
|||
isIOS,
|
||||
isString,
|
||||
nativeToJSError,
|
||||
} from '../../utils';
|
||||
import { getNativeModule } from '../../utils/native';
|
||||
} from '../../../utils';
|
||||
import { getNativeModule } from '../../../utils/native';
|
||||
|
||||
import type Auth from './';
|
||||
import type Auth from '../';
|
||||
|
||||
type PhoneAuthSnapshot = {
|
||||
state: 'sent' | 'timeout' | 'verified' | 'error',
|
|
@ -32,6 +32,7 @@ export default class RemoteConfig extends ModuleBase {
|
|||
super(app, {
|
||||
moduleName: MODULE_NAME,
|
||||
multiApp: false,
|
||||
hasShards: false,
|
||||
namespace: NAMESPACE,
|
||||
});
|
||||
this._developerModeEnabled = false;
|
||||
|
@ -143,11 +144,11 @@ export default class RemoteConfig extends ModuleBase {
|
|||
* "source" : OneOf<String>(remoteConfigSourceRemote|remoteConfigSourceDefault|remoteConfigSourceStatic)
|
||||
* }
|
||||
*/
|
||||
getValues(keys: Array<String>) {
|
||||
getValues(keys: Array<string>) {
|
||||
return getNativeModule(this)
|
||||
.getValues(keys || [])
|
||||
.then(nativeValues => {
|
||||
const values: { [String]: Object } = {};
|
||||
const values: { [string]: Object } = {};
|
||||
for (let i = 0, len = keys.length; i < len; i++) {
|
||||
values[keys[i]] = this._nativeValueToJS(nativeValues[i]);
|
||||
}
|
||||
|
|
|
@ -18,8 +18,13 @@ import Crashlytics, {
|
|||
} from '../fabric/crashlytics';
|
||||
import Database, { NAMESPACE as DatabaseNamespace } from '../database';
|
||||
import Firestore, { NAMESPACE as FirestoreNamespace } from '../firestore';
|
||||
import InstanceId, { NAMESPACE as InstanceIdNamespace } from '../instanceid';
|
||||
import Invites, { NAMESPACE as InvitesNamespace } from '../invites';
|
||||
import Links, { NAMESPACE as LinksNamespace } from '../links';
|
||||
import Messaging, { NAMESPACE as MessagingNamespace } from '../messaging';
|
||||
import Notifications, {
|
||||
NAMESPACE as NotificationsNamespace,
|
||||
} from '../notifications';
|
||||
import Performance, { NAMESPACE as PerfNamespace } from '../perf';
|
||||
import Storage, { NAMESPACE as StorageNamespace } from '../storage';
|
||||
import Utils, { NAMESPACE as UtilsNamespace } from '../utils';
|
||||
|
@ -44,8 +49,11 @@ export default class App {
|
|||
crashlytics: () => Crashlytics,
|
||||
};
|
||||
firestore: () => Firestore;
|
||||
instanceid: () => InstanceId;
|
||||
invites: () => Invites;
|
||||
links: () => Links;
|
||||
messaging: () => Messaging;
|
||||
notifications: () => Notifications;
|
||||
perf: () => Performance;
|
||||
storage: () => Storage;
|
||||
utils: () => Utils;
|
||||
|
@ -83,8 +91,15 @@ export default class App {
|
|||
crashlytics: APPS.appModule(this, CrashlyticsNamespace, Crashlytics),
|
||||
};
|
||||
this.firestore = APPS.appModule(this, FirestoreNamespace, Firestore);
|
||||
this.instanceid = APPS.appModule(this, InstanceIdNamespace, InstanceId);
|
||||
this.invites = APPS.appModule(this, InvitesNamespace, Invites);
|
||||
this.links = APPS.appModule(this, LinksNamespace, Links);
|
||||
this.messaging = APPS.appModule(this, MessagingNamespace, Messaging);
|
||||
this.notifications = APPS.appModule(
|
||||
this,
|
||||
NotificationsNamespace,
|
||||
Notifications
|
||||
);
|
||||
this.perf = APPS.appModule(this, PerfNamespace, Performance);
|
||||
this.storage = APPS.appModule(this, StorageNamespace, Storage);
|
||||
this.utils = APPS.appModule(this, UtilsNamespace, Utils);
|
||||
|
|
|
@ -38,6 +38,14 @@ import {
|
|||
statics as FirestoreStatics,
|
||||
MODULE_NAME as FirestoreModuleName,
|
||||
} from '../firestore';
|
||||
import {
|
||||
statics as InstanceIdStatics,
|
||||
MODULE_NAME as InstanceIdModuleName,
|
||||
} from '../instanceid';
|
||||
import {
|
||||
statics as InvitesStatics,
|
||||
MODULE_NAME as InvitesModuleName,
|
||||
} from '../invites';
|
||||
import {
|
||||
statics as LinksStatics,
|
||||
MODULE_NAME as LinksModuleName,
|
||||
|
@ -46,6 +54,10 @@ import {
|
|||
statics as MessagingStatics,
|
||||
MODULE_NAME as MessagingModuleName,
|
||||
} from '../messaging';
|
||||
import {
|
||||
statics as NotificationsStatics,
|
||||
MODULE_NAME as NotificationsModuleName,
|
||||
} from '../notifications';
|
||||
import {
|
||||
statics as PerformanceStatics,
|
||||
MODULE_NAME as PerfModuleName,
|
||||
|
@ -69,8 +81,11 @@ import type {
|
|||
FabricModule,
|
||||
FirebaseOptions,
|
||||
FirestoreModule,
|
||||
InstanceIdModule,
|
||||
InvitesModule,
|
||||
LinksModule,
|
||||
MessagingModule,
|
||||
NotificationsModule,
|
||||
PerformanceModule,
|
||||
StorageModule,
|
||||
UtilsModule,
|
||||
|
@ -87,8 +102,11 @@ class Firebase {
|
|||
database: DatabaseModule;
|
||||
fabric: FabricModule;
|
||||
firestore: FirestoreModule;
|
||||
instanceid: InstanceIdModule;
|
||||
invites: InvitesModule;
|
||||
links: LinksModule;
|
||||
messaging: MessagingModule;
|
||||
notifications: NotificationsModule;
|
||||
perf: PerformanceModule;
|
||||
storage: StorageModule;
|
||||
utils: UtilsModule;
|
||||
|
@ -130,12 +148,27 @@ class Firebase {
|
|||
FirestoreStatics,
|
||||
FirestoreModuleName
|
||||
);
|
||||
this.instanceid = APPS.moduleAndStatics(
|
||||
'instanceid',
|
||||
InstanceIdStatics,
|
||||
InstanceIdModuleName
|
||||
);
|
||||
this.invites = APPS.moduleAndStatics(
|
||||
'invites',
|
||||
InvitesStatics,
|
||||
InvitesModuleName
|
||||
);
|
||||
this.links = APPS.moduleAndStatics('links', LinksStatics, LinksModuleName);
|
||||
this.messaging = APPS.moduleAndStatics(
|
||||
'messaging',
|
||||
MessagingStatics,
|
||||
MessagingModuleName
|
||||
);
|
||||
this.notifications = APPS.moduleAndStatics(
|
||||
'notifications',
|
||||
NotificationsStatics,
|
||||
NotificationsModuleName
|
||||
);
|
||||
this.perf = APPS.moduleAndStatics(
|
||||
'perf',
|
||||
PerformanceStatics,
|
||||
|
|
|
@ -16,6 +16,7 @@ export default class Crash extends ModuleBase {
|
|||
super(app, {
|
||||
moduleName: MODULE_NAME,
|
||||
multiApp: false,
|
||||
hasShards: false,
|
||||
namespace: NAMESPACE,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -289,7 +289,7 @@ export default class Reference extends ReferenceBase {
|
|||
})
|
||||
.catch(error => {
|
||||
if (isFunction(cancelOrContext)) return cancelOrContext(error);
|
||||
return error;
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -497,7 +497,7 @@ export default class Reference extends ReferenceBase {
|
|||
* @returns {string}
|
||||
*/
|
||||
toString(): string {
|
||||
return `${this._database.app.options.databaseURL}/${this.path}`;
|
||||
return `${this._database.databaseUrl}/${this.path}`;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -606,7 +606,7 @@ export default class Reference extends ReferenceBase {
|
|||
* @return {string}
|
||||
*/
|
||||
_getRegistrationKey(eventType: string): string {
|
||||
return `$${this._database.app.name}$/${
|
||||
return `$${this._database.databaseUrl}$/${
|
||||
this.path
|
||||
}$${this._query.queryIdentifier()}$${listeners}$${eventType}`;
|
||||
}
|
||||
|
@ -618,8 +618,8 @@ export default class Reference extends ReferenceBase {
|
|||
* @return {string}
|
||||
* @private
|
||||
*/
|
||||
_getRefKey(): string {
|
||||
return `$${this._database.app.name}$/${
|
||||
_getRefKey() {
|
||||
return `$${this._database.databaseUrl}$/${
|
||||
this.path
|
||||
}$${this._query.queryIdentifier()}`;
|
||||
}
|
||||
|
@ -758,6 +758,7 @@ export default class Reference extends ReferenceBase {
|
|||
path: this.path,
|
||||
key: this._getRefKey(),
|
||||
appName: this._database.app.name,
|
||||
dbURL: this._database.databaseUrl,
|
||||
eventRegistrationKey,
|
||||
};
|
||||
|
||||
|
@ -776,6 +777,7 @@ export default class Reference extends ReferenceBase {
|
|||
path: this.path,
|
||||
key: this._getRefKey(),
|
||||
appName: this._database.app.name,
|
||||
dbURL: this._database.databaseUrl,
|
||||
eventType: `${eventType}$cancelled`,
|
||||
eventRegistrationKey: registrationCancellationKey,
|
||||
listener: _context
|
||||
|
|
|
@ -10,6 +10,7 @@ import ModuleBase from '../../utils/ModuleBase';
|
|||
import { getNativeModule } from '../../utils/native';
|
||||
|
||||
import type App from '../core/app';
|
||||
import firebase from '../core/firebase';
|
||||
|
||||
const NATIVE_EVENTS = [
|
||||
'database_transaction_event',
|
||||
|
@ -26,14 +27,32 @@ export default class Database extends ModuleBase {
|
|||
_offsetRef: Reference;
|
||||
_serverTimeOffset: number;
|
||||
_transactionHandler: TransactionHandler;
|
||||
_serviceUrl: string;
|
||||
|
||||
constructor(app: App, options: Object = {}) {
|
||||
super(app, {
|
||||
events: NATIVE_EVENTS,
|
||||
moduleName: MODULE_NAME,
|
||||
multiApp: true,
|
||||
namespace: NAMESPACE,
|
||||
});
|
||||
constructor(appOrUrl: App | string, options: Object = {}) {
|
||||
let app;
|
||||
let serviceUrl;
|
||||
if (typeof appOrUrl === 'string') {
|
||||
app = firebase.app();
|
||||
serviceUrl = appOrUrl.endsWith('/') ? appOrUrl : `${appOrUrl}/`;
|
||||
} else {
|
||||
app = appOrUrl;
|
||||
serviceUrl = app.options.databaseURL;
|
||||
}
|
||||
|
||||
super(
|
||||
app,
|
||||
{
|
||||
events: NATIVE_EVENTS,
|
||||
moduleName: MODULE_NAME,
|
||||
multiApp: true,
|
||||
hasShards: true,
|
||||
namespace: NAMESPACE,
|
||||
},
|
||||
serviceUrl
|
||||
);
|
||||
|
||||
this._serviceUrl = serviceUrl;
|
||||
this._transactionHandler = new TransactionHandler(this);
|
||||
|
||||
if (options.persistence) {
|
||||
|
@ -83,6 +102,14 @@ export default class Database extends ModuleBase {
|
|||
ref(path: string): Reference {
|
||||
return new Reference(this, path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the database url
|
||||
* @returns {string}
|
||||
*/
|
||||
get databaseUrl(): string {
|
||||
return this._serviceUrl;
|
||||
}
|
||||
}
|
||||
|
||||
export const statics = {
|
||||
|
|
|
@ -15,6 +15,7 @@ export default class Crashlytics extends ModuleBase {
|
|||
super(app, {
|
||||
moduleName: MODULE_NAME,
|
||||
multiApp: false,
|
||||
hasShards: false,
|
||||
namespace: NAMESPACE,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -4,12 +4,11 @@
|
|||
*/
|
||||
import CollectionReference from './CollectionReference';
|
||||
import DocumentSnapshot from './DocumentSnapshot';
|
||||
import FieldPath from './FieldPath';
|
||||
import { mergeFieldPathData } from './utils';
|
||||
import { parseUpdateArgs } from './utils';
|
||||
import { buildNativeMap } from './utils/serialize';
|
||||
import { getAppEventName, SharedEventEmitter } from '../../utils/events';
|
||||
import { getLogger } from '../../utils/log';
|
||||
import { firestoreAutoId, isFunction, isObject, isString } from '../../utils';
|
||||
import { firestoreAutoId, isFunction, isObject } from '../../utils';
|
||||
import { getNativeModule } from '../../utils/native';
|
||||
|
||||
import type Firestore from './';
|
||||
|
@ -50,9 +49,7 @@ export default class DocumentReference {
|
|||
|
||||
get parent(): CollectionReference {
|
||||
const parentPath = this._documentPath.parent();
|
||||
if (!parentPath) {
|
||||
throw new Error('Invalid document path');
|
||||
}
|
||||
// $FlowExpectedError: parentPath can never be null
|
||||
return new CollectionReference(this._firestore, parentPath);
|
||||
}
|
||||
|
||||
|
@ -232,34 +229,7 @@ export default class DocumentReference {
|
|||
}
|
||||
|
||||
update(...args: any[]): Promise<void> {
|
||||
let data = {};
|
||||
if (args.length === 1) {
|
||||
if (!isObject(args[0])) {
|
||||
throw new Error(
|
||||
'DocumentReference.update failed: If using a single argument, it must be an object.'
|
||||
);
|
||||
}
|
||||
// eslint-disable-next-line prefer-destructuring
|
||||
data = args[0];
|
||||
} else if (args.length % 2 === 1) {
|
||||
throw new Error(
|
||||
'DocumentReference.update failed: Must have either a single object argument, or equal numbers of key/value pairs.'
|
||||
);
|
||||
} else {
|
||||
for (let i = 0; i < args.length; i += 2) {
|
||||
const key = args[i];
|
||||
const value = args[i + 1];
|
||||
if (isString(key)) {
|
||||
data[key] = value;
|
||||
} else if (key instanceof FieldPath) {
|
||||
data = mergeFieldPathData(data, key._segments, value);
|
||||
} else {
|
||||
throw new Error(
|
||||
`DocumentReference.update failed: Argument at index ${i} must be a string or FieldPath`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
const data = parseUpdateArgs(args, 'DocumentReference.update');
|
||||
const nativeData = buildNativeMap(data);
|
||||
return getNativeModule(this._firestore).documentUpdate(
|
||||
this.path,
|
||||
|
|
|
@ -14,10 +14,7 @@ export default class Path {
|
|||
}
|
||||
|
||||
get id(): string | null {
|
||||
if (this._parts.length > 0) {
|
||||
return this._parts[this._parts.length - 1];
|
||||
}
|
||||
return null;
|
||||
return this._parts.length > 0 ? this._parts[this._parts.length - 1] : null;
|
||||
}
|
||||
|
||||
get isDocument(): boolean {
|
||||
|
@ -37,11 +34,9 @@ export default class Path {
|
|||
}
|
||||
|
||||
parent(): Path | null {
|
||||
if (this._parts.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Path(this._parts.slice(0, this._parts.length - 1));
|
||||
return this._parts.length > 0
|
||||
? new Path(this._parts.slice(0, this._parts.length - 1))
|
||||
: null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -50,10 +45,6 @@ export default class Path {
|
|||
*/
|
||||
static fromName(name: string): Path {
|
||||
const parts = name.split('/');
|
||||
|
||||
if (parts.length === 0) {
|
||||
return new Path([]);
|
||||
}
|
||||
return new Path(parts);
|
||||
return parts.length === 0 ? new Path([]) : new Path(parts);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,22 +2,20 @@
|
|||
* @flow
|
||||
* Firestore Transaction representation wrapper
|
||||
*/
|
||||
import { mergeFieldPathData } from './utils';
|
||||
import { parseUpdateArgs } from './utils';
|
||||
import { buildNativeMap } from './utils/serialize';
|
||||
|
||||
import type Firestore from './';
|
||||
import type { TransactionMeta } from './TransactionHandler';
|
||||
import type DocumentReference from './DocumentReference';
|
||||
import DocumentSnapshot from './DocumentSnapshot';
|
||||
import { isObject, isString } from '../../utils';
|
||||
import FieldPath from './FieldPath';
|
||||
import { getNativeModule } from '../../utils/native';
|
||||
|
||||
type Command = {
|
||||
type: 'set' | 'update' | 'delete',
|
||||
path: string,
|
||||
data: ?{ [string]: any },
|
||||
options: ?{ merge: boolean },
|
||||
data?: { [string]: any },
|
||||
options?: SetOptions | {},
|
||||
};
|
||||
|
||||
type SetOptions = {
|
||||
|
@ -124,35 +122,7 @@ export default class Transaction {
|
|||
*/
|
||||
update(documentRef: DocumentReference, ...args: Array<any>): Transaction {
|
||||
// todo validate doc ref
|
||||
let data = {};
|
||||
if (args.length === 1) {
|
||||
if (!isObject(args[0])) {
|
||||
throw new Error(
|
||||
'Transaction.update failed: If using a single data argument, it must be an object.'
|
||||
);
|
||||
}
|
||||
|
||||
[data] = args;
|
||||
} else if (args.length % 2 === 1) {
|
||||
throw new Error(
|
||||
'Transaction.update failed: Must have either a single object data argument, or equal numbers of data key/value pairs.'
|
||||
);
|
||||
} else {
|
||||
for (let i = 0; i < args.length; i += 2) {
|
||||
const key = args[i];
|
||||
const value = args[i + 1];
|
||||
if (isString(key)) {
|
||||
data[key] = value;
|
||||
} else if (key instanceof FieldPath) {
|
||||
data = mergeFieldPathData(data, key._segments, value);
|
||||
} else {
|
||||
throw new Error(
|
||||
`Transaction.update failed: Argument at index ${i} must be a string or FieldPath`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const data = parseUpdateArgs(args, 'Transaction.update');
|
||||
this._commandBuffer.push({
|
||||
type: 'update',
|
||||
path: documentRef.path,
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
* Firestore Transaction representation wrapper
|
||||
*/
|
||||
import { getAppEventName, SharedEventEmitter } from '../../utils/events';
|
||||
import { getLogger } from '../../utils/log';
|
||||
import { getNativeModule } from '../../utils/native';
|
||||
import Transaction from './Transaction';
|
||||
import type Firestore from './';
|
||||
|
@ -19,9 +18,9 @@ const generateTransactionId = (): number => transactionId++;
|
|||
|
||||
export type TransactionMeta = {
|
||||
id: number,
|
||||
stack: Array<string>,
|
||||
reject: null | Function,
|
||||
resolve: null | Function,
|
||||
stack: string[],
|
||||
reject?: Function,
|
||||
resolve?: Function,
|
||||
transaction: Transaction,
|
||||
updateFunction: (transaction: Transaction) => Promise<any>,
|
||||
};
|
||||
|
@ -37,7 +36,12 @@ type TransactionEvent = {
|
|||
*/
|
||||
export default class TransactionHandler {
|
||||
_firestore: Firestore;
|
||||
_pending: { [number]: TransactionMeta };
|
||||
_pending: {
|
||||
[number]: {
|
||||
meta: TransactionMeta,
|
||||
transaction: Transaction,
|
||||
},
|
||||
};
|
||||
|
||||
constructor(firestore: Firestore) {
|
||||
this._pending = {};
|
||||
|
@ -62,10 +66,9 @@ export default class TransactionHandler {
|
|||
updateFunction: (transaction: Transaction) => Promise<any>
|
||||
): Promise<any> {
|
||||
const id = generateTransactionId();
|
||||
const meta = {
|
||||
// $FlowExpectedError: Transaction has to be populated
|
||||
const meta: TransactionMeta = {
|
||||
id,
|
||||
reject: null,
|
||||
resolve: null,
|
||||
updateFunction,
|
||||
stack: new Error().stack
|
||||
.split('\n')
|
||||
|
@ -73,8 +76,10 @@ export default class TransactionHandler {
|
|||
.join('\n'),
|
||||
};
|
||||
|
||||
meta.transaction = new Transaction(this._firestore, meta);
|
||||
this._pending[id] = meta;
|
||||
this._pending[id] = {
|
||||
meta,
|
||||
transaction: new Transaction(this._firestore, meta),
|
||||
};
|
||||
|
||||
// deferred promise
|
||||
return new Promise((resolve, reject) => {
|
||||
|
@ -97,9 +102,7 @@ export default class TransactionHandler {
|
|||
* @private
|
||||
*/
|
||||
_remove(id) {
|
||||
// todo confirm pending arg no longer needed
|
||||
getNativeModule(this._firestore).transactionDispose(id);
|
||||
// TODO may need delaying to next event loop
|
||||
delete this._pending[id];
|
||||
}
|
||||
|
||||
|
@ -118,19 +121,17 @@ export default class TransactionHandler {
|
|||
* @private
|
||||
*/
|
||||
_handleTransactionEvent(event: TransactionEvent) {
|
||||
// eslint-disable-next-line default-case
|
||||
switch (event.type) {
|
||||
case 'update':
|
||||
return this._handleUpdate(event);
|
||||
this._handleUpdate(event);
|
||||
break;
|
||||
case 'error':
|
||||
return this._handleError(event);
|
||||
this._handleError(event);
|
||||
break;
|
||||
case 'complete':
|
||||
return this._handleComplete(event);
|
||||
default:
|
||||
getLogger(this._firestore).warn(
|
||||
`Unknown transaction event type: '${event.type}'`,
|
||||
event
|
||||
);
|
||||
return undefined;
|
||||
this._handleComplete(event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -145,7 +146,8 @@ export default class TransactionHandler {
|
|||
// abort if no longer exists js side
|
||||
if (!this._pending[id]) return this._remove(id);
|
||||
|
||||
const { updateFunction, transaction, reject } = this._pending[id];
|
||||
const { meta, transaction } = this._pending[id];
|
||||
const { updateFunction, reject } = meta;
|
||||
|
||||
// clear any saved state from previous transaction runs
|
||||
transaction._prepare();
|
||||
|
@ -178,6 +180,7 @@ export default class TransactionHandler {
|
|||
// update is failed when either the users updateFunction
|
||||
// throws an error or rejects a promise
|
||||
if (updateFailed) {
|
||||
// $FlowExpectedError: Reject will always be present
|
||||
return reject(finalError);
|
||||
}
|
||||
|
||||
|
@ -201,17 +204,20 @@ export default class TransactionHandler {
|
|||
*/
|
||||
_handleError(event: TransactionEvent) {
|
||||
const { id, error } = event;
|
||||
const meta = this._pending[id];
|
||||
const { meta } = this._pending[id];
|
||||
|
||||
if (meta) {
|
||||
if (meta && error) {
|
||||
const { code, message } = error;
|
||||
// build a JS error and replace its stack
|
||||
// with the captured one at start of transaction
|
||||
// so it's actually relevant to the user
|
||||
const errorWithStack = new Error(message);
|
||||
// $FlowExpectedError: code is needed for Firebase errors
|
||||
errorWithStack.code = code;
|
||||
// $FlowExpectedError: stack should be a stack trace
|
||||
errorWithStack.stack = meta.stack;
|
||||
|
||||
// $FlowExpectedError: Reject will always be present
|
||||
meta.reject(errorWithStack);
|
||||
}
|
||||
}
|
||||
|
@ -224,10 +230,11 @@ export default class TransactionHandler {
|
|||
*/
|
||||
_handleComplete(event: TransactionEvent) {
|
||||
const { id } = event;
|
||||
const meta = this._pending[id];
|
||||
const { meta, transaction } = this._pending[id];
|
||||
|
||||
if (meta) {
|
||||
const pendingResult = meta.transaction._pendingResult;
|
||||
const pendingResult = transaction._pendingResult;
|
||||
// $FlowExpectedError: Resolve will always be present
|
||||
meta.resolve(pendingResult);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,10 +2,8 @@
|
|||
* @flow
|
||||
* WriteBatch representation wrapper
|
||||
*/
|
||||
import FieldPath from './FieldPath';
|
||||
import { mergeFieldPathData } from './utils';
|
||||
import { parseUpdateArgs } from './utils';
|
||||
import { buildNativeMap } from './utils/serialize';
|
||||
import { isObject, isString } from '../../utils';
|
||||
import { getNativeModule } from '../../utils/native';
|
||||
|
||||
import type DocumentReference from './DocumentReference';
|
||||
|
@ -66,38 +64,9 @@ export default class WriteBatch {
|
|||
update(docRef: DocumentReference, ...args: any[]): WriteBatch {
|
||||
// TODO: Validation
|
||||
// validate.isDocumentReference('docRef', docRef);
|
||||
let data = {};
|
||||
if (args.length === 1) {
|
||||
if (!isObject(args[0])) {
|
||||
throw new Error(
|
||||
'WriteBatch.update failed: If using two arguments, the second must be an object.'
|
||||
);
|
||||
}
|
||||
// eslint-disable-next-line prefer-destructuring
|
||||
data = args[0];
|
||||
} else if (args.length % 2 === 1) {
|
||||
throw new Error(
|
||||
'WriteBatch.update failed: Must have a document reference, followed by either a single object argument, or equal numbers of key/value pairs.'
|
||||
);
|
||||
} else {
|
||||
for (let i = 0; i < args.length; i += 2) {
|
||||
const key = args[i];
|
||||
const value = args[i + 1];
|
||||
if (isString(key)) {
|
||||
data[key] = value;
|
||||
} else if (key instanceof FieldPath) {
|
||||
data = mergeFieldPathData(data, key._segments, value);
|
||||
} else {
|
||||
throw new Error(
|
||||
`WriteBatch.update failed: Argument at index ${i} must be a string or FieldPath`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const nativeData = buildNativeMap(data);
|
||||
const data = parseUpdateArgs(args, 'WriteBatch.update');
|
||||
this._writes.push({
|
||||
data: nativeData,
|
||||
data: buildNativeMap(data),
|
||||
path: docRef.path,
|
||||
type: 'UPDATE',
|
||||
});
|
||||
|
|
|
@ -58,6 +58,7 @@ export default class Firestore extends ModuleBase {
|
|||
events: NATIVE_EVENTS,
|
||||
moduleName: MODULE_NAME,
|
||||
multiApp: true,
|
||||
hasShards: false,
|
||||
namespace: NAMESPACE,
|
||||
});
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
/**
|
||||
* @flow
|
||||
*/
|
||||
import FieldPath from '../FieldPath';
|
||||
import { isObject, isString } from '../../../utils';
|
||||
|
||||
const buildFieldPathData = (segments: string[], value: any): Object => {
|
||||
if (segments.length === 1) {
|
||||
|
@ -39,3 +41,34 @@ export const mergeFieldPathData = (
|
|||
[segments[0]]: buildFieldPathData(segments.slice(1), value),
|
||||
};
|
||||
};
|
||||
|
||||
export const parseUpdateArgs = (args: any[], methodName: string) => {
|
||||
let data = {};
|
||||
if (args.length === 1) {
|
||||
if (!isObject(args[0])) {
|
||||
throw new Error(
|
||||
`${methodName} failed: If using a single update argument, it must be an object.`
|
||||
);
|
||||
}
|
||||
[data] = args;
|
||||
} else if (args.length % 2 === 1) {
|
||||
throw new Error(
|
||||
`${methodName} failed: The update arguments must be either a single object argument, or equal numbers of key/value pairs.`
|
||||
);
|
||||
} else {
|
||||
for (let i = 0; i < args.length; i += 2) {
|
||||
const key = args[i];
|
||||
const value = args[i + 1];
|
||||
if (isString(key)) {
|
||||
data[key] = value;
|
||||
} else if (key instanceof FieldPath) {
|
||||
data = mergeFieldPathData(data, key._segments, value);
|
||||
} else {
|
||||
throw new Error(
|
||||
`${methodName} failed: Argument at index ${i} must be a string or FieldPath`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
return data;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
/**
|
||||
* @flow
|
||||
* Instance ID representation wrapper
|
||||
*/
|
||||
import ModuleBase from '../../utils/ModuleBase';
|
||||
import { getNativeModule } from '../../utils/native';
|
||||
|
||||
import type App from '../core/app';
|
||||
|
||||
export const MODULE_NAME = 'RNFirebaseInstanceId';
|
||||
export const NAMESPACE = 'instanceid';
|
||||
|
||||
export default class InstanceId extends ModuleBase {
|
||||
constructor(app: App) {
|
||||
super(app, {
|
||||
hasShards: false,
|
||||
moduleName: MODULE_NAME,
|
||||
multiApp: false,
|
||||
namespace: NAMESPACE,
|
||||
});
|
||||
}
|
||||
|
||||
delete(): Promise<void> {
|
||||
return getNativeModule(this).delete();
|
||||
}
|
||||
|
||||
get(): Promise<string> {
|
||||
return getNativeModule(this).get();
|
||||
}
|
||||
}
|
||||
|
||||
export const statics = {};
|
|
@ -0,0 +1,69 @@
|
|||
/**
|
||||
* @flow
|
||||
* AndroidInvitation representation wrapper
|
||||
*/
|
||||
import type Invitation from './Invitation';
|
||||
import type { NativeAndroidInvitation } from './types';
|
||||
|
||||
export default class AndroidInvitation {
|
||||
_additionalReferralParameters: { [string]: string } | void;
|
||||
_emailHtmlContent: string | void;
|
||||
_emailSubject: string | void;
|
||||
_googleAnalyticsTrackingId: string | void;
|
||||
_invitation: Invitation;
|
||||
|
||||
constructor(invitation: Invitation) {
|
||||
this._invitation = invitation;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param additionalReferralParameters
|
||||
* @returns {Invitation}
|
||||
*/
|
||||
setAdditionalReferralParameters(additionalReferralParameters: {
|
||||
[string]: string,
|
||||
}): Invitation {
|
||||
this._additionalReferralParameters = additionalReferralParameters;
|
||||
return this._invitation;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param emailHtmlContent
|
||||
* @returns {Invitation}
|
||||
*/
|
||||
setEmailHtmlContent(emailHtmlContent: string): Invitation {
|
||||
this._emailHtmlContent = emailHtmlContent;
|
||||
return this._invitation;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param emailSubject
|
||||
* @returns {Invitation}
|
||||
*/
|
||||
setEmailSubject(emailSubject: string): Invitation {
|
||||
this._emailSubject = emailSubject;
|
||||
return this._invitation;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param googleAnalyticsTrackingId
|
||||
* @returns {Invitation}
|
||||
*/
|
||||
setGoogleAnalyticsTrackingId(googleAnalyticsTrackingId: string): Invitation {
|
||||
this._googleAnalyticsTrackingId = googleAnalyticsTrackingId;
|
||||
return this._invitation;
|
||||
}
|
||||
|
||||
build(): NativeAndroidInvitation {
|
||||
return {
|
||||
additionalReferralParameters: this._additionalReferralParameters,
|
||||
emailHtmlContent: this._emailHtmlContent,
|
||||
emailSubject: this._emailSubject,
|
||||
googleAnalyticsTrackingId: this._googleAnalyticsTrackingId,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
/**
|
||||
* @flow
|
||||
* Invitation representation wrapper
|
||||
*/
|
||||
import { Platform } from 'react-native';
|
||||
import AndroidInvitation from './AndroidInvitation';
|
||||
|
||||
import type { NativeInvitation } from './types';
|
||||
|
||||
export default class Invitation {
|
||||
_android: AndroidInvitation;
|
||||
_androidClientId: string | void;
|
||||
_androidMinimumVersionCode: number | void;
|
||||
_callToActionText: string | void;
|
||||
_customImage: string | void;
|
||||
_deepLink: string | void;
|
||||
_iosClientId: string | void;
|
||||
_message: string;
|
||||
_title: string;
|
||||
|
||||
constructor(title: string, message: string) {
|
||||
this._android = new AndroidInvitation(this);
|
||||
this._message = message;
|
||||
this._title = title;
|
||||
}
|
||||
|
||||
get android(): AndroidInvitation {
|
||||
return this._android;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param androidClientId
|
||||
* @returns {Invitation}
|
||||
*/
|
||||
setAndroidClientId(androidClientId: string): Invitation {
|
||||
this._androidClientId = androidClientId;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param androidMinimumVersionCode
|
||||
* @returns {Invitation}
|
||||
*/
|
||||
setAndroidMinimumVersionCode(androidMinimumVersionCode: number): Invitation {
|
||||
this._androidMinimumVersionCode = androidMinimumVersionCode;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param callToActionText
|
||||
* @returns {Invitation}
|
||||
*/
|
||||
setCallToActionText(callToActionText: string): Invitation {
|
||||
this._callToActionText = callToActionText;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param customImage
|
||||
* @returns {Invitation}
|
||||
*/
|
||||
setCustomImage(customImage: string): Invitation {
|
||||
this._customImage = customImage;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param deepLink
|
||||
* @returns {Invitation}
|
||||
*/
|
||||
setDeepLink(deepLink: string): Invitation {
|
||||
this._deepLink = deepLink;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param iosClientId
|
||||
* @returns {Invitation}
|
||||
*/
|
||||
setIOSClientId(iosClientId: string): Invitation {
|
||||
this._iosClientId = iosClientId;
|
||||
return this;
|
||||
}
|
||||
|
||||
build(): NativeInvitation {
|
||||
if (!this._message) {
|
||||
throw new Error('Invitation: Missing required `message` property');
|
||||
} else if (!this._title) {
|
||||
throw new Error('Invitation: Missing required `title` property');
|
||||
}
|
||||
|
||||
return {
|
||||
android: Platform.OS === 'android' ? this._android.build() : undefined,
|
||||
androidClientId: this._androidClientId,
|
||||
androidMinimumVersionCode: this._androidMinimumVersionCode,
|
||||
callToActionText: this._callToActionText,
|
||||
customImage: this._customImage,
|
||||
deepLink: this._deepLink,
|
||||
iosClientId: this._iosClientId,
|
||||
message: this._message,
|
||||
title: this._title,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
/**
|
||||
* @flow
|
||||
* Invites representation wrapper
|
||||
*/
|
||||
import { SharedEventEmitter } from '../../utils/events';
|
||||
import { getLogger } from '../../utils/log';
|
||||
import ModuleBase from '../../utils/ModuleBase';
|
||||
import { getNativeModule } from '../../utils/native';
|
||||
import Invitation from './Invitation';
|
||||
|
||||
import type App from '../core/app';
|
||||
|
||||
export const MODULE_NAME = 'RNFirebaseInvites';
|
||||
export const NAMESPACE = 'invites';
|
||||
const NATIVE_EVENTS = ['invites_invitation_received'];
|
||||
|
||||
type InvitationOpen = {
|
||||
deepLink: string,
|
||||
invitationId: string,
|
||||
};
|
||||
|
||||
export default class Invites extends ModuleBase {
|
||||
constructor(app: App) {
|
||||
super(app, {
|
||||
events: NATIVE_EVENTS,
|
||||
hasShards: false,
|
||||
moduleName: MODULE_NAME,
|
||||
multiApp: false,
|
||||
namespace: NAMESPACE,
|
||||
});
|
||||
|
||||
SharedEventEmitter.addListener(
|
||||
// sub to internal native event - this fans out to
|
||||
// public event name: onMessage
|
||||
'invites_invitation_received',
|
||||
(invitation: InvitationOpen) => {
|
||||
SharedEventEmitter.emit('onInvitation', invitation);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the invitation that triggered application open
|
||||
* @returns {Promise.<Object>}
|
||||
*/
|
||||
getInitialInvitation(): Promise<string> {
|
||||
return getNativeModule(this).getInitialInvitation();
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to invites
|
||||
* @param listener
|
||||
* @returns {Function}
|
||||
*/
|
||||
onInvitation(listener: InvitationOpen => any) {
|
||||
getLogger(this).info('Creating onInvitation listener');
|
||||
|
||||
SharedEventEmitter.addListener('onInvitation', listener);
|
||||
|
||||
return () => {
|
||||
getLogger(this).info('Removing onInvitation listener');
|
||||
SharedEventEmitter.removeListener('onInvitation', listener);
|
||||
};
|
||||
}
|
||||
|
||||
sendInvitation(invitation: Invitation): Promise<string[]> {
|
||||
if (!(invitation instanceof Invitation)) {
|
||||
throw new Error(
|
||||
`Invites:sendInvitation expects an 'Invitation' but got type ${typeof invitation}`
|
||||
);
|
||||
}
|
||||
return getNativeModule(this).sendInvitation(invitation.build());
|
||||
}
|
||||
}
|
||||
|
||||
export const statics = {
|
||||
Invitation,
|
||||
};
|
|
@ -0,0 +1,21 @@
|
|||
/**
|
||||
* @flow
|
||||
*/
|
||||
export type NativeAndroidInvitation = {|
|
||||
additionalReferralParameters?: { [string]: string },
|
||||
emailHtmlContent?: string,
|
||||
emailSubject?: string,
|
||||
googleAnalyticsTrackingId?: string,
|
||||
|};
|
||||
|
||||
export type NativeInvitation = {|
|
||||
android?: NativeAndroidInvitation,
|
||||
androidClientId?: string,
|
||||
androidMinimumVersionCode?: number,
|
||||
callToActionText?: string,
|
||||
customImage?: string,
|
||||
deepLink?: string,
|
||||
iosClientId?: string,
|
||||
message: string,
|
||||
title: string,
|
||||
|};
|
|
@ -0,0 +1,79 @@
|
|||
/**
|
||||
* @flow
|
||||
* AnalyticsParameters representation wrapper
|
||||
*/
|
||||
import type DynamicLink from './DynamicLink';
|
||||
import type { NativeAnalyticsParameters } from './types';
|
||||
|
||||
export default class AnalyticsParameters {
|
||||
_campaign: string | void;
|
||||
_content: string | void;
|
||||
_link: DynamicLink;
|
||||
_medium: string | void;
|
||||
_source: string | void;
|
||||
_term: string | void;
|
||||
|
||||
constructor(link: DynamicLink) {
|
||||
this._link = link;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param campaign
|
||||
* @returns {DynamicLink}
|
||||
*/
|
||||
setCampaign(campaign: string): DynamicLink {
|
||||
this._campaign = campaign;
|
||||
return this._link;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param content
|
||||
* @returns {DynamicLink}
|
||||
*/
|
||||
setContent(content: string): DynamicLink {
|
||||
this._content = content;
|
||||
return this._link;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param medium
|
||||
* @returns {DynamicLink}
|
||||
*/
|
||||
setMedium(medium: string): DynamicLink {
|
||||
this._medium = medium;
|
||||
return this._link;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param source
|
||||
* @returns {DynamicLink}
|
||||
*/
|
||||
setSource(source: string): DynamicLink {
|
||||
this._source = source;
|
||||
return this._link;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param term
|
||||
* @returns {DynamicLink}
|
||||
*/
|
||||
setTerm(term: string): DynamicLink {
|
||||
this._term = term;
|
||||
return this._link;
|
||||
}
|
||||
|
||||
build(): NativeAnalyticsParameters {
|
||||
return {
|
||||
campaign: this._campaign,
|
||||
content: this._content,
|
||||
medium: this._medium,
|
||||
source: this._source,
|
||||
term: this._term,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
/**
|
||||
* @flow
|
||||
* AndroidParameters representation wrapper
|
||||
*/
|
||||
import type DynamicLink from './DynamicLink';
|
||||
import type { NativeAndroidParameters } from './types';
|
||||
|
||||
export default class AndroidParameters {
|
||||
_fallbackUrl: string | void;
|
||||
_link: DynamicLink;
|
||||
_minimumVersion: number | void;
|
||||
_packageName: string | void;
|
||||
|
||||
constructor(link: DynamicLink) {
|
||||
this._link = link;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param fallbackUrl
|
||||
* @returns {DynamicLink}
|
||||
*/
|
||||
setFallbackUrl(fallbackUrl: string): DynamicLink {
|
||||
this._fallbackUrl = fallbackUrl;
|
||||
return this._link;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param minimumVersion
|
||||
* @returns {DynamicLink}
|
||||
*/
|
||||
setMinimumVersion(minimumVersion: number): DynamicLink {
|
||||
this._minimumVersion = minimumVersion;
|
||||
return this._link;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param packageName
|
||||
* @returns {DynamicLink}
|
||||
*/
|
||||
setPackageName(packageName: string): DynamicLink {
|
||||
this._packageName = packageName;
|
||||
return this._link;
|
||||
}
|
||||
|
||||
build(): NativeAndroidParameters {
|
||||
if ((this._fallbackUrl || this._minimumVersion) && !this._packageName) {
|
||||
throw new Error(
|
||||
'AndroidParameters: Missing required `packageName` property'
|
||||
);
|
||||
}
|
||||
return {
|
||||
fallbackUrl: this._fallbackUrl,
|
||||
minimumVersion: this._minimumVersion,
|
||||
packageName: this._packageName,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
/**
|
||||
* @flow
|
||||
* DynamicLink representation wrapper
|
||||
*/
|
||||
import AnalyticsParameters from './AnalyticsParameters';
|
||||
import AndroidParameters from './AndroidParameters';
|
||||
import IOSParameters from './IOSParameters';
|
||||
import ITunesParameters from './ITunesParameters';
|
||||
import NavigationParameters from './NavigationParameters';
|
||||
import SocialParameters from './SocialParameters';
|
||||
|
||||
import type { NativeDynamicLink } from './types';
|
||||
|
||||
export default class DynamicLink {
|
||||
_analytics: AnalyticsParameters;
|
||||
_android: AndroidParameters;
|
||||
_dynamicLinkDomain: string;
|
||||
_ios: IOSParameters;
|
||||
_itunes: ITunesParameters;
|
||||
_link: string;
|
||||
_navigation: NavigationParameters;
|
||||
_social: SocialParameters;
|
||||
|
||||
constructor(link: string, dynamicLinkDomain: string) {
|
||||
this._analytics = new AnalyticsParameters(this);
|
||||
this._android = new AndroidParameters(this);
|
||||
this._dynamicLinkDomain = dynamicLinkDomain;
|
||||
this._ios = new IOSParameters(this);
|
||||
this._itunes = new ITunesParameters(this);
|
||||
this._link = link;
|
||||
this._navigation = new NavigationParameters(this);
|
||||
this._social = new SocialParameters(this);
|
||||
}
|
||||
|
||||
get analytics(): AnalyticsParameters {
|
||||
return this._analytics;
|
||||
}
|
||||
|
||||
get android(): AndroidParameters {
|
||||
return this._android;
|
||||
}
|
||||
|
||||
get ios(): IOSParameters {
|
||||
return this._ios;
|
||||
}
|
||||
|
||||
get itunes(): ITunesParameters {
|
||||
return this._itunes;
|
||||
}
|
||||
|
||||
get navigation(): NavigationParameters {
|
||||
return this._navigation;
|
||||
}
|
||||
|
||||
get social(): SocialParameters {
|
||||
return this._social;
|
||||
}
|
||||
|
||||
build(): NativeDynamicLink {
|
||||
if (!this._link) {
|
||||
throw new Error('DynamicLink: Missing required `link` property');
|
||||
} else if (!this._dynamicLinkDomain) {
|
||||
throw new Error(
|
||||
'DynamicLink: Missing required `dynamicLinkDomain` property'
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
analytics: this._analytics.build(),
|
||||
android: this._android.build(),
|
||||
dynamicLinkDomain: this._dynamicLinkDomain,
|
||||
ios: this._ios.build(),
|
||||
itunes: this._itunes.build(),
|
||||
link: this._link,
|
||||
navigation: this._navigation.build(),
|
||||
social: this._social.build(),
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
/**
|
||||
* @flow
|
||||
* IOSParameters representation wrapper
|
||||
*/
|
||||
import type DynamicLink from './DynamicLink';
|
||||
import type { NativeIOSParameters } from './types';
|
||||
|
||||
export default class IOSParameters {
|
||||
_appStoreId: string | void;
|
||||
_bundleId: string | void;
|
||||
_customScheme: string | void;
|
||||
_fallbackUrl: string | void;
|
||||
_iPadBundleId: string | void;
|
||||
_iPadFallbackUrl: string | void;
|
||||
_link: DynamicLink;
|
||||
_minimumVersion: string | void;
|
||||
|
||||
constructor(link: DynamicLink) {
|
||||
this._link = link;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param appStoreId
|
||||
* @returns {DynamicLink}
|
||||
*/
|
||||
setAppStoreId(appStoreId: string): DynamicLink {
|
||||
this._appStoreId = appStoreId;
|
||||
return this._link;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param bundleId
|
||||
* @returns {DynamicLink}
|
||||
*/
|
||||
setBundleId(bundleId: string): DynamicLink {
|
||||
this._bundleId = bundleId;
|
||||
return this._link;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param customScheme
|
||||
* @returns {DynamicLink}
|
||||
*/
|
||||
setCustomScheme(customScheme: string): DynamicLink {
|
||||
this._customScheme = customScheme;
|
||||
return this._link;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param fallbackUrl
|
||||
* @returns {DynamicLink}
|
||||
*/
|
||||
setFallbackUrl(fallbackUrl: string): DynamicLink {
|
||||
this._fallbackUrl = fallbackUrl;
|
||||
return this._link;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param iPadBundleId
|
||||
* @returns {DynamicLink}
|
||||
*/
|
||||
setIPadBundleId(iPadBundleId: string): DynamicLink {
|
||||
this._iPadBundleId = iPadBundleId;
|
||||
return this._link;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param iPadFallbackUrl
|
||||
* @returns {DynamicLink}
|
||||
*/
|
||||
setIPadFallbackUrl(iPadFallbackUrl: string): DynamicLink {
|
||||
this._iPadFallbackUrl = iPadFallbackUrl;
|
||||
return this._link;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param minimumVersion
|
||||
* @returns {DynamicLink}
|
||||
*/
|
||||
setMinimumVersion(minimumVersion: string): DynamicLink {
|
||||
this._minimumVersion = minimumVersion;
|
||||
return this._link;
|
||||
}
|
||||
|
||||
build(): NativeIOSParameters {
|
||||
if (
|
||||
(this._appStoreId ||
|
||||
this._customScheme ||
|
||||
this._fallbackUrl ||
|
||||
this._iPadBundleId ||
|
||||
this._iPadFallbackUrl ||
|
||||
this._minimumVersion) &&
|
||||
!this._bundleId
|
||||
) {
|
||||
throw new Error('IOSParameters: Missing required `bundleId` property');
|
||||
}
|
||||
return {
|
||||
appStoreId: this._appStoreId,
|
||||
bundleId: this._bundleId,
|
||||
customScheme: this._customScheme,
|
||||
fallbackUrl: this._fallbackUrl,
|
||||
iPadBundleId: this._iPadBundleId,
|
||||
iPadFallbackUrl: this._iPadFallbackUrl,
|
||||
minimumVersion: this._minimumVersion,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/**
|
||||
* @flow
|
||||
* ITunesParameters representation wrapper
|
||||
*/
|
||||
import type DynamicLink from './DynamicLink';
|
||||
import type { NativeITunesParameters } from './types';
|
||||
|
||||
export default class ITunesParameters {
|
||||
_affiliateToken: string | void;
|
||||
_campaignToken: string | void;
|
||||
_link: DynamicLink;
|
||||
_providerToken: string | void;
|
||||
|
||||
constructor(link: DynamicLink) {
|
||||
this._link = link;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param affiliateToken
|
||||
* @returns {DynamicLink}
|
||||
*/
|
||||
setAffiliateToken(affiliateToken: string): DynamicLink {
|
||||
this._affiliateToken = affiliateToken;
|
||||
return this._link;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param campaignToken
|
||||
* @returns {DynamicLink}
|
||||
*/
|
||||
setCampaignToken(campaignToken: string): DynamicLink {
|
||||
this._campaignToken = campaignToken;
|
||||
return this._link;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param providerToken
|
||||
* @returns {DynamicLink}
|
||||
*/
|
||||
setProviderToken(providerToken: string): DynamicLink {
|
||||
this._providerToken = providerToken;
|
||||
return this._link;
|
||||
}
|
||||
|
||||
build(): NativeITunesParameters {
|
||||
return {
|
||||
affiliateToken: this._affiliateToken,
|
||||
campaignToken: this._campaignToken,
|
||||
providerToken: this._providerToken,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/**
|
||||
* @flow
|
||||
* NavigationParameters representation wrapper
|
||||
*/
|
||||
import type DynamicLink from './DynamicLink';
|
||||
import type { NativeNavigationParameters } from './types';
|
||||
|
||||
export default class NavigationParameters {
|
||||
_forcedRedirectEnabled: string | void;
|
||||
_link: DynamicLink;
|
||||
|
||||
constructor(link: DynamicLink) {
|
||||
this._link = link;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param forcedRedirectEnabled
|
||||
* @returns {DynamicLink}
|
||||
*/
|
||||
setForcedRedirectEnabled(forcedRedirectEnabled: string): DynamicLink {
|
||||
this._forcedRedirectEnabled = forcedRedirectEnabled;
|
||||
return this._link;
|
||||
}
|
||||
|
||||
build(): NativeNavigationParameters {
|
||||
return {
|
||||
forcedRedirectEnabled: this._forcedRedirectEnabled,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/**
|
||||
* @flow
|
||||
* SocialParameters representation wrapper
|
||||
*/
|
||||
import type DynamicLink from './DynamicLink';
|
||||
import type { NativeSocialParameters } from './types';
|
||||
|
||||
export default class SocialParameters {
|
||||
_descriptionText: string | void;
|
||||
_imageUrl: string | void;
|
||||
_link: DynamicLink;
|
||||
_title: string | void;
|
||||
|
||||
constructor(link: DynamicLink) {
|
||||
this._link = link;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param descriptionText
|
||||
* @returns {DynamicLink}
|
||||
*/
|
||||
setDescriptionText(descriptionText: string): DynamicLink {
|
||||
this._descriptionText = descriptionText;
|
||||
return this._link;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param imageUrl
|
||||
* @returns {DynamicLink}
|
||||
*/
|
||||
setImageUrl(imageUrl: string): DynamicLink {
|
||||
this._imageUrl = imageUrl;
|
||||
return this._link;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param title
|
||||
* @returns {DynamicLink}
|
||||
*/
|
||||
setTitle(title: string): DynamicLink {
|
||||
this._title = title;
|
||||
return this._link;
|
||||
}
|
||||
|
||||
build(): NativeSocialParameters {
|
||||
return {
|
||||
descriptionText: this._descriptionText,
|
||||
imageUrl: this._imageUrl,
|
||||
title: this._title,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -2,75 +2,19 @@
|
|||
* @flow
|
||||
* Dynamic Links representation wrapper
|
||||
*/
|
||||
import DynamicLink from './DynamicLink';
|
||||
import { SharedEventEmitter } from '../../utils/events';
|
||||
import { getLogger } from '../../utils/log';
|
||||
import ModuleBase from '../../utils/ModuleBase';
|
||||
import { areObjectKeysContainedInOther, isObject, isString } from '../../utils';
|
||||
import { getNativeModule } from '../../utils/native';
|
||||
|
||||
import type App from '../core/app';
|
||||
|
||||
const EVENT_TYPE = {
|
||||
Link: 'dynamic_link_received',
|
||||
};
|
||||
|
||||
const NATIVE_EVENTS = [EVENT_TYPE.Link];
|
||||
const NATIVE_EVENTS = ['links_link_received'];
|
||||
|
||||
export const MODULE_NAME = 'RNFirebaseLinks';
|
||||
export const NAMESPACE = 'links';
|
||||
|
||||
function validateParameters(parameters: Object): void {
|
||||
const suportedParametersObject = {
|
||||
dynamicLinkDomain: 'string',
|
||||
link: 'string',
|
||||
androidInfo: {
|
||||
androidPackageName: 'string',
|
||||
androidFallbackLink: 'string',
|
||||
androidMinPackageVersionCode: 'string',
|
||||
androidLink: 'string',
|
||||
},
|
||||
iosInfo: {
|
||||
iosBundleId: 'string',
|
||||
iosFallbackLink: 'string',
|
||||
iosCustomScheme: 'string',
|
||||
iosIpadFallbackLink: 'string',
|
||||
iosIpadBundleId: 'string',
|
||||
iosAppStoreId: 'string',
|
||||
},
|
||||
socialMetaTagInfo: {
|
||||
socialTitle: 'string',
|
||||
socialDescription: 'string',
|
||||
socialImageLink: 'string',
|
||||
},
|
||||
suffix: {
|
||||
option: 'string',
|
||||
},
|
||||
};
|
||||
if (!areObjectKeysContainedInOther(parameters, suportedParametersObject)) {
|
||||
throw new Error('Invalid Parameters.');
|
||||
}
|
||||
}
|
||||
|
||||
function checkForMandatoryParameters(parameters: Object): void {
|
||||
if (!isString(parameters.dynamicLinkDomain)) {
|
||||
throw new Error('No dynamicLinkDomain was specified.');
|
||||
}
|
||||
if (!isString(parameters.link)) {
|
||||
throw new Error('No link was specified.');
|
||||
}
|
||||
if (
|
||||
isObject(parameters.androidInfo) &&
|
||||
!isString(parameters.androidInfo.androidPackageName)
|
||||
) {
|
||||
throw new Error('No androidPackageName was specified.');
|
||||
}
|
||||
if (
|
||||
isObject(parameters.iosInfo) &&
|
||||
!isString(parameters.iosInfo.iosBundleId)
|
||||
) {
|
||||
throw new Error('No iosBundleId was specified.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @class Links
|
||||
*/
|
||||
|
@ -80,12 +24,49 @@ export default class Links extends ModuleBase {
|
|||
events: NATIVE_EVENTS,
|
||||
moduleName: MODULE_NAME,
|
||||
multiApp: false,
|
||||
hasShards: false,
|
||||
namespace: NAMESPACE,
|
||||
});
|
||||
|
||||
SharedEventEmitter.addListener(
|
||||
// sub to internal native event - this fans out to
|
||||
// public event name: onMessage
|
||||
'links_link_received',
|
||||
(link: string) => {
|
||||
SharedEventEmitter.emit('onLink', link);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
get EVENT_TYPE(): Object {
|
||||
return EVENT_TYPE;
|
||||
/**
|
||||
* Create long Dynamic Link from parameters
|
||||
* @param parameters
|
||||
* @returns {Promise.<String>}
|
||||
*/
|
||||
createDynamicLink(link: DynamicLink): Promise<string> {
|
||||
if (!(link instanceof DynamicLink)) {
|
||||
throw new Error(
|
||||
`Links:createDynamicLink expects a 'DynamicLink' but got type ${typeof link}`
|
||||
);
|
||||
}
|
||||
return getNativeModule(this).createDynamicLink(link.build());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create short Dynamic Link from parameters
|
||||
* @param parameters
|
||||
* @returns {Promise.<String>}
|
||||
*/
|
||||
createShortDynamicLink(
|
||||
link: DynamicLink,
|
||||
type?: 'SHORT' | 'UNGUESSABLE'
|
||||
): Promise<String> {
|
||||
if (!(link instanceof DynamicLink)) {
|
||||
throw new Error(
|
||||
`Links:createShortDynamicLink expects a 'DynamicLink' but got type ${typeof link}`
|
||||
);
|
||||
}
|
||||
return getNativeModule(this).createShortDynamicLink(link.build(), type);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -101,45 +82,16 @@ export default class Links extends ModuleBase {
|
|||
* @param listener
|
||||
* @returns {Function}
|
||||
*/
|
||||
onLink(listener: Function): () => any {
|
||||
const rnListener = SharedEventEmitter.addListener(
|
||||
EVENT_TYPE.Link,
|
||||
listener
|
||||
);
|
||||
return () => rnListener.remove();
|
||||
}
|
||||
onLink(listener: string => any): () => any {
|
||||
getLogger(this).info('Creating onLink listener');
|
||||
|
||||
/**
|
||||
* Create long Dynamic Link from parameters
|
||||
* @param parameters
|
||||
* @returns {Promise.<String>}
|
||||
*/
|
||||
createDynamicLink(parameters: Object = {}): Promise<string> {
|
||||
try {
|
||||
checkForMandatoryParameters(parameters);
|
||||
validateParameters(parameters);
|
||||
return getNativeModule(this).createDynamicLink(parameters);
|
||||
} catch (error) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
}
|
||||
SharedEventEmitter.addListener('onLink', listener);
|
||||
|
||||
/**
|
||||
* Create short Dynamic Link from parameters
|
||||
* @param parameters
|
||||
* @returns {Promise.<String>}
|
||||
*/
|
||||
createShortDynamicLink(parameters: Object = {}): Promise<String> {
|
||||
try {
|
||||
checkForMandatoryParameters(parameters);
|
||||
validateParameters(parameters);
|
||||
return getNativeModule(this).createShortDynamicLink(parameters);
|
||||
} catch (error) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
return () => {
|
||||
getLogger(this).info('Removing onLink listener');
|
||||
SharedEventEmitter.removeListener('onLink', listener);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export const statics = {
|
||||
EVENT_TYPE,
|
||||
};
|
||||
export const statics = {};
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
/**
|
||||
* @flow
|
||||
*/
|
||||
export type NativeAnalyticsParameters = {|
|
||||
campaign?: string,
|
||||
content?: string,
|
||||
medium?: string,
|
||||
source?: string,
|
||||
term?: string,
|
||||
|};
|
||||
|
||||
export type NativeAndroidParameters = {|
|
||||
fallbackUrl?: string,
|
||||
minimumVersion?: number,
|
||||
packageName?: string,
|
||||
|};
|
||||
|
||||
export type NativeIOSParameters = {|
|
||||
appStoreId?: string,
|
||||
bundleId?: string,
|
||||
customScheme?: string,
|
||||
fallbackUrl?: string,
|
||||
iPadBundleId?: string,
|
||||
iPadFallbackUrl?: string,
|
||||
minimumVersion?: string,
|
||||
|};
|
||||
|
||||
export type NativeITunesParameters = {|
|
||||
affiliateToken?: string,
|
||||
campaignToken?: string,
|
||||
providerToken?: string,
|
||||
|};
|
||||
|
||||
export type NativeNavigationParameters = {|
|
||||
forcedRedirectEnabled?: string,
|
||||
|};
|
||||
|
||||
export type NativeSocialParameters = {|
|
||||
descriptionText?: string,
|
||||
imageUrl?: string,
|
||||
title?: string,
|
||||
|};
|
||||
|
||||
export type NativeDynamicLink = {|
|
||||
analytics: NativeAnalyticsParameters,
|
||||
android: NativeAndroidParameters,
|
||||
dynamicLinkDomain: string,
|
||||
ios: NativeIOSParameters,
|
||||
itunes: NativeITunesParameters,
|
||||
link: string,
|
||||
navigation: NativeNavigationParameters,
|
||||
social: NativeSocialParameters,
|
||||
|};
|
|
@ -1,61 +1,79 @@
|
|||
/**
|
||||
* @flow
|
||||
* Remote message representation wrapper
|
||||
* RemoteMessage representation wrapper
|
||||
*/
|
||||
import { isObject, generatePushID } from './../../utils';
|
||||
|
||||
import type {
|
||||
NativeInboundRemoteMessage,
|
||||
NativeOutboundRemoteMessage,
|
||||
} from './types';
|
||||
|
||||
export default class RemoteMessage {
|
||||
properties: Object;
|
||||
_collapseKey: string | void;
|
||||
_data: { [string]: string };
|
||||
_from: string | void;
|
||||
_messageId: string;
|
||||
_messageType: string | void;
|
||||
_sentTime: number | void;
|
||||
_to: string;
|
||||
_ttl: number;
|
||||
|
||||
constructor(sender: string) {
|
||||
this.properties = {
|
||||
id: generatePushID(),
|
||||
ttl: 3600,
|
||||
// add the googleapis sender id part if not already added.
|
||||
sender: `${sender}`.includes('@')
|
||||
? sender
|
||||
: `${sender}@gcm.googleapis.com`,
|
||||
type: 'remote',
|
||||
data: {},
|
||||
};
|
||||
constructor(inboundMessage?: NativeInboundRemoteMessage) {
|
||||
if (inboundMessage) {
|
||||
this._collapseKey = inboundMessage.collapseKey;
|
||||
this._data = inboundMessage.data;
|
||||
this._from = inboundMessage.from;
|
||||
this._messageId = inboundMessage.messageId;
|
||||
this._messageType = inboundMessage.messageType;
|
||||
this._sentTime = inboundMessage.sentTime;
|
||||
}
|
||||
// defaults
|
||||
this._data = this._data || {};
|
||||
// TODO: Is this the best way to generate an ID?
|
||||
this._messageId = this._messageId || generatePushID();
|
||||
this._ttl = 3600;
|
||||
}
|
||||
|
||||
get collapseKey(): ?string {
|
||||
return this._collapseKey;
|
||||
}
|
||||
|
||||
get data(): { [string]: string } {
|
||||
return this._data;
|
||||
}
|
||||
|
||||
get from(): ?string {
|
||||
return this._from;
|
||||
}
|
||||
|
||||
get messageId(): ?string {
|
||||
return this._messageId;
|
||||
}
|
||||
|
||||
get messageType(): ?string {
|
||||
return this._messageType;
|
||||
}
|
||||
|
||||
get sentTime(): ?number {
|
||||
return this._sentTime;
|
||||
}
|
||||
|
||||
get to(): ?string {
|
||||
return this._to;
|
||||
}
|
||||
|
||||
get ttl(): ?number {
|
||||
return this._ttl;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param ttl
|
||||
* @param collapseKey
|
||||
* @returns {RemoteMessage}
|
||||
*/
|
||||
setTtl(ttl: number): RemoteMessage {
|
||||
this.properties.ttl = ttl;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param id
|
||||
*/
|
||||
setId(id: string): RemoteMessage {
|
||||
this.properties.id = `${id}`;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param type
|
||||
* @returns {RemoteMessage}
|
||||
*/
|
||||
setType(type: string): RemoteMessage {
|
||||
this.properties.type = `${type}`;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param key
|
||||
* @returns {RemoteMessage}
|
||||
*/
|
||||
setCollapseKey(key: string): RemoteMessage {
|
||||
this.properties.collapseKey = `${key}`;
|
||||
setCollapseKey(collapseKey: string): RemoteMessage {
|
||||
this._collapseKey = collapseKey;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -64,26 +82,74 @@ export default class RemoteMessage {
|
|||
* @param data
|
||||
* @returns {RemoteMessage}
|
||||
*/
|
||||
setData(data: Object = {}) {
|
||||
setData(data: { [string]: string } = {}) {
|
||||
if (!isObject(data)) {
|
||||
throw new Error(
|
||||
`RemoteMessage:setData expects an object as the first parameter but got type '${typeof data}'.`
|
||||
`RemoteMessage:setData expects an object but got type '${typeof data}'.`
|
||||
);
|
||||
}
|
||||
|
||||
const props = Object.keys(data);
|
||||
|
||||
// coerce all property values to strings as
|
||||
// remote message data only supports strings
|
||||
for (let i = 0, len = props.length; i < len; i++) {
|
||||
const prop = props[i];
|
||||
this.properties.data[prop] = `${data[prop]}`;
|
||||
}
|
||||
|
||||
this._data = data;
|
||||
return this;
|
||||
}
|
||||
|
||||
toJSON(): Object {
|
||||
return Object.assign({}, this.properties);
|
||||
/**
|
||||
*
|
||||
* @param messageId
|
||||
* @returns {RemoteMessage}
|
||||
*/
|
||||
setMessageId(messageId: string): RemoteMessage {
|
||||
this._messageId = messageId;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param messageType
|
||||
* @returns {RemoteMessage}
|
||||
*/
|
||||
setMessageType(messageType: string): RemoteMessage {
|
||||
this._messageType = messageType;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param to
|
||||
* @returns {RemoteMessage}
|
||||
*/
|
||||
setTo(to: string): RemoteMessage {
|
||||
this._to = to;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param ttl
|
||||
* @returns {RemoteMessage}
|
||||
*/
|
||||
setTtl(ttl: number): RemoteMessage {
|
||||
this._ttl = ttl;
|
||||
return this;
|
||||
}
|
||||
|
||||
build(): NativeOutboundRemoteMessage {
|
||||
if (!this._data) {
|
||||
throw new Error('RemoteMessage: Missing required `data` property');
|
||||
} else if (!this._messageId) {
|
||||
throw new Error('RemoteMessage: Missing required `messageId` property');
|
||||
} else if (!this._to) {
|
||||
throw new Error('RemoteMessage: Missing required `to` property');
|
||||
} else if (!this._ttl) {
|
||||
throw new Error('RemoteMessage: Missing required `ttl` property');
|
||||
}
|
||||
|
||||
return {
|
||||
collapseKey: this._collapseKey,
|
||||
data: this._data,
|
||||
messageId: this._messageId,
|
||||
messageType: this._messageType,
|
||||
to: this._to,
|
||||
ttl: this._ttl,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,94 +1,34 @@
|
|||
/**
|
||||
* @flow
|
||||
* Messaging representation wrapper
|
||||
* Messaging (FCM) representation wrapper
|
||||
*/
|
||||
import { Platform, NativeModules } from 'react-native';
|
||||
import { SharedEventEmitter } from '../../utils/events';
|
||||
import INTERNALS from '../../utils/internals';
|
||||
import { getLogger } from '../../utils/log';
|
||||
import ModuleBase from '../../utils/ModuleBase';
|
||||
import RemoteMessage from './RemoteMessage';
|
||||
import { getNativeModule } from '../../utils/native';
|
||||
import { isFunction, isObject } from '../../utils';
|
||||
import RemoteMessage from './RemoteMessage';
|
||||
|
||||
import type App from '../core/app';
|
||||
import type { NativeInboundRemoteMessage } from './types';
|
||||
|
||||
const EVENT_TYPE = {
|
||||
RefreshToken: 'messaging_token_refreshed',
|
||||
Notification: 'messaging_notification_received',
|
||||
type OnMessage = RemoteMessage => any;
|
||||
|
||||
type OnMessageObserver = {
|
||||
next: OnMessage,
|
||||
};
|
||||
|
||||
const NOTIFICATION_TYPE = {
|
||||
Remote: 'remote_notification',
|
||||
NotificationResponse: 'notification_response',
|
||||
WillPresent: 'will_present_notification',
|
||||
Local: 'local_notification',
|
||||
type OnTokenRefresh = String => any;
|
||||
|
||||
type OnTokenRefreshObserver = {
|
||||
next: OnTokenRefresh,
|
||||
};
|
||||
|
||||
const REMOTE_NOTIFICATION_RESULT = {
|
||||
NewData: 'UIBackgroundFetchResultNewData',
|
||||
NoData: 'UIBackgroundFetchResultNoData',
|
||||
ResultFailed: 'UIBackgroundFetchResultFailed',
|
||||
};
|
||||
|
||||
const WILL_PRESENT_RESULT = {
|
||||
All: 'UNNotificationPresentationOptionAll',
|
||||
None: 'UNNotificationPresentationOptionNone',
|
||||
};
|
||||
|
||||
const NATIVE_EVENTS = [EVENT_TYPE.RefreshToken, EVENT_TYPE.Notification];
|
||||
|
||||
const FirebaseMessaging = NativeModules.RNFirebaseMessaging;
|
||||
|
||||
/**
|
||||
* IOS only finish function
|
||||
* @param data
|
||||
*/
|
||||
function finish(data) {
|
||||
if (Platform.OS !== 'ios') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this._finishCalled && this._completionHandlerId) {
|
||||
let result = data;
|
||||
|
||||
this._finishCalled = true;
|
||||
|
||||
switch (this._notificationType) {
|
||||
case NOTIFICATION_TYPE.Remote:
|
||||
result = result || REMOTE_NOTIFICATION_RESULT.NoData;
|
||||
if (!Object.values(REMOTE_NOTIFICATION_RESULT).includes(result)) {
|
||||
throw new Error(
|
||||
'Invalid REMOTE_NOTIFICATION_RESULT value, use messaging().REMOTE_NOTIFICATION_RESULT'
|
||||
);
|
||||
}
|
||||
|
||||
FirebaseMessaging.finishRemoteNotification(
|
||||
this._completionHandlerId,
|
||||
result
|
||||
);
|
||||
return;
|
||||
case NOTIFICATION_TYPE.NotificationResponse:
|
||||
FirebaseMessaging.finishNotificationResponse(this._completionHandlerId);
|
||||
return;
|
||||
case NOTIFICATION_TYPE.WillPresent:
|
||||
result =
|
||||
result ||
|
||||
(this.show_in_foreground
|
||||
? WILL_PRESENT_RESULT.All
|
||||
: WILL_PRESENT_RESULT.None);
|
||||
if (!Object.values(WILL_PRESENT_RESULT).includes(result)) {
|
||||
throw new Error(
|
||||
'Invalid WILL_PRESENT_RESULT value, use messaging().WILL_PRESENT_RESULT'
|
||||
);
|
||||
}
|
||||
|
||||
FirebaseMessaging.finishWillPresentNotification(
|
||||
this._completionHandlerId,
|
||||
result
|
||||
);
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
const NATIVE_EVENTS = [
|
||||
'messaging_message_received',
|
||||
'messaging_token_refreshed',
|
||||
];
|
||||
|
||||
export const MODULE_NAME = 'RNFirebaseMessaging';
|
||||
export const NAMESPACE = 'messaging';
|
||||
|
@ -102,206 +42,140 @@ export default class Messaging extends ModuleBase {
|
|||
events: NATIVE_EVENTS,
|
||||
moduleName: MODULE_NAME,
|
||||
multiApp: false,
|
||||
hasShards: false,
|
||||
namespace: NAMESPACE,
|
||||
});
|
||||
|
||||
SharedEventEmitter.addListener(
|
||||
// sub to internal native event - this fans out to
|
||||
// public event name: onMessage
|
||||
'messaging_message_received',
|
||||
(message: NativeInboundRemoteMessage) => {
|
||||
SharedEventEmitter.emit('onMessage', new RemoteMessage(message));
|
||||
}
|
||||
);
|
||||
|
||||
SharedEventEmitter.addListener(
|
||||
// sub to internal native event - this fans out to
|
||||
// public event name: onMessage
|
||||
'messaging_token_refreshed',
|
||||
(token: string) => {
|
||||
SharedEventEmitter.emit('onTokenRefresh', token);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
get EVENT_TYPE(): Object {
|
||||
return EVENT_TYPE;
|
||||
}
|
||||
|
||||
get NOTIFICATION_TYPE(): Object {
|
||||
return NOTIFICATION_TYPE;
|
||||
}
|
||||
|
||||
get REMOTE_NOTIFICATION_RESULT(): Object {
|
||||
return REMOTE_NOTIFICATION_RESULT;
|
||||
}
|
||||
|
||||
get WILL_PRESENT_RESULT(): Object {
|
||||
return WILL_PRESENT_RESULT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the notification that triggered application open
|
||||
* @returns {*}
|
||||
*/
|
||||
getInitialNotification(): Promise<Object> {
|
||||
return getNativeModule(this).getInitialNotification();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the fcm token for the current device
|
||||
* @returns {*|Promise.<String>}
|
||||
*/
|
||||
getToken(): Promise<string> {
|
||||
return getNativeModule(this).getToken();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset Instance ID and revokes all tokens.
|
||||
* @returns {*|Promise.<*>}
|
||||
*/
|
||||
deleteInstanceId(): Promise<void> {
|
||||
return getNativeModule(this).deleteInstanceId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and display a local notification
|
||||
* @param notification
|
||||
* @returns {*}
|
||||
*/
|
||||
createLocalNotification(notification: Object): Promise<void> {
|
||||
const _notification = Object.assign({}, notification);
|
||||
_notification.id = _notification.id || new Date().getTime().toString();
|
||||
_notification.local_notification = true;
|
||||
return getNativeModule(this).createLocalNotification(_notification);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param notification
|
||||
* @returns {*}
|
||||
*/
|
||||
scheduleLocalNotification(notification: Object): Promise<void> {
|
||||
const _notification = Object.assign({}, notification);
|
||||
if (!notification.id)
|
||||
return Promise.reject(
|
||||
new Error('An id is required to schedule a local notification.')
|
||||
onMessage(nextOrObserver: OnMessage | OnMessageObserver): () => any {
|
||||
let listener: RemoteMessage => any;
|
||||
if (isFunction(nextOrObserver)) {
|
||||
// $FlowExpectedError: Not coping with the overloaded method signature
|
||||
listener = nextOrObserver;
|
||||
} else if (isObject(nextOrObserver) && isFunction(nextOrObserver.next)) {
|
||||
listener = nextOrObserver.next;
|
||||
} else {
|
||||
throw new Error(
|
||||
'Messaging.onMessage failed: First argument must be a function or observer object with a `next` function.'
|
||||
);
|
||||
_notification.local_notification = true;
|
||||
return getNativeModule(this).scheduleLocalNotification(_notification);
|
||||
}
|
||||
|
||||
getLogger(this).info('Creating onMessage listener');
|
||||
|
||||
SharedEventEmitter.addListener('onMessage', listener);
|
||||
|
||||
return () => {
|
||||
getLogger(this).info('Removing onMessage listener');
|
||||
SharedEventEmitter.removeListener('onMessage', listener);
|
||||
};
|
||||
}
|
||||
|
||||
onTokenRefresh(
|
||||
nextOrObserver: OnTokenRefresh | OnTokenRefreshObserver
|
||||
): () => any {
|
||||
let listener: String => any;
|
||||
if (isFunction(nextOrObserver)) {
|
||||
// $FlowExpectedError: Not coping with the overloaded method signature
|
||||
listener = nextOrObserver;
|
||||
} else if (isObject(nextOrObserver) && isFunction(nextOrObserver.next)) {
|
||||
listener = nextOrObserver.next;
|
||||
} else {
|
||||
throw new Error(
|
||||
'Messaging.OnTokenRefresh failed: First argument must be a function or observer object with a `next` function.'
|
||||
);
|
||||
}
|
||||
|
||||
getLogger(this).info('Creating onTokenRefresh listener');
|
||||
SharedEventEmitter.addListener('onTokenRefresh', listener);
|
||||
|
||||
return () => {
|
||||
getLogger(this).info('Removing onTokenRefresh listener');
|
||||
SharedEventEmitter.removeListener('onTokenRefresh', listener);
|
||||
};
|
||||
}
|
||||
|
||||
requestPermission(): Promise<void> {
|
||||
return getNativeModule(this).requestPermission();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of all scheduled notifications
|
||||
* @returns {Promise.<Array>}
|
||||
* NON WEB-SDK METHODS
|
||||
*/
|
||||
getScheduledLocalNotifications(): Promise<Object[]> {
|
||||
return getNativeModule(this).getScheduledLocalNotifications();
|
||||
hasPermission(): Promise<boolean> {
|
||||
return getNativeModule(this).hasPermission();
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel a local notification by id - using '*' will cancel
|
||||
* all local notifications.
|
||||
* @param id
|
||||
* @returns {*}
|
||||
*/
|
||||
cancelLocalNotification(id: string): Promise<void> {
|
||||
if (!id) return Promise.reject(new Error('Missing notification id'));
|
||||
if (id === '*') return getNativeModule(this).cancelAllLocalNotifications();
|
||||
return getNativeModule(this).cancelLocalNotification(id);
|
||||
sendMessage(remoteMessage: RemoteMessage): Promise<void> {
|
||||
if (!(remoteMessage instanceof RemoteMessage)) {
|
||||
throw new Error(
|
||||
`Messaging:sendMessage expects a 'RemoteMessage' but got type ${typeof remoteMessage}`
|
||||
);
|
||||
}
|
||||
return getNativeModule(this).sendMessage(remoteMessage.build());
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a delivered notification - using '*' will remove
|
||||
* all delivered notifications.
|
||||
* @param id
|
||||
* @returns {*}
|
||||
*/
|
||||
removeDeliveredNotification(id: string): Promise<void> {
|
||||
if (!id) return Promise.reject(new Error('Missing notification id'));
|
||||
if (id === '*')
|
||||
return getNativeModule(this).removeAllDeliveredNotifications();
|
||||
return getNativeModule(this).removeDeliveredNotification(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Request notification permission
|
||||
* @platforms ios
|
||||
* @returns {*|Promise.<*>}
|
||||
*/
|
||||
requestPermissions(): Promise<void> {
|
||||
return getNativeModule(this).requestPermissions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set notification count badge number
|
||||
* @param n
|
||||
*/
|
||||
setBadgeNumber(n: number): void {
|
||||
getNativeModule(this).setBadgeNumber(n);
|
||||
}
|
||||
|
||||
/**
|
||||
* set notification count badge number
|
||||
* @returns {Promise.<Number>}
|
||||
*/
|
||||
getBadgeNumber(): Promise<number> {
|
||||
return getNativeModule(this).getBadgeNumber();
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to messages / notifications
|
||||
* @param listener
|
||||
* @returns {*}
|
||||
*/
|
||||
onMessage(listener: Object => any): () => any {
|
||||
const rnListener = SharedEventEmitter.addListener(
|
||||
EVENT_TYPE.Notification,
|
||||
async event => {
|
||||
const data = {
|
||||
...event,
|
||||
finish,
|
||||
};
|
||||
await listener(data);
|
||||
|
||||
if (!data._finishCalled) {
|
||||
data.finish();
|
||||
}
|
||||
}
|
||||
);
|
||||
return () => rnListener.remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to token refresh events
|
||||
* @param listener
|
||||
* @returns {*}
|
||||
*/
|
||||
onTokenRefresh(listener: string => any): () => any {
|
||||
const rnListener = SharedEventEmitter.addListener(
|
||||
EVENT_TYPE.RefreshToken,
|
||||
listener
|
||||
);
|
||||
return () => rnListener.remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to a topic
|
||||
* @param topic
|
||||
*/
|
||||
subscribeToTopic(topic: string): void {
|
||||
getNativeModule(this).subscribeToTopic(topic);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsubscribe from a topic
|
||||
* @param topic
|
||||
*/
|
||||
unsubscribeFromTopic(topic: string): void {
|
||||
getNativeModule(this).unsubscribeFromTopic(topic);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an upstream message
|
||||
* @param remoteMessage
|
||||
* KNOWN UNSUPPORTED METHODS
|
||||
*/
|
||||
send(remoteMessage: RemoteMessage): Promise<void> {
|
||||
if (!(remoteMessage instanceof RemoteMessage)) {
|
||||
throw new Error(
|
||||
'messaging().send requires an instance of RemoteMessage as the first argument.'
|
||||
);
|
||||
}
|
||||
|
||||
return getNativeModule(this).send(remoteMessage.toJSON());
|
||||
deleteToken() {
|
||||
throw new Error(
|
||||
INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD(
|
||||
'messaging',
|
||||
'deleteToken'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
setBackgroundMessageHandler() {
|
||||
throw new Error(
|
||||
INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD(
|
||||
'messaging',
|
||||
'setBackgroundMessageHandler'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
useServiceWorker() {
|
||||
throw new Error(
|
||||
INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD(
|
||||
'messaging',
|
||||
'useServiceWorker'
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const statics = {
|
||||
EVENT_TYPE,
|
||||
NOTIFICATION_TYPE,
|
||||
REMOTE_NOTIFICATION_RESULT,
|
||||
WILL_PRESENT_RESULT,
|
||||
RemoteMessage,
|
||||
};
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
/**
|
||||
* @flow
|
||||
*/
|
||||
export type Notification = {
|
||||
body: string,
|
||||
bodyLocalizationArgs?: string[],
|
||||
bodyLocalizationKey?: string,
|
||||
clickAction?: string,
|
||||
color?: string,
|
||||
icon?: string,
|
||||
link?: string,
|
||||
sound: string,
|
||||
subtitle?: string,
|
||||
tag?: string,
|
||||
title: string,
|
||||
titleLocalizationArgs?: string[],
|
||||
titleLocalizationKey?: string,
|
||||
};
|
||||
|
||||
export type NativeInboundRemoteMessage = {
|
||||
collapseKey?: string,
|
||||
data: { [string]: string },
|
||||
from?: string,
|
||||
messageId: string,
|
||||
messageType?: string,
|
||||
sentTime?: number,
|
||||
to?: string,
|
||||
ttl?: number,
|
||||
};
|
||||
|
||||
export type NativeOutboundRemoteMessage = {
|
||||
collapseKey?: string,
|
||||
data: { [string]: string },
|
||||
messageId: string,
|
||||
messageType?: string,
|
||||
to: string,
|
||||
ttl: number,
|
||||
};
|
|
@ -0,0 +1,150 @@
|
|||
/**
|
||||
* @flow
|
||||
* AndroidAction representation wrapper
|
||||
*/
|
||||
import RemoteInput, {
|
||||
fromNativeAndroidRemoteInput,
|
||||
} from './AndroidRemoteInput';
|
||||
import { SemanticAction } from './types';
|
||||
import type { NativeAndroidAction, SemanticActionType } from './types';
|
||||
|
||||
export default class AndroidAction {
|
||||
_action: string;
|
||||
_allowGeneratedReplies: boolean | void;
|
||||
_icon: string;
|
||||
_remoteInputs: RemoteInput[];
|
||||
_semanticAction: SemanticActionType | void;
|
||||
_showUserInterface: boolean | void;
|
||||
_title: string;
|
||||
|
||||
constructor(action: string, icon: string, title: string) {
|
||||
this._action = action;
|
||||
this._icon = icon;
|
||||
this._remoteInputs = [];
|
||||
this._title = title;
|
||||
}
|
||||
|
||||
get action(): string {
|
||||
return this._action;
|
||||
}
|
||||
|
||||
get allowGeneratedReplies(): ?boolean {
|
||||
return this._allowGeneratedReplies;
|
||||
}
|
||||
|
||||
get icon(): string {
|
||||
return this._icon;
|
||||
}
|
||||
|
||||
get remoteInputs(): RemoteInput[] {
|
||||
return this._remoteInputs;
|
||||
}
|
||||
|
||||
get semanticAction(): ?SemanticActionType {
|
||||
return this._semanticAction;
|
||||
}
|
||||
|
||||
get showUserInterface(): ?boolean {
|
||||
return this._showUserInterface;
|
||||
}
|
||||
|
||||
get title(): string {
|
||||
return this._title;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param remoteInput
|
||||
* @returns {AndroidAction}
|
||||
*/
|
||||
addRemoteInput(remoteInput: RemoteInput): AndroidAction {
|
||||
if (!(remoteInput instanceof RemoteInput)) {
|
||||
throw new Error(
|
||||
`AndroidAction:addRemoteInput expects an 'RemoteInput' but got type ${typeof remoteInput}`
|
||||
);
|
||||
}
|
||||
this._remoteInputs.push(remoteInput);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param allowGeneratedReplies
|
||||
* @returns {AndroidAction}
|
||||
*/
|
||||
setAllowGenerateReplies(allowGeneratedReplies: boolean): AndroidAction {
|
||||
this._allowGeneratedReplies = allowGeneratedReplies;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param semanticAction
|
||||
* @returns {AndroidAction}
|
||||
*/
|
||||
setSemanticAction(semanticAction: SemanticActionType): AndroidAction {
|
||||
if (!Object.values(SemanticAction).includes(semanticAction)) {
|
||||
throw new Error(
|
||||
`AndroidAction:setSemanticAction Invalid Semantic Action: ${semanticAction}`
|
||||
);
|
||||
}
|
||||
this._semanticAction = semanticAction;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param showUserInterface
|
||||
* @returns {AndroidAction}
|
||||
*/
|
||||
setShowUserInterface(showUserInterface: boolean): AndroidAction {
|
||||
this._showUserInterface = showUserInterface;
|
||||
return this;
|
||||
}
|
||||
|
||||
build(): NativeAndroidAction {
|
||||
if (!this._action) {
|
||||
throw new Error('AndroidAction: Missing required `action` property');
|
||||
} else if (!this._icon) {
|
||||
throw new Error('AndroidAction: Missing required `icon` property');
|
||||
} else if (!this._title) {
|
||||
throw new Error('AndroidAction: Missing required `title` property');
|
||||
}
|
||||
|
||||
return {
|
||||
action: this._action,
|
||||
allowGeneratedReplies: this._allowGeneratedReplies,
|
||||
icon: this._icon,
|
||||
remoteInputs: this._remoteInputs.map(remoteInput => remoteInput.build()),
|
||||
semanticAction: this._semanticAction,
|
||||
showUserInterface: this._showUserInterface,
|
||||
title: this._title,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export const fromNativeAndroidAction = (
|
||||
nativeAction: NativeAndroidAction
|
||||
): AndroidAction => {
|
||||
const action = new AndroidAction(
|
||||
nativeAction.action,
|
||||
nativeAction.icon,
|
||||
nativeAction.title
|
||||
);
|
||||
if (nativeAction.allowGeneratedReplies) {
|
||||
action.setAllowGenerateReplies(nativeAction.allowGeneratedReplies);
|
||||
}
|
||||
if (nativeAction.remoteInputs) {
|
||||
nativeAction.remoteInputs.forEach(remoteInput => {
|
||||
action.addRemoteInput(fromNativeAndroidRemoteInput(remoteInput));
|
||||
});
|
||||
}
|
||||
if (nativeAction.semanticAction) {
|
||||
action.setSemanticAction(nativeAction.semanticAction);
|
||||
}
|
||||
if (nativeAction.showUserInterface) {
|
||||
action.setShowUserInterface(nativeAction.showUserInterface);
|
||||
}
|
||||
|
||||
return action;
|
||||
};
|
|
@ -0,0 +1,198 @@
|
|||
/**
|
||||
* @flow
|
||||
* AndroidChannel representation wrapper
|
||||
*/
|
||||
import { Importance, Visibility } from './types';
|
||||
import type { ImportanceType, VisibilityType } from './types';
|
||||
|
||||
type NativeAndroidChannel = {|
|
||||
bypassDnd?: boolean,
|
||||
channelId: string,
|
||||
description?: string,
|
||||
group?: string,
|
||||
importance: ImportanceType,
|
||||
lightColor?: string,
|
||||
lockScreenVisibility?: VisibilityType,
|
||||
name: string,
|
||||
showBadge?: boolean,
|
||||
sound?: string,
|
||||
vibrationPattern?: number[],
|
||||
|};
|
||||
|
||||
export default class AndroidChannel {
|
||||
_bypassDnd: boolean | void;
|
||||
_channelId: string;
|
||||
_description: string | void;
|
||||
_group: string | void;
|
||||
_importance: ImportanceType;
|
||||
_lightColor: string | void;
|
||||
_lockScreenVisibility: VisibilityType;
|
||||
_name: string;
|
||||
_showBadge: boolean | void;
|
||||
_sound: string | void;
|
||||
_vibrationPattern: number[] | void;
|
||||
|
||||
constructor(channelId: string, name: string, importance: ImportanceType) {
|
||||
if (!Object.values(Importance).includes(importance)) {
|
||||
throw new Error(`AndroidChannel() Invalid Importance: ${importance}`);
|
||||
}
|
||||
this._channelId = channelId;
|
||||
this._name = name;
|
||||
this._importance = importance;
|
||||
}
|
||||
|
||||
get bypassDnd(): ?boolean {
|
||||
return this._bypassDnd;
|
||||
}
|
||||
|
||||
get channelId(): string {
|
||||
return this._channelId;
|
||||
}
|
||||
|
||||
get description(): ?string {
|
||||
return this._description;
|
||||
}
|
||||
|
||||
get group(): ?string {
|
||||
return this._group;
|
||||
}
|
||||
|
||||
get importance(): ImportanceType {
|
||||
return this._importance;
|
||||
}
|
||||
|
||||
get lightColor(): ?string {
|
||||
return this._lightColor;
|
||||
}
|
||||
|
||||
get lockScreenVisibility(): ?VisibilityType {
|
||||
return this._lockScreenVisibility;
|
||||
}
|
||||
|
||||
get name(): string {
|
||||
return this._name;
|
||||
}
|
||||
|
||||
get showBadge(): ?boolean {
|
||||
return this._showBadge;
|
||||
}
|
||||
|
||||
get sound(): ?string {
|
||||
return this._sound;
|
||||
}
|
||||
|
||||
get vibrationPattern(): ?(number[]) {
|
||||
return this._vibrationPattern;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param bypassDnd
|
||||
* @returns {AndroidChannel}
|
||||
*/
|
||||
setBypassDnd(bypassDnd: boolean): AndroidChannel {
|
||||
this._bypassDnd = bypassDnd;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param description
|
||||
* @returns {AndroidChannel}
|
||||
*/
|
||||
setDescription(description: string): AndroidChannel {
|
||||
this._description = description;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param group
|
||||
* @returns {AndroidChannel}
|
||||
*/
|
||||
setGroup(groupId: string): AndroidChannel {
|
||||
this._group = groupId;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param lightColor
|
||||
* @returns {AndroidChannel}
|
||||
*/
|
||||
setLightColor(lightColor: string): AndroidChannel {
|
||||
this._lightColor = lightColor;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param lockScreenVisibility
|
||||
* @returns {AndroidChannel}
|
||||
*/
|
||||
setLockScreenVisibility(
|
||||
lockScreenVisibility: VisibilityType
|
||||
): AndroidChannel {
|
||||
if (!Object.values(Visibility).includes(lockScreenVisibility)) {
|
||||
throw new Error(
|
||||
`AndroidChannel:setLockScreenVisibility Invalid Visibility: ${lockScreenVisibility}`
|
||||
);
|
||||
}
|
||||
this._lockScreenVisibility = lockScreenVisibility;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param showBadge
|
||||
* @returns {AndroidChannel}
|
||||
*/
|
||||
setShowBadge(showBadge: boolean): AndroidChannel {
|
||||
this._showBadge = showBadge;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param sound
|
||||
* @returns {AndroidChannel}
|
||||
*/
|
||||
setSound(sound: string): AndroidChannel {
|
||||
this._sound = sound;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param vibrationPattern
|
||||
* @returns {AndroidChannel}
|
||||
*/
|
||||
setVibrationPattern(vibrationPattern: number[]): AndroidChannel {
|
||||
this._vibrationPattern = vibrationPattern;
|
||||
return this;
|
||||
}
|
||||
|
||||
build(): NativeAndroidChannel {
|
||||
if (!this._channelId) {
|
||||
throw new Error('AndroidChannel: Missing required `channelId` property');
|
||||
} else if (!this._importance) {
|
||||
throw new Error('AndroidChannel: Missing required `importance` property');
|
||||
} else if (!this._name) {
|
||||
throw new Error('AndroidChannel: Missing required `name` property');
|
||||
}
|
||||
|
||||
return {
|
||||
bypassDnd: this._bypassDnd,
|
||||
channelId: this._channelId,
|
||||
description: this._description,
|
||||
group: this._group,
|
||||
importance: this._importance,
|
||||
lightColor: this._lightColor,
|
||||
lockScreenVisibility: this._lockScreenVisibility,
|
||||
name: this._name,
|
||||
showBadge: this._showBadge,
|
||||
sound: this._sound,
|
||||
vibrationPattern: this._vibrationPattern,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
/**
|
||||
* @flow
|
||||
* AndroidChannelGroup representation wrapper
|
||||
*/
|
||||
|
||||
type NativeAndroidChannelGroup = {|
|
||||
groupId: string,
|
||||
name: string,
|
||||
|};
|
||||
|
||||
export default class AndroidChannelGroup {
|
||||
_groupId: string;
|
||||
_name: string;
|
||||
|
||||
constructor(groupId: string, name: string) {
|
||||
this._groupId = groupId;
|
||||
this._name = name;
|
||||
}
|
||||
|
||||
get groupId(): string {
|
||||
return this._groupId;
|
||||
}
|
||||
|
||||
get name(): string {
|
||||
return this._name;
|
||||
}
|
||||
|
||||
build(): NativeAndroidChannelGroup {
|
||||
if (!this._groupId) {
|
||||
throw new Error(
|
||||
'AndroidChannelGroup: Missing required `groupId` property'
|
||||
);
|
||||
} else if (!this._name) {
|
||||
throw new Error('AndroidChannelGroup: Missing required `name` property');
|
||||
}
|
||||
|
||||
return {
|
||||
groupId: this._groupId,
|
||||
name: this._name,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,676 @@
|
|||
/**
|
||||
* @flow
|
||||
* AndroidNotification representation wrapper
|
||||
*/
|
||||
import AndroidAction, { fromNativeAndroidAction } from './AndroidAction';
|
||||
import { BadgeIconType, Category, GroupAlert, Priority } from './types';
|
||||
import type Notification from './Notification';
|
||||
import type {
|
||||
BadgeIconTypeType,
|
||||
CategoryType,
|
||||
DefaultsType,
|
||||
GroupAlertType,
|
||||
Lights,
|
||||
NativeAndroidNotification,
|
||||
PriorityType,
|
||||
Progress,
|
||||
SmallIcon,
|
||||
VisibilityType,
|
||||
} from './types';
|
||||
|
||||
export default class AndroidNotification {
|
||||
_actions: AndroidAction[];
|
||||
_autoCancel: boolean | void;
|
||||
_badgeIconType: BadgeIconTypeType | void;
|
||||
_category: CategoryType | void;
|
||||
_channelId: string;
|
||||
_clickAction: string | void;
|
||||
_color: string | void;
|
||||
_colorized: boolean | void;
|
||||
_contentInfo: string | void;
|
||||
_defaults: DefaultsType[] | void;
|
||||
_group: string | void;
|
||||
_groupAlertBehaviour: GroupAlertType | void;
|
||||
_groupSummary: boolean | void;
|
||||
_largeIcon: string | void;
|
||||
_lights: Lights | void;
|
||||
_localOnly: boolean | void;
|
||||
_notification: Notification;
|
||||
_number: number | void;
|
||||
_ongoing: boolean | void;
|
||||
_onlyAlertOnce: boolean | void;
|
||||
_people: string[];
|
||||
_priority: PriorityType | void;
|
||||
_progress: Progress | void;
|
||||
// _publicVersion: Notification;
|
||||
_remoteInputHistory: string[] | void;
|
||||
_shortcutId: string | void;
|
||||
_showWhen: boolean | void;
|
||||
_smallIcon: SmallIcon;
|
||||
_sortKey: string | void;
|
||||
// TODO: style: Style; // Need to figure out if this can work
|
||||
_ticker: string | void;
|
||||
_timeoutAfter: number | void;
|
||||
_usesChronometer: boolean | void;
|
||||
_vibrate: number[] | void;
|
||||
_visibility: VisibilityType | void;
|
||||
_when: number | void;
|
||||
|
||||
// android unsupported
|
||||
// content: RemoteViews
|
||||
// contentIntent: PendingIntent - need to look at what this is
|
||||
// customBigContentView: RemoteViews
|
||||
// customContentView: RemoteViews
|
||||
// customHeadsUpContentView: RemoteViews
|
||||
// deleteIntent: PendingIntent
|
||||
// fullScreenIntent: PendingIntent
|
||||
// sound.streamType
|
||||
|
||||
constructor(notification: Notification, data?: NativeAndroidNotification) {
|
||||
this._notification = notification;
|
||||
|
||||
if (data) {
|
||||
this._actions = data.actions
|
||||
? data.actions.map(action => fromNativeAndroidAction(action))
|
||||
: [];
|
||||
this._autoCancel = data.autoCancel;
|
||||
this._badgeIconType = data.badgeIconType;
|
||||
this._category = data.category;
|
||||
this._channelId = data.channelId;
|
||||
this._clickAction = data.clickAction;
|
||||
this._color = data.color;
|
||||
this._colorized = data.colorized;
|
||||
this._contentInfo = data.contentInfo;
|
||||
this._defaults = data.defaults;
|
||||
this._group = data.group;
|
||||
this._groupAlertBehaviour = data.groupAlertBehaviour;
|
||||
this._groupSummary = data.groupSummary;
|
||||
this._largeIcon = data.largeIcon;
|
||||
this._lights = data.lights;
|
||||
this._localOnly = data.localOnly;
|
||||
this._number = data.number;
|
||||
this._ongoing = data.ongoing;
|
||||
this._onlyAlertOnce = data.onlyAlertOnce;
|
||||
this._people = data.people;
|
||||
this._priority = data.priority;
|
||||
this._progress = data.progress;
|
||||
// _publicVersion: Notification;
|
||||
this._remoteInputHistory = data.remoteInputHistory;
|
||||
this._shortcutId = data.shortcutId;
|
||||
this._showWhen = data.showWhen;
|
||||
this._smallIcon = data.smallIcon;
|
||||
this._sortKey = data.sortKey;
|
||||
this._ticker = data.ticker;
|
||||
this._timeoutAfter = data.timeoutAfter;
|
||||
this._usesChronometer = data.usesChronometer;
|
||||
this._vibrate = data.vibrate;
|
||||
this._visibility = data.visibility;
|
||||
this._when = data.when;
|
||||
}
|
||||
|
||||
// Defaults
|
||||
this._actions = this._actions || [];
|
||||
this._people = this._people || [];
|
||||
this._smallIcon = this._smallIcon || {
|
||||
icon: 'ic_launcher',
|
||||
};
|
||||
}
|
||||
|
||||
get actions(): AndroidAction[] {
|
||||
return this._actions;
|
||||
}
|
||||
|
||||
get autoCancel(): ?boolean {
|
||||
return this._autoCancel;
|
||||
}
|
||||
|
||||
get badgeIconType(): ?BadgeIconTypeType {
|
||||
return this._badgeIconType;
|
||||
}
|
||||
|
||||
get category(): ?CategoryType {
|
||||
return this._category;
|
||||
}
|
||||
|
||||
get channelId(): string {
|
||||
return this._channelId;
|
||||
}
|
||||
|
||||
get clickAction(): ?string {
|
||||
return this._clickAction;
|
||||
}
|
||||
|
||||
get color(): ?string {
|
||||
return this._color;
|
||||
}
|
||||
|
||||
get colorized(): ?boolean {
|
||||
return this._colorized;
|
||||
}
|
||||
|
||||
get contentInfo(): ?string {
|
||||
return this._contentInfo;
|
||||
}
|
||||
|
||||
get defaults(): ?(DefaultsType[]) {
|
||||
return this._defaults;
|
||||
}
|
||||
|
||||
get group(): ?string {
|
||||
return this._group;
|
||||
}
|
||||
|
||||
get groupAlertBehaviour(): ?GroupAlertType {
|
||||
return this._groupAlertBehaviour;
|
||||
}
|
||||
|
||||
get groupSummary(): ?boolean {
|
||||
return this._groupSummary;
|
||||
}
|
||||
|
||||
get largeIcon(): ?string {
|
||||
return this._largeIcon;
|
||||
}
|
||||
|
||||
get lights(): ?Lights {
|
||||
return this._lights;
|
||||
}
|
||||
|
||||
get localOnly(): ?boolean {
|
||||
return this._localOnly;
|
||||
}
|
||||
|
||||
get number(): ?number {
|
||||
return this._number;
|
||||
}
|
||||
|
||||
get ongoing(): ?boolean {
|
||||
return this._ongoing;
|
||||
}
|
||||
|
||||
get onlyAlertOnce(): ?boolean {
|
||||
return this._onlyAlertOnce;
|
||||
}
|
||||
|
||||
get people(): string[] {
|
||||
return this._people;
|
||||
}
|
||||
|
||||
get priority(): ?PriorityType {
|
||||
return this._priority;
|
||||
}
|
||||
|
||||
get progress(): ?Progress {
|
||||
return this._progress;
|
||||
}
|
||||
|
||||
get remoteInputHistory(): ?(string[]) {
|
||||
return this._remoteInputHistory;
|
||||
}
|
||||
|
||||
get shortcutId(): ?string {
|
||||
return this._shortcutId;
|
||||
}
|
||||
|
||||
get showWhen(): ?boolean {
|
||||
return this._showWhen;
|
||||
}
|
||||
|
||||
get smallIcon(): SmallIcon {
|
||||
return this._smallIcon;
|
||||
}
|
||||
|
||||
get sortKey(): ?string {
|
||||
return this._sortKey;
|
||||
}
|
||||
|
||||
get ticker(): ?string {
|
||||
return this._ticker;
|
||||
}
|
||||
|
||||
get timeoutAfter(): ?number {
|
||||
return this._timeoutAfter;
|
||||
}
|
||||
|
||||
get usesChronometer(): ?boolean {
|
||||
return this._usesChronometer;
|
||||
}
|
||||
|
||||
get vibrate(): ?(number[]) {
|
||||
return this._vibrate;
|
||||
}
|
||||
|
||||
get visibility(): ?VisibilityType {
|
||||
return this._visibility;
|
||||
}
|
||||
|
||||
get when(): ?number {
|
||||
return this._when;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param action
|
||||
* @returns {Notification}
|
||||
*/
|
||||
addAction(action: AndroidAction): Notification {
|
||||
if (!(action instanceof AndroidAction)) {
|
||||
throw new Error(
|
||||
`AndroidNotification:addAction expects an 'AndroidAction' but got type ${typeof action}`
|
||||
);
|
||||
}
|
||||
this._actions.push(action);
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param person
|
||||
* @returns {Notification}
|
||||
*/
|
||||
addPerson(person: string): Notification {
|
||||
this._people.push(person);
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param autoCancel
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setAutoCancel(autoCancel: boolean): Notification {
|
||||
this._autoCancel = autoCancel;
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param badgeIconType
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setBadgeIconType(badgeIconType: BadgeIconTypeType): Notification {
|
||||
if (!Object.values(BadgeIconType).includes(badgeIconType)) {
|
||||
throw new Error(
|
||||
`AndroidNotification:setBadgeIconType Invalid BadgeIconType: ${badgeIconType}`
|
||||
);
|
||||
}
|
||||
this._badgeIconType = badgeIconType;
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param category
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setCategory(category: CategoryType): Notification {
|
||||
if (!Object.values(Category).includes(category)) {
|
||||
throw new Error(
|
||||
`AndroidNotification:setCategory Invalid Category: ${category}`
|
||||
);
|
||||
}
|
||||
this._category = category;
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param channelId
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setChannelId(channelId: string): Notification {
|
||||
this._channelId = channelId;
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param clickAction
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setClickAction(clickAction: string): Notification {
|
||||
this._clickAction = clickAction;
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param color
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setColor(color: string): Notification {
|
||||
this._color = color;
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param colorized
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setColorized(colorized: boolean): Notification {
|
||||
this._colorized = colorized;
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param contentInfo
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setContentInfo(contentInfo: string): Notification {
|
||||
this._contentInfo = contentInfo;
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param defaults
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setDefaults(defaults: DefaultsType[]): Notification {
|
||||
this._defaults = defaults;
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param group
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setGroup(group: string): Notification {
|
||||
this._group = group;
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param groupAlertBehaviour
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setGroupAlertBehaviour(groupAlertBehaviour: GroupAlertType): Notification {
|
||||
if (!Object.values(GroupAlert).includes(groupAlertBehaviour)) {
|
||||
throw new Error(
|
||||
`AndroidNotification:setGroupAlertBehaviour Invalid GroupAlert: ${groupAlertBehaviour}`
|
||||
);
|
||||
}
|
||||
this._groupAlertBehaviour = groupAlertBehaviour;
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param groupSummary
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setGroupSummary(groupSummary: boolean): Notification {
|
||||
this._groupSummary = groupSummary;
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param largeIcon
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setLargeIcon(largeIcon: string): Notification {
|
||||
this._largeIcon = largeIcon;
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param argb
|
||||
* @param onMs
|
||||
* @param offMs
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setLights(argb: number, onMs: number, offMs: number): Notification {
|
||||
this._lights = {
|
||||
argb,
|
||||
onMs,
|
||||
offMs,
|
||||
};
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param localOnly
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setLocalOnly(localOnly: boolean): Notification {
|
||||
this._localOnly = localOnly;
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param number
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setNumber(number: number): Notification {
|
||||
this._number = number;
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param ongoing
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setOngoing(ongoing: boolean): Notification {
|
||||
this._ongoing = ongoing;
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param onlyAlertOnce
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setOnlyAlertOnce(onlyAlertOnce: boolean): Notification {
|
||||
this._onlyAlertOnce = onlyAlertOnce;
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param priority
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setPriority(priority: PriorityType): Notification {
|
||||
if (!Object.values(Priority).includes(priority)) {
|
||||
throw new Error(
|
||||
`AndroidNotification:setPriority Invalid Priority: ${priority}`
|
||||
);
|
||||
}
|
||||
this._priority = priority;
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param max
|
||||
* @param progress
|
||||
* @param indeterminate
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setProgress(
|
||||
max: number,
|
||||
progress: number,
|
||||
indeterminate: boolean
|
||||
): Notification {
|
||||
this._progress = {
|
||||
max,
|
||||
progress,
|
||||
indeterminate,
|
||||
};
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param publicVersion
|
||||
* @returns {Notification}
|
||||
*/
|
||||
/* setPublicVersion(publicVersion: Notification): Notification {
|
||||
this._publicVersion = publicVersion;
|
||||
return this._notification;
|
||||
} */
|
||||
|
||||
/**
|
||||
*
|
||||
* @param remoteInputHistory
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setRemoteInputHistory(remoteInputHistory: string[]): Notification {
|
||||
this._remoteInputHistory = remoteInputHistory;
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param shortcutId
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setShortcutId(shortcutId: string): Notification {
|
||||
this._shortcutId = shortcutId;
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param showWhen
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setShowWhen(showWhen: boolean): Notification {
|
||||
this._showWhen = showWhen;
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param icon
|
||||
* @param level
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setSmallIcon(icon: string, level?: number): Notification {
|
||||
this._smallIcon = {
|
||||
icon,
|
||||
level,
|
||||
};
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param sortKey
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setSortKey(sortKey: string): Notification {
|
||||
this._sortKey = sortKey;
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param ticker
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setTicker(ticker: string): Notification {
|
||||
this._ticker = ticker;
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param timeoutAfter
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setTimeoutAfter(timeoutAfter: number): Notification {
|
||||
this._timeoutAfter = timeoutAfter;
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param usesChronometer
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setUsesChronometer(usesChronometer: boolean): Notification {
|
||||
this._usesChronometer = usesChronometer;
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param vibrate
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setVibrate(vibrate: number[]): Notification {
|
||||
this._vibrate = vibrate;
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param when
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setWhen(when: number): Notification {
|
||||
this._when = when;
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
build(): NativeAndroidNotification {
|
||||
// TODO: Validation of required fields
|
||||
if (!this._channelId) {
|
||||
throw new Error(
|
||||
'AndroidNotification: Missing required `channelId` property'
|
||||
);
|
||||
} else if (!this._smallIcon) {
|
||||
throw new Error(
|
||||
'AndroidNotification: Missing required `smallIcon` property'
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
actions: this._actions.map(action => action.build()),
|
||||
autoCancel: this._autoCancel,
|
||||
badgeIconType: this._badgeIconType,
|
||||
category: this._category,
|
||||
channelId: this._channelId,
|
||||
clickAction: this._clickAction,
|
||||
color: this._color,
|
||||
colorized: this._colorized,
|
||||
contentInfo: this._contentInfo,
|
||||
defaults: this._defaults,
|
||||
group: this._group,
|
||||
groupAlertBehaviour: this._groupAlertBehaviour,
|
||||
groupSummary: this._groupSummary,
|
||||
largeIcon: this._largeIcon,
|
||||
lights: this._lights,
|
||||
localOnly: this._localOnly,
|
||||
number: this._number,
|
||||
ongoing: this._ongoing,
|
||||
onlyAlertOnce: this._onlyAlertOnce,
|
||||
people: this._people,
|
||||
priority: this._priority,
|
||||
progress: this._progress,
|
||||
// publicVersion: this._publicVersion,
|
||||
remoteInputHistory: this._remoteInputHistory,
|
||||
shortcutId: this._shortcutId,
|
||||
showWhen: this._showWhen,
|
||||
smallIcon: this._smallIcon,
|
||||
sortKey: this._sortKey,
|
||||
// TODO: style: Style,
|
||||
ticker: this._ticker,
|
||||
timeoutAfter: this._timeoutAfter,
|
||||
usesChronometer: this._usesChronometer,
|
||||
vibrate: this._vibrate,
|
||||
visibility: this._visibility,
|
||||
when: this._when,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
/**
|
||||
* @flow
|
||||
* AndroidNotifications representation wrapper
|
||||
*/
|
||||
import { Platform } from 'react-native';
|
||||
import AndroidChannel from './AndroidChannel';
|
||||
import AndroidChannelGroup from './AndroidChannelGroup';
|
||||
import { getNativeModule } from '../../utils/native';
|
||||
|
||||
import type Notifications from './';
|
||||
|
||||
export default class AndroidNotifications {
|
||||
_notifications: Notifications;
|
||||
|
||||
constructor(notifications: Notifications) {
|
||||
this._notifications = notifications;
|
||||
}
|
||||
|
||||
createChannel(channel: AndroidChannel): Promise<void> {
|
||||
if (Platform.OS === 'android') {
|
||||
if (!(channel instanceof AndroidChannel)) {
|
||||
throw new Error(
|
||||
`AndroidNotifications:createChannel expects an 'AndroidChannel' but got type ${typeof channel}`
|
||||
);
|
||||
}
|
||||
return getNativeModule(this._notifications).createChannel(
|
||||
channel.build()
|
||||
);
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
createChannelGroup(channelGroup: AndroidChannelGroup): Promise<void> {
|
||||
if (Platform.OS === 'android') {
|
||||
if (!(channelGroup instanceof AndroidChannelGroup)) {
|
||||
throw new Error(
|
||||
`AndroidNotifications:createChannelGroup expects an 'AndroidChannelGroup' but got type ${typeof channelGroup}`
|
||||
);
|
||||
}
|
||||
return getNativeModule(this._notifications).createChannelGroup(
|
||||
channelGroup.build()
|
||||
);
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
createChannelGroups(channelGroups: AndroidChannelGroup[]): Promise<void> {
|
||||
if (Platform.OS === 'android') {
|
||||
if (!Array.isArray(channelGroups)) {
|
||||
throw new Error(
|
||||
`AndroidNotifications:createChannelGroups expects an 'Array' but got type ${typeof channelGroups}`
|
||||
);
|
||||
}
|
||||
const nativeChannelGroups = [];
|
||||
for (let i = 0; i < channelGroups.length; i++) {
|
||||
const channelGroup = channelGroups[i];
|
||||
if (!(channelGroup instanceof AndroidChannelGroup)) {
|
||||
throw new Error(
|
||||
`AndroidNotifications:createChannelGroups expects array items of type 'AndroidChannelGroup' but got type ${typeof channelGroup}`
|
||||
);
|
||||
}
|
||||
nativeChannelGroups.push(channelGroup.build());
|
||||
}
|
||||
return getNativeModule(this._notifications).createChannelGroups(
|
||||
nativeChannelGroups
|
||||
);
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
createChannels(channels: AndroidChannel[]): Promise<void> {
|
||||
if (Platform.OS === 'android') {
|
||||
if (!Array.isArray(channels)) {
|
||||
throw new Error(
|
||||
`AndroidNotifications:createChannels expects an 'Array' but got type ${typeof channels}`
|
||||
);
|
||||
}
|
||||
const nativeChannels = [];
|
||||
for (let i = 0; i < channels.length; i++) {
|
||||
const channel = channels[i];
|
||||
if (!(channel instanceof AndroidChannel)) {
|
||||
throw new Error(
|
||||
`AndroidNotifications:createChannels expects array items of type 'AndroidChannel' but got type ${typeof channel}`
|
||||
);
|
||||
}
|
||||
nativeChannels.push(channel.build());
|
||||
}
|
||||
return getNativeModule(this._notifications).createChannels(
|
||||
nativeChannels
|
||||
);
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,123 @@
|
|||
/**
|
||||
* @flow
|
||||
* AndroidRemoteInput representation wrapper
|
||||
*/
|
||||
|
||||
import type { AndroidAllowDataType, NativeAndroidRemoteInput } from './types';
|
||||
|
||||
export default class AndroidRemoteInput {
|
||||
_allowedDataTypes: AndroidAllowDataType[];
|
||||
_allowFreeFormInput: boolean | void;
|
||||
_choices: string[];
|
||||
_label: string | void;
|
||||
_resultKey: string;
|
||||
|
||||
constructor(resultKey: string) {
|
||||
this._allowedDataTypes = [];
|
||||
this._choices = [];
|
||||
this._resultKey = resultKey;
|
||||
}
|
||||
|
||||
get allowedDataTypes(): AndroidAllowDataType[] {
|
||||
return this._allowedDataTypes;
|
||||
}
|
||||
|
||||
get allowFreeFormInput(): ?boolean {
|
||||
return this._allowFreeFormInput;
|
||||
}
|
||||
|
||||
get choices(): string[] {
|
||||
return this._choices;
|
||||
}
|
||||
|
||||
get label(): ?string {
|
||||
return this._label;
|
||||
}
|
||||
|
||||
get resultKey(): string {
|
||||
return this._resultKey;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param mimeType
|
||||
* @param allow
|
||||
* @returns {AndroidRemoteInput}
|
||||
*/
|
||||
setAllowDataType(mimeType: string, allow: boolean): AndroidRemoteInput {
|
||||
this._allowedDataTypes.push({
|
||||
allow,
|
||||
mimeType,
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param allowFreeFormInput
|
||||
* @returns {AndroidRemoteInput}
|
||||
*/
|
||||
setAllowFreeFormInput(allowFreeFormInput: boolean): AndroidRemoteInput {
|
||||
this._allowFreeFormInput = allowFreeFormInput;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param choices
|
||||
* @returns {AndroidRemoteInput}
|
||||
*/
|
||||
setChoices(choices: string[]): AndroidRemoteInput {
|
||||
this._choices = choices;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param label
|
||||
* @returns {AndroidRemoteInput}
|
||||
*/
|
||||
setLabel(label: string): AndroidRemoteInput {
|
||||
this._label = label;
|
||||
return this;
|
||||
}
|
||||
|
||||
build(): NativeAndroidRemoteInput {
|
||||
if (!this._resultKey) {
|
||||
throw new Error(
|
||||
'AndroidRemoteInput: Missing required `resultKey` property'
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
allowedDataTypes: this._allowedDataTypes,
|
||||
allowFreeFormInput: this._allowFreeFormInput,
|
||||
choices: this._choices,
|
||||
label: this._label,
|
||||
resultKey: this._resultKey,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export const fromNativeAndroidRemoteInput = (
|
||||
nativeRemoteInput: NativeAndroidRemoteInput
|
||||
): AndroidRemoteInput => {
|
||||
const remoteInput = new AndroidRemoteInput(nativeRemoteInput.resultKey);
|
||||
if (nativeRemoteInput.allowDataType) {
|
||||
for (let i = 0; i < nativeRemoteInput.allowDataType.length; i++) {
|
||||
const allowDataType = nativeRemoteInput.allowDataType[i];
|
||||
remoteInput.setAllowDataType(allowDataType.mimeType, allowDataType.allow);
|
||||
}
|
||||
}
|
||||
if (nativeRemoteInput.allowFreeFormInput) {
|
||||
remoteInput.setAllowFreeFormInput(nativeRemoteInput.allowFreeFormInput);
|
||||
}
|
||||
if (nativeRemoteInput.choices) {
|
||||
remoteInput.setChoices(nativeRemoteInput.choices);
|
||||
}
|
||||
if (nativeRemoteInput.label) {
|
||||
remoteInput.setLabel(nativeRemoteInput.label);
|
||||
}
|
||||
|
||||
return remoteInput;
|
||||
};
|
|
@ -0,0 +1,160 @@
|
|||
/**
|
||||
* @flow
|
||||
* IOSNotification representation wrapper
|
||||
*/
|
||||
import type Notification from './Notification';
|
||||
import type {
|
||||
IOSAttachment,
|
||||
IOSAttachmentOptions,
|
||||
NativeIOSNotification,
|
||||
} from './types';
|
||||
|
||||
export default class IOSNotification {
|
||||
_alertAction: string | void; // alertAction | N/A
|
||||
_attachments: IOSAttachment[]; // N/A | attachments
|
||||
_badge: number | void; // applicationIconBadgeNumber | badge
|
||||
_category: string | void;
|
||||
_hasAction: boolean | void; // hasAction | N/A
|
||||
_launchImage: string | void; // alertLaunchImage | launchImageName
|
||||
_notification: Notification;
|
||||
_threadIdentifier: string | void; // N/A | threadIdentifier
|
||||
|
||||
constructor(notification: Notification, data?: NativeIOSNotification) {
|
||||
this._notification = notification;
|
||||
|
||||
if (data) {
|
||||
this._alertAction = data.alertAction;
|
||||
this._attachments = data.attachments;
|
||||
this._badge = data.badge;
|
||||
this._category = data.category;
|
||||
this._hasAction = data.hasAction;
|
||||
this._launchImage = data.launchImage;
|
||||
this._threadIdentifier = data.threadIdentifier;
|
||||
}
|
||||
|
||||
// Defaults
|
||||
this._attachments = this._attachments || [];
|
||||
}
|
||||
|
||||
get alertAction(): ?string {
|
||||
return this._alertAction;
|
||||
}
|
||||
|
||||
get attachments(): IOSAttachment[] {
|
||||
return this._attachments;
|
||||
}
|
||||
|
||||
get badge(): ?number {
|
||||
return this._badge;
|
||||
}
|
||||
|
||||
get category(): ?string {
|
||||
return this._category;
|
||||
}
|
||||
|
||||
get hasAction(): ?boolean {
|
||||
return this._hasAction;
|
||||
}
|
||||
|
||||
get launchImage(): ?string {
|
||||
return this._launchImage;
|
||||
}
|
||||
|
||||
get threadIdentifier(): ?string {
|
||||
return this._threadIdentifier;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param identifier
|
||||
* @param url
|
||||
* @param options
|
||||
* @returns {Notification}
|
||||
*/
|
||||
addAttachment(
|
||||
identifier: string,
|
||||
url: string,
|
||||
options?: IOSAttachmentOptions
|
||||
): Notification {
|
||||
this._attachments.push({
|
||||
identifier,
|
||||
options,
|
||||
url,
|
||||
});
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param alertAction
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setAlertAction(alertAction: string): Notification {
|
||||
this._alertAction = alertAction;
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param badge
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setBadge(badge: number): Notification {
|
||||
this._badge = badge;
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param category
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setCategory(category: string): Notification {
|
||||
this._category = category;
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param hasAction
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setHasAction(hasAction: boolean): Notification {
|
||||
this._hasAction = hasAction;
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param launchImage
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setLaunchImage(launchImage: string): Notification {
|
||||
this._launchImage = launchImage;
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param threadIdentifier
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setThreadIdentifier(threadIdentifier: string): Notification {
|
||||
this._threadIdentifier = threadIdentifier;
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
build(): NativeIOSNotification {
|
||||
// TODO: Validation of required fields
|
||||
|
||||
return {
|
||||
alertAction: this._alertAction,
|
||||
attachments: this._attachments,
|
||||
badge: this._badge,
|
||||
category: this._category,
|
||||
hasAction: this._hasAction,
|
||||
launchImage: this._launchImage,
|
||||
threadIdentifier: this._threadIdentifier,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,169 @@
|
|||
/**
|
||||
* @flow
|
||||
* Notification representation wrapper
|
||||
*/
|
||||
import { Platform } from 'react-native';
|
||||
import AndroidNotification from './AndroidNotification';
|
||||
import IOSNotification from './IOSNotification';
|
||||
import { generatePushID, isObject } from '../../utils';
|
||||
|
||||
import type { NativeNotification } from './types';
|
||||
|
||||
export type NotificationOpen = {|
|
||||
action: string,
|
||||
notification: Notification,
|
||||
results?: { [string]: string },
|
||||
|};
|
||||
|
||||
export default class Notification {
|
||||
// iOS 8/9 | 10+ | Android
|
||||
_android: AndroidNotification;
|
||||
_body: string; // alertBody | body | contentText
|
||||
_data: { [string]: string }; // userInfo | userInfo | extras
|
||||
_ios: IOSNotification;
|
||||
_notificationId: string;
|
||||
_sound: string | void; // soundName | sound | sound
|
||||
_subtitle: string | void; // N/A | subtitle | subText
|
||||
_title: string; // alertTitle | title | contentTitle
|
||||
|
||||
constructor(data?: NativeNotification) {
|
||||
this._android = new AndroidNotification(this, data && data.android);
|
||||
this._ios = new IOSNotification(this, data && data.ios);
|
||||
|
||||
if (data) {
|
||||
this._body = data.body;
|
||||
this._data = data.data;
|
||||
this._notificationId = data.notificationId;
|
||||
this._sound = data.sound;
|
||||
this._subtitle = data.subtitle;
|
||||
this._title = data.title;
|
||||
}
|
||||
|
||||
// Defaults
|
||||
this._data = this._data || {};
|
||||
// TODO: Is this the best way to generate an ID?
|
||||
this._notificationId = this._notificationId || generatePushID();
|
||||
}
|
||||
|
||||
get android(): AndroidNotification {
|
||||
return this._android;
|
||||
}
|
||||
|
||||
get body(): string {
|
||||
return this._body;
|
||||
}
|
||||
|
||||
get data(): { [string]: string } {
|
||||
return this._data;
|
||||
}
|
||||
|
||||
get ios(): IOSNotification {
|
||||
return this._ios;
|
||||
}
|
||||
|
||||
get notificationId(): string {
|
||||
return this._notificationId;
|
||||
}
|
||||
|
||||
get sound(): ?string {
|
||||
return this._sound;
|
||||
}
|
||||
|
||||
get subtitle(): ?string {
|
||||
return this._subtitle;
|
||||
}
|
||||
|
||||
get title(): string {
|
||||
return this._title;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param body
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setBody(body: string): Notification {
|
||||
this._body = body;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param data
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setData(data: Object = {}): Notification {
|
||||
if (!isObject(data)) {
|
||||
throw new Error(
|
||||
`Notification:withData expects an object but got type '${typeof data}'.`
|
||||
);
|
||||
}
|
||||
this._data = data;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param notificationId
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setNotificationId(notificationId: string): Notification {
|
||||
this._notificationId = notificationId;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param sound
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setSound(sound: string): Notification {
|
||||
this._sound = sound;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param subtitle
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setSubtitle(subtitle: string): Notification {
|
||||
this._subtitle = subtitle;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param title
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setTitle(title: string): Notification {
|
||||
this._title = title;
|
||||
return this;
|
||||
}
|
||||
|
||||
build(): NativeNotification {
|
||||
// Android required fields: body, title, smallicon
|
||||
// iOS required fields: TODO
|
||||
if (!this._body) {
|
||||
throw new Error('Notification: Missing required `body` property');
|
||||
} else if (!this._notificationId) {
|
||||
throw new Error(
|
||||
'Notification: Missing required `notificationId` property'
|
||||
);
|
||||
} else if (!this._title) {
|
||||
throw new Error('Notification: Missing required `title` property');
|
||||
}
|
||||
|
||||
return {
|
||||
android: Platform.OS === 'android' ? this._android.build() : undefined,
|
||||
body: this._body,
|
||||
data: this._data,
|
||||
ios: Platform.OS === 'ios' ? this._ios.build() : undefined,
|
||||
notificationId: this._notificationId,
|
||||
sound: this._sound,
|
||||
subtitle: this._subtitle,
|
||||
title: this._title,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,318 @@
|
|||
/**
|
||||
* @flow
|
||||
* Notifications representation wrapper
|
||||
*/
|
||||
import { SharedEventEmitter } from '../../utils/events';
|
||||
import { getLogger } from '../../utils/log';
|
||||
import ModuleBase from '../../utils/ModuleBase';
|
||||
import { getNativeModule } from '../../utils/native';
|
||||
import { isFunction, isObject } from '../../utils';
|
||||
import AndroidAction from './AndroidAction';
|
||||
import AndroidChannel from './AndroidChannel';
|
||||
import AndroidChannelGroup from './AndroidChannelGroup';
|
||||
import AndroidNotifications from './AndroidNotifications';
|
||||
import AndroidRemoteInput from './AndroidRemoteInput';
|
||||
import Notification from './Notification';
|
||||
import {
|
||||
BadgeIconType,
|
||||
Category,
|
||||
Defaults,
|
||||
GroupAlert,
|
||||
Importance,
|
||||
Priority,
|
||||
SemanticAction,
|
||||
Visibility,
|
||||
} from './types';
|
||||
|
||||
import type App from '../core/app';
|
||||
import type { NotificationOpen } from './Notification';
|
||||
import type {
|
||||
NativeNotification,
|
||||
NativeNotificationOpen,
|
||||
Schedule,
|
||||
} from './types';
|
||||
|
||||
type OnNotification = Notification => any;
|
||||
|
||||
type OnNotificationObserver = {
|
||||
next: OnNotification,
|
||||
};
|
||||
|
||||
type OnNotificationOpened = NotificationOpen => any;
|
||||
|
||||
type OnNotificationOpenedObserver = {
|
||||
next: NotificationOpen,
|
||||
};
|
||||
|
||||
const NATIVE_EVENTS = [
|
||||
'notifications_notification_displayed',
|
||||
'notifications_notification_opened',
|
||||
'notifications_notification_received',
|
||||
];
|
||||
|
||||
export const MODULE_NAME = 'RNFirebaseNotifications';
|
||||
export const NAMESPACE = 'notifications';
|
||||
|
||||
// iOS 8/9 scheduling
|
||||
// fireDate: Date;
|
||||
// timeZone: TimeZone;
|
||||
// repeatInterval: NSCalendar.Unit;
|
||||
// repeatCalendar: Calendar;
|
||||
// region: CLRegion;
|
||||
// regionTriggersOnce: boolean;
|
||||
|
||||
// iOS 10 scheduling
|
||||
// TODO
|
||||
|
||||
// Android scheduling
|
||||
// TODO
|
||||
|
||||
/**
|
||||
* @class Notifications
|
||||
*/
|
||||
export default class Notifications extends ModuleBase {
|
||||
_android: AndroidNotifications;
|
||||
|
||||
constructor(app: App) {
|
||||
super(app, {
|
||||
events: NATIVE_EVENTS,
|
||||
hasShards: false,
|
||||
moduleName: MODULE_NAME,
|
||||
multiApp: false,
|
||||
namespace: NAMESPACE,
|
||||
});
|
||||
this._android = new AndroidNotifications(this);
|
||||
|
||||
SharedEventEmitter.addListener(
|
||||
// sub to internal native event - this fans out to
|
||||
// public event name: onNotificationDisplayed
|
||||
'notifications_notification_displayed',
|
||||
(notification: NativeNotification) => {
|
||||
SharedEventEmitter.emit(
|
||||
'onNotificationDisplayed',
|
||||
new Notification(notification)
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
SharedEventEmitter.addListener(
|
||||
// sub to internal native event - this fans out to
|
||||
// public event name: onNotificationOpened
|
||||
'notifications_notification_opened',
|
||||
(notificationOpen: NativeNotificationOpen) => {
|
||||
SharedEventEmitter.emit('onNotificationOpened', {
|
||||
action: notificationOpen.action,
|
||||
notification: new Notification(notificationOpen.notification),
|
||||
results: notificationOpen.results,
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
SharedEventEmitter.addListener(
|
||||
// sub to internal native event - this fans out to
|
||||
// public event name: onNotification
|
||||
'notifications_notification_received',
|
||||
(notification: NativeNotification) => {
|
||||
SharedEventEmitter.emit(
|
||||
'onNotification',
|
||||
new Notification(notification)
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
get android(): AndroidNotifications {
|
||||
return this._android;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel all notifications
|
||||
*/
|
||||
cancelAllNotifications(): void {
|
||||
getNativeModule(this).cancelAllNotifications();
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel a notification by id.
|
||||
* @param notificationId
|
||||
*/
|
||||
cancelNotification(notificationId: string): void {
|
||||
if (!notificationId) {
|
||||
throw new Error(
|
||||
'Notifications: cancelNotification expects a `notificationId`'
|
||||
);
|
||||
}
|
||||
getNativeModule(this).cancelNotification(notificationId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a notification
|
||||
* @param notification
|
||||
* @returns {*}
|
||||
*/
|
||||
displayNotification(notification: Notification): Promise<void> {
|
||||
if (!(notification instanceof Notification)) {
|
||||
throw new Error(
|
||||
`Notifications:displayNotification expects a 'Notification' but got type ${typeof notification}`
|
||||
);
|
||||
}
|
||||
return getNativeModule(this).displayNotification(notification.build());
|
||||
}
|
||||
|
||||
getBadge(): Promise<number> {
|
||||
return getNativeModule(this).getBadge();
|
||||
}
|
||||
|
||||
getInitialNotification(): Promise<NotificationOpen> {
|
||||
return getNativeModule(this)
|
||||
.getInitialNotification()
|
||||
.then((notificationOpen: NativeNotificationOpen) => {
|
||||
if (notificationOpen) {
|
||||
return {
|
||||
action: notificationOpen.action,
|
||||
notification: new Notification(notificationOpen.notification),
|
||||
results: notificationOpen.results,
|
||||
};
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of all scheduled notifications
|
||||
* @returns {Promise.<Array>}
|
||||
*/
|
||||
getScheduledNotifications(): Promise<Notification[]> {
|
||||
return getNativeModule(this).getScheduledNotifications();
|
||||
}
|
||||
|
||||
onNotification(
|
||||
nextOrObserver: OnNotification | OnNotificationObserver
|
||||
): () => any {
|
||||
let listener;
|
||||
if (isFunction(nextOrObserver)) {
|
||||
listener = nextOrObserver;
|
||||
} else if (isObject(nextOrObserver) && isFunction(nextOrObserver.next)) {
|
||||
listener = nextOrObserver.next;
|
||||
} else {
|
||||
throw new Error(
|
||||
'Notifications.onNotification failed: First argument must be a function or observer object with a `next` function.'
|
||||
);
|
||||
}
|
||||
|
||||
getLogger(this).info('Creating onNotification listener');
|
||||
SharedEventEmitter.addListener('onNotification', listener);
|
||||
|
||||
return () => {
|
||||
getLogger(this).info('Removing onNotification listener');
|
||||
SharedEventEmitter.removeListener('onNotification', listener);
|
||||
};
|
||||
}
|
||||
|
||||
onNotificationDisplayed(
|
||||
nextOrObserver: OnNotification | OnNotificationObserver
|
||||
): () => any {
|
||||
let listener;
|
||||
if (isFunction(nextOrObserver)) {
|
||||
listener = nextOrObserver;
|
||||
} else if (isObject(nextOrObserver) && isFunction(nextOrObserver.next)) {
|
||||
listener = nextOrObserver.next;
|
||||
} else {
|
||||
throw new Error(
|
||||
'Notifications.onNotificationDisplayed failed: First argument must be a function or observer object with a `next` function.'
|
||||
);
|
||||
}
|
||||
|
||||
getLogger(this).info('Creating onNotificationDisplayed listener');
|
||||
SharedEventEmitter.addListener('onNotificationDisplayed', listener);
|
||||
|
||||
return () => {
|
||||
getLogger(this).info('Removing onNotificationDisplayed listener');
|
||||
SharedEventEmitter.removeListener('onNotificationDisplayed', listener);
|
||||
};
|
||||
}
|
||||
|
||||
onNotificationOpened(
|
||||
nextOrObserver: OnNotificationOpened | OnNotificationOpenedObserver
|
||||
): () => any {
|
||||
let listener;
|
||||
if (isFunction(nextOrObserver)) {
|
||||
listener = nextOrObserver;
|
||||
} else if (isObject(nextOrObserver) && isFunction(nextOrObserver.next)) {
|
||||
listener = nextOrObserver.next;
|
||||
} else {
|
||||
throw new Error(
|
||||
'Notifications.onNotificationOpened failed: First argument must be a function or observer object with a `next` function.'
|
||||
);
|
||||
}
|
||||
|
||||
getLogger(this).info('Creating onNotificationOpened listener');
|
||||
SharedEventEmitter.addListener('onNotificationOpened', listener);
|
||||
|
||||
return () => {
|
||||
getLogger(this).info('Removing onNotificationOpened listener');
|
||||
SharedEventEmitter.removeListener('onNotificationOpened', listener);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all delivered notifications.
|
||||
*/
|
||||
removeAllDeliveredNotifications(): void {
|
||||
getNativeModule(this).removeAllDeliveredNotifications();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a delivered notification.
|
||||
* @param notificationId
|
||||
*/
|
||||
removeDeliveredNotification(notificationId: string): void {
|
||||
if (!notificationId) {
|
||||
throw new Error(
|
||||
'Notifications: removeDeliveredNotification expects a `notificationId`'
|
||||
);
|
||||
}
|
||||
getNativeModule(this).removeDeliveredNotification(notificationId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule a notification
|
||||
* @param notification
|
||||
* @returns {*}
|
||||
*/
|
||||
scheduleNotification(
|
||||
notification: Notification,
|
||||
schedule: Schedule
|
||||
): Promise<void> {
|
||||
if (!(notification instanceof Notification)) {
|
||||
throw new Error(
|
||||
`Notifications:scheduleNotification expects a 'Notification' but got type ${typeof notification}`
|
||||
);
|
||||
}
|
||||
const nativeNotification = notification.build();
|
||||
nativeNotification.schedule = schedule;
|
||||
return getNativeModule(this).scheduleNotification(nativeNotification);
|
||||
}
|
||||
|
||||
setBadge(badge: number): void {
|
||||
getNativeModule(this).setBadge(badge);
|
||||
}
|
||||
}
|
||||
|
||||
export const statics = {
|
||||
Android: {
|
||||
Action: AndroidAction,
|
||||
BadgeIconType,
|
||||
Category,
|
||||
Channel: AndroidChannel,
|
||||
ChannelGroup: AndroidChannelGroup,
|
||||
Defaults,
|
||||
GroupAlert,
|
||||
Importance,
|
||||
Priority,
|
||||
RemoteInput: AndroidRemoteInput,
|
||||
SemanticAction,
|
||||
Visibility,
|
||||
},
|
||||
Notification,
|
||||
};
|
|
@ -0,0 +1,216 @@
|
|||
/**
|
||||
* @flow
|
||||
*/
|
||||
export const BadgeIconType = {
|
||||
Large: 2,
|
||||
None: 0,
|
||||
Small: 1,
|
||||
};
|
||||
|
||||
export const Category = {
|
||||
Alarm: 'alarm',
|
||||
Call: 'call',
|
||||
Email: 'email',
|
||||
Error: 'err',
|
||||
Event: 'event',
|
||||
Message: 'msg',
|
||||
Progress: 'progress',
|
||||
Promo: 'promo',
|
||||
Recommendation: 'recommendation',
|
||||
Reminder: 'reminder',
|
||||
Service: 'service',
|
||||
Social: 'social',
|
||||
Status: 'status',
|
||||
System: 'system',
|
||||
Transport: 'transport',
|
||||
};
|
||||
|
||||
export const Defaults = {
|
||||
All: -1,
|
||||
Lights: 4,
|
||||
Sound: 1,
|
||||
Vibrate: 2,
|
||||
};
|
||||
|
||||
export const GroupAlert = {
|
||||
All: 0,
|
||||
Children: 2,
|
||||
Summary: 1,
|
||||
};
|
||||
|
||||
export const Importance = {
|
||||
Default: 3,
|
||||
High: 4,
|
||||
Low: 2,
|
||||
Max: 5,
|
||||
Min: 1,
|
||||
None: 3,
|
||||
Unspecified: -1000,
|
||||
};
|
||||
|
||||
export const Priority = {
|
||||
Default: 0,
|
||||
High: 1,
|
||||
Low: -1,
|
||||
Max: 2,
|
||||
Min: -2,
|
||||
};
|
||||
|
||||
export const SemanticAction = {
|
||||
Archive: 5,
|
||||
Call: 10,
|
||||
Delete: 4,
|
||||
MarkAsRead: 2,
|
||||
MarkAsUnread: 3,
|
||||
Mute: 6,
|
||||
None: 0,
|
||||
Reply: 1,
|
||||
ThumbsDown: 9,
|
||||
ThumbsUp: 8,
|
||||
Unmute: 7,
|
||||
};
|
||||
|
||||
export const Visibility = {
|
||||
Private: 0,
|
||||
Public: 1,
|
||||
Secret: -1,
|
||||
};
|
||||
|
||||
export type BadgeIconTypeType = $Values<typeof BadgeIconType>;
|
||||
export type CategoryType = $Values<typeof Category>;
|
||||
export type DefaultsType = $Values<typeof Defaults>;
|
||||
export type GroupAlertType = $Values<typeof GroupAlert>;
|
||||
export type ImportanceType = $Values<typeof Importance>;
|
||||
export type PriorityType = $Values<typeof Priority>;
|
||||
export type SemanticActionType = $Values<typeof SemanticAction>;
|
||||
export type VisibilityType = $Values<typeof Visibility>;
|
||||
|
||||
export type Lights = {|
|
||||
argb: number,
|
||||
onMs: number,
|
||||
offMs: number,
|
||||
|};
|
||||
|
||||
export type Progress = {|
|
||||
max: number,
|
||||
progress: number,
|
||||
indeterminate: boolean,
|
||||
|};
|
||||
|
||||
export type SmallIcon = {|
|
||||
icon: string,
|
||||
level?: number,
|
||||
|};
|
||||
|
||||
export type AndroidAllowDataType = {
|
||||
allow: boolean,
|
||||
mimeType: string,
|
||||
};
|
||||
|
||||
export type NativeAndroidRemoteInput = {|
|
||||
allowedDataTypes: AndroidAllowDataType[],
|
||||
allowFreeFormInput?: boolean,
|
||||
choices: string[],
|
||||
label?: string,
|
||||
resultKey: string,
|
||||
|};
|
||||
|
||||
export type NativeAndroidAction = {|
|
||||
action: string,
|
||||
allowGeneratedReplies?: boolean,
|
||||
icon: string,
|
||||
remoteInputs: NativeAndroidRemoteInput[],
|
||||
semanticAction?: SemanticActionType,
|
||||
showUserInterface?: boolean,
|
||||
title: string,
|
||||
|};
|
||||
|
||||
export type NativeAndroidNotification = {|
|
||||
actions?: NativeAndroidAction[],
|
||||
autoCancel?: boolean,
|
||||
badgeIconType?: BadgeIconTypeType,
|
||||
category?: CategoryType,
|
||||
channelId: string,
|
||||
clickAction?: string,
|
||||
color?: string,
|
||||
colorized?: boolean,
|
||||
contentInfo?: string,
|
||||
defaults?: DefaultsType[],
|
||||
group?: string,
|
||||
groupAlertBehaviour?: GroupAlertType,
|
||||
groupSummary?: boolean,
|
||||
largeIcon?: string,
|
||||
lights?: Lights,
|
||||
localOnly?: boolean,
|
||||
number?: number,
|
||||
ongoing?: boolean,
|
||||
onlyAlertOnce?: boolean,
|
||||
people: string[],
|
||||
priority?: PriorityType,
|
||||
progress?: Progress,
|
||||
// publicVersion: Notification,
|
||||
remoteInputHistory?: string[],
|
||||
shortcutId?: string,
|
||||
showWhen?: boolean,
|
||||
smallIcon: SmallIcon,
|
||||
sortKey?: string,
|
||||
// TODO: style: Style,
|
||||
ticker?: string,
|
||||
timeoutAfter?: number,
|
||||
usesChronometer?: boolean,
|
||||
vibrate?: number[],
|
||||
visibility?: VisibilityType,
|
||||
when?: number,
|
||||
|};
|
||||
|
||||
export type IOSAttachmentOptions = {|
|
||||
typeHint: string,
|
||||
thumbnailHidden: boolean,
|
||||
thumbnailClippingRect: {
|
||||
height: number,
|
||||
width: number,
|
||||
x: number,
|
||||
y: number,
|
||||
},
|
||||
thumbnailTime: number,
|
||||
|};
|
||||
|
||||
export type IOSAttachment = {|
|
||||
identifier: string,
|
||||
options?: IOSAttachmentOptions,
|
||||
url: string,
|
||||
|};
|
||||
|
||||
export type NativeIOSNotification = {|
|
||||
alertAction?: string,
|
||||
attachments: IOSAttachment[],
|
||||
badge?: number,
|
||||
category?: string,
|
||||
hasAction?: boolean,
|
||||
launchImage?: string,
|
||||
threadIdentifier?: string,
|
||||
|};
|
||||
|
||||
export type Schedule = {|
|
||||
exact?: boolean,
|
||||
fireDate: number,
|
||||
repeatInterval?: 'minute' | 'hour' | 'day' | 'week',
|
||||
|};
|
||||
|
||||
export type NativeNotification = {|
|
||||
android?: NativeAndroidNotification,
|
||||
body: string,
|
||||
data: { [string]: string },
|
||||
ios?: NativeIOSNotification,
|
||||
notificationId: string,
|
||||
schedule?: Schedule,
|
||||
sound?: string,
|
||||
subtitle?: string,
|
||||
title: string,
|
||||
|};
|
||||
|
||||
export type NativeNotificationOpen = {|
|
||||
action: string,
|
||||
notification: NativeNotification,
|
||||
results?: { [string]: string },
|
||||
|};
|
|
@ -16,6 +16,7 @@ export default class PerformanceMonitoring extends ModuleBase {
|
|||
super(app, {
|
||||
moduleName: MODULE_NAME,
|
||||
multiApp: false,
|
||||
hasShards: false,
|
||||
namespace: NAMESPACE,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ export default class Storage extends ModuleBase {
|
|||
events: NATIVE_EVENTS,
|
||||
moduleName: MODULE_NAME,
|
||||
multiApp: true,
|
||||
hasShards: false,
|
||||
namespace: NAMESPACE,
|
||||
});
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ export default class RNFirebaseUtils extends ModuleBase {
|
|||
super(app, {
|
||||
moduleName: MODULE_NAME,
|
||||
multiApp: false,
|
||||
hasShards: false,
|
||||
namespace: NAMESPACE,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -15,10 +15,16 @@ import type Database from '../modules/database';
|
|||
import { typeof statics as DatabaseStatics } from '../modules/database';
|
||||
import type Firestore from '../modules/firestore';
|
||||
import { typeof statics as FirestoreStatics } from '../modules/firestore';
|
||||
import type InstanceId from '../modules/instanceid';
|
||||
import { typeof statics as InstanceIdStatics } from '../modules/instanceid';
|
||||
import type Invites from '../modules/invites';
|
||||
import { typeof statics as InvitesStatics } from '../modules/invites';
|
||||
import type Links from '../modules/links';
|
||||
import { typeof statics as LinksStatics } from '../modules/links';
|
||||
import type Messaging from '../modules/messaging';
|
||||
import { typeof statics as MessagingStatics } from '../modules/messaging';
|
||||
import type Notifications from '../modules/notifications';
|
||||
import { typeof statics as NotificationsStatics } from '../modules/notifications';
|
||||
import type ModuleBase from '../utils/ModuleBase';
|
||||
import type Performance from '../modules/perf';
|
||||
import { typeof statics as PerformanceStatics } from '../modules/perf';
|
||||
|
@ -44,6 +50,7 @@ export type FirebaseModuleConfig = {
|
|||
events?: string[],
|
||||
moduleName: FirebaseModuleName,
|
||||
multiApp: boolean,
|
||||
hasShards: boolean,
|
||||
namespace: FirebaseNamespace,
|
||||
};
|
||||
|
||||
|
@ -56,8 +63,11 @@ export type FirebaseModuleName =
|
|||
| 'RNFirebaseCrashlytics'
|
||||
| 'RNFirebaseDatabase'
|
||||
| 'RNFirebaseFirestore'
|
||||
| 'RNFirebaseInstanceId'
|
||||
| 'RNFirebaseInvites'
|
||||
| 'RNFirebaseLinks'
|
||||
| 'RNFirebaseMessaging'
|
||||
| 'RNFirebaseNotifications'
|
||||
| 'RNFirebasePerformance'
|
||||
| 'RNFirebaseStorage'
|
||||
| 'RNFirebaseUtils';
|
||||
|
@ -71,8 +81,11 @@ export type FirebaseNamespace =
|
|||
| 'crashlytics'
|
||||
| 'database'
|
||||
| 'firestore'
|
||||
| 'instanceid'
|
||||
| 'invites'
|
||||
| 'links'
|
||||
| 'messaging'
|
||||
| 'notifications'
|
||||
| 'perf'
|
||||
| 'storage'
|
||||
| 'utils';
|
||||
|
@ -162,6 +175,20 @@ export type FirestoreModule = {
|
|||
nativeModuleExists: boolean,
|
||||
} & FirestoreStatics;
|
||||
|
||||
/* InstanceId types */
|
||||
|
||||
export type InstanceIdModule = {
|
||||
(): InstanceId,
|
||||
nativeModuleExists: boolean,
|
||||
} & InstanceIdStatics;
|
||||
|
||||
/* Invites types */
|
||||
|
||||
export type InvitesModule = {
|
||||
(): Invites,
|
||||
nativeModuleExists: boolean,
|
||||
} & InvitesStatics;
|
||||
|
||||
/* Links types */
|
||||
|
||||
export type LinksModule = {
|
||||
|
@ -176,6 +203,13 @@ export type MessagingModule = {
|
|||
nativeModuleExists: boolean,
|
||||
} & MessagingStatics;
|
||||
|
||||
/* Notifications types */
|
||||
|
||||
export type NotificationsModule = {
|
||||
(): Notifications,
|
||||
nativeModuleExists: boolean,
|
||||
} & NotificationsStatics;
|
||||
|
||||
/* Performance types */
|
||||
|
||||
export type PerformanceModule = {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue