Merge branch 'fcm-rewrite-separate-events' into fcm-rewrite
This commit is contained in:
commit
aa367e7be8
|
@ -1,49 +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 setApplicationForeground(boolean foreground){
|
||||
mIsForeground = foreground;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,16 +1,12 @@
|
|||
package io.invertase.firebase.messaging;
|
||||
|
||||
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.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;
|
||||
|
@ -18,32 +14,20 @@ 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 me.leolin.shortcutbadger.ShortcutBadger;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class RNFirebaseMessaging extends ReactContextBaseJavaModule implements ActivityEventListener {
|
||||
private static final String BADGE_FILE = "BadgeCountFile";
|
||||
private static final String BADGE_KEY = "BadgeCount";
|
||||
|
||||
public class RNFirebaseMessaging extends ReactContextBaseJavaModule {
|
||||
private static final String TAG = "RNFirebaseMessaging";
|
||||
|
||||
private SharedPreferences sharedPreferences = null;
|
||||
|
||||
public RNFirebaseMessaging(ReactApplicationContext context) {
|
||||
super(context);
|
||||
context.addActivityEventListener(this);
|
||||
|
||||
sharedPreferences = context.getSharedPreferences(BADGE_FILE, Context.MODE_PRIVATE);
|
||||
|
||||
LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(context);
|
||||
|
||||
// Subscribe to message events
|
||||
|
@ -73,24 +57,6 @@ public class RNFirebaseMessaging extends ReactContextBaseJavaModule implements A
|
|||
}
|
||||
|
||||
// Non Web SDK methods
|
||||
|
||||
@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 getInitialMessage(Promise promise) {
|
||||
if (getCurrentActivity() == null) {
|
||||
promise.resolve(null);
|
||||
} else {
|
||||
WritableMap messageMap = parseIntentForMessage(getCurrentActivity().getIntent());
|
||||
promise.resolve(messageMap);
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void hasPermission(Promise promise) {
|
||||
promise.resolve(true);
|
||||
|
@ -132,19 +98,6 @@ public class RNFirebaseMessaging extends ReactContextBaseJavaModule implements A
|
|||
promise.resolve(null);
|
||||
}
|
||||
|
||||
@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 subscribeToTopic(String topic) {
|
||||
FirebaseMessaging.getInstance().subscribeToTopic(topic);
|
||||
|
@ -155,60 +108,6 @@ public class RNFirebaseMessaging extends ReactContextBaseJavaModule implements A
|
|||
FirebaseMessaging.getInstance().unsubscribeFromTopic(topic);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// 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 messageMap = parseIntentForMessage(intent);
|
||||
if (messageMap != null) {
|
||||
Log.d(TAG, "onNewIntent called with new FCM message");
|
||||
Utils.sendEvent(getReactApplicationContext(), "messaging_message_received", messageMap);
|
||||
}
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// End ActivityEventListener methods
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
private WritableMap parseIntentForMessage(Intent intent) {
|
||||
// Check if FCM data exists
|
||||
if (intent.getExtras() == null || !intent.hasExtra("google.message_id")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Bundle extras = intent.getExtras();
|
||||
|
||||
WritableMap messageMap = Arguments.createMap();
|
||||
WritableMap dataMap = Arguments.createMap();
|
||||
|
||||
for (String key : extras.keySet()) {
|
||||
if (key.equals("collapse_key")) {
|
||||
messageMap.putString("collapseKey", extras.getString("collapse_key"));
|
||||
} else if (key.equals("from")) {
|
||||
messageMap.putString("from", extras.getString("from"));
|
||||
} else if (key.equals("google.message_id")) {
|
||||
messageMap.putString("messageId", extras.getString("google.message_id"));
|
||||
} else if (key.equals("google.sent_time")) {
|
||||
messageMap.putDouble("sentTime", extras.getLong("google.sent_time"));
|
||||
} else if (key.equals("google.ttl")) {
|
||||
messageMap.putDouble("ttl", extras.getDouble("google.ttl"));
|
||||
} else if (key.equals("_fbSourceApplicationHasBeenSet")) {
|
||||
// ignore known unneeded fields
|
||||
} else {
|
||||
dataMap.putString(key, extras.getString(key));
|
||||
}
|
||||
}
|
||||
messageMap.putMap("data", dataMap);
|
||||
messageMap.putBoolean("openedFromTray", true);
|
||||
|
||||
return messageMap;
|
||||
}
|
||||
|
||||
private class MessageReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
|
@ -246,67 +145,10 @@ public class RNFirebaseMessaging extends ReactContextBaseJavaModule implements A
|
|||
if (message.getMessageType() != null) {
|
||||
messageMap.putString("messageType", message.getMessageType());
|
||||
}
|
||||
|
||||
if (message.getNotification() != null) {
|
||||
Notification notification = message.getNotification();
|
||||
|
||||
WritableMap notificationMap = Arguments.createMap();
|
||||
|
||||
if (notification.getBody() != null) {
|
||||
notificationMap.putString("body", notification.getBody());
|
||||
}
|
||||
if (notification.getBodyLocalizationArgs() != null) {
|
||||
WritableArray bodyArgs = Arguments.createArray();
|
||||
for (String arg : notification.getBodyLocalizationArgs()) {
|
||||
bodyArgs.pushString(arg);
|
||||
}
|
||||
notificationMap.putArray("bodyLocalizationArgs", bodyArgs);
|
||||
}
|
||||
if (notification.getBodyLocalizationKey() != null) {
|
||||
notificationMap.putString("bodyLocalizationKey", notification.getBodyLocalizationKey());
|
||||
}
|
||||
if (notification.getClickAction() != null) {
|
||||
notificationMap.putString("clickAction", notification.getClickAction());
|
||||
}
|
||||
if (notification.getColor() != null) {
|
||||
notificationMap.putString("color", notification.getColor());
|
||||
}
|
||||
if (notification.getIcon() != null) {
|
||||
notificationMap.putString("icon", notification.getIcon());
|
||||
}
|
||||
if (notification.getLink() != null) {
|
||||
notificationMap.putString("link", notification.getLink().toString());
|
||||
}
|
||||
if (notification.getSound() != null) {
|
||||
notificationMap.putString("sound", notification.getSound());
|
||||
}
|
||||
if (notification.getTag() != null) {
|
||||
notificationMap.putString("tag", notification.getTag());
|
||||
}
|
||||
if (notification.getTitle() != null) {
|
||||
notificationMap.putString("title", notification.getTitle());
|
||||
}
|
||||
if (notification.getTitleLocalizationArgs() != null) {
|
||||
WritableArray titleArgs = Arguments.createArray();
|
||||
for (String arg : notification.getTitleLocalizationArgs()) {
|
||||
titleArgs.pushString(arg);
|
||||
}
|
||||
notificationMap.putArray("titleLocalizationArgs", titleArgs);
|
||||
}
|
||||
if (notification.getTitleLocalizationKey() != null) {
|
||||
notificationMap.putString("titleLocalizationKey", notification.getTitleLocalizationKey());
|
||||
}
|
||||
|
||||
messageMap.putMap("notification", notificationMap);
|
||||
}
|
||||
|
||||
messageMap.putBoolean("openedFromTray", false);
|
||||
messageMap.putDouble("sentTime", message.getSentTime());
|
||||
|
||||
if (message.getTo() != null) {
|
||||
messageMap.putString("to", message.getTo());
|
||||
}
|
||||
|
||||
messageMap.putDouble("ttl", message.getTtl());
|
||||
|
||||
return messageMap;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -10,16 +10,25 @@ import com.google.firebase.messaging.RemoteMessage;
|
|||
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");
|
||||
|
||||
// Build an Intent to pass the token to the RN Application
|
||||
Intent messageEvent = new Intent(MESSAGE_EVENT);
|
||||
messageEvent.putExtra("message", message);
|
||||
Intent event;
|
||||
|
||||
if (message.getNotification() != null) {
|
||||
// It's a notification, pass to the notification module
|
||||
event = new Intent(REMOTE_NOTIFICATION_EVENT);
|
||||
event.putExtra("notification", message);
|
||||
} else {
|
||||
// It's a data message, pass to the messaging module
|
||||
event = new Intent(MESSAGE_EVENT);
|
||||
event.putExtra("message", message);
|
||||
}
|
||||
|
||||
// Broadcast it so it is only available to the RN Application
|
||||
LocalBroadcastManager.getInstance(this).sendBroadcast(messageEvent);
|
||||
LocalBroadcastManager.getInstance(this).sendBroadcast(event);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@ 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;
|
||||
|
@ -21,6 +23,7 @@ import android.util.Log;
|
|||
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
import com.facebook.react.bridge.Promise;
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
|
||||
import org.json.JSONException;
|
||||
|
@ -30,6 +33,7 @@ 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.messaging.BundleJSONConverter;
|
||||
|
@ -71,6 +75,42 @@ public class RNFirebaseNotificationManager {
|
|||
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);
|
||||
|
@ -155,12 +195,14 @@ public class RNFirebaseNotificationManager {
|
|||
return;
|
||||
}
|
||||
|
||||
String channelId = notification.getString("channelId");
|
||||
Bundle android = notification.getBundle("android");
|
||||
|
||||
String channelId = android.getString("channelId");
|
||||
String notificationId = notification.getString("notificationId");
|
||||
|
||||
NotificationCompat.Builder nb;
|
||||
// TODO: Change 27 to 'Build.VERSION_CODES.O_MR1' when using appsupport v27
|
||||
if (Build.VERSION.SDK_INT >= 27) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
nb = new NotificationCompat.Builder(context, channelId);
|
||||
} else {
|
||||
nb = new NotificationCompat.Builder(context);
|
||||
|
@ -173,16 +215,8 @@ public class RNFirebaseNotificationManager {
|
|||
nb = nb.setExtras(notification.getBundle("data"));
|
||||
}
|
||||
if (notification.containsKey("sound")) {
|
||||
String sound = notification.getString("sound");
|
||||
if (sound.equalsIgnoreCase("default")) {
|
||||
nb = nb.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION));
|
||||
} else {
|
||||
int soundResourceId = getResourceId("raw", sound);
|
||||
if (soundResourceId == 0) {
|
||||
soundResourceId = getResourceId("raw", sound.substring(0, sound.lastIndexOf('.')));
|
||||
}
|
||||
nb = nb.setSound(Uri.parse("android.resource://" + context.getPackageName() + "/" + soundResourceId));
|
||||
}
|
||||
Uri sound = getSound(notification.getString("sound"));
|
||||
nb = nb.setSound(sound);
|
||||
}
|
||||
if (notification.containsKey("subtitle")) {
|
||||
nb = nb.setSubText(notification.getString("subtitle"));
|
||||
|
@ -191,125 +225,138 @@ public class RNFirebaseNotificationManager {
|
|||
nb = nb.setContentTitle(notification.getString("title"));
|
||||
}
|
||||
|
||||
if (notification.containsKey("autoCancel")) {
|
||||
nb = nb.setAutoCancel(notification.getBoolean("autoCancel"));
|
||||
if (android.containsKey("autoCancel")) {
|
||||
nb = nb.setAutoCancel(android.getBoolean("autoCancel"));
|
||||
}
|
||||
if (notification.containsKey("badgeIconType")) {
|
||||
nb = nb.setBadgeIconType(notification.getInt("badgeIconType"));
|
||||
if (android.containsKey("badgeIconType")) {
|
||||
Double badgeIconType = android.getDouble("badgeIconType");
|
||||
nb = nb.setBadgeIconType(badgeIconType.intValue());
|
||||
}
|
||||
if (notification.containsKey("category")) {
|
||||
nb = nb.setCategory(notification.getString("category"));
|
||||
if (android.containsKey("category")) {
|
||||
nb = nb.setCategory(android.getString("category"));
|
||||
}
|
||||
if (notification.containsKey("color")) {
|
||||
String color = notification.getString("color");
|
||||
if (android.containsKey("color")) {
|
||||
String color = android.getString("color");
|
||||
nb = nb.setColor(Color.parseColor(color));
|
||||
}
|
||||
if (notification.containsKey("colorized")) {
|
||||
nb = nb.setColorized(notification.getBoolean("colorized"));
|
||||
if (android.containsKey("colorized")) {
|
||||
nb = nb.setColorized(android.getBoolean("colorized"));
|
||||
}
|
||||
if (notification.containsKey("contentInfo")) {
|
||||
nb = nb.setContentInfo(notification.getString("contentInfo"));
|
||||
if (android.containsKey("contentInfo")) {
|
||||
nb = nb.setContentInfo(android.getString("contentInfo"));
|
||||
}
|
||||
if (notification.containsKey("defaults")) {
|
||||
int[] defaultsArray = notification.getIntArray("defaults");
|
||||
double[] defaultsArray = android.getDoubleArray("defaults");
|
||||
int defaults = 0;
|
||||
for (int d : defaultsArray) {
|
||||
defaults |= d;
|
||||
for (Double d : defaultsArray) {
|
||||
defaults |= d.intValue();
|
||||
}
|
||||
nb = nb.setDefaults(defaults);
|
||||
}
|
||||
if (notification.containsKey("group")) {
|
||||
nb = nb.setGroup(notification.getString("group"));
|
||||
if (android.containsKey("group")) {
|
||||
nb = nb.setGroup(android.getString("group"));
|
||||
}
|
||||
if (notification.containsKey("groupAlertBehaviour")) {
|
||||
nb = nb.setGroupAlertBehavior(notification.getInt("groupAlertBehaviour"));
|
||||
if (android.containsKey("groupAlertBehaviour")) {
|
||||
Double groupAlertBehaviour = android.getDouble("groupAlertBehaviour");
|
||||
nb = nb.setGroupAlertBehavior(groupAlertBehaviour.intValue());
|
||||
}
|
||||
if (notification.containsKey("groupSummary")) {
|
||||
nb = nb.setGroupSummary(notification.getBoolean("groupSummary"));
|
||||
if (android.containsKey("groupSummary")) {
|
||||
nb = nb.setGroupSummary(android.getBoolean("groupSummary"));
|
||||
}
|
||||
if (notification.containsKey("largeIcon")) {
|
||||
Bitmap largeIcon = getBitmap(notification.getString("largeIcon"));
|
||||
if (android.containsKey("largeIcon")) {
|
||||
Bitmap largeIcon = getBitmap(android.getString("largeIcon"));
|
||||
if (largeIcon != null) {
|
||||
nb = nb.setLargeIcon(largeIcon);
|
||||
}
|
||||
}
|
||||
if (notification.containsKey("lights")) {
|
||||
Bundle lights = notification.getBundle("lights");
|
||||
nb = nb.setLights(lights.getInt("argb"), lights.getInt("onMs"), lights.getInt("offMs"));
|
||||
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 (notification.containsKey("localOnly")) {
|
||||
nb = nb.setLocalOnly(notification.getBoolean("localOnly"));
|
||||
if (android.containsKey("localOnly")) {
|
||||
nb = nb.setLocalOnly(android.getBoolean("localOnly"));
|
||||
}
|
||||
|
||||
if (notification.containsKey("number")) {
|
||||
nb = nb.setNumber(notification.getInt("number"));
|
||||
if (android.containsKey("number")) {
|
||||
Double number = android.getDouble("number");
|
||||
nb = nb.setNumber(number.intValue());
|
||||
}
|
||||
if (notification.containsKey("ongoing")) {
|
||||
nb = nb.setOngoing(notification.getBoolean("ongoing"));
|
||||
if (android.containsKey("ongoing")) {
|
||||
nb = nb.setOngoing(android.getBoolean("ongoing"));
|
||||
}
|
||||
if (notification.containsKey("onlyAlertOnce")) {
|
||||
nb = nb.setOngoing(notification.getBoolean("onlyAlertOnce"));
|
||||
if (android.containsKey("onlyAlertOnce")) {
|
||||
nb = nb.setOngoing(android.getBoolean("onlyAlertOnce"));
|
||||
}
|
||||
if (notification.containsKey("people")) {
|
||||
String[] people = notification.getStringArray("people");
|
||||
for (String person : people) {
|
||||
nb = nb.addPerson(person);
|
||||
if (android.containsKey("people")) {
|
||||
String[] people = android.getStringArray("people");
|
||||
if (people != null) {
|
||||
for (String person : people) {
|
||||
nb = nb.addPerson(person);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (notification.containsKey("priority")) {
|
||||
nb = nb.setPriority(notification.getInt("priority"));
|
||||
if (android.containsKey("priority")) {
|
||||
Double priority = android.getDouble("priority");
|
||||
nb = nb.setPriority(priority.intValue());
|
||||
}
|
||||
if (notification.containsKey("progress")) {
|
||||
Bundle progress = notification.getBundle("lights");
|
||||
nb = nb.setProgress(progress.getInt("max"), progress.getInt("progress"), progress.getBoolean("indeterminate"));
|
||||
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 (notification.containsKey("publicVersion")) {
|
||||
/* if (android.containsKey("publicVersion")) {
|
||||
nb = nb.setPublicVersion();
|
||||
} */
|
||||
if (notification.containsKey("remoteInputHistory")) {
|
||||
nb = nb.setRemoteInputHistory(notification.getStringArray("remoteInputHistory"));
|
||||
if (android.containsKey("remoteInputHistory")) {
|
||||
nb = nb.setRemoteInputHistory(android.getStringArray("remoteInputHistory"));
|
||||
}
|
||||
if (notification.containsKey("shortcutId")) {
|
||||
nb = nb.setShortcutId(notification.getString("shortcutId"));
|
||||
if (android.containsKey("shortcutId")) {
|
||||
nb = nb.setShortcutId(android.getString("shortcutId"));
|
||||
}
|
||||
if (notification.containsKey("showWhen")) {
|
||||
nb = nb.setShowWhen(notification.getBoolean("showWhen"));
|
||||
if (android.containsKey("showWhen")) {
|
||||
nb = nb.setShowWhen(android.getBoolean("showWhen"));
|
||||
}
|
||||
if (notification.containsKey("smallIcon")) {
|
||||
Bundle smallIcon = notification.getBundle("smallIcon");
|
||||
if (android.containsKey("smallIcon")) {
|
||||
Bundle smallIcon = android.getBundle("smallIcon");
|
||||
int smallIconResourceId = getResourceId("mipmap", smallIcon.getString("icon"));
|
||||
if (smallIconResourceId == 0) {
|
||||
smallIconResourceId = getResourceId("drawable", smallIcon.getString("icon"));
|
||||
}
|
||||
if (smallIconResourceId != 0) {
|
||||
if (smallIcon.containsKey("level")) {
|
||||
nb = nb.setSmallIcon(smallIconResourceId, smallIcon.getInt("level"));
|
||||
Double level = smallIcon.getDouble("level");
|
||||
nb = nb.setSmallIcon(smallIconResourceId, level.intValue());
|
||||
} else {
|
||||
nb = nb.setSmallIcon(smallIconResourceId);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (notification.containsKey("sortKey")) {
|
||||
nb = nb.setSortKey(notification.getString("sortKey"));
|
||||
if (android.containsKey("sortKey")) {
|
||||
nb = nb.setSortKey(android.getString("sortKey"));
|
||||
}
|
||||
if (notification.containsKey("ticker")) {
|
||||
nb = nb.setTicker(notification.getString("ticker"));
|
||||
if (android.containsKey("ticker")) {
|
||||
nb = nb.setTicker(android.getString("ticker"));
|
||||
}
|
||||
if (notification.containsKey("timeoutAfter")) {
|
||||
nb = nb.setTimeoutAfter(notification.getLong("timeoutAfter"));
|
||||
if (android.containsKey("timeoutAfter")) {
|
||||
nb = nb.setTimeoutAfter(android.getLong("timeoutAfter"));
|
||||
}
|
||||
if (notification.containsKey("usesChronometer")) {
|
||||
nb = nb.setUsesChronometer(notification.getBoolean("usesChronometer"));
|
||||
if (android.containsKey("usesChronometer")) {
|
||||
nb = nb.setUsesChronometer(android.getBoolean("usesChronometer"));
|
||||
}
|
||||
if (notification.containsKey("vibrate")) {
|
||||
nb = nb.setVibrate(notification.getLongArray("vibrate"));
|
||||
if (android.containsKey("vibrate")) {
|
||||
nb = nb.setVibrate(android.getLongArray("vibrate"));
|
||||
}
|
||||
if (notification.containsKey("visibility")) {
|
||||
nb = nb.setVisibility(notification.getInt("visibility"));
|
||||
if (android.containsKey("visibility")) {
|
||||
Double visibility = android.getDouble("visibility");
|
||||
nb = nb.setVisibility(visibility.intValue());
|
||||
}
|
||||
if (notification.containsKey("when")) {
|
||||
nb = nb.setWhen(notification.getLong("when"));
|
||||
if (android.containsKey("when")) {
|
||||
nb = nb.setWhen(android.getLong("when"));
|
||||
}
|
||||
|
||||
// TODO: Big text / Big picture
|
||||
|
@ -335,8 +382,8 @@ public class RNFirebaseNotificationManager {
|
|||
Intent intent = new Intent(context, intentClass);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||
intent.putExtras(notification);
|
||||
if (notification.containsKey("clickAction")) {
|
||||
intent.setAction(notification.getString("clickAction"));
|
||||
if (android.containsKey("clickAction")) {
|
||||
intent.setAction(android.getString("clickAction"));
|
||||
}
|
||||
|
||||
PendingIntent contentIntent = PendingIntent.getActivity(context, notificationId.hashCode(), intent,
|
||||
|
@ -391,6 +438,71 @@ public class RNFirebaseNotificationManager {
|
|||
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) {
|
||||
|
|
|
@ -1,38 +1,57 @@
|
|||
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.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.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 LifecycleEventListener {
|
||||
public class RNFirebaseNotifications extends ReactContextBaseJavaModule implements ActivityEventListener, LifecycleEventListener {
|
||||
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);
|
||||
notificationManager = new RNFirebaseNotificationManager(context.getApplicationContext());
|
||||
context.addActivityEventListener(this);
|
||||
context.addLifecycleEventListener(this);
|
||||
|
||||
notificationManager = new RNFirebaseNotificationManager(context.getApplicationContext());
|
||||
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));
|
||||
|
@ -58,9 +77,22 @@ public class RNFirebaseNotifications extends ReactContextBaseJavaModule implemen
|
|||
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) {
|
||||
// TODO
|
||||
if (getCurrentActivity() == null) {
|
||||
promise.resolve(null);
|
||||
} else {
|
||||
WritableMap notificationOpenMap = parseIntentForRemoteNotification(getCurrentActivity().getIntent());
|
||||
promise.resolve(notificationOpenMap);
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
|
@ -83,11 +115,74 @@ public class RNFirebaseNotifications extends ReactContextBaseJavaModule implemen
|
|||
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 = parseIntentForRemoteNotification(intent);
|
||||
if (notificationOpenMap != null) {
|
||||
Log.d(TAG, "onNewIntent called with new remote notification");
|
||||
Utils.sendEvent(getReactApplicationContext(), "notifications_notification_opened", notificationOpenMap);
|
||||
}
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// End ActivityEventListener methods
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Start LifecycleEventListener methods
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
@ -109,10 +204,102 @@ public class RNFirebaseNotifications extends ReactContextBaseJavaModule implemen
|
|||
// End LifecycleEventListener methods
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
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) {
|
||||
|
|
|
@ -38,6 +38,11 @@ static NSString *const MESSAGING_TOKEN_REFRESHED = @"messaging_token_refreshed";
|
|||
// TODO: Remove
|
||||
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";
|
||||
|
|
|
@ -16,8 +16,7 @@
|
|||
|
||||
#if !TARGET_OS_TV
|
||||
- (void)didReceiveRemoteNotification:(nonnull NSDictionary *)userInfo;
|
||||
- (void)didReceiveRemoteNotification:(nonnull NSDictionary *)userInfo fetchCompletionHandler:(void (^_Nonnull)(UIBackgroundFetchResult))completionHandler;
|
||||
- (void)didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings;
|
||||
- (void)didRegisterUserNotificationSettings:(nonnull UIUserNotificationSettings *)notificationSettings;
|
||||
#endif
|
||||
|
||||
@end
|
||||
|
|
|
@ -11,17 +11,9 @@
|
|||
#import <React/RCTConvert.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 RNFirebaseMessaging () <UNUserNotificationCenterDelegate>
|
||||
#else
|
||||
@interface RNFirebaseMessaging ()
|
||||
#endif
|
||||
@property (nonatomic, strong) NSMutableDictionary *callbackHandlers;
|
||||
@end
|
||||
|
||||
|
||||
@implementation RNFirebaseMessaging
|
||||
|
||||
|
@ -48,17 +40,9 @@ RCT_EXPORT_MODULE()
|
|||
|
||||
// Establish Firebase managed data channel
|
||||
[FIRMessaging messaging].shouldEstablishDirectChannel = YES;
|
||||
|
||||
// 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
|
||||
theRNFirebaseMessaging = self;
|
||||
|
||||
// Initialise callback handlers dictionary
|
||||
_callbackHandlers = [NSMutableDictionary dictionary];
|
||||
}
|
||||
|
||||
// *******************************************************
|
||||
|
@ -66,25 +50,6 @@ RCT_EXPORT_MODULE()
|
|||
// ** iOS 8/9 Only
|
||||
// *******************************************************
|
||||
|
||||
// Listen for background messages
|
||||
- (void)didReceiveRemoteNotification:(nonnull NSDictionary *)userInfo {
|
||||
BOOL isFromBackground = (RCTSharedApplication().applicationState == UIApplicationStateInactive);
|
||||
NSDictionary *message = [self parseUserInfo:userInfo messageType:@"RemoteNotification" clickAction:nil openedFromTray:isFromBackground];
|
||||
|
||||
[RNFirebaseUtil sendJSEvent:self name:MESSAGING_MESSAGE_RECEIVED body:message];
|
||||
}
|
||||
|
||||
// Listen for background messages
|
||||
- (void)didReceiveRemoteNotification:(NSDictionary *)userInfo
|
||||
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
|
||||
BOOL isFromBackground = (RCTSharedApplication().applicationState == UIApplicationStateInactive);
|
||||
NSDictionary *message = [self parseUserInfo:userInfo messageType:@"RemoteNotificationHandler" clickAction:nil openedFromTray:isFromBackground];
|
||||
|
||||
[_callbackHandlers setObject:[completionHandler copy] forKey:message[@"messageId"]];
|
||||
|
||||
[RNFirebaseUtil sendJSEvent:self name:MESSAGING_MESSAGE_RECEIVED body:message];
|
||||
}
|
||||
|
||||
// Listen for permission response
|
||||
- (void) didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings {
|
||||
if (notificationSettings.types == UIUserNotificationTypeNone) {
|
||||
|
@ -98,50 +63,17 @@ RCT_EXPORT_MODULE()
|
|||
_permissionResolver = nil;
|
||||
}
|
||||
|
||||
// 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];
|
||||
}
|
||||
|
||||
// *******************************************************
|
||||
// ** 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 {
|
||||
NSDictionary *message = [self parseUNNotification:notification messageType:@"PresentNotification" openedFromTray:false];
|
||||
|
||||
[_callbackHandlers setObject:[completionHandler copy] forKey:message[@"messageId"]];
|
||||
|
||||
[RNFirebaseUtil sendJSEvent:self name:MESSAGING_MESSAGE_RECEIVED body:message];
|
||||
}
|
||||
|
||||
// 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 parseUNNotification:response.notification messageType:@"NotificationResponse" openedFromTray:true];
|
||||
|
||||
[_callbackHandlers setObject:[completionHandler copy] forKey:message[@"messageId"]];
|
||||
|
||||
[RNFirebaseUtil sendJSEvent:self name:MESSAGING_MESSAGE_RECEIVED body:message];
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// *******************************************************
|
||||
// ** Finish UNUserNotificationCenterDelegate methods
|
||||
// *******************************************************
|
||||
|
||||
|
||||
// *******************************************************
|
||||
// ** Start FIRMessagingDelegate methods
|
||||
// ** iOS 8+
|
||||
|
@ -155,8 +87,15 @@ didReceiveNotificationResponse:(UNNotificationResponse *)response
|
|||
|
||||
// Listen for data messages in the foreground
|
||||
- (void)applicationReceivedRemoteMessage:(nonnull FIRMessagingRemoteMessage *)remoteMessage {
|
||||
NSDictionary *message = [self parseFIRMessagingRemoteMessage:remoteMessage openedFromTray:false];
|
||||
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];
|
||||
}
|
||||
|
||||
|
@ -204,23 +143,6 @@ RCT_EXPORT_METHOD(requestPermission:(RCTPromiseResolveBlock)resolve rejecter:(RC
|
|||
}
|
||||
|
||||
// Non Web SDK methods
|
||||
|
||||
RCT_EXPORT_METHOD(getBadge: (RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
resolve(@([RCTSharedApplication() applicationIconBadgeNumber]));
|
||||
});
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(getInitialMessage:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject){
|
||||
NSDictionary *notification = [self bridge].launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey];
|
||||
if (notification) {
|
||||
NSDictionary *message = [self parseUserInfo:notification messageType:@"InitialMessage" clickAction:nil openedFromTray:true];
|
||||
resolve(message);
|
||||
} else {
|
||||
resolve(nil);
|
||||
}
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(hasPermission: (RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
|
||||
if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_9_x_Max) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
|
@ -248,12 +170,9 @@ RCT_EXPORT_METHOD(sendMessage: (NSDictionary *) message
|
|||
NSDictionary *data = message[@"data"];
|
||||
|
||||
[[FIRMessaging messaging] sendMessage:data to:to withMessageID:messageId timeToLive:[ttl intValue]];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(setBadge: (NSInteger) number) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[RCTSharedApplication() setApplicationIconBadgeNumber:number];
|
||||
});
|
||||
|
||||
// TODO: Listen for send success / errors
|
||||
resolve(nil);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(subscribeToTopic: (NSString*) topic) {
|
||||
|
@ -264,64 +183,9 @@ RCT_EXPORT_METHOD(unsubscribeFromTopic: (NSString*) topic) {
|
|||
[[FIRMessaging messaging] unsubscribeFromTopic:topic];
|
||||
}
|
||||
|
||||
// Response handler methods
|
||||
|
||||
RCT_EXPORT_METHOD(completeNotificationResponse: (NSString*) messageId) {
|
||||
void(^callbackHandler)() = [_callbackHandlers objectForKey:messageId];
|
||||
if (!callbackHandler) {
|
||||
NSLog(@"There is no callback handler for messageId: %@", messageId);
|
||||
return;
|
||||
}
|
||||
callbackHandler();
|
||||
[_callbackHandlers removeObjectForKey:messageId];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(completePresentNotification: (NSString*) messageId
|
||||
result: (NSString*) result) {
|
||||
void(^callbackHandler)(UNNotificationPresentationOptions) = [_callbackHandlers objectForKey:messageId];
|
||||
if (!callbackHandler) {
|
||||
NSLog(@"There is no callback handler for messageId: %@", messageId);
|
||||
return;
|
||||
}
|
||||
UNNotificationPresentationOptions options;
|
||||
if ([result isEqualToString:@"UNNotificationPresentationOptionAll"]) {
|
||||
options = UNNotificationPresentationOptionAlert | UNNotificationPresentationOptionBadge | UNNotificationPresentationOptionSound;
|
||||
} else if ([result isEqualToString:@"UNNotificationPresentationOptionNone"]) {
|
||||
options = UNNotificationPresentationOptionNone;
|
||||
} else {
|
||||
NSLog(@"Invalid result for PresentNotification: %@", result);
|
||||
return;
|
||||
}
|
||||
callbackHandler(options);
|
||||
[_callbackHandlers removeObjectForKey:messageId];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(completeRemoteNotification: (NSString*) messageId
|
||||
result: (NSString*) result) {
|
||||
void(^callbackHandler)(UIBackgroundFetchResult) = [_callbackHandlers objectForKey:messageId];
|
||||
if (!callbackHandler) {
|
||||
NSLog(@"There is no callback handler for messageId: %@", messageId);
|
||||
return;
|
||||
}
|
||||
UIBackgroundFetchResult fetchResult;
|
||||
if ([result isEqualToString:@"UIBackgroundFetchResultNewData"]) {
|
||||
fetchResult = UIBackgroundFetchResultNewData;
|
||||
} else if ([result isEqualToString:@"UIBackgroundFetchResultNoData"]) {
|
||||
fetchResult = UIBackgroundFetchResultNoData;
|
||||
} else if ([result isEqualToString:@"UIBackgroundFetchResultFailed"]) {
|
||||
fetchResult = UIBackgroundFetchResultFailed;
|
||||
} else {
|
||||
NSLog(@"Invalid result for PresentNotification: %@", result);
|
||||
return;
|
||||
}
|
||||
callbackHandler(fetchResult);
|
||||
[_callbackHandlers removeObjectForKey:messageId];
|
||||
}
|
||||
|
||||
// ** Start internals **
|
||||
|
||||
- (NSDictionary*)parseFIRMessagingRemoteMessage:(FIRMessagingRemoteMessage *)remoteMessage
|
||||
openedFromTray:(bool)openedFromTray {
|
||||
- (NSDictionary*)parseFIRMessagingRemoteMessage:(FIRMessagingRemoteMessage *)remoteMessage {
|
||||
NSDictionary *appData = remoteMessage.appData;
|
||||
|
||||
NSMutableDictionary *message = [[NSMutableDictionary alloc] init];
|
||||
|
@ -332,98 +196,24 @@ RCT_EXPORT_METHOD(completeRemoteNotification: (NSString*) messageId
|
|||
} else if ([k1 isEqualToString:@"from"]) {
|
||||
message[@"from"] = appData[k1];
|
||||
} else if ([k1 isEqualToString:@"notification"]) {
|
||||
NSDictionary *notification = appData[k1];
|
||||
NSMutableDictionary *notif = [[NSMutableDictionary alloc] init];
|
||||
for (id k2 in notification) {
|
||||
if ([k2 isEqualToString:@"badge"]) {
|
||||
notif[@"badge"] = notification[k2];
|
||||
} else if ([k2 isEqualToString:@"body"]) {
|
||||
notif[@"body"] = notification[k2];
|
||||
} else if ([k2 isEqualToString:@"body_loc_args"]) {
|
||||
notif[@"bodyLocalizationArgs"] = notification[k2];
|
||||
} else if ([k2 isEqualToString:@"body_loc_key"]) {
|
||||
notif[@"bodyLocalizationKey"] = notification[k2];
|
||||
} else if ([k2 isEqualToString:@"click_action"]) {
|
||||
notif[@"clickAction"] = notification[k2];
|
||||
} else if ([k2 isEqualToString:@"sound"]) {
|
||||
notif[@"sound"] = notification[k2];
|
||||
} else if ([k2 isEqualToString:@"subtitle"]) {
|
||||
notif[@"subtitle"] = notification[k2];
|
||||
} else if ([k2 isEqualToString:@"title"]) {
|
||||
notif[@"title"] = notification[k2];
|
||||
} else if ([k2 isEqualToString:@"title_loc_args"]) {
|
||||
notif[@"titleLocalizationArgs"] = notification[k2];
|
||||
} else if ([k2 isEqualToString:@"title_loc_key"]) {
|
||||
notif[@"titleLocalizationKey"] = notification[k2];
|
||||
} else {
|
||||
NSLog(@"Unknown notification key: %@", k2);
|
||||
}
|
||||
}
|
||||
message[@"notification"] = notif;
|
||||
// Ignore for messages
|
||||
} else {
|
||||
// Assume custom data key
|
||||
data[k1] = appData[k1];
|
||||
}
|
||||
}
|
||||
message[@"messageType"] = @"RemoteMessage";
|
||||
message[@"data"] = data;
|
||||
message[@"openedFromTray"] = @(false);
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
- (NSDictionary*)parseUNNotification:(UNNotification *)notification
|
||||
messageType:(NSString *)messageType
|
||||
openedFromTray:(bool)openedFromTray {
|
||||
NSDictionary *userInfo = notification.request.content.userInfo;
|
||||
NSString *clickAction = notification.request.content.categoryIdentifier;
|
||||
|
||||
return [self parseUserInfo:userInfo messageType:messageType clickAction:clickAction openedFromTray:openedFromTray];
|
||||
}
|
||||
|
||||
- (NSDictionary*)parseUserInfo:(NSDictionary *)userInfo
|
||||
messageType:(NSString *) messageType
|
||||
clickAction:(NSString *) clickAction
|
||||
openedFromTray:(bool)openedFromTray {
|
||||
- (NSDictionary*)parseUserInfo:(NSDictionary *)userInfo {
|
||||
NSMutableDictionary *message = [[NSMutableDictionary alloc] init];
|
||||
NSMutableDictionary *notif = [[NSMutableDictionary alloc] init];
|
||||
NSMutableDictionary *data = [[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"]) {
|
||||
notif[@"body"] = alert[k3];
|
||||
} else if ([k3 isEqualToString:@"loc-args"]) {
|
||||
notif[@"bodyLocalizationArgs"] = alert[k3];
|
||||
} else if ([k3 isEqualToString:@"loc-key"]) {
|
||||
notif[@"bodyLocalizationKey"] = alert[k3];
|
||||
} else if ([k3 isEqualToString:@"subtitle"]) {
|
||||
notif[@"subtitle"] = alert[k3];
|
||||
} else if ([k3 isEqualToString:@"title"]) {
|
||||
notif[@"title"] = alert[k3];
|
||||
} else if ([k3 isEqualToString:@"title-loc-args"]) {
|
||||
notif[@"titleLocalizationArgs"] = alert[k3];
|
||||
} else if ([k3 isEqualToString:@"title-loc-key"]) {
|
||||
notif[@"titleLocalizationKey"] = alert[k3];
|
||||
} else {
|
||||
NSLog(@"Unknown alert key: %@", k2);
|
||||
}
|
||||
}
|
||||
} else if ([k2 isEqualToString:@"badge"]) {
|
||||
notif[@"badge"] = aps[k2];
|
||||
} else if ([k2 isEqualToString:@"category"]) {
|
||||
notif[@"clickAction"] = aps[k2];
|
||||
} else if ([k2 isEqualToString:@"sound"]) {
|
||||
notif[@"sound"] = aps[k2];
|
||||
} else {
|
||||
NSLog(@"Unknown aps key: %@", k2);
|
||||
}
|
||||
}
|
||||
// Ignore notification section
|
||||
} else if ([k1 isEqualToString:@"gcm.message_id"]) {
|
||||
message[@"messageId"] = userInfo[k1];
|
||||
} else if ([k1 isEqualToString:@"google.c.a.ts"]) {
|
||||
|
@ -440,22 +230,9 @@ RCT_EXPORT_METHOD(completeRemoteNotification: (NSString*) messageId
|
|||
data[k1] = userInfo[k1];
|
||||
}
|
||||
}
|
||||
|
||||
if (!notif[@"clickAction"] && clickAction) {
|
||||
notif[@"clickAction"] = clickAction;
|
||||
}
|
||||
|
||||
// Generate a message ID if one was not present in the notification
|
||||
// This is used for resolving click handlers
|
||||
if (!message[@"messageId"]) {
|
||||
message[@"messageId"] = [[NSUUID UUID] UUIDString];
|
||||
}
|
||||
message[@"messageType"] = messageType;
|
||||
|
||||
|
||||
message[@"data"] = data;
|
||||
message[@"notification"] = notif;
|
||||
message[@"openedFromTray"] = @(openedFromTray);
|
||||
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
|
@ -474,3 +251,4 @@ RCT_EXPORT_METHOD(completeRemoteNotification: (NSString*) messageId
|
|||
@implementation RNFirebaseMessaging
|
||||
@end
|
||||
#endif
|
||||
|
||||
|
|
|
@ -8,8 +8,12 @@
|
|||
|
||||
@interface RNFirebaseNotifications : RCTEventEmitter<RCTBridgeModule>
|
||||
|
||||
#if !TARGET_OS_TV
|
||||
+ (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
|
||||
|
@ -20,3 +24,4 @@
|
|||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -2,17 +2,208 @@
|
|||
|
||||
#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 (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_9_x_Max) {
|
||||
if ([self isIOS89]) {
|
||||
[RCTSharedApplication() cancelAllLocalNotifications];
|
||||
} else {
|
||||
#if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
|
||||
|
@ -25,7 +216,7 @@ RCT_EXPORT_METHOD(cancelAllNotifications) {
|
|||
}
|
||||
|
||||
RCT_EXPORT_METHOD(cancelNotification:(NSString*) notificationId) {
|
||||
if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_9_x_Max) {
|
||||
if ([self isIOS89]) {
|
||||
for (UILocalNotification *notification in RCTSharedApplication().scheduledLocalNotifications) {
|
||||
NSDictionary *notificationInfo = notification.userInfo;
|
||||
if ([notificationId isEqualToString:[notificationInfo valueForKey:@"notificationId"]]) {
|
||||
|
@ -45,7 +236,7 @@ RCT_EXPORT_METHOD(cancelNotification:(NSString*) notificationId) {
|
|||
RCT_EXPORT_METHOD(displayNotification:(NSDictionary*) notification
|
||||
resolver:(RCTPromiseResolveBlock)resolve
|
||||
rejecter:(RCTPromiseRejectBlock)reject) {
|
||||
if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_9_x_Max) {
|
||||
if ([self isIOS89]) {
|
||||
UILocalNotification* notif = [self buildUILocalNotification:notification withSchedule:false];
|
||||
[RCTSharedApplication() presentLocalNotificationNow:notif];
|
||||
resolve(nil);
|
||||
|
@ -55,7 +246,7 @@ RCT_EXPORT_METHOD(displayNotification:(NSDictionary*) notification
|
|||
[[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
|
||||
if (!error) {
|
||||
resolve(nil);
|
||||
}else{
|
||||
} else{
|
||||
reject(@"notifications/display_notification_error", @"Failed to display notificaton", error);
|
||||
}
|
||||
}];
|
||||
|
@ -63,19 +254,40 @@ RCT_EXPORT_METHOD(displayNotification:(NSDictionary*) notification
|
|||
}
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(getInitialNotification:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject){
|
||||
UILocalNotification *localNotification = [self bridge].launchOptions[UIApplicationLaunchOptionsLocalNotificationKey];
|
||||
if (localNotification) {
|
||||
NSDictionary *notification = [self parseUILocalNotification:localNotification];
|
||||
resolve(notification);
|
||||
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 {
|
||||
resolve(nil);
|
||||
// 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 (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_9_x_Max) {
|
||||
if ([self isIOS89]) {
|
||||
NSMutableArray* notifications = [[NSMutableArray alloc] init];
|
||||
for (UILocalNotification *notif in [RCTSharedApplication() scheduledLocalNotifications]){
|
||||
NSDictionary *notification = [self parseUILocalNotification:notif];
|
||||
|
@ -97,7 +309,7 @@ RCT_EXPORT_METHOD(getScheduledNotifications:(RCTPromiseResolveBlock)resolve
|
|||
}
|
||||
|
||||
RCT_EXPORT_METHOD(removeAllDeliveredNotifications) {
|
||||
if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_9_x_Max) {
|
||||
if ([self isIOS89]) {
|
||||
// No such functionality on iOS 8/9
|
||||
} else {
|
||||
#if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
|
||||
|
@ -110,7 +322,7 @@ RCT_EXPORT_METHOD(removeAllDeliveredNotifications) {
|
|||
}
|
||||
|
||||
RCT_EXPORT_METHOD(removeDeliveredNotification:(NSString*) notificationId) {
|
||||
if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_9_x_Max) {
|
||||
if ([self isIOS89]) {
|
||||
// No such functionality on iOS 8/9
|
||||
} else {
|
||||
#if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
|
||||
|
@ -125,7 +337,7 @@ RCT_EXPORT_METHOD(removeDeliveredNotification:(NSString*) notificationId) {
|
|||
RCT_EXPORT_METHOD(scheduleNotification:(NSDictionary*) notification
|
||||
resolver:(RCTPromiseResolveBlock)resolve
|
||||
rejecter:(RCTPromiseRejectBlock)reject) {
|
||||
if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_9_x_Max) {
|
||||
if ([self isIOS89]) {
|
||||
UILocalNotification* notif = [self buildUILocalNotification:notification withSchedule:true];
|
||||
[RCTSharedApplication() scheduleLocalNotification:notif];
|
||||
resolve(nil);
|
||||
|
@ -135,7 +347,7 @@ RCT_EXPORT_METHOD(scheduleNotification:(NSDictionary*) notification
|
|||
[[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
|
||||
if (!error) {
|
||||
resolve(nil);
|
||||
}else{
|
||||
} else{
|
||||
reject(@"notification/schedule_notification_error", @"Failed to schedule notificaton", error);
|
||||
}
|
||||
}];
|
||||
|
@ -143,6 +355,31 @@ RCT_EXPORT_METHOD(scheduleNotification:(NSDictionary*) notification
|
|||
}
|
||||
}
|
||||
|
||||
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];
|
||||
|
@ -182,7 +419,7 @@ RCT_EXPORT_METHOD(scheduleNotification:(NSDictionary*) notification
|
|||
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"]) {
|
||||
|
@ -195,9 +432,9 @@ RCT_EXPORT_METHOD(scheduleNotification:(NSDictionary*) notification
|
|||
localNotification.repeatInterval = NSCalendarUnitWeekday;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
return localNotification;
|
||||
}
|
||||
|
||||
|
@ -226,10 +463,27 @@ RCT_EXPORT_METHOD(scheduleNotification:(NSDictionary*) notification
|
|||
for (NSDictionary *a in ios[@"attachments"]) {
|
||||
NSString *identifier = a[@"identifier"];
|
||||
NSURL *url = [NSURL URLWithString:a[@"url"]];
|
||||
NSDictionary *options = a[@"options"];
|
||||
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:options error:&error];
|
||||
UNNotificationAttachment *attachment = [UNNotificationAttachment attachmentWithIdentifier:identifier URL:url options:attachmentOptions error:&error];
|
||||
if (attachment) {
|
||||
[attachments addObject:attachment];
|
||||
} else {
|
||||
|
@ -252,13 +506,13 @@ RCT_EXPORT_METHOD(scheduleNotification:(NSDictionary*) notification
|
|||
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"]) {
|
||||
|
@ -274,7 +528,7 @@ RCT_EXPORT_METHOD(scheduleNotification:(NSDictionary*) notification
|
|||
// 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];
|
||||
|
@ -285,7 +539,7 @@ RCT_EXPORT_METHOD(scheduleNotification:(NSDictionary*) notification
|
|||
|
||||
- (NSDictionary*) parseUILocalNotification:(UILocalNotification *) localNotification {
|
||||
NSMutableDictionary *notification = [[NSMutableDictionary alloc] init];
|
||||
|
||||
|
||||
if (localNotification.alertBody) {
|
||||
notification[@"body"] = localNotification.alertBody;
|
||||
}
|
||||
|
@ -298,7 +552,7 @@ RCT_EXPORT_METHOD(scheduleNotification:(NSDictionary*) notification
|
|||
if (localNotification.alertTitle) {
|
||||
notification[@"title"] = localNotification.alertTitle;
|
||||
}
|
||||
|
||||
|
||||
NSMutableDictionary *ios = [[NSMutableDictionary alloc] init];
|
||||
if (localNotification.alertAction) {
|
||||
ios[@"alertAction"] = localNotification.alertAction;
|
||||
|
@ -316,36 +570,58 @@ RCT_EXPORT_METHOD(scheduleNotification:(NSDictionary*) notification
|
|||
ios[@"launchImage"] = localNotification.alertLaunchImage;
|
||||
}
|
||||
notification[@"ios"] = ios;
|
||||
|
||||
|
||||
return notification;
|
||||
}
|
||||
|
||||
- (NSDictionary*) parseUNNotificationRequest:(UNNotificationRequest *) localNotification {
|
||||
- (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"] = localNotification.identifier;
|
||||
|
||||
if (localNotification.content.body) {
|
||||
notification[@"body"] = localNotification.content.body;
|
||||
notification[@"notificationId"] = notificationRequest.identifier;
|
||||
|
||||
if (notificationRequest.content.body) {
|
||||
notification[@"body"] = notificationRequest.content.body;
|
||||
}
|
||||
if (localNotification.content.userInfo) {
|
||||
notification[@"data"] = localNotification.content.userInfo;
|
||||
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 (localNotification.content.sound) {
|
||||
notification[@"sound"] = localNotification.content.sound;
|
||||
if (notificationRequest.content.sound) {
|
||||
notification[@"sound"] = notificationRequest.content.sound;
|
||||
}
|
||||
if (localNotification.content.subtitle) {
|
||||
notification[@"subtitle"] = localNotification.content.subtitle;
|
||||
if (notificationRequest.content.subtitle) {
|
||||
notification[@"subtitle"] = notificationRequest.content.subtitle;
|
||||
}
|
||||
if (localNotification.content.title) {
|
||||
notification[@"title"] = localNotification.content.title;
|
||||
if (notificationRequest.content.title) {
|
||||
notification[@"title"] = notificationRequest.content.title;
|
||||
}
|
||||
|
||||
|
||||
NSMutableDictionary *ios = [[NSMutableDictionary alloc] init];
|
||||
|
||||
if (localNotification.content.attachments) {
|
||||
|
||||
if (notificationRequest.content.attachments) {
|
||||
NSMutableArray *attachments = [[NSMutableArray alloc] init];
|
||||
for (UNNotificationAttachment *a in localNotification.content.attachments) {
|
||||
for (UNNotificationAttachment *a in notificationRequest.content.attachments) {
|
||||
NSMutableDictionary *attachment = [[NSMutableDictionary alloc] init];
|
||||
attachment[@"identifier"] = a.identifier;
|
||||
attachment[@"type"] = a.type;
|
||||
|
@ -354,26 +630,86 @@ RCT_EXPORT_METHOD(scheduleNotification:(NSDictionary*) notification
|
|||
}
|
||||
ios[@"attachments"] = attachments;
|
||||
}
|
||||
|
||||
if (localNotification.content.badge) {
|
||||
ios[@"badge"] = localNotification.content.badge;
|
||||
|
||||
if (notificationRequest.content.badge) {
|
||||
ios[@"badge"] = notificationRequest.content.badge;
|
||||
}
|
||||
if (localNotification.content.categoryIdentifier) {
|
||||
ios[@"category"] = localNotification.content.categoryIdentifier;
|
||||
if (notificationRequest.content.categoryIdentifier) {
|
||||
ios[@"category"] = notificationRequest.content.categoryIdentifier;
|
||||
}
|
||||
if (localNotification.content.launchImageName) {
|
||||
ios[@"launchImage"] = localNotification.content.launchImageName;
|
||||
if (notificationRequest.content.launchImageName) {
|
||||
ios[@"launchImage"] = notificationRequest.content.launchImageName;
|
||||
}
|
||||
if (localNotification.content.threadIdentifier) {
|
||||
ios[@"threadIdentifier"] = localNotification.content.threadIdentifier;
|
||||
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_RECEIVED];
|
||||
return @[NOTIFICATIONS_NOTIFICATION_DISPLAYED, NOTIFICATIONS_NOTIFICATION_OPENED, NOTIFICATIONS_NOTIFICATION_RECEIVED];
|
||||
}
|
||||
|
||||
+ (BOOL)requiresMainQueueSetup
|
||||
|
@ -387,4 +723,3 @@ RCT_EXPORT_METHOD(scheduleNotification:(NSDictionary*) notification
|
|||
@implementation RNFirebaseNotifications
|
||||
@end
|
||||
#endif
|
||||
|
||||
|
|
|
@ -68,7 +68,6 @@ export type { default as WriteBatch } from './modules/firestore/WriteBatch';
|
|||
/*
|
||||
* Export Messaging types
|
||||
*/
|
||||
export type { default as Message } from './modules/messaging/Message';
|
||||
export type {
|
||||
default as RemoteMessage,
|
||||
} from './modules/messaging/RemoteMessage';
|
||||
|
|
|
@ -1,122 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
* Message representation wrapper
|
||||
*/
|
||||
import { Platform } from 'react-native';
|
||||
import { getNativeModule } from '../../utils/native';
|
||||
import {
|
||||
MessageType,
|
||||
PresentNotificationResult,
|
||||
RemoteNotificationResult,
|
||||
} from './types';
|
||||
import type Messaging from './';
|
||||
import type {
|
||||
MessageTypeType,
|
||||
NativeMessage,
|
||||
Notification,
|
||||
PresentNotificationResultType,
|
||||
RemoteNotificationResultType,
|
||||
} from './types';
|
||||
|
||||
/**
|
||||
* @class Message
|
||||
*/
|
||||
export default class Message {
|
||||
_completed: boolean;
|
||||
_messaging: Messaging;
|
||||
_message: NativeMessage;
|
||||
|
||||
constructor(messaging: Messaging, message: NativeMessage) {
|
||||
this._messaging = messaging;
|
||||
this._message = message;
|
||||
}
|
||||
|
||||
get collapseKey(): ?string {
|
||||
return this._message.collapseKey;
|
||||
}
|
||||
|
||||
get data(): { [string]: string } {
|
||||
return this._message.data;
|
||||
}
|
||||
|
||||
get from(): ?string {
|
||||
return this._message.from;
|
||||
}
|
||||
|
||||
get messageId(): ?string {
|
||||
return this._message.messageId;
|
||||
}
|
||||
|
||||
get messageType(): ?MessageTypeType {
|
||||
return this._message.messageType;
|
||||
}
|
||||
|
||||
get openedFromTray(): boolean {
|
||||
return this._message.openedFromTray;
|
||||
}
|
||||
|
||||
get notification(): ?Notification {
|
||||
return this._message.notification;
|
||||
}
|
||||
|
||||
get sentTime(): ?number {
|
||||
return this._message.sentTime;
|
||||
}
|
||||
|
||||
get to(): ?string {
|
||||
return this._message.to;
|
||||
}
|
||||
|
||||
get ttl(): ?number {
|
||||
return this._message.ttl;
|
||||
}
|
||||
|
||||
complete(
|
||||
result?: PresentNotificationResultType | RemoteNotificationResultType
|
||||
): void {
|
||||
if (Platform.OS === 'android') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this._completed) {
|
||||
this._completed = true;
|
||||
|
||||
switch (this.messageType) {
|
||||
case MessageType.NotificationResponse:
|
||||
getNativeModule(this._messaging).completeNotificationResponse(
|
||||
this.messageId
|
||||
);
|
||||
break;
|
||||
|
||||
case MessageType.PresentNotification:
|
||||
if (
|
||||
result &&
|
||||
!Object.values(PresentNotificationResult).includes(result)
|
||||
) {
|
||||
throw new Error(`Invalid PresentNotificationResult: ${result}`);
|
||||
}
|
||||
getNativeModule(this._messaging).completePresentNotification(
|
||||
this.messageId,
|
||||
result || PresentNotificationResult.None
|
||||
);
|
||||
break;
|
||||
|
||||
case MessageType.RemoteNotificationHandler:
|
||||
if (
|
||||
result &&
|
||||
!Object.values(RemoteNotificationResult).includes(result)
|
||||
) {
|
||||
throw new Error(`Invalid RemoteNotificationResult: ${result}`);
|
||||
}
|
||||
getNativeModule(this._messaging).completeRemoteNotification(
|
||||
this.messageId,
|
||||
result || RemoteNotificationResult.NoData
|
||||
);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,30 +4,69 @@
|
|||
*/
|
||||
import { isObject, generatePushID } from './../../utils';
|
||||
|
||||
type NativeRemoteMessage = {
|
||||
collapseKey?: string,
|
||||
data: { [string]: string },
|
||||
messageId: string,
|
||||
messageType?: string,
|
||||
to: string,
|
||||
ttl: number,
|
||||
};
|
||||
import type {
|
||||
NativeInboundRemoteMessage,
|
||||
NativeOutboundRemoteMessage,
|
||||
} from './types';
|
||||
|
||||
export default class RemoteMessage {
|
||||
_collapseKey: string | void;
|
||||
_data: { [string]: string };
|
||||
_from: string | void;
|
||||
_messageId: string;
|
||||
_messageType: string | void;
|
||||
_sentTime: number | void;
|
||||
_to: string;
|
||||
_ttl: number;
|
||||
|
||||
constructor() {
|
||||
this._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 = generatePushID();
|
||||
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 collapseKey
|
||||
|
@ -73,6 +112,16 @@ export default class RemoteMessage {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param to
|
||||
* @returns {RemoteMessage}
|
||||
*/
|
||||
setTo(to: string): RemoteMessage {
|
||||
this._to = to;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param ttl
|
||||
|
@ -83,7 +132,7 @@ export default class RemoteMessage {
|
|||
return this;
|
||||
}
|
||||
|
||||
build(): NativeRemoteMessage {
|
||||
build(): NativeOutboundRemoteMessage {
|
||||
if (!this.data) {
|
||||
throw new Error('RemoteMessage: Missing required `data` property');
|
||||
} else if (!this.messageId) {
|
||||
|
|
|
@ -8,18 +8,12 @@ import { getLogger } from '../../utils/log';
|
|||
import ModuleBase from '../../utils/ModuleBase';
|
||||
import { getNativeModule } from '../../utils/native';
|
||||
import { isFunction, isObject } from '../../utils';
|
||||
import Message from './Message';
|
||||
import RemoteMessage from './RemoteMessage';
|
||||
import {
|
||||
MessageType,
|
||||
PresentNotificationResult,
|
||||
RemoteNotificationResult,
|
||||
} from './types';
|
||||
|
||||
import type App from '../core/app';
|
||||
import type { NativeMessage } from './types';
|
||||
import type { NativeInboundRemoteMessage } from './types';
|
||||
|
||||
type OnMessage = Message => any;
|
||||
type OnMessage = RemoteMessage => any;
|
||||
|
||||
type OnMessageObserver = {
|
||||
next: OnMessage,
|
||||
|
@ -55,8 +49,8 @@ export default class Messaging extends ModuleBase {
|
|||
// sub to internal native event - this fans out to
|
||||
// public event name: onMessage
|
||||
'messaging_message_received',
|
||||
(message: Message) => {
|
||||
SharedEventEmitter.emit('onMessage', message);
|
||||
(message: NativeInboundRemoteMessage) => {
|
||||
SharedEventEmitter.emit('onMessage', new RemoteMessage(message));
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -75,7 +69,7 @@ export default class Messaging extends ModuleBase {
|
|||
}
|
||||
|
||||
onMessage(nextOrObserver: OnMessage | OnMessageObserver): () => any {
|
||||
let listener: Message => any;
|
||||
let listener: RemoteMessage => any;
|
||||
if (isFunction(nextOrObserver)) {
|
||||
// $FlowBug: Not coping with the overloaded method signature
|
||||
listener = nextOrObserver;
|
||||
|
@ -89,25 +83,20 @@ export default class Messaging extends ModuleBase {
|
|||
|
||||
getLogger(this).info('Creating onMessage listener');
|
||||
|
||||
const wrappedListener = async (nativeMessage: NativeMessage) => {
|
||||
const message = new Message(this, nativeMessage);
|
||||
await listener(message);
|
||||
message.complete();
|
||||
};
|
||||
|
||||
SharedEventEmitter.addListener('onMessage', wrappedListener);
|
||||
SharedEventEmitter.addListener('onMessage', listener);
|
||||
|
||||
return () => {
|
||||
getLogger(this).info('Removing onMessage listener');
|
||||
SharedEventEmitter.removeListener('onMessage', wrappedListener);
|
||||
SharedEventEmitter.removeListener('onMessage', listener);
|
||||
};
|
||||
}
|
||||
|
||||
onTokenRefresh(
|
||||
nextOrObserver: OnTokenRefresh | OnTokenRefreshObserver
|
||||
): () => any {
|
||||
let listener;
|
||||
let listener: String => any;
|
||||
if (isFunction(nextOrObserver)) {
|
||||
// $FlowBug: Not coping with the overloaded method signature
|
||||
listener = nextOrObserver;
|
||||
} else if (isObject(nextOrObserver) && isFunction(nextOrObserver.next)) {
|
||||
listener = nextOrObserver.next;
|
||||
|
@ -126,7 +115,6 @@ export default class Messaging extends ModuleBase {
|
|||
};
|
||||
}
|
||||
|
||||
// TODO: Permission structure?
|
||||
requestPermission(): Promise<void> {
|
||||
return getNativeModule(this).requestPermission();
|
||||
}
|
||||
|
@ -134,16 +122,6 @@ export default class Messaging extends ModuleBase {
|
|||
/**
|
||||
* NON WEB-SDK METHODS
|
||||
*/
|
||||
getBadge(): Promise<number> {
|
||||
return getNativeModule(this).getBadge();
|
||||
}
|
||||
|
||||
getInitialMessage(): Promise<?Message> {
|
||||
return getNativeModule(this)
|
||||
.getInitialMessage()
|
||||
.then(message => (message ? new Message(this, message) : null));
|
||||
}
|
||||
|
||||
hasPermission(): Promise<boolean> {
|
||||
return getNativeModule(this).hasPermission();
|
||||
}
|
||||
|
@ -157,10 +135,6 @@ export default class Messaging extends ModuleBase {
|
|||
return getNativeModule(this).send(remoteMessage.build());
|
||||
}
|
||||
|
||||
setBadge(badge: number): void {
|
||||
getNativeModule(this).setBadge(badge);
|
||||
}
|
||||
|
||||
subscribeToTopic(topic: string): void {
|
||||
getNativeModule(this).subscribeToTopic(topic);
|
||||
}
|
||||
|
@ -202,8 +176,5 @@ export default class Messaging extends ModuleBase {
|
|||
}
|
||||
|
||||
export const statics = {
|
||||
MessageType,
|
||||
PresentNotificationResult,
|
||||
RemoteMessage,
|
||||
RemoteNotificationResult,
|
||||
};
|
||||
|
|
|
@ -1,35 +1,6 @@
|
|||
/**
|
||||
* @flow
|
||||
*/
|
||||
|
||||
export const MessageType = {
|
||||
InitialMessage: 'InitialMessage',
|
||||
NotificationResponse: 'NotificationResponse',
|
||||
PresentNotification: 'PresentNotification',
|
||||
RemoteMessage: 'RemoteMessage',
|
||||
RemoteNotification: 'RemoteNotification',
|
||||
RemoteNotificationHandler: 'RemoteNotificationHandler',
|
||||
};
|
||||
|
||||
export const PresentNotificationResult = {
|
||||
All: 'UNNotificationPresentationOptionAll',
|
||||
None: 'UNNotificationPresentationOptionNone',
|
||||
};
|
||||
|
||||
export const RemoteNotificationResult = {
|
||||
NewData: 'UIBackgroundFetchResultNewData',
|
||||
NoData: 'UIBackgroundFetchResultNoData',
|
||||
ResultFailed: 'UIBackgroundFetchResultFailed',
|
||||
};
|
||||
|
||||
export type MessageTypeType = $Values<typeof MessageType>;
|
||||
export type PresentNotificationResultType = $Values<
|
||||
typeof PresentNotificationResult
|
||||
>;
|
||||
export type RemoteNotificationResultType = $Values<
|
||||
typeof RemoteNotificationResult
|
||||
>;
|
||||
|
||||
export type Notification = {
|
||||
body: string,
|
||||
bodyLocalizationArgs?: string[],
|
||||
|
@ -46,15 +17,22 @@ export type Notification = {
|
|||
titleLocalizationKey?: string,
|
||||
};
|
||||
|
||||
export type NativeMessage = {
|
||||
export type NativeInboundRemoteMessage = {
|
||||
collapseKey?: string,
|
||||
data: { [string]: string },
|
||||
from?: string,
|
||||
messageId: string,
|
||||
messageType?: MessageTypeType,
|
||||
openedFromTray: boolean,
|
||||
notification?: Notification,
|
||||
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,213 @@
|
|||
/**
|
||||
* @flow
|
||||
* AndroidChannel representation wrapper
|
||||
*/
|
||||
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;
|
||||
|
||||
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 channelId
|
||||
* @returns {AndroidChannel}
|
||||
*/
|
||||
setChannelId(channelId: string): AndroidChannel {
|
||||
this._channelId = channelId;
|
||||
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 importance
|
||||
* @returns {AndroidChannel}
|
||||
*/
|
||||
setImportance(importance: ImportanceType): AndroidChannel {
|
||||
this._importance = importance;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param lightColor
|
||||
* @returns {AndroidChannel}
|
||||
*/
|
||||
setLightColor(lightColor: string): AndroidChannel {
|
||||
this._lightColor = lightColor;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param lockScreenVisibility
|
||||
* @returns {AndroidChannel}
|
||||
*/
|
||||
setLockScreenVisibility(
|
||||
lockScreenVisibility: VisibilityType
|
||||
): AndroidChannel {
|
||||
this._lockScreenVisibility = lockScreenVisibility;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param name
|
||||
* @returns {AndroidChannel}
|
||||
*/
|
||||
setName(name: string): AndroidChannel {
|
||||
this._name = name;
|
||||
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,57 @@
|
|||
/**
|
||||
* @flow
|
||||
* AndroidChannelGroup representation wrapper
|
||||
*/
|
||||
|
||||
type NativeAndroidChannelGroup = {|
|
||||
groupId: string,
|
||||
name: string,
|
||||
|};
|
||||
|
||||
export default class AndroidChannel {
|
||||
_groupId: string;
|
||||
_name: string;
|
||||
|
||||
get groupId(): string {
|
||||
return this._groupId;
|
||||
}
|
||||
|
||||
get name(): string {
|
||||
return this._name;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param groupId
|
||||
* @returns {AndroidChannel}
|
||||
*/
|
||||
setGroupId(groupId: string): AndroidChannel {
|
||||
this._groupId = groupId;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param name
|
||||
* @returns {AndroidChannel}
|
||||
*/
|
||||
setName(name: string): AndroidChannel {
|
||||
this._name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -2,160 +2,59 @@
|
|||
* @flow
|
||||
* AndroidNotification representation wrapper
|
||||
*/
|
||||
import { Category } from './types';
|
||||
import type Notification from './Notification';
|
||||
|
||||
type Lights = {
|
||||
argb: number,
|
||||
onMs: number,
|
||||
offMs: number,
|
||||
};
|
||||
|
||||
type Progress = {
|
||||
max: number,
|
||||
progress: number,
|
||||
indeterminate: boolean,
|
||||
};
|
||||
|
||||
type SmallIcon = {
|
||||
icon: string,
|
||||
level?: number,
|
||||
};
|
||||
|
||||
export type NativeAndroidNotification = {|
|
||||
// TODO actions: Action[],
|
||||
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 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 Priority = {
|
||||
Default: 0,
|
||||
High: 1,
|
||||
Low: -1,
|
||||
Max: 2,
|
||||
Min: -2,
|
||||
};
|
||||
|
||||
export const Visibility = {
|
||||
Private: 0,
|
||||
Public: 1,
|
||||
Secret: -1,
|
||||
};
|
||||
|
||||
type BadgeIconTypeType = $Values<typeof BadgeIconType>;
|
||||
type CategoryType = $Values<typeof Category>;
|
||||
type DefaultsType = $Values<typeof Defaults>;
|
||||
type GroupAlertType = $Values<typeof GroupAlert>;
|
||||
type PriorityType = $Values<typeof Priority>;
|
||||
type VisibilityType = $Values<typeof Visibility>;
|
||||
import type {
|
||||
BadgeIconTypeType,
|
||||
CategoryType,
|
||||
DefaultsType,
|
||||
GroupAlertType,
|
||||
Lights,
|
||||
NativeAndroidNotification,
|
||||
PriorityType,
|
||||
Progress,
|
||||
SmallIcon,
|
||||
VisibilityType,
|
||||
} from './types';
|
||||
|
||||
export default class AndroidNotification {
|
||||
// TODO optional fields
|
||||
// TODO actions: Action[]; // icon, title, ??pendingIntent??, allowGeneratedReplies, extender, extras, remoteinput (ugh)
|
||||
_autoCancel: boolean;
|
||||
_badgeIconType: BadgeIconTypeType;
|
||||
_category: CategoryType;
|
||||
_autoCancel: boolean | void;
|
||||
_badgeIconType: BadgeIconTypeType | void;
|
||||
_category: CategoryType | void;
|
||||
_channelId: string;
|
||||
_clickAction: string;
|
||||
_color: string;
|
||||
_colorized: boolean;
|
||||
_contentInfo: string;
|
||||
_defaults: DefaultsType[];
|
||||
_group: string;
|
||||
_groupAlertBehaviour: GroupAlertType;
|
||||
_groupSummary: boolean;
|
||||
_largeIcon: string;
|
||||
_lights: Lights;
|
||||
_localOnly: boolean;
|
||||
_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;
|
||||
_ongoing: boolean;
|
||||
_onlyAlertOnce: boolean;
|
||||
_number: number | void;
|
||||
_ongoing: boolean | void;
|
||||
_onlyAlertOnce: boolean | void;
|
||||
_people: string[];
|
||||
_priority: PriorityType;
|
||||
_progress: Progress;
|
||||
_priority: PriorityType | void;
|
||||
_progress: Progress | void;
|
||||
// _publicVersion: Notification;
|
||||
_remoteInputHistory: string[];
|
||||
_shortcutId: string;
|
||||
_showWhen: boolean;
|
||||
_smallIcon: SmallIcon = {
|
||||
icon: 'ic_launcher',
|
||||
};
|
||||
_sortKey: string;
|
||||
_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;
|
||||
_timeoutAfter: number;
|
||||
_usesChronometer: boolean;
|
||||
_vibrate: number[];
|
||||
_visibility: VisibilityType;
|
||||
_when: number;
|
||||
_ticker: string | void;
|
||||
_timeoutAfter: number | void;
|
||||
_usesChronometer: boolean | void;
|
||||
_vibrate: number[] | void;
|
||||
_visibility: VisibilityType | void;
|
||||
_when: number | void;
|
||||
|
||||
// android unsupported
|
||||
// content: RemoteViews
|
||||
|
@ -167,9 +66,178 @@ export default class AndroidNotification {
|
|||
// fullScreenIntent: PendingIntent
|
||||
// sound.streamType
|
||||
|
||||
constructor(notification: Notification) {
|
||||
constructor(notification: Notification, data?: NativeAndroidNotification) {
|
||||
this._notification = notification;
|
||||
this._people = [];
|
||||
|
||||
if (data) {
|
||||
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._people = this._people || [];
|
||||
this._smallIcon = this._smallIcon || {
|
||||
icon: 'ic_launcher',
|
||||
};
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -506,11 +574,15 @@ export default class AndroidNotification {
|
|||
}
|
||||
|
||||
build(): NativeAndroidNotification {
|
||||
// TODO: Validation
|
||||
// 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 {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -3,48 +3,65 @@
|
|||
* IOSNotification representation wrapper
|
||||
*/
|
||||
import type Notification from './Notification';
|
||||
|
||||
type AttachmentOptions = {|
|
||||
TypeHint: string,
|
||||
ThumbnailHidden: boolean,
|
||||
ThumbnailClippingRect: {
|
||||
height: number,
|
||||
width: number,
|
||||
x: number,
|
||||
y: number,
|
||||
},
|
||||
ThumbnailTime: number,
|
||||
|};
|
||||
|
||||
type Attachment = {|
|
||||
identifier: string,
|
||||
options?: AttachmentOptions,
|
||||
url: string,
|
||||
|};
|
||||
|
||||
export type NativeIOSNotification = {|
|
||||
alertAction?: string,
|
||||
attachments: Attachment[],
|
||||
badge?: number,
|
||||
category?: string,
|
||||
hasAction?: boolean,
|
||||
launchImage?: string,
|
||||
threadIdentifier?: string,
|
||||
|};
|
||||
import type {
|
||||
IOSAttachment,
|
||||
IOSAttachmentOptions,
|
||||
NativeIOSNotification,
|
||||
} from './types';
|
||||
|
||||
export default class IOSNotification {
|
||||
_alertAction: string; // alertAction | N/A
|
||||
_attachments: Attachment[]; // N/A | attachments
|
||||
_badge: number; // applicationIconBadgeNumber | badge
|
||||
_category: string;
|
||||
_hasAction: boolean; // hasAction | N/A
|
||||
_launchImage: string; // alertLaunchImage | launchImageName
|
||||
_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; // N/A | threadIdentifier
|
||||
_threadIdentifier: string | void; // N/A | threadIdentifier
|
||||
|
||||
constructor(notification: Notification) {
|
||||
this._attachments = [];
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -57,7 +74,7 @@ export default class IOSNotification {
|
|||
addAttachment(
|
||||
identifier: string,
|
||||
url: string,
|
||||
options?: AttachmentOptions
|
||||
options?: IOSAttachmentOptions
|
||||
): Notification {
|
||||
this._attachments.push({
|
||||
identifier,
|
||||
|
@ -128,7 +145,7 @@ export default class IOSNotification {
|
|||
}
|
||||
|
||||
build(): NativeIOSNotification {
|
||||
// TODO: Validation
|
||||
// TODO: Validation of required fields
|
||||
|
||||
return {
|
||||
alertAction: this._alertAction,
|
||||
|
|
|
@ -7,21 +7,12 @@ import AndroidNotification from './AndroidNotification';
|
|||
import IOSNotification from './IOSNotification';
|
||||
import { generatePushID, isObject } from '../../utils';
|
||||
|
||||
import type { NativeAndroidNotification } from './AndroidNotification';
|
||||
import type { NativeIOSNotification } from './IOSNotification';
|
||||
import type { Schedule } from './';
|
||||
import type { NativeNotification } from './types';
|
||||
|
||||
type NativeNotification = {|
|
||||
android?: NativeAndroidNotification,
|
||||
body: string,
|
||||
data: { [string]: string },
|
||||
ios?: NativeIOSNotification,
|
||||
notificationId: string,
|
||||
schedule?: Schedule,
|
||||
sound?: string,
|
||||
subtitle?: string,
|
||||
title: string,
|
||||
|};
|
||||
export type NotificationOpen = {
|
||||
action: string,
|
||||
notification: Notification,
|
||||
};
|
||||
|
||||
export default class Notification {
|
||||
// iOS 8/9 | 10+ | Android
|
||||
|
@ -34,22 +25,57 @@ export default class Notification {
|
|||
_subtitle: string | void; // N/A | subtitle | subText
|
||||
_title: string; // alertTitle | title | contentTitle
|
||||
|
||||
constructor() {
|
||||
this._android = new AndroidNotification(this);
|
||||
this._data = {};
|
||||
this._ios = new IOSNotification(this);
|
||||
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 = generatePushID();
|
||||
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
|
||||
|
|
|
@ -1,38 +1,51 @@
|
|||
/**
|
||||
* @flow
|
||||
* Messaging (FCM) representation wrapper
|
||||
* 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 AndroidChannel from './AndroidChannel';
|
||||
import AndroidChannelGroup from './AndroidChannelGroup';
|
||||
import AndroidNotifications from './AndroidNotifications';
|
||||
import Notification from './Notification';
|
||||
import {
|
||||
BadgeIconType,
|
||||
Category,
|
||||
Defaults,
|
||||
GroupAlert,
|
||||
Importance,
|
||||
Priority,
|
||||
Visibility,
|
||||
} from './AndroidNotification';
|
||||
} from './types';
|
||||
|
||||
import type App from '../core/app';
|
||||
import type { NotificationOpen } from './Notification';
|
||||
import type {
|
||||
NativeNotification,
|
||||
NativeNotificationOpen,
|
||||
Schedule,
|
||||
} from './types';
|
||||
|
||||
// TODO: Received notification type will be different from sent notification
|
||||
type OnNotification = Notification => any;
|
||||
|
||||
type OnNotificationObserver = {
|
||||
next: OnNotification,
|
||||
};
|
||||
|
||||
export type Schedule = {
|
||||
exact?: boolean,
|
||||
fireDate: number,
|
||||
repeatInterval?: 'minute' | 'hour' | 'day' | 'week',
|
||||
type OnNotificationOpened = NotificationOpen => any;
|
||||
|
||||
type OnNotificationOpenedObserver = {
|
||||
next: OnNotificationOpen,
|
||||
};
|
||||
|
||||
const NATIVE_EVENTS = ['notifications_notification_received'];
|
||||
const NATIVE_EVENTS = [
|
||||
'notifications_notification_displayed',
|
||||
'notifications_notification_opened',
|
||||
'notifications_notification_received',
|
||||
];
|
||||
|
||||
export const MODULE_NAME = 'RNFirebaseNotifications';
|
||||
export const NAMESPACE = 'notifications';
|
||||
|
@ -55,6 +68,8 @@ export const NAMESPACE = 'notifications';
|
|||
* @class Notifications
|
||||
*/
|
||||
export default class Notifications extends ModuleBase {
|
||||
_android: AndroidNotifications;
|
||||
|
||||
constructor(app: App) {
|
||||
super(app, {
|
||||
events: NATIVE_EVENTS,
|
||||
|
@ -62,35 +77,71 @@ export default class Notifications extends ModuleBase {
|
|||
multiApp: false,
|
||||
namespace: NAMESPACE,
|
||||
});
|
||||
this._android = new AndroidNotifications(this);
|
||||
|
||||
SharedEventEmitter.addListener(
|
||||
// sub to internal native event - this fans out to
|
||||
// public event name: onMessage
|
||||
// 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),
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
SharedEventEmitter.addListener(
|
||||
// sub to internal native event - this fans out to
|
||||
// public event name: onNotification
|
||||
'notifications_notification_received',
|
||||
(notification: Notification) => {
|
||||
SharedEventEmitter.emit('onNotification', notification);
|
||||
(notification: NativeNotification) => {
|
||||
SharedEventEmitter.emit(
|
||||
'onNotification',
|
||||
new Notification(notification)
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
cancelAllNotifications(): Promise<void> {
|
||||
return getNativeModule(this).cancelAllNotifications();
|
||||
get android(): AndroidNotifications {
|
||||
return this._android;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel a local notification by id.
|
||||
* @param id
|
||||
* @returns {*}
|
||||
* Cancel all notifications
|
||||
*/
|
||||
cancelNotification(notificationId: string): Promise<void> {
|
||||
if (!notificationId) {
|
||||
return Promise.reject(new Error('Missing notificationId'));
|
||||
}
|
||||
return getNativeModule(this).cancelNotification(notificationId);
|
||||
cancelAllNotifications(): void {
|
||||
getNativeModule(this).cancelAllNotifications();
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a local notification
|
||||
* 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 {*}
|
||||
*/
|
||||
|
@ -103,10 +154,16 @@ export default class Notifications extends ModuleBase {
|
|||
return getNativeModule(this).displayNotification(notification.build());
|
||||
}
|
||||
|
||||
getBadge(): Promise<number> {
|
||||
return getNativeModule(this).getBadge();
|
||||
}
|
||||
|
||||
getInitialNotification(): Promise<Object> {
|
||||
return getNativeModule(this).getInitialNotification();
|
||||
// TODO
|
||||
// .then(notification => (notification ? new Notification(this, notification) : null));
|
||||
return getNativeModule(this)
|
||||
.getInitialNotification()
|
||||
.then(
|
||||
notification => (notification ? new Notification(notification) : null)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -131,7 +188,6 @@ export default class Notifications extends ModuleBase {
|
|||
);
|
||||
}
|
||||
|
||||
// TODO: iOS finish
|
||||
getLogger(this).info('Creating onNotification listener');
|
||||
SharedEventEmitter.addListener('onNotification', listener);
|
||||
|
||||
|
@ -141,28 +197,74 @@ export default class Notifications extends ModuleBase {
|
|||
};
|
||||
}
|
||||
|
||||
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.
|
||||
* @returns {*}
|
||||
*/
|
||||
removeAllDeliveredNotifications(): Promise<void> {
|
||||
return getNativeModule(this).removeAllDeliveredNotifications();
|
||||
removeAllDeliveredNotifications(): void {
|
||||
getNativeModule(this).removeAllDeliveredNotifications();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a delivered notification.
|
||||
* @param notificationId
|
||||
* @returns {*}
|
||||
*/
|
||||
removeDeliveredNotification(notificationId: string): Promise<void> {
|
||||
removeDeliveredNotification(notificationId: string): void {
|
||||
if (!notificationId) {
|
||||
return Promise.reject(new Error('Missing notificationId'));
|
||||
throw new Error(
|
||||
'Notifications: removeDeliveredNotification expects a `notificationId`'
|
||||
);
|
||||
}
|
||||
return getNativeModule(this).removeDeliveredNotification(notificationId);
|
||||
getNativeModule(this).removeDeliveredNotification(notificationId);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Schedule a notification
|
||||
* @param notification
|
||||
* @returns {*}
|
||||
*/
|
||||
|
@ -179,14 +281,21 @@ export default class Notifications extends ModuleBase {
|
|||
nativeNotification.schedule = schedule;
|
||||
return getNativeModule(this).scheduleNotification(nativeNotification);
|
||||
}
|
||||
|
||||
setBadge(badge: number): void {
|
||||
getNativeModule(this).setBadge(badge);
|
||||
}
|
||||
}
|
||||
|
||||
export const statics = {
|
||||
Android: {
|
||||
BadgeIconType,
|
||||
Category,
|
||||
Channel: AndroidChannel,
|
||||
ChannelGroup: AndroidChannelGroup,
|
||||
Defaults,
|
||||
GroupAlert,
|
||||
Importance,
|
||||
Priority,
|
||||
Visibility,
|
||||
},
|
||||
|
|
|
@ -0,0 +1,178 @@
|
|||
/**
|
||||
* @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 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 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 NativeAndroidNotification = {|
|
||||
// TODO actions: Action[],
|
||||
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,
|
||||
|};
|
|
@ -18,7 +18,7 @@ import type {
|
|||
const FirebaseCoreModule = NativeModules.RNFirebase;
|
||||
|
||||
const APPS: { [string]: App } = {};
|
||||
const APP_MODULES: { [App]: { [string]: FirebaseModule } } = {};
|
||||
const APP_MODULES: { [string]: { [string]: FirebaseModule } } = {};
|
||||
const DEFAULT_APP_NAME = '[DEFAULT]';
|
||||
|
||||
export default {
|
||||
|
@ -49,8 +49,8 @@ export default {
|
|||
InstanceClass: Class<M>
|
||||
): () => FirebaseModule {
|
||||
return (): M => {
|
||||
if (!APP_MODULES[app]) {
|
||||
APP_MODULES[app] = {};
|
||||
if (!APP_MODULES[app._name]) {
|
||||
APP_MODULES[app._name] = {};
|
||||
}
|
||||
|
||||
if (
|
||||
|
@ -62,11 +62,11 @@ export default {
|
|||
app.utils().checkPlayServicesAvailability();
|
||||
}
|
||||
|
||||
if (!APP_MODULES[app][namespace]) {
|
||||
APP_MODULES[app][namespace] = new InstanceClass(app, app.options);
|
||||
if (!APP_MODULES[app._name][namespace]) {
|
||||
APP_MODULES[app._name][namespace] = new InstanceClass(app, app.options);
|
||||
}
|
||||
|
||||
return APP_MODULES[app][namespace];
|
||||
return APP_MODULES[app._name][namespace];
|
||||
};
|
||||
},
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ import io.invertase.firebase.firestore.RNFirebaseFirestorePackage;
|
|||
import io.invertase.firebase.instanceid.RNFirebaseInstanceIdPackage;
|
||||
import io.invertase.firebase.links.RNFirebaseLinksPackage;
|
||||
import io.invertase.firebase.messaging.RNFirebaseMessagingPackage;
|
||||
import io.invertase.firebase.notifications.RNFirebaseNotificationsPackage;
|
||||
import io.invertase.firebase.perf.RNFirebasePerformancePackage;
|
||||
import io.invertase.firebase.storage.RNFirebaseStoragePackage;
|
||||
import com.oblador.vectoricons.VectorIconsPackage;
|
||||
|
@ -51,6 +52,7 @@ public class MainApplication extends Application implements ReactApplication {
|
|||
new RNFirebaseInstanceIdPackage(),
|
||||
new RNFirebaseLinksPackage(),
|
||||
new RNFirebaseMessagingPackage(),
|
||||
new RNFirebaseNotificationsPackage(),
|
||||
new RNFirebasePerformancePackage(),
|
||||
new RNFirebaseStoragePackage()
|
||||
);
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
#import <React/RCTBundleURLProvider.h>
|
||||
#import <React/RCTRootView.h>
|
||||
#import <Firebase.h>
|
||||
#import <RNFirebaseMessaging.h>
|
||||
#import <RNFirebaseNotifications.h>
|
||||
|
||||
@implementation AppDelegate
|
||||
|
||||
|
@ -38,13 +38,9 @@
|
|||
return YES;
|
||||
}
|
||||
|
||||
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
|
||||
[[RNFirebaseMessaging instance] didReceiveRemoteNotification:userInfo];
|
||||
}
|
||||
|
||||
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
|
||||
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
|
||||
[[RNFirebaseMessaging instance] didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler];
|
||||
[[RNFirebaseNotifications instance] didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -7,14 +7,6 @@ import DatabaseContents from './tests/support/DatabaseContents';
|
|||
RNfirebase.database.enableLogging(true);
|
||||
RNfirebase.firestore.enableLogging(true);
|
||||
|
||||
RNfirebase.messaging().onMessage(message => {
|
||||
console.log('got new message: ', message);
|
||||
});
|
||||
|
||||
RNfirebase.messaging().onTokenRefresh(token => {
|
||||
console.log('got new token: ', token);
|
||||
});
|
||||
|
||||
const init = async () => {
|
||||
try {
|
||||
await RNfirebase.messaging().requestPermission();
|
||||
|
@ -22,9 +14,47 @@ const init = async () => {
|
|||
console.log('instanceid: ', instanceid);
|
||||
const token = await RNfirebase.messaging().getToken();
|
||||
console.log('token: ', token);
|
||||
const initialMessage = await RNfirebase.messaging().getInitialMessage();
|
||||
console.log('initial message: ', initialMessage);
|
||||
const initialNotification = await RNfirebase.notifications().getInitialNotification();
|
||||
console.log('initialNotification: ', initialNotification);
|
||||
|
||||
RNfirebase.messaging().onMessage(message => {
|
||||
console.log('onMessage: ', message);
|
||||
});
|
||||
RNfirebase.messaging().onTokenRefresh(deviceToken => {
|
||||
dispatch(fcmTokenReceived(deviceToken));
|
||||
});
|
||||
RNfirebase.notifications().onNotification(notification => {
|
||||
console.log('onNotification: ', notification);
|
||||
});
|
||||
RNfirebase.notifications().onNotificationOpened(notification => {
|
||||
console.log('onNotificationOpened: ', notification);
|
||||
});
|
||||
RNfirebase.notifications().onNotificationDisplayed(notification => {
|
||||
console.log('onNotificationDisplayed: ', notification);
|
||||
});
|
||||
// RNfirebase.instanceid().delete();
|
||||
const channel = new RNfirebase.notifications.Android.Channel();
|
||||
channel
|
||||
.setChannelId('test')
|
||||
.setName('test')
|
||||
.setImportance(RNfirebase.notifications.Android.Importance.Max)
|
||||
.setDescription('test channel');
|
||||
RNfirebase.notifications().android.createChannel(channel);
|
||||
|
||||
const notification = new RNfirebase.notifications.Notification();
|
||||
notification
|
||||
.setTitle('Test title')
|
||||
.setBody('Test body')
|
||||
.android.setChannelId('test')
|
||||
.android.setPriority(RNfirebase.notifications.Android.Priority.Max);
|
||||
const date = new Date();
|
||||
date.setMinutes(date.getMinutes() + 1);
|
||||
setTimeout(() => {
|
||||
RNfirebase.notifications().displayNotification(notification);
|
||||
}, 5);
|
||||
RNfirebase.notifications().scheduleNotification(notification, {
|
||||
fireDate: date.getTime(),
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('messaging init error:', error);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue