From 850f04914f53f689e1e10ccc7f9fb5568a291373 Mon Sep 17 00:00:00 2001 From: Chris Bianca Date: Fri, 2 Feb 2018 08:40:48 +0000 Subject: [PATCH] [fcm] Android instanceid and core fcm support; iOS instance and basic fcm support --- android/build.gradle | 2 +- .../instanceid/RNFirebaseInstanceId.java | 45 ++ .../RNFirebaseInstanceIdPackage.java | 38 ++ .../firebase/messaging/BadgeHelper.java | 43 -- .../firebase/messaging/InstanceIdService.java | 32 -- .../firebase/messaging/MessagingService.java | 64 --- .../RNFirebaseInstanceIdService.java | 24 + .../messaging/RNFirebaseMessaging.java | 468 +++++++++--------- .../messaging/RNFirebaseMessagingService.java | 25 + ios/RNFirebase.xcodeproj/project.pbxproj | 48 +- .../instanceid/RNFirebaseInstanceId.h | 19 + .../instanceid/RNFirebaseInstanceId.m | 35 ++ .../messaging/NewRNFirebaseMessaging.h | 23 - .../messaging/NewRNFirebaseMessaging.m | 132 ----- .../messaging/RNFirebaseMessaging.h | 14 +- .../messaging/RNFirebaseMessaging.m | 447 ++--------------- .../notifications/RNFirebaseNotifications.h | 22 + .../notifications/RNFirebaseNotifications.m | 14 + lib/modules/core/firebase-app.js | 17 +- lib/modules/core/firebase.js | 29 +- lib/modules/instanceid/index.js | 31 ++ lib/modules/messaging/index.js | 388 ++++++--------- lib/modules/messaging/messagingIndex.js | 186 ------- .../{notificationsIndex.js => index.js} | 17 +- lib/types/index.js | 25 +- .../android/app/src/main/AndroidManifest.xml | 8 +- .../MainApplication.java | 2 + tests/src/firebase.js | 37 +- 28 files changed, 824 insertions(+), 1411 deletions(-) create mode 100644 android/src/main/java/io/invertase/firebase/instanceid/RNFirebaseInstanceId.java create mode 100644 android/src/main/java/io/invertase/firebase/instanceid/RNFirebaseInstanceIdPackage.java delete mode 100644 android/src/main/java/io/invertase/firebase/messaging/BadgeHelper.java delete mode 100644 android/src/main/java/io/invertase/firebase/messaging/InstanceIdService.java delete mode 100644 android/src/main/java/io/invertase/firebase/messaging/MessagingService.java create mode 100644 android/src/main/java/io/invertase/firebase/messaging/RNFirebaseInstanceIdService.java create mode 100644 android/src/main/java/io/invertase/firebase/messaging/RNFirebaseMessagingService.java create mode 100644 ios/RNFirebase/instanceid/RNFirebaseInstanceId.h create mode 100644 ios/RNFirebase/instanceid/RNFirebaseInstanceId.m delete mode 100644 ios/RNFirebase/messaging/NewRNFirebaseMessaging.h delete mode 100644 ios/RNFirebase/messaging/NewRNFirebaseMessaging.m create mode 100644 ios/RNFirebase/notifications/RNFirebaseNotifications.h create mode 100644 ios/RNFirebase/notifications/RNFirebaseNotifications.m create mode 100644 lib/modules/instanceid/index.js delete mode 100644 lib/modules/messaging/messagingIndex.js rename lib/modules/notifications/{notificationsIndex.js => index.js} (93%) diff --git a/android/build.gradle b/android/build.gradle index 5de53c85..ba96df94 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -82,7 +82,7 @@ rootProject.gradle.buildFinished { buildResult -> dependencies { compile fileTree(include: ['*.jar'], dir: 'libs') compile "com.facebook.react:react-native:+" // From node_modules - compile 'me.leolin:ShortcutBadger:1.1.18@aar' + compile 'me.leolin:ShortcutBadger:1.1.21@aar' compile "com.google.android.gms:play-services-base:$firebaseVersion" compile "com.google.firebase:firebase-core:$firebaseVersion" compile "com.google.firebase:firebase-config:$firebaseVersion" diff --git a/android/src/main/java/io/invertase/firebase/instanceid/RNFirebaseInstanceId.java b/android/src/main/java/io/invertase/firebase/instanceid/RNFirebaseInstanceId.java new file mode 100644 index 00000000..b95b5126 --- /dev/null +++ b/android/src/main/java/io/invertase/firebase/instanceid/RNFirebaseInstanceId.java @@ -0,0 +1,45 @@ +package io.invertase.firebase.instanceid; + + +import android.util.Log; + +import com.facebook.react.bridge.Promise; +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.bridge.ReactContextBaseJavaModule; +import com.facebook.react.bridge.ReactMethod; +import com.google.firebase.iid.FirebaseInstanceId; + +import java.io.IOException; + +public class RNFirebaseInstanceId extends ReactContextBaseJavaModule { + + private static final String TAG = "RNFirebaseInstanceId"; + + public RNFirebaseInstanceId(ReactApplicationContext reactContext) { + super(reactContext); + Log.d(TAG, "New instance"); + } + + @Override + public String getName() { + return TAG; + } + + @ReactMethod + public void delete(Promise promise){ + try { + Log.d(TAG, "Deleting instance id"); + FirebaseInstanceId.getInstance().deleteInstanceId(); + promise.resolve(null); + } catch (IOException e) { + Log.e(TAG, e.getMessage()); + promise.reject("instance_id_error", e.getMessage()); + } + } + + @ReactMethod + public void get(Promise promise){ + String id = FirebaseInstanceId.getInstance().getId(); + promise.resolve(id); + } +} diff --git a/android/src/main/java/io/invertase/firebase/instanceid/RNFirebaseInstanceIdPackage.java b/android/src/main/java/io/invertase/firebase/instanceid/RNFirebaseInstanceIdPackage.java new file mode 100644 index 00000000..2d635dc6 --- /dev/null +++ b/android/src/main/java/io/invertase/firebase/instanceid/RNFirebaseInstanceIdPackage.java @@ -0,0 +1,38 @@ +package io.invertase.firebase.instanceid; + +import com.facebook.react.ReactPackage; +import com.facebook.react.bridge.NativeModule; +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.uimanager.UIManagerModule; +import com.facebook.react.uimanager.ViewManager; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +@SuppressWarnings("unused") +public class RNFirebaseInstanceIdPackage implements ReactPackage { + public RNFirebaseInstanceIdPackage() { + } + + /** + * @param reactContext react application context that can be used to create modules + * @return list of native modules to register with the newly created catalyst instance + */ + @Override + public List createNativeModules(ReactApplicationContext reactContext) { + List modules = new ArrayList<>(); + modules.add(new RNFirebaseInstanceId(reactContext)); + + return modules; + } + + /** + * @param reactContext + * @return a list of view managers that should be registered with {@link UIManagerModule} + */ + @Override + public List createViewManagers(ReactApplicationContext reactContext) { + return Collections.emptyList(); + } +} diff --git a/android/src/main/java/io/invertase/firebase/messaging/BadgeHelper.java b/android/src/main/java/io/invertase/firebase/messaging/BadgeHelper.java deleted file mode 100644 index ba88bc7e..00000000 --- a/android/src/main/java/io/invertase/firebase/messaging/BadgeHelper.java +++ /dev/null @@ -1,43 +0,0 @@ -package io.invertase.firebase.messaging; - -import android.content.Context; -import android.content.SharedPreferences; -import android.util.Log; - -import me.leolin.shortcutbadger.ShortcutBadger; - -public class BadgeHelper { - - private static final String TAG = "BadgeHelper"; - private static final String PREFERENCES_FILE = "BadgeCountFile"; - private static final String BADGE_COUNT_KEY = "BadgeCount"; - - private Context mContext; - private SharedPreferences sharedPreferences = null; - - public BadgeHelper(Context context) { - mContext = context; - sharedPreferences = (SharedPreferences) mContext.getSharedPreferences(PREFERENCES_FILE, Context.MODE_PRIVATE); - } - - public int getBadgeCount() { - return sharedPreferences.getInt(BADGE_COUNT_KEY, 0); - } - - public void setBadgeCount(int badgeCount) { - storeBadgeCount(badgeCount); - if (badgeCount == 0) { - ShortcutBadger.removeCount(mContext); - Log.d(TAG, "Remove count"); - } else { - ShortcutBadger.applyCount(mContext, badgeCount); - Log.d(TAG, "Apply count: " + badgeCount); - } - } - - private void storeBadgeCount(int badgeCount) { - SharedPreferences.Editor editor = sharedPreferences.edit(); - editor.putInt(BADGE_COUNT_KEY, badgeCount); - editor.apply(); - } -} diff --git a/android/src/main/java/io/invertase/firebase/messaging/InstanceIdService.java b/android/src/main/java/io/invertase/firebase/messaging/InstanceIdService.java deleted file mode 100644 index 9e487be3..00000000 --- a/android/src/main/java/io/invertase/firebase/messaging/InstanceIdService.java +++ /dev/null @@ -1,32 +0,0 @@ -package io.invertase.firebase.messaging; - -import android.content.Intent; -import android.os.Bundle; -import android.util.Log; - -import com.google.firebase.iid.FirebaseInstanceId; -import com.google.firebase.iid.FirebaseInstanceIdService; - -public class InstanceIdService extends FirebaseInstanceIdService { - - private static final String TAG = "InstanceIdService"; - - /** - * Called if InstanceID token is updated. This may occur if the security of - * the previous token had been compromised. This call is initiated by the - * InstanceID provider. - */ - @Override - public void onTokenRefresh() { - // Get updated InstanceID token. - String refreshedToken = FirebaseInstanceId.getInstance().getToken(); - Log.d(TAG, "Refreshed token: " + refreshedToken); - - // Broadcast refreshed token - Intent i = new Intent("io.invertase.firebase.messaging.FCMRefreshToken"); - Bundle bundle = new Bundle(); - bundle.putString("token", refreshedToken); - i.putExtras(bundle); - sendBroadcast(i); - } -} diff --git a/android/src/main/java/io/invertase/firebase/messaging/MessagingService.java b/android/src/main/java/io/invertase/firebase/messaging/MessagingService.java deleted file mode 100644 index 9b361e25..00000000 --- a/android/src/main/java/io/invertase/firebase/messaging/MessagingService.java +++ /dev/null @@ -1,64 +0,0 @@ -package io.invertase.firebase.messaging; - -import java.util.Map; -import android.content.Intent; -import android.os.Bundle; -import android.util.Log; - -import com.google.firebase.messaging.FirebaseMessagingService; -import com.google.firebase.messaging.RemoteMessage; - -import org.json.JSONException; -import org.json.JSONObject; - -public class MessagingService extends FirebaseMessagingService { - - private static final String TAG = "MessagingService"; - - @Override - public void onMessageReceived(RemoteMessage remoteMessage) { - Log.d(TAG, "Remote message received"); - Intent i = new Intent("io.invertase.firebase.messaging.ReceiveNotification"); - i.putExtra("data", remoteMessage); - handleBadge(remoteMessage); - buildLocalNotification(remoteMessage); - sendOrderedBroadcast(i, null); - } - - private void handleBadge(RemoteMessage remoteMessage) { - BadgeHelper badgeHelper = new BadgeHelper(this); - if (remoteMessage.getData() == null) { - return; - } - - Map data = remoteMessage.getData(); - if (data.get("badge") == null) { - return; - } - - try { - int badgeCount = Integer.parseInt((String)data.get("badge")); - badgeHelper.setBadgeCount(badgeCount); - } catch (Exception e) { - Log.e(TAG, "Badge count needs to be an integer", e); - } - } - - private void buildLocalNotification(RemoteMessage remoteMessage) { - if(remoteMessage.getData() == null){ - return; - } - Map data = remoteMessage.getData(); - String customNotification = data.get("custom_notification"); - if(customNotification != null){ - try { - Bundle bundle = BundleJSONConverter.convertToBundle(new JSONObject(customNotification)); - RNFirebaseLocalMessagingHelper helper = new RNFirebaseLocalMessagingHelper(this.getApplication()); - helper.sendNotification(bundle); - } catch (JSONException e) { - e.printStackTrace(); - } - - } - } -} diff --git a/android/src/main/java/io/invertase/firebase/messaging/RNFirebaseInstanceIdService.java b/android/src/main/java/io/invertase/firebase/messaging/RNFirebaseInstanceIdService.java new file mode 100644 index 00000000..349760f4 --- /dev/null +++ b/android/src/main/java/io/invertase/firebase/messaging/RNFirebaseInstanceIdService.java @@ -0,0 +1,24 @@ +package io.invertase.firebase.messaging; + + +import android.content.Intent; +import android.support.v4.content.LocalBroadcastManager; +import android.util.Log; + +import com.google.firebase.iid.FirebaseInstanceIdService; + +public class RNFirebaseInstanceIdService extends FirebaseInstanceIdService { + private static final String TAG = "RNFInstanceIdService"; + public static final String TOKEN_REFRESH_EVENT = "messaging-token-refresh"; + + @Override + public void onTokenRefresh() { + Log.d(TAG, "onTokenRefresh event received"); + + // Build an Intent to pass the token to the RN Application + Intent tokenRefreshEvent = new Intent(TOKEN_REFRESH_EVENT); + + // Broadcast it so it is only available to the RN Application + LocalBroadcastManager.getInstance(this).sendBroadcast(tokenRefreshEvent); + } +} diff --git a/android/src/main/java/io/invertase/firebase/messaging/RNFirebaseMessaging.java b/android/src/main/java/io/invertase/firebase/messaging/RNFirebaseMessaging.java index bc6d1d7b..9292eb2d 100644 --- a/android/src/main/java/io/invertase/firebase/messaging/RNFirebaseMessaging.java +++ b/android/src/main/java/io/invertase/firebase/messaging/RNFirebaseMessaging.java @@ -1,17 +1,17 @@ package io.invertase.firebase.messaging; import android.app.Activity; -import android.app.Application; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.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; @@ -26,26 +26,33 @@ 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.io.IOException; -import java.util.ArrayList; import java.util.Map; -import java.util.Set; -public class RNFirebaseMessaging extends ReactContextBaseJavaModule implements LifecycleEventListener, ActivityEventListener { - private final static String TAG = RNFirebaseMessaging.class.getCanonicalName(); - private RNFirebaseLocalMessagingHelper mRNFirebaseLocalMessagingHelper; - private BadgeHelper mBadgeHelper; +public class RNFirebaseMessaging extends ReactContextBaseJavaModule implements ActivityEventListener { + private static final String BADGE_FILE = "BadgeCountFile"; + private static final String BADGE_KEY = "BadgeCount"; - public RNFirebaseMessaging(ReactApplicationContext reactContext) { - super(reactContext); - mRNFirebaseLocalMessagingHelper = new RNFirebaseLocalMessagingHelper((Application) reactContext.getApplicationContext()); - mBadgeHelper = new BadgeHelper(reactContext.getApplicationContext()); - getReactApplicationContext().addLifecycleEventListener(this); - getReactApplicationContext().addActivityEventListener(this); - registerTokenRefreshHandler(); - registerMessageHandler(); - registerLocalMessageHandler(); + private static final String TAG = RNFirebaseMessaging.class.getCanonicalName(); + + 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 + localBroadcastManager.registerReceiver(new MessageReceiver(), + new IntentFilter(RNFirebaseMessagingService.MESSAGE_EVENT)); + + // Subscribe to token refresh events + localBroadcastManager.registerReceiver(new RefreshTokenReceiver(), + new IntentFilter(RNFirebaseInstanceIdService.TOKEN_REFRESH_EVENT)); } @Override @@ -54,73 +61,84 @@ public class RNFirebaseMessaging extends ReactContextBaseJavaModule implements L } @ReactMethod - public void getInitialNotification(Promise promise) { - Activity activity = getCurrentActivity(); - if (activity == null) { + public void getToken(Promise promise) { + String token = FirebaseInstanceId.getInstance().getToken(); + Log.d(TAG, "Firebase token: " + token); + promise.resolve(token); + } + + @ReactMethod + public void requestPermission(Promise promise) { + // TODO: Object structure? + promise.resolve(null); + } + + // 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 sendMessage(ReadableMap messageMap, Promise promise) { + if (!messageMap.hasKey("to")) { + promise.reject("messaging/invalid-message", "The supplied message is missing a 'to' field"); return; } - promise.resolve(parseIntent(getCurrentActivity().getIntent())); - } - @ReactMethod - public void requestPermissions() { - } + RemoteMessage.Builder mb = new RemoteMessage.Builder(messageMap.getString("to")); - @ReactMethod - public void getToken(Promise promise) { - Log.d(TAG, "Firebase token: " + FirebaseInstanceId.getInstance().getToken()); - promise.resolve(FirebaseInstanceId.getInstance().getToken()); - } - - @ReactMethod - public void deleteInstanceId(Promise promise){ - try { - Log.d(TAG, "Deleting instance id"); - FirebaseInstanceId.getInstance().deleteInstanceId(); - promise.resolve(null); - } catch (IOException e) { - Log.e(TAG, e.getMessage()); - promise.reject(null, e.getMessage()); + if (messageMap.hasKey("collapseKey")) { + mb = mb.setCollapseKey(messageMap.getString("collapseKey")); } - } - - @ReactMethod - public void createLocalNotification(ReadableMap details) { - Bundle bundle = Arguments.toBundle(details); - mRNFirebaseLocalMessagingHelper.sendNotification(bundle); - } - - @ReactMethod - public void scheduleLocalNotification(ReadableMap details) { - Bundle bundle = Arguments.toBundle(details); - mRNFirebaseLocalMessagingHelper.sendNotificationScheduled(bundle); - } - - @ReactMethod - public void cancelLocalNotification(String notificationID) { - mRNFirebaseLocalMessagingHelper.cancelLocalNotification(notificationID); - } - - @ReactMethod - public void cancelAllLocalNotifications() { - try { - mRNFirebaseLocalMessagingHelper.cancelAllLocalNotifications(); - } catch (SecurityException e) { - // In some devices/situations cancelAllLocalNotifications will throw a SecurityException - // We can safely ignore this error for now as the UX impact of this not working is minor. - Log.w(TAG, e.getMessage()); + if (messageMap.hasKey("messageId")) { + mb = mb.setMessageId(messageMap.getString("messageId")); } + if (messageMap.hasKey("messageType")) { + mb = mb.setMessageType(messageMap.getString("messageType")); + } + if (messageMap.hasKey("ttl")) { + mb = mb.setTtl(messageMap.getInt("ttl")); + } + if (messageMap.hasKey("data")) { + ReadableMap dataMap = messageMap.getMap("data"); + ReadableMapKeySetIterator iterator = dataMap.keySetIterator(); + while (iterator.hasNextKey()) { + String key = iterator.nextKey(); + mb = mb.addData(key, dataMap.getString(key)); + } + } + + FirebaseMessaging.getInstance().send(mb.build()); + + // TODO: Listen to onMessageSent and onSendError for better feedback? + promise.resolve(null); } @ReactMethod - public void removeDeliveredNotification(String notificationID) { - mRNFirebaseLocalMessagingHelper.removeDeliveredNotification(notificationID); - } - - @ReactMethod - public void removeAllDeliveredNotifications() { - mRNFirebaseLocalMessagingHelper.removeAllDeliveredNotifications(); + 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 @@ -133,159 +151,163 @@ public class RNFirebaseMessaging extends ReactContextBaseJavaModule implements L FirebaseMessaging.getInstance().unsubscribeFromTopic(topic); } - @ReactMethod - public void getScheduledLocalNotifications(Promise promise) { - ArrayList bundles = mRNFirebaseLocalMessagingHelper.getScheduledLocalNotifications(); - WritableArray array = Arguments.createArray(); - for (Bundle bundle : bundles) { - array.pushMap(Arguments.fromBundle(bundle)); - } - promise.resolve(array); - } - - @ReactMethod - public void setBadgeNumber(int badgeNumber) { - mBadgeHelper.setBadgeCount(badgeNumber); - } - - @ReactMethod - public void getBadgeNumber(Promise promise) { - promise.resolve(mBadgeHelper.getBadgeCount()); - } - - private void registerTokenRefreshHandler() { - IntentFilter intentFilter = new IntentFilter("io.invertase.firebase.messaging.FCMRefreshToken"); - getReactApplicationContext().registerReceiver(new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if (getReactApplicationContext().hasActiveCatalystInstance()) { - String token = intent.getStringExtra("token"); - Utils.sendEvent(getReactApplicationContext(), "messaging_token_refreshed", token); - } - } - }, intentFilter); - } - - @ReactMethod - public void send(ReadableMap remoteMessage) { - FirebaseMessaging fm = FirebaseMessaging.getInstance(); - RemoteMessage.Builder message = new RemoteMessage.Builder(remoteMessage.getString("sender")); - - message.setTtl(remoteMessage.getInt("ttl")); - message.setMessageId(remoteMessage.getString("id")); - message.setMessageType(remoteMessage.getString("type")); - - if (remoteMessage.hasKey("collapseKey")) { - message.setCollapseKey(remoteMessage.getString("collapseKey")); - } - - // get data keys and values and add to builder - // js side ensures all data values are strings - // so no need to check types - ReadableMap data = remoteMessage.getMap("data"); - ReadableMapKeySetIterator iterator = data.keySetIterator(); - while (iterator.hasNextKey()) { - String key = iterator.nextKey(); - String value = data.getString(key); - message.addData(key, value); - } - - fm.send(message.build()); - } - - private void registerMessageHandler() { - IntentFilter intentFilter = new IntentFilter("io.invertase.firebase.messaging.ReceiveNotification"); - - getReactApplicationContext().registerReceiver(new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if (getReactApplicationContext().hasActiveCatalystInstance()) { - RemoteMessage message = intent.getParcelableExtra("data"); - WritableMap params = Arguments.createMap(); - WritableMap fcmData = Arguments.createMap(); - - if (message.getNotification() != null) { - Notification notification = message.getNotification(); - fcmData.putString("title", notification.getTitle()); - fcmData.putString("body", notification.getBody()); - fcmData.putString("color", notification.getColor()); - fcmData.putString("icon", notification.getIcon()); - fcmData.putString("tag", notification.getTag()); - fcmData.putString("action", notification.getClickAction()); - } - params.putMap("fcm", fcmData); - - if (message.getData() != null) { - Map data = message.getData(); - Set keysIterator = data.keySet(); - for (String key : keysIterator) { - params.putString(key, data.get(key)); - } - } - Utils.sendEvent(getReactApplicationContext(), "messaging_notification_received", params); - - } - } - }, intentFilter); - } - - private void registerLocalMessageHandler() { - IntentFilter intentFilter = new IntentFilter("io.invertase.firebase.messaging.ReceiveLocalNotification"); - - getReactApplicationContext().registerReceiver(new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if (getReactApplicationContext().hasActiveCatalystInstance()) { - Utils.sendEvent(getReactApplicationContext(), "messaging_notification_received", Arguments.fromBundle(intent.getExtras())); - } - } - }, intentFilter); - } - - private WritableMap parseIntent(Intent intent) { - WritableMap params; - Bundle extras = intent.getExtras(); - if (extras != null) { - try { - params = Arguments.fromBundle(extras); - } catch (Exception e) { - Log.e(TAG, e.getMessage()); - params = Arguments.createMap(); - } - } else { - params = Arguments.createMap(); - } - WritableMap fcm = Arguments.createMap(); - fcm.putString("action", intent.getAction()); - params.putMap("fcm", fcm); - - params.putBoolean("opened_from_tray", true); - return params; - } - - @Override - public void onHostResume() { - mRNFirebaseLocalMessagingHelper.setApplicationForeground(true); - } - - @Override - public void onHostPause() { - mRNFirebaseLocalMessagingHelper.setApplicationForeground(false); - } - - @Override - public void onHostDestroy() { - - } - @Override public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) { - // todo hmm? + // FCM functionality does not need this function } @Override public void onNewIntent(Intent intent) { - // todo hmm? - Utils.sendEvent(getReactApplicationContext(), "messaging_notification_received", parseIntent(intent)); + WritableMap messageMap = parseIntentForMessage(intent); + if (messageMap != null) { + Log.d(TAG, "onNewIntent called with new FCM message"); + Utils.sendEvent(getReactApplicationContext(), "messaging_message_received", messageMap); + } + } + + 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 { + 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) { + if (getReactApplicationContext().hasActiveCatalystInstance()) { + Log.d(TAG, "Received new message"); + + RemoteMessage message = intent.getParcelableExtra("message"); + WritableMap messageMap = buildMessageMap(message); + + Utils.sendEvent(getReactApplicationContext(), "messaging_message_received", messageMap); + } + } + + private WritableMap buildMessageMap(RemoteMessage message) { + WritableMap messageMap = Arguments.createMap(); + WritableMap dataMap = Arguments.createMap(); + + if (message.getCollapseKey() != null) { + messageMap.putString("collapseKey", message.getCollapseKey()); + } + + if (message.getData() != null) { + for (Map.Entry e : message.getData().entrySet()) { + dataMap.putString(e.getKey(), e.getValue()); + } + } + messageMap.putMap("data", dataMap); + + if (message.getFrom() != null) { + messageMap.putString("from", message.getFrom()); + } + if (message.getMessageId() != null) { + messageMap.putString("messageId", message.getMessageId()); + } + if (message.getMessageType() != null) { + messageMap.putString("messageType", message.getMessageType()); + } + + 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; + } + } + + private class RefreshTokenReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + if (getReactApplicationContext().hasActiveCatalystInstance()) { + String token = FirebaseInstanceId.getInstance().getToken(); + Log.d(TAG, "Received new token: " + token); + + Utils.sendEvent(getReactApplicationContext(), "messaging_token_refreshed", token); + } + } } } diff --git a/android/src/main/java/io/invertase/firebase/messaging/RNFirebaseMessagingService.java b/android/src/main/java/io/invertase/firebase/messaging/RNFirebaseMessagingService.java new file mode 100644 index 00000000..fd350e76 --- /dev/null +++ b/android/src/main/java/io/invertase/firebase/messaging/RNFirebaseMessagingService.java @@ -0,0 +1,25 @@ +package io.invertase.firebase.messaging; + +import android.content.Intent; +import android.support.v4.content.LocalBroadcastManager; +import android.util.Log; + +import com.google.firebase.messaging.FirebaseMessagingService; +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"; + + @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); + + // Broadcast it so it is only available to the RN Application + LocalBroadcastManager.getInstance(this).sendBroadcast(messageEvent); + } +} diff --git a/ios/RNFirebase.xcodeproj/project.pbxproj b/ios/RNFirebase.xcodeproj/project.pbxproj index bc43b797..46815506 100644 --- a/ios/RNFirebase.xcodeproj/project.pbxproj +++ b/ios/RNFirebase.xcodeproj/project.pbxproj @@ -17,7 +17,9 @@ 8376F7141F7C149100D45A85 /* RNFirebaseFirestoreDocumentReference.m in Sources */ = {isa = PBXBuildFile; fileRef = 8376F70E1F7C149000D45A85 /* RNFirebaseFirestoreDocumentReference.m */; }; 8376F7151F7C149100D45A85 /* RNFirebaseFirestore.m in Sources */ = {isa = PBXBuildFile; fileRef = 8376F7101F7C149000D45A85 /* RNFirebaseFirestore.m */; }; 8376F7161F7C149100D45A85 /* RNFirebaseFirestoreCollectionReference.m in Sources */ = {isa = PBXBuildFile; fileRef = 8376F7111F7C149000D45A85 /* RNFirebaseFirestoreCollectionReference.m */; }; - 838E36FE201B9169004DCD3A /* NewRNFirebaseMessaging.m in Sources */ = {isa = PBXBuildFile; fileRef = 838E36FD201B9169004DCD3A /* NewRNFirebaseMessaging.m */; }; + 838E36FE201B9169004DCD3A /* RNFirebaseMessaging.m in Sources */ = {isa = PBXBuildFile; fileRef = 838E36FD201B9169004DCD3A /* RNFirebaseMessaging.m */; }; + 838E372320231DF0004DCD3A /* RNFirebaseInstanceId.m in Sources */ = {isa = PBXBuildFile; fileRef = 838E372220231DF0004DCD3A /* RNFirebaseInstanceId.m */; }; + 838E372720231E15004DCD3A /* RNFirebaseNotifications.m in Sources */ = {isa = PBXBuildFile; fileRef = 838E372520231E15004DCD3A /* RNFirebaseNotifications.m */; }; 839D916C1EF3E20B0077C7C8 /* RNFirebaseAdMob.m in Sources */ = {isa = PBXBuildFile; fileRef = 839D914F1EF3E20A0077C7C8 /* RNFirebaseAdMob.m */; }; 839D916D1EF3E20B0077C7C8 /* RNFirebaseAdMobInterstitial.m in Sources */ = {isa = PBXBuildFile; fileRef = 839D91511EF3E20A0077C7C8 /* RNFirebaseAdMobInterstitial.m */; }; 839D916E1EF3E20B0077C7C8 /* RNFirebaseAdMobRewardedVideo.m in Sources */ = {isa = PBXBuildFile; fileRef = 839D91531EF3E20A0077C7C8 /* RNFirebaseAdMobRewardedVideo.m */; }; @@ -26,7 +28,6 @@ 839D91711EF3E20B0077C7C8 /* RNFirebaseRemoteConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = 839D915C1EF3E20A0077C7C8 /* RNFirebaseRemoteConfig.m */; }; 839D91721EF3E20B0077C7C8 /* RNFirebaseCrash.m in Sources */ = {isa = PBXBuildFile; fileRef = 839D915F1EF3E20A0077C7C8 /* RNFirebaseCrash.m */; }; 839D91731EF3E20B0077C7C8 /* RNFirebaseDatabase.m in Sources */ = {isa = PBXBuildFile; fileRef = 839D91621EF3E20A0077C7C8 /* RNFirebaseDatabase.m */; }; - 839D91741EF3E20B0077C7C8 /* RNFirebaseMessaging.m in Sources */ = {isa = PBXBuildFile; fileRef = 839D91651EF3E20A0077C7C8 /* RNFirebaseMessaging.m */; }; 839D91751EF3E20B0077C7C8 /* RNFirebasePerformance.m in Sources */ = {isa = PBXBuildFile; fileRef = 839D91681EF3E20A0077C7C8 /* RNFirebasePerformance.m */; }; 83C3EEEE1FA1EACC00B64D3C /* RNFirebaseUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = 83C3EEEC1FA1EACC00B64D3C /* RNFirebaseUtil.m */; }; BA84AE571FA9E59800E79390 /* RNFirebaseStorage.m in Sources */ = {isa = PBXBuildFile; fileRef = BA84AE561FA9E59800E79390 /* RNFirebaseStorage.m */; }; @@ -67,8 +68,12 @@ 8376F7111F7C149000D45A85 /* RNFirebaseFirestoreCollectionReference.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFirebaseFirestoreCollectionReference.m; sourceTree = ""; }; 8376F7121F7C149000D45A85 /* RNFirebaseFirestoreDocumentReference.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNFirebaseFirestoreDocumentReference.h; sourceTree = ""; }; 8376F7131F7C149000D45A85 /* RNFirebaseFirestoreCollectionReference.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNFirebaseFirestoreCollectionReference.h; sourceTree = ""; }; - 838E36FC201B9169004DCD3A /* NewRNFirebaseMessaging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NewRNFirebaseMessaging.h; sourceTree = ""; }; - 838E36FD201B9169004DCD3A /* NewRNFirebaseMessaging.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NewRNFirebaseMessaging.m; sourceTree = ""; }; + 838E36FC201B9169004DCD3A /* RNFirebaseMessaging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNFirebaseMessaging.h; sourceTree = ""; }; + 838E36FD201B9169004DCD3A /* RNFirebaseMessaging.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFirebaseMessaging.m; sourceTree = ""; }; + 838E372120231DF0004DCD3A /* RNFirebaseInstanceId.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNFirebaseInstanceId.h; sourceTree = ""; }; + 838E372220231DF0004DCD3A /* RNFirebaseInstanceId.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFirebaseInstanceId.m; sourceTree = ""; }; + 838E372520231E15004DCD3A /* RNFirebaseNotifications.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFirebaseNotifications.m; sourceTree = ""; }; + 838E372620231E15004DCD3A /* RNFirebaseNotifications.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNFirebaseNotifications.h; sourceTree = ""; }; 839D914E1EF3E20A0077C7C8 /* RNFirebaseAdMob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNFirebaseAdMob.h; sourceTree = ""; }; 839D914F1EF3E20A0077C7C8 /* RNFirebaseAdMob.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFirebaseAdMob.m; sourceTree = ""; }; 839D91501EF3E20A0077C7C8 /* RNFirebaseAdMobInterstitial.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNFirebaseAdMobInterstitial.h; sourceTree = ""; }; @@ -85,8 +90,6 @@ 839D915F1EF3E20A0077C7C8 /* RNFirebaseCrash.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFirebaseCrash.m; sourceTree = ""; }; 839D91611EF3E20A0077C7C8 /* RNFirebaseDatabase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNFirebaseDatabase.h; sourceTree = ""; }; 839D91621EF3E20A0077C7C8 /* RNFirebaseDatabase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFirebaseDatabase.m; sourceTree = ""; }; - 839D91641EF3E20A0077C7C8 /* RNFirebaseMessaging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNFirebaseMessaging.h; sourceTree = ""; }; - 839D91651EF3E20A0077C7C8 /* RNFirebaseMessaging.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFirebaseMessaging.m; sourceTree = ""; }; 839D91671EF3E20A0077C7C8 /* RNFirebasePerformance.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNFirebasePerformance.h; sourceTree = ""; }; 839D91681EF3E20A0077C7C8 /* RNFirebasePerformance.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFirebasePerformance.m; sourceTree = ""; }; 839D91771EF3E22F0077C7C8 /* RNFirebaseEvents.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNFirebaseEvents.h; path = RNFirebase/RNFirebaseEvents.h; sourceTree = ""; }; @@ -130,6 +133,8 @@ 58B511D21A9E6C8500147676 = { isa = PBXGroup; children = ( + 838E372420231E15004DCD3A /* notifications */, + 838E372020231DF0004DCD3A /* instanceid */, 8336930F1FD80DE800AA806B /* fabric */, BA84AE541FA9E59800E79390 /* storage */, 17AF4F681F59CDBF00C02336 /* links */, @@ -182,6 +187,26 @@ path = RNFirebase/firestore; sourceTree = ""; }; + 838E372020231DF0004DCD3A /* instanceid */ = { + isa = PBXGroup; + children = ( + 838E372120231DF0004DCD3A /* RNFirebaseInstanceId.h */, + 838E372220231DF0004DCD3A /* RNFirebaseInstanceId.m */, + ); + name = instanceid; + path = RNFirebase/instanceid; + sourceTree = ""; + }; + 838E372420231E15004DCD3A /* notifications */ = { + isa = PBXGroup; + children = ( + 838E372520231E15004DCD3A /* RNFirebaseNotifications.m */, + 838E372620231E15004DCD3A /* RNFirebaseNotifications.h */, + ); + name = notifications; + path = RNFirebase/notifications; + sourceTree = ""; + }; 839D914D1EF3E20A0077C7C8 /* admob */ = { isa = PBXGroup; children = ( @@ -259,10 +284,8 @@ 839D91631EF3E20A0077C7C8 /* messaging */ = { isa = PBXGroup; children = ( - 838E36FC201B9169004DCD3A /* NewRNFirebaseMessaging.h */, - 838E36FD201B9169004DCD3A /* NewRNFirebaseMessaging.m */, - 839D91641EF3E20A0077C7C8 /* RNFirebaseMessaging.h */, - 839D91651EF3E20A0077C7C8 /* RNFirebaseMessaging.m */, + 838E36FC201B9169004DCD3A /* RNFirebaseMessaging.h */, + 838E36FD201B9169004DCD3A /* RNFirebaseMessaging.m */, ); name = messaging; path = RNFirebase/messaging; @@ -344,11 +367,12 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 838E372320231DF0004DCD3A /* RNFirebaseInstanceId.m in Sources */, 839D916E1EF3E20B0077C7C8 /* RNFirebaseAdMobRewardedVideo.m in Sources */, 839D916C1EF3E20B0077C7C8 /* RNFirebaseAdMob.m in Sources */, 17AF4F6B1F59CDBF00C02336 /* RNFirebaseLinks.m in Sources */, 8376F7161F7C149100D45A85 /* RNFirebaseFirestoreCollectionReference.m in Sources */, - 838E36FE201B9169004DCD3A /* NewRNFirebaseMessaging.m in Sources */, + 838E36FE201B9169004DCD3A /* RNFirebaseMessaging.m in Sources */, 8376F7151F7C149100D45A85 /* RNFirebaseFirestore.m in Sources */, 839D91701EF3E20B0077C7C8 /* RNFirebaseAuth.m in Sources */, 8323CF091F6FBD870071420B /* RNFirebaseAdMobNativeExpressManager.m in Sources */, @@ -358,11 +382,11 @@ 833693131FD824EF00AA806B /* RNFirebaseCrashlytics.m in Sources */, D950369E1D19C77400F7094D /* RNFirebase.m in Sources */, 839D91731EF3E20B0077C7C8 /* RNFirebaseDatabase.m in Sources */, + 838E372720231E15004DCD3A /* RNFirebaseNotifications.m in Sources */, BA84AE571FA9E59800E79390 /* RNFirebaseStorage.m in Sources */, 8323CF071F6FBD870071420B /* NativeExpressComponent.m in Sources */, 83C3EEEE1FA1EACC00B64D3C /* RNFirebaseUtil.m in Sources */, 839D91721EF3E20B0077C7C8 /* RNFirebaseCrash.m in Sources */, - 839D91741EF3E20B0077C7C8 /* RNFirebaseMessaging.m in Sources */, 839D91751EF3E20B0077C7C8 /* RNFirebasePerformance.m in Sources */, 8323CF061F6FBD870071420B /* BannerComponent.m in Sources */, 839D916D1EF3E20B0077C7C8 /* RNFirebaseAdMobInterstitial.m in Sources */, diff --git a/ios/RNFirebase/instanceid/RNFirebaseInstanceId.h b/ios/RNFirebase/instanceid/RNFirebaseInstanceId.h new file mode 100644 index 00000000..e90f72b9 --- /dev/null +++ b/ios/RNFirebase/instanceid/RNFirebaseInstanceId.h @@ -0,0 +1,19 @@ +#ifndef RNFirebaseInstanceId_h +#define RNFirebaseInstanceId_h +#import + +#if __has_include() +#import + +@interface RNFirebaseInstanceId : NSObject { + +} + +@end + +#else +@interface RNFirebaseInstanceId : NSObject +@end +#endif + +#endif diff --git a/ios/RNFirebase/instanceid/RNFirebaseInstanceId.m b/ios/RNFirebase/instanceid/RNFirebaseInstanceId.m new file mode 100644 index 00000000..d03ec1a3 --- /dev/null +++ b/ios/RNFirebase/instanceid/RNFirebaseInstanceId.m @@ -0,0 +1,35 @@ +#import "RNFirebaseInstanceId.h" + +#if __has_include() +#import + +@implementation RNFirebaseInstanceId +RCT_EXPORT_MODULE(); + +RCT_EXPORT_METHOD(delete:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { + [[FIRInstanceID instanceID] deleteIDWithHandler:^(NSError * _Nullable error) { + if (error) { + reject(@"instance_id_error", @"Failed to delete instance id", error); + } else { + resolve(nil); + } + }]; +} + +RCT_EXPORT_METHOD(get:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { + [[FIRInstanceID instanceID] getIDWithHandler:^(NSString * _Nullable identity, NSError * _Nullable error) { + if (error) { + reject(@"instance_id_error", @"Failed to get instance id", error); + } else { + resolve(identity); + } + }]; +} + +@end + +#else +@implementation RNFirebaseInstanceId +@end +#endif + diff --git a/ios/RNFirebase/messaging/NewRNFirebaseMessaging.h b/ios/RNFirebase/messaging/NewRNFirebaseMessaging.h deleted file mode 100644 index 53511103..00000000 --- a/ios/RNFirebase/messaging/NewRNFirebaseMessaging.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef NewRNFirebaseMessaging_h -#define NewRNFirebaseMessaging_h -#import - -#if __has_include() -#import -#import -#import - -@interface NewRNFirebaseMessaging : RCTEventEmitter - -#if !TARGET_OS_TV - -#endif - -@end - -#else -@interface NewRNFirebaseMessaging : NSObject -@end -#endif - -#endif diff --git a/ios/RNFirebase/messaging/NewRNFirebaseMessaging.m b/ios/RNFirebase/messaging/NewRNFirebaseMessaging.m deleted file mode 100644 index 0880b834..00000000 --- a/ios/RNFirebase/messaging/NewRNFirebaseMessaging.m +++ /dev/null @@ -1,132 +0,0 @@ -#import "NewRNFirebaseMessaging.h" - -#if __has_include() -@import UserNotifications; -#import "RNFirebaseEvents.h" -#import "RNFirebaseUtil.h" -#import -#import - -#import -#import -#import - -@interface NewRNFirebaseMessaging () - -@end - -@implementation NewRNFirebaseMessaging - -RCT_EXPORT_MODULE() - -- (id)init { - self = [super init]; - if (self != nil) { - NSLog(@"Setting up RNFirebaseMessaging instance"); - [self initialiseMessaging]; - } - return self; -} - -- (void)initialiseMessaging { - // Establish Firebase managed data channel - [FIRMessaging messaging].shouldEstablishDirectChannel = YES; -} - -- (void)dealloc { - -} - -// ** Start React Module methods ** -RCT_EXPORT_METHOD(getToken:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { - resolve([[FIRInstanceID instanceID] token]); -} - -RCT_EXPORT_METHOD(requestPermission:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { - if (RCTRunningInAppExtension()) { - reject(@"request_permission_unavailable", @"requestPermission is not supported in App Extensions", nil); - return; - } - - if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_9_x_Max) { - UIUserNotificationType types = (UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge); - [RCTSharedApplication() registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:types categories:nil]]; - // Unfortunately on iOS 9 or below, there's no way to tell whether the user accepted or - // rejected the permissions popup - // TODO: Is there something we can listen for? - resolve(@{@"status":@"unknown"}); - } else { - // iOS 10 or later - #if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 - // For iOS 10 display notification (sent via APNS) - UNAuthorizationOptions authOptions = UNAuthorizationOptionAlert | UNAuthorizationOptionSound | UNAuthorizationOptionBadge; - [[UNUserNotificationCenter currentNotificationCenter] requestAuthorizationWithOptions:authOptions completionHandler:^(BOOL granted, NSError * _Nullable error) { - if (granted) { - resolve(@{@"status": @"granted"}); - } else { - reject(@"permission_error", @"Failed to grant permission", error); - } - }]; - #endif - } - - dispatch_async(dispatch_get_main_queue(), ^{ - [RCTSharedApplication() registerForRemoteNotifications]; - }); -} - -// Non Web SDK methods - -RCT_EXPORT_METHOD(deleteInstanceId:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { - [[FIRInstanceID instanceID] deleteIDWithHandler:^(NSError * _Nullable error) { - if (!error) { - resolve(nil); - } else { - reject(@"instance_id_error", @"Failed to delete instance id", error); - } - }]; -} - -RCT_EXPORT_METHOD(getBadgeNumber: (RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { - resolve(@([RCTSharedApplication() applicationIconBadgeNumber])); -} - -RCT_EXPORT_METHOD(getInitialNotification:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject){ - UILocalNotification *localUserInfo = [self bridge].launchOptions[UIApplicationLaunchOptionsLocalNotificationKey]; - if (localUserInfo) { - resolve([[localUserInfo userInfo] copy]); - } else { - resolve([[self bridge].launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey] copy]); - } -} - -RCT_EXPORT_METHOD(subscribeToTopic: (NSString*) topic) { - [[FIRMessaging messaging] subscribeToTopic:topic]; -} - -RCT_EXPORT_METHOD(unsubscribeFromTopic: (NSString*) topic) { - [[FIRMessaging messaging] unsubscribeFromTopic:topic]; -} - - -RCT_EXPORT_METHOD(setBadgeNumber: (NSInteger*) number) { - dispatch_async(dispatch_get_main_queue(), ^{ - [RCTSharedApplication() setApplicationIconBadgeNumber:*number]; - }); -} - -- (NSArray *)supportedEvents { - return @[MESSAGING_MESSAGE_RECEIVED, MESSAGING_TOKEN_REFRESHED]; -} - -+ (BOOL)requiresMainQueueSetup -{ - return YES; -} - -@end - -#else -@implementation NewRNFirebaseMessaging -@end -#endif diff --git a/ios/RNFirebase/messaging/RNFirebaseMessaging.h b/ios/RNFirebase/messaging/RNFirebaseMessaging.h index ccdc700d..c493eabf 100644 --- a/ios/RNFirebase/messaging/RNFirebaseMessaging.h +++ b/ios/RNFirebase/messaging/RNFirebaseMessaging.h @@ -7,22 +7,10 @@ #import #import -@import UserNotifications; - @interface RNFirebaseMessaging : RCTEventEmitter -typedef void (^RCTRemoteNotificationCallback)(UIBackgroundFetchResult result); -typedef void (^RCTWillPresentNotificationCallback)(UNNotificationPresentationOptions result); -typedef void (^RCTNotificationResponseCallback)(); - -@property (nonatomic, assign) bool connectedToFCM; - #if !TARGET_OS_TV -+ (void)didReceiveRemoteNotification:(nonnull NSDictionary *)userInfo; -+ (void)didReceiveRemoteNotification:(nonnull NSDictionary *)userInfo fetchCompletionHandler:(nonnull RCTRemoteNotificationCallback)completionHandler; -+ (void)didReceiveLocalNotification:(nonnull UILocalNotification *)notification; -+ (void)didReceiveNotificationResponse:(nonnull UNNotificationResponse *)response withCompletionHandler:(nonnull RCTNotificationResponseCallback)completionHandler; -+ (void)willPresentNotification:(nonnull UNNotification *)notification withCompletionHandler:(nonnull RCTWillPresentNotificationCallback)completionHandler; + #endif @end diff --git a/ios/RNFirebase/messaging/RNFirebaseMessaging.m b/ios/RNFirebase/messaging/RNFirebaseMessaging.m index 226b30a9..0bffc7d6 100644 --- a/ios/RNFirebase/messaging/RNFirebaseMessaging.m +++ b/ios/RNFirebase/messaging/RNFirebaseMessaging.m @@ -1,7 +1,7 @@ #import "RNFirebaseMessaging.h" -@import UserNotifications; #if __has_include() +@import UserNotifications; #import "RNFirebaseEvents.h" #import "RNFirebaseUtil.h" #import @@ -11,167 +11,14 @@ #import #import -@implementation RCTConvert (NSCalendarUnit) - -RCT_ENUM_CONVERTER(NSCalendarUnit, - (@{ - @"year": @(NSCalendarUnitYear), - @"month": @(NSCalendarUnitMonth), - @"week": @(NSCalendarUnitWeekOfYear), - @"day": @(NSCalendarUnitDay), - @"hour": @(NSCalendarUnitHour), - @"minute": @(NSCalendarUnitMinute) - }), - 0, - integerValue) -@end - - -@implementation RCTConvert (UNNotificationRequest) - -+ (UNNotificationRequest *)UNNotificationRequest:(id)json -{ - NSDictionary *details = [self NSDictionary:json]; - UNMutableNotificationContent *content = [UNMutableNotificationContent new]; - content.title =[RCTConvert NSString:details[@"title"]]; - content.body =[RCTConvert NSString:details[@"body"]]; - NSString* sound = [RCTConvert NSString:details[@"sound"]]; - if(sound != nil){ - content.sound = [UNNotificationSound soundNamed:sound]; - }else{ - content.sound = [UNNotificationSound defaultSound]; - } - content.categoryIdentifier = [RCTConvert NSString:details[@"click_action"]]; - content.userInfo = details; - content.badge = [RCTConvert NSNumber:details[@"badge"]]; - - if([details objectForKey:@"show_in_foreground"] != nil) { - if([(NSNumber *)details[@"show_in_foreground"] boolValue] == YES) { - [content setValue:@YES forKeyPath:@"shouldAlwaysAlertWhileAppIsForeground"]; - } - } - - NSDate *fireDate = [RCTConvert NSDate:details[@"fire_date"]]; - - if(fireDate == nil){ - return [UNNotificationRequest requestWithIdentifier:[RCTConvert NSString:details[@"id"]] content:content trigger:nil]; - } - - NSCalendarUnit interval = [RCTConvert NSCalendarUnit:details[@"repeat_interval"]]; - NSCalendarUnit unitFlags; - switch (interval) { - case NSCalendarUnitMinute: { - unitFlags = NSCalendarUnitSecond; - break; - } - case NSCalendarUnitHour: { - unitFlags = NSCalendarUnitMinute | NSCalendarUnitSecond; - break; - } - case NSCalendarUnitDay: { - unitFlags = NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond; - break; - } - case NSCalendarUnitWeekOfYear: { - unitFlags = NSCalendarUnitWeekday | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond; - break; - } - case NSCalendarUnitMonth:{ - unitFlags = NSCalendarUnitDay | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond; - } - case NSCalendarUnitYear:{ - unitFlags = NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond; - } - default: - unitFlags = NSCalendarUnitDay | NSCalendarUnitMonth | NSCalendarUnitYear | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond; - break; - } - NSDateComponents *components = [[NSCalendar currentCalendar] components:unitFlags fromDate:fireDate]; - UNCalendarNotificationTrigger *trigger = [UNCalendarNotificationTrigger triggerWithDateMatchingComponents:components repeats:interval != 0]; - return [UNNotificationRequest requestWithIdentifier:[RCTConvert NSString:details[@"id"]] content:content trigger:trigger]; -} - -@end - -@implementation RCTConvert (UILocalNotification) - -+ (UILocalNotification *)UILocalNotification:(id)json -{ - NSDictionary *details = [self NSDictionary:json]; - UILocalNotification *notification = [UILocalNotification new]; - notification.fireDate = [RCTConvert NSDate:details[@"fire_date"]] ?: [NSDate date]; - if([notification respondsToSelector:@selector(setAlertTitle:)]){ - [notification setAlertTitle:[RCTConvert NSString:details[@"title"]]]; - } - notification.alertBody = [RCTConvert NSString:details[@"body"]]; - notification.alertAction = [RCTConvert NSString:details[@"alert_action"]]; - notification.soundName = [RCTConvert NSString:details[@"sound"]] ?: UILocalNotificationDefaultSoundName; - notification.userInfo = details; - notification.category = [RCTConvert NSString:details[@"click_action"]]; - notification.repeatInterval = [RCTConvert NSCalendarUnit:details[@"repeat_interval"]]; - notification.applicationIconBadgeNumber = [RCTConvert NSInteger:details[@"badge"]]; - return notification; -} - -RCT_ENUM_CONVERTER(UIBackgroundFetchResult, (@{ - @"UIBackgroundFetchResultNewData": @(UIBackgroundFetchResultNewData), - @"UIBackgroundFetchResultNoData": @(UIBackgroundFetchResultNoData), - @"UIBackgroundFetchResultFailed": @(UIBackgroundFetchResultFailed), - }), UIBackgroundFetchResultNoData, integerValue) - -RCT_ENUM_CONVERTER(UNNotificationPresentationOptions, (@{ - @"UNNotificationPresentationOptionAll": @(UNNotificationPresentationOptionAlert | UNNotificationPresentationOptionBadge | UNNotificationPresentationOptionSound), - @"UNNotificationPresentationOptionNone": @(UNNotificationPresentationOptionNone)}), UIBackgroundFetchResultNoData, integerValue) - -@end - @interface RNFirebaseMessaging () -@property (nonatomic, strong) NSMutableDictionary *notificationCallbacks; + @end @implementation RNFirebaseMessaging RCT_EXPORT_MODULE() -+ (void)didReceiveRemoteNotification:(nonnull NSDictionary *)userInfo { - NSMutableDictionary* data = [[NSMutableDictionary alloc] initWithDictionary: userInfo]; - [data setValue:@"remote_notification" forKey:@"_notificationType"]; - [data setValue:@(RCTSharedApplication().applicationState == UIApplicationStateInactive) forKey:@"opened_from_tray"]; - [[NSNotificationCenter defaultCenter] postNotificationName:MESSAGING_NOTIFICATION_RECEIVED object:self userInfo:@{@"data": data}]; -} - -+ (void)didReceiveRemoteNotification:(nonnull NSDictionary *)userInfo fetchCompletionHandler:(nonnull RCTRemoteNotificationCallback)completionHandler { - NSMutableDictionary* data = [[NSMutableDictionary alloc] initWithDictionary: userInfo]; - [data setValue:@"remote_notification" forKey:@"_notificationType"]; - [data setValue:@(RCTSharedApplication().applicationState == UIApplicationStateInactive) forKey:@"opened_from_tray"]; - [[NSNotificationCenter defaultCenter] postNotificationName:MESSAGING_NOTIFICATION_RECEIVED object:self userInfo:@{@"data": data, @"completionHandler": completionHandler}]; -} - -+ (void)didReceiveLocalNotification:(UILocalNotification *)notification { - NSMutableDictionary* data = [[NSMutableDictionary alloc] initWithDictionary: notification.userInfo]; - [data setValue:@"local_notification" forKey:@"_notificationType"]; - [data setValue:@(RCTSharedApplication().applicationState == UIApplicationStateInactive) forKey:@"opened_from_tray"]; - [[NSNotificationCenter defaultCenter] postNotificationName:MESSAGING_NOTIFICATION_RECEIVED object:self userInfo:@{@"data": data}]; -} - -+ (void)willPresentNotification:(UNNotification *)notification withCompletionHandler:(nonnull RCTWillPresentNotificationCallback)completionHandler -{ - NSMutableDictionary* data = [[NSMutableDictionary alloc] initWithDictionary: notification.request.content.userInfo]; - [data setValue:@"will_present_notification" forKey:@"_notificationType"]; - [[NSNotificationCenter defaultCenter] postNotificationName:MESSAGING_NOTIFICATION_RECEIVED object:self userInfo:@{@"data": data, @"completionHandler": completionHandler}]; -} - -+ (void)didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(nonnull RCTNotificationResponseCallback)completionHandler -{ - NSMutableDictionary* data = [[NSMutableDictionary alloc] initWithDictionary: response.notification.request.content.userInfo]; - [data setValue:@"notification_response" forKey:@"_notificationType"]; - [data setValue:@YES forKey:@"opened_from_tray"]; - if (response.actionIdentifier) { - [data setValue:response.actionIdentifier forKey:@"_actionIdentifier"]; - } - [[NSNotificationCenter defaultCenter] postNotificationName:MESSAGING_NOTIFICATION_RECEIVED object:self userInfo:@{@"data": data, @"completionHandler": completionHandler}]; -} - - (id)init { self = [super init]; if (self != nil) { @@ -184,74 +31,56 @@ RCT_EXPORT_MODULE() - (void)initialiseMessaging { // Establish Firebase managed data channel [FIRMessaging messaging].shouldEstablishDirectChannel = YES; - - // Set up listeners for data messages - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(sendDataMessageFailure:) - name:FIRMessagingSendErrorNotification - object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(sendDataMessageSuccess:) - name:FIRMessagingSendSuccessNotification - object:nil]; - - // Set up internal listener to send notification over bridge - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(handleNotificationReceived:) - name:MESSAGING_NOTIFICATION_RECEIVED - object:nil]; - - // Set this as a delegate for FIRMessaging - dispatch_async(dispatch_get_main_queue(), ^{ - [FIRMessaging messaging].delegate = self; - }); } - (void)dealloc { - [[NSNotificationCenter defaultCenter] removeObserver:self]; + } -- (void)handleNotificationReceived:(NSNotification *)notification { - id completionHandler = notification.userInfo[@"completionHandler"]; - NSMutableDictionary* data = notification.userInfo[@"data"]; - if(completionHandler != nil){ - NSString *completionHandlerId = [[NSUUID UUID] UUIDString]; - if (!self.notificationCallbacks) { - // Lazy initialization - self.notificationCallbacks = [NSMutableDictionary dictionary]; - } - self.notificationCallbacks[completionHandlerId] = completionHandler; - data[@"_completionHandlerId"] = completionHandlerId; - } - - [RNFirebaseUtil sendJSEvent:self name:MESSAGING_NOTIFICATION_RECEIVED body:data]; -} - - -- (void)sendDataMessageFailure:(NSNotification *)notification { - NSString *messageID = (NSString *)notification.userInfo[@"messageID"]; - NSLog(@"sendDataMessageFailure: %@", messageID); -} - -- (void)sendDataMessageSuccess:(NSNotification *)notification { - NSString *messageID = (NSString *)notification.userInfo[@"messageID"]; - NSLog(@"sendDataMessageSuccess: %@", messageID); -} - -// ** Start FIRMessagingDelegate methods ** -// Handle data messages in the background -- (void)applicationReceivedRemoteMessage:(FIRMessagingRemoteMessage *)remoteMessage { - [RNFirebaseUtil sendJSEvent:self name:MESSAGING_NOTIFICATION_RECEIVED body:[remoteMessage appData]]; -} - -// Listen for token refreshes -- (void)messaging:(nonnull FIRMessaging *)messaging didRefreshRegistrationToken:(nonnull NSString *)fcmToken { - NSLog(@"FCM registration token: %@", fcmToken); - [RNFirebaseUtil sendJSEvent:self name:MESSAGING_TOKEN_REFRESHED body:fcmToken]; -} -// ** End FIRMessagingDelegate methods ** - // ** Start React Module methods ** +RCT_EXPORT_METHOD(getToken:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { + resolve([[FIRInstanceID instanceID] token]); +} + +RCT_EXPORT_METHOD(requestPermission:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { + if (RCTRunningInAppExtension()) { + reject(@"request_permission_unavailable", @"requestPermission is not supported in App Extensions", nil); + return; + } + + if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_9_x_Max) { + UIUserNotificationType types = (UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge); + [RCTSharedApplication() registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:types categories:nil]]; + // Unfortunately on iOS 9 or below, there's no way to tell whether the user accepted or + // rejected the permissions popup + // TODO: Is there something we can listen for? + resolve(@{@"status":@"unknown"}); + } else { + // iOS 10 or later + #if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 + // For iOS 10 display notification (sent via APNS) + UNAuthorizationOptions authOptions = UNAuthorizationOptionAlert | UNAuthorizationOptionSound | UNAuthorizationOptionBadge; + [[UNUserNotificationCenter currentNotificationCenter] requestAuthorizationWithOptions:authOptions completionHandler:^(BOOL granted, NSError * _Nullable error) { + if (granted) { + resolve(@{@"status": @"granted"}); + } else { + reject(@"permission_error", @"Failed to grant permission", error); + } + }]; + #endif + } + + dispatch_async(dispatch_get_main_queue(), ^{ + [RCTSharedApplication() registerForRemoteNotifications]; + }); +} + +// Non Web SDK methods + +RCT_EXPORT_METHOD(getBadge: (RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { + resolve(@([RCTSharedApplication() applicationIconBadgeNumber])); +} + RCT_EXPORT_METHOD(getInitialNotification:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject){ UILocalNotification *localUserInfo = [self bridge].launchOptions[UIApplicationLaunchOptionsLocalNotificationKey]; if (localUserInfo) { @@ -261,51 +90,9 @@ RCT_EXPORT_METHOD(getInitialNotification:(RCTPromiseResolveBlock)resolve rejecte } } -RCT_EXPORT_METHOD(getToken:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { - resolve([FIRMessaging messaging].FCMToken); -} - -RCT_EXPORT_METHOD(deleteInstanceId:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { - [[FIRInstanceID instanceID] deleteIDWithHandler:^(NSError * _Nullable error) { - if (!error) { - resolve(nil); - } else { - reject(@"instance_id_error", @"Failed to delete instance id", error); - } - }]; -} - -RCT_EXPORT_METHOD(requestPermissions:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { - if (RCTRunningInAppExtension()) { - return; - } - - if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_9_x_Max) { - UIUserNotificationType allNotificationTypes = - (UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge); - UIUserNotificationSettings *settings = - [UIUserNotificationSettings settingsForTypes:allNotificationTypes categories:nil]; - [RCTSharedApplication() registerUserNotificationSettings:settings]; - // Unfortunately on iOS 9 or below, there's no way to tell whether the user accepted or - // rejected the permissions popup - resolve(@{@"status":@"unknown"}); - } else { - // iOS 10 or later -#if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 - // For iOS 10 display notification (sent via APNS) - [UNUserNotificationCenter currentNotificationCenter].delegate = self; - UNAuthorizationOptions authOptions = - UNAuthorizationOptionAlert - | UNAuthorizationOptionSound - | UNAuthorizationOptionBadge; - [[UNUserNotificationCenter currentNotificationCenter] requestAuthorizationWithOptions:authOptions completionHandler:^(BOOL granted, NSError * _Nullable error) { - resolve(@{@"granted":@(granted)}); - }]; -#endif - } - +RCT_EXPORT_METHOD(setBadge: (NSInteger*) number) { dispatch_async(dispatch_get_main_queue(), ^{ - [RCTSharedApplication() registerForRemoteNotifications]; + [RCTSharedApplication() setApplicationIconBadgeNumber:*number]; }); } @@ -317,144 +104,8 @@ RCT_EXPORT_METHOD(unsubscribeFromTopic: (NSString*) topic) { [[FIRMessaging messaging] unsubscribeFromTopic:topic]; } -RCT_EXPORT_METHOD(createLocalNotification:(id)data resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { - if([UNUserNotificationCenter currentNotificationCenter] != nil){ - UNNotificationRequest* request = [RCTConvert UNNotificationRequest:data]; - [[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) { - if (!error) { - resolve(nil); - }else{ - reject(@"notification_error", @"Failed to present local notificaton", error); - } - }]; - }else{ - UILocalNotification* notif = [RCTConvert UILocalNotification:data]; - [RCTSharedApplication() presentLocalNotificationNow:notif]; - resolve(nil); - } -} - -RCT_EXPORT_METHOD(scheduleLocalNotification:(id)data resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { - if([UNUserNotificationCenter currentNotificationCenter] != nil){ - UNNotificationRequest* request = [RCTConvert UNNotificationRequest:data]; - [[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) { - if (!error) { - resolve(nil); - }else{ - reject(@"notification_error", @"Failed to present local notificaton", error); - } - }]; - }else{ - UILocalNotification* notif = [RCTConvert UILocalNotification:data]; - [RCTSharedApplication() scheduleLocalNotification:notif]; - resolve(nil); - } -} - -RCT_EXPORT_METHOD(removeDeliveredNotification:(NSString*) notificationId) { - if([UNUserNotificationCenter currentNotificationCenter] != nil){ - [[UNUserNotificationCenter currentNotificationCenter] removeDeliveredNotificationsWithIdentifiers:@[notificationId]]; - } -} - -RCT_EXPORT_METHOD(removeAllDeliveredNotifications) { - if([UNUserNotificationCenter currentNotificationCenter] != nil){ - [[UNUserNotificationCenter currentNotificationCenter] removeAllDeliveredNotifications]; - } else { - [RCTSharedApplication() setApplicationIconBadgeNumber: 0]; - } -} - -RCT_EXPORT_METHOD(cancelAllLocalNotifications) { - if([UNUserNotificationCenter currentNotificationCenter] != nil){ - [[UNUserNotificationCenter currentNotificationCenter] removeAllPendingNotificationRequests]; - } else { - [RCTSharedApplication() cancelAllLocalNotifications]; - } -} - -RCT_EXPORT_METHOD(cancelLocalNotification:(NSString*) notificationId) { - if([UNUserNotificationCenter currentNotificationCenter] != nil){ - [[UNUserNotificationCenter currentNotificationCenter] removePendingNotificationRequestsWithIdentifiers:@[notificationId]]; - } else { - for (UILocalNotification *notification in RCTSharedApplication().scheduledLocalNotifications) { - NSDictionary *notificationInfo = notification.userInfo; - if([notificationId isEqualToString:[notificationInfo valueForKey:@"id"]]){ - [RCTSharedApplication() cancelLocalNotification:notification]; - } - } - } -} - -RCT_EXPORT_METHOD(getScheduledLocalNotifications:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { - if([UNUserNotificationCenter currentNotificationCenter] != nil){ - [[UNUserNotificationCenter currentNotificationCenter] getPendingNotificationRequestsWithCompletionHandler:^(NSArray * _Nonnull requests) { - NSMutableArray* list = [[NSMutableArray alloc] init]; - for(UNNotificationRequest * notif in requests){ - UNMutableNotificationContent *content = notif.content; - [list addObject:content.userInfo]; - } - resolve(list); - }]; - } else { - NSMutableArray* list = [[NSMutableArray alloc] init]; - for(UILocalNotification * notif in [RCTSharedApplication() scheduledLocalNotifications]){ - [list addObject:notif.userInfo]; - } - resolve(list); - } -} - -RCT_EXPORT_METHOD(setBadgeNumber: (NSInteger*) number) { - dispatch_async(dispatch_get_main_queue(), ^{ - [RCTSharedApplication() setApplicationIconBadgeNumber:number]; - }); -} - -RCT_EXPORT_METHOD(getBadgeNumber: (RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { - resolve(@([RCTSharedApplication() applicationIconBadgeNumber])); -} - -RCT_EXPORT_METHOD(send:(NSDictionary *)remoteMessage) { - int64_t ttl = @([[remoteMessage valueForKey:@"ttl"] intValue]).doubleValue; - NSString * mId = [remoteMessage valueForKey:@"id"]; - NSString * receiver = [remoteMessage valueForKey:@"sender"]; - NSDictionary * data = [remoteMessage valueForKey:@"data"]; - [[FIRMessaging messaging] sendMessage:data to:receiver withMessageID:mId timeToLive:ttl]; -} - -RCT_EXPORT_METHOD(finishRemoteNotification: (NSString *)completionHandlerId fetchResult:(UIBackgroundFetchResult)result) { - RCTRemoteNotificationCallback completionHandler = self.notificationCallbacks[completionHandlerId]; - if (!completionHandler) { - RCTLogError(@"There is no completion handler with completionHandlerId: %@", completionHandlerId); - return; - } - completionHandler(result); - [self.notificationCallbacks removeObjectForKey:completionHandlerId]; -} - -RCT_EXPORT_METHOD(finishWillPresentNotification: (NSString *)completionHandlerId fetchResult:(UNNotificationPresentationOptions)result) { - RCTWillPresentNotificationCallback completionHandler = self.notificationCallbacks[completionHandlerId]; - if (!completionHandler) { - RCTLogError(@"There is no completion handler with completionHandlerId: %@", completionHandlerId); - return; - } - completionHandler(result); - [self.notificationCallbacks removeObjectForKey:completionHandlerId]; -} - -RCT_EXPORT_METHOD(finishNotificationResponse: (NSString *)completionHandlerId) { - RCTNotificationResponseCallback completionHandler = self.notificationCallbacks[completionHandlerId]; - if (!completionHandler) { - RCTLogError(@"There is no completion handler with completionHandlerId: %@", completionHandlerId); - return; - } - completionHandler(); - [self.notificationCallbacks removeObjectForKey:completionHandlerId]; -} - - (NSArray *)supportedEvents { - return @[MESSAGING_TOKEN_REFRESHED, MESSAGING_NOTIFICATION_RECEIVED]; + return @[MESSAGING_MESSAGE_RECEIVED, MESSAGING_TOKEN_REFRESHED]; } + (BOOL)requiresMainQueueSetup diff --git a/ios/RNFirebase/notifications/RNFirebaseNotifications.h b/ios/RNFirebase/notifications/RNFirebaseNotifications.h new file mode 100644 index 00000000..16298e70 --- /dev/null +++ b/ios/RNFirebase/notifications/RNFirebaseNotifications.h @@ -0,0 +1,22 @@ +#ifndef RNFirebaseNotifications_h +#define RNFirebaseNotifications_h +#import + +#if __has_include() +#import +#import + +@interface RNFirebaseNotifications : RCTEventEmitter + +#if !TARGET_OS_TV + +#endif + +@end + +#else +@interface RNFirebaseNotifications : NSObject +@end +#endif + +#endif diff --git a/ios/RNFirebase/notifications/RNFirebaseNotifications.m b/ios/RNFirebase/notifications/RNFirebaseNotifications.m new file mode 100644 index 00000000..5f149eea --- /dev/null +++ b/ios/RNFirebase/notifications/RNFirebaseNotifications.m @@ -0,0 +1,14 @@ +#import "RNFirebaseNotifications.h" + +#if __has_include() + +@implementation RNFirebaseNotifications +RCT_EXPORT_MODULE(); + +@end + +#else +@implementation RNFirebaseNotifications +@end +#endif + diff --git a/lib/modules/core/firebase-app.js b/lib/modules/core/firebase-app.js index 0c79517f..aa90eb57 100644 --- a/lib/modules/core/firebase-app.js +++ b/lib/modules/core/firebase-app.js @@ -18,11 +18,12 @@ import Crashlytics, { } from '../fabric/crashlytics'; import Database, { NAMESPACE as DatabaseNamespace } from '../database'; import Firestore, { NAMESPACE as FirestoreNamespace } from '../firestore'; +import InstanceId, { NAMESPACE as InstanceIdNamespace } from '../instanceid'; import Links, { NAMESPACE as LinksNamespace } from '../links'; import Messaging, { NAMESPACE as MessagingNamespace } from '../messaging'; -import NewMessaging, { - NAMESPACE as NewMessagingNamespace, -} from '../messaging/messagingIndex'; +import Notifications, { + NAMESPACE as NotificationsNamespace, +} from '../notifications'; import Performance, { NAMESPACE as PerfNamespace } from '../perf'; import Storage, { NAMESPACE as StorageNamespace } from '../storage'; import Utils, { NAMESPACE as UtilsNamespace } from '../utils'; @@ -47,9 +48,10 @@ export default class App { crashlytics: () => Crashlytics, }; firestore: () => Firestore; + instanceid: () => InstanceId; links: () => Links; messaging: () => Messaging; - newmessaging: () => NewMessaging; + notifications: () => Notifications; perf: () => Performance; storage: () => Storage; utils: () => Utils; @@ -87,12 +89,13 @@ export default class App { crashlytics: APPS.appModule(this, CrashlyticsNamespace, Crashlytics), }; this.firestore = APPS.appModule(this, FirestoreNamespace, Firestore); + this.instanceid = APPS.appModule(this, InstanceIdNamespace, InstanceId); this.links = APPS.appModule(this, LinksNamespace, Links); this.messaging = APPS.appModule(this, MessagingNamespace, Messaging); - this.newmessaging = APPS.appModule( + this.notifications = APPS.appModule( this, - NewMessagingNamespace, - NewMessaging + NotificationsNamespace, + Notifications ); this.perf = APPS.appModule(this, PerfNamespace, Performance); this.storage = APPS.appModule(this, StorageNamespace, Storage); diff --git a/lib/modules/core/firebase.js b/lib/modules/core/firebase.js index 119d9fc6..a8647ba8 100644 --- a/lib/modules/core/firebase.js +++ b/lib/modules/core/firebase.js @@ -39,6 +39,10 @@ import { statics as FirestoreStatics, MODULE_NAME as FirestoreModuleName, } from '../firestore'; +import { + statics as InstanceIdStatics, + MODULE_NAME as InstanceIdModuleName, +} from '../instanceid'; import { statics as LinksStatics, MODULE_NAME as LinksModuleName, @@ -48,9 +52,9 @@ import { MODULE_NAME as MessagingModuleName, } from '../messaging'; import { - statics as NewMessagingStatics, - MODULE_NAME as NewMessagingModuleName, -} from '../messaging/messagingIndex'; + statics as NotificationsStatics, + MODULE_NAME as NotificationsModuleName, +} from '../notifications'; import { statics as PerformanceStatics, MODULE_NAME as PerfModuleName, @@ -74,9 +78,10 @@ import type { FabricModule, FirebaseOptions, FirestoreModule, + InstanceIdModule, LinksModule, MessagingModule, - NewMessagingModule, + NotificationsModule, PerformanceModule, StorageModule, UtilsModule, @@ -93,9 +98,10 @@ class Firebase { database: DatabaseModule; fabric: FabricModule; firestore: FirestoreModule; + instanceid: InstanceIdModule; links: LinksModule; messaging: MessagingModule; - newmessaging: NewMessagingModule; + notifications: NotificationsModule; perf: PerformanceModule; storage: StorageModule; utils: UtilsModule; @@ -137,16 +143,21 @@ class Firebase { FirestoreStatics, FirestoreModuleName ); + this.instanceid = APPS.moduleAndStatics( + 'instanceid', + InstanceIdStatics, + InstanceIdModuleName + ); this.links = APPS.moduleAndStatics('links', LinksStatics, LinksModuleName); this.messaging = APPS.moduleAndStatics( 'messaging', MessagingStatics, MessagingModuleName ); - this.newmessaging = APPS.moduleAndStatics( - 'newmessaging', - NewMessagingStatics, - NewMessagingModuleName + this.notifications = APPS.moduleAndStatics( + 'notifications', + NotificationsStatics, + NotificationsModuleName ); this.perf = APPS.moduleAndStatics( 'perf', diff --git a/lib/modules/instanceid/index.js b/lib/modules/instanceid/index.js new file mode 100644 index 00000000..d4f6ad68 --- /dev/null +++ b/lib/modules/instanceid/index.js @@ -0,0 +1,31 @@ +/** + * @flow + * Instance ID representation wrapper + */ +import ModuleBase from '../../utils/ModuleBase'; +import { getNativeModule } from '../../utils/native'; + +import type App from '../core/firebase-app'; + +export const MODULE_NAME = 'RNFirebaseInstanceId'; +export const NAMESPACE = 'instanceid'; + +export default class InstanceId extends ModuleBase { + constructor(app: App) { + super(app, { + moduleName: MODULE_NAME, + multiApp: false, + namespace: NAMESPACE, + }); + } + + delete(): Promise { + return getNativeModule(this).delete(); + } + + get(): Promise { + return getNativeModule(this).get(); + } +} + +export const statics = {}; diff --git a/lib/modules/messaging/index.js b/lib/modules/messaging/index.js index 4030c5ba..b1486210 100644 --- a/lib/modules/messaging/index.js +++ b/lib/modules/messaging/index.js @@ -1,94 +1,69 @@ /** * @flow - * Messaging representation wrapper + * Messaging (FCM) representation wrapper */ -import { Platform, NativeModules } from 'react-native'; import { SharedEventEmitter } from '../../utils/events'; +import INTERNALS from '../../utils/internals'; +import { getLogger } from '../../utils/log'; import ModuleBase from '../../utils/ModuleBase'; -import RemoteMessage from './RemoteMessage'; import { getNativeModule } from '../../utils/native'; +import { isFunction, isObject } from '../../utils'; import type App from '../core/firebase-app'; -const EVENT_TYPE = { - RefreshToken: 'messaging_token_refreshed', - Notification: 'messaging_notification_received', +type Notification = { + body: string, + bodyLocalizationArgs: string[], + bodyLocalizationKey: string, + clickAction: string, + color: string, + icon: string, + link: string, + sound: string, + tag: string, + title: string, + titleLocalizationArgs: string[], + titleLocalizationKey: string, }; -const NOTIFICATION_TYPE = { - Remote: 'remote_notification', - NotificationResponse: 'notification_response', - WillPresent: 'will_present_notification', - Local: 'local_notification', +type Message = { + collapseKey: string, + data: { [string]: string }, + from: string, + messageId: string, + messageType?: string, + openedFromTray: boolean, + notification?: Notification, + sentTime: number, + to?: string, + ttl?: number, }; -const REMOTE_NOTIFICATION_RESULT = { - NewData: 'UIBackgroundFetchResultNewData', - NoData: 'UIBackgroundFetchResultNoData', - ResultFailed: 'UIBackgroundFetchResultFailed', +type OnMessage = Message => any; + +type OnMessageObserver = { + next: OnMessage, }; -const WILL_PRESENT_RESULT = { - All: 'UNNotificationPresentationOptionAll', - None: 'UNNotificationPresentationOptionNone', +type OnTokenRefresh = String => any; + +type OnTokenRefreshObserver = { + next: OnTokenRefresh, }; -const NATIVE_EVENTS = [EVENT_TYPE.RefreshToken, EVENT_TYPE.Notification]; +type RemoteMessage = { + collapseKey?: string, + data: { [string]: string }, + messageId?: string, + messageType?: string, + to: string, + ttl: number, +}; -const FirebaseMessaging = NativeModules.RNFirebaseMessaging; - -/** - * IOS only finish function - * @param data - */ -function finish(data) { - if (Platform.OS !== 'ios') { - return; - } - - if (!this._finishCalled && this._completionHandlerId) { - let result = data; - - this._finishCalled = true; - - switch (this._notificationType) { - case NOTIFICATION_TYPE.Remote: - result = result || REMOTE_NOTIFICATION_RESULT.NoData; - if (!Object.values(REMOTE_NOTIFICATION_RESULT).includes(result)) { - throw new Error( - 'Invalid REMOTE_NOTIFICATION_RESULT value, use messaging().REMOTE_NOTIFICATION_RESULT' - ); - } - - FirebaseMessaging.finishRemoteNotification( - this._completionHandlerId, - result - ); - return; - case NOTIFICATION_TYPE.NotificationResponse: - FirebaseMessaging.finishNotificationResponse(this._completionHandlerId); - return; - case NOTIFICATION_TYPE.WillPresent: - result = - result || - (this.show_in_foreground - ? WILL_PRESENT_RESULT.All - : WILL_PRESENT_RESULT.None); - if (!Object.values(WILL_PRESENT_RESULT).includes(result)) { - throw new Error( - 'Invalid WILL_PRESENT_RESULT value, use messaging().WILL_PRESENT_RESULT' - ); - } - - FirebaseMessaging.finishWillPresentNotification( - this._completionHandlerId, - result - ); - break; - default: - } - } -} +const NATIVE_EVENTS = [ + 'messaging_message_received', + 'messaging_token_refreshed', +]; export const MODULE_NAME = 'RNFirebaseMessaging'; export const NAMESPACE = 'messaging'; @@ -104,204 +79,139 @@ export default class Messaging extends ModuleBase { multiApp: false, namespace: NAMESPACE, }); + + SharedEventEmitter.addListener( + // sub to internal native event - this fans out to + // public event name: onMessage + 'messaging_message_received', + (message: Message) => { + SharedEventEmitter.emit('onMessage', message); + } + ); + + SharedEventEmitter.addListener( + // sub to internal native event - this fans out to + // public event name: onMessage + 'messaging_token_refreshed', + (token: string) => { + SharedEventEmitter.emit('onTokenRefresh', token); + } + ); } - get EVENT_TYPE(): Object { - return EVENT_TYPE; - } - - get NOTIFICATION_TYPE(): Object { - return NOTIFICATION_TYPE; - } - - get REMOTE_NOTIFICATION_RESULT(): Object { - return REMOTE_NOTIFICATION_RESULT; - } - - get WILL_PRESENT_RESULT(): Object { - return WILL_PRESENT_RESULT; - } - - /** - * Returns the notification that triggered application open - * @returns {*} - */ - getInitialNotification(): Promise { - return getNativeModule(this).getInitialNotification(); - } - - /** - * Returns the fcm token for the current device - * @returns {*|Promise.} - */ getToken(): Promise { return getNativeModule(this).getToken(); } - /** - * Reset Instance ID and revokes all tokens. - * @returns {*|Promise.<*>} - */ - deleteInstanceId(): Promise { - return getNativeModule(this).deleteInstanceId(); - } - - /** - * Create and display a local notification - * @param notification - * @returns {*} - */ - createLocalNotification(notification: Object): Promise { - const _notification = Object.assign({}, notification); - _notification.id = _notification.id || new Date().getTime().toString(); - _notification.local_notification = true; - return getNativeModule(this).createLocalNotification(_notification); - } - - /** - * - * @param notification - * @returns {*} - */ - scheduleLocalNotification(notification: Object): Promise { - const _notification = Object.assign({}, notification); - if (!notification.id) - return Promise.reject( - new Error('An id is required to schedule a local notification.') + onMessage(nextOrObserver: OnMessage | OnMessageObserver): () => any { + let listener; + if (isFunction(nextOrObserver)) { + listener = nextOrObserver; + } else if (isObject(nextOrObserver) && isFunction(nextOrObserver.next)) { + listener = nextOrObserver.next; + } else { + throw new Error( + 'Messaging.onMessage failed: First argument must be a function or observer object with a `next` function.' ); - _notification.local_notification = true; - return getNativeModule(this).scheduleLocalNotification(_notification); + } + + // TODO: iOS finish + getLogger(this).info('Creating onMessage listener'); + SharedEventEmitter.addListener('onMessage', listener); + + return () => { + getLogger(this).info('Removing onMessage listener'); + SharedEventEmitter.removeListener('onMessage', listener); + }; + } + + onTokenRefresh( + nextOrObserver: OnTokenRefresh | OnTokenRefreshObserver + ): () => any { + let listener; + if (isFunction(nextOrObserver)) { + listener = nextOrObserver; + } else if (isObject(nextOrObserver) && isFunction(nextOrObserver.next)) { + listener = nextOrObserver.next; + } else { + throw new Error( + 'Messaging.OnTokenRefresh failed: First argument must be a function or observer object with a `next` function.' + ); + } + + getLogger(this).info('Creating onTokenRefresh listener'); + SharedEventEmitter.addListener('onTokenRefresh', listener); + + return () => { + getLogger(this).info('Removing onTokenRefresh listener'); + SharedEventEmitter.removeListener('onTokenRefresh', listener); + }; + } + + // TODO: Permission structure? + requestPermission(): Promise { + return getNativeModule(this).requestPermission(); } /** - * Returns an array of all scheduled notifications - * @returns {Promise.} + * NON WEB-SDK METHODS */ - getScheduledLocalNotifications(): Promise { - return getNativeModule(this).getScheduledLocalNotifications(); + getBadge(): Promise { + return getNativeModule(this).getBadge(); } - /** - * Cancel a local notification by id - using '*' will cancel - * all local notifications. - * @param id - * @returns {*} - */ - cancelLocalNotification(id: string): Promise { - if (!id) return Promise.reject(new Error('Missing notification id')); - if (id === '*') return getNativeModule(this).cancelAllLocalNotifications(); - return getNativeModule(this).cancelLocalNotification(id); + getInitialMessage(): Promise { + return getNativeModule(this).getInitialMessage(); } - /** - * Remove a delivered notification - using '*' will remove - * all delivered notifications. - * @param id - * @returns {*} - */ - removeDeliveredNotification(id: string): Promise { - if (!id) return Promise.reject(new Error('Missing notification id')); - if (id === '*') - return getNativeModule(this).removeAllDeliveredNotifications(); - return getNativeModule(this).removeDeliveredNotification(id); + sendMessage(remoteMessage: RemoteMessage): Promise { + return getNativeModule(this).send(remoteMessage); } - /** - * Request notification permission - * @platforms ios - * @returns {*|Promise.<*>} - */ - requestPermissions(): Promise { - return getNativeModule(this).requestPermissions(); + setBadge(badge: number): void { + getNativeModule(this).setBadge(badge); } - /** - * Set notification count badge number - * @param n - */ - setBadgeNumber(n: number): void { - getNativeModule(this).setBadgeNumber(n); - } - - /** - * set notification count badge number - * @returns {Promise.} - */ - getBadgeNumber(): Promise { - return getNativeModule(this).getBadgeNumber(); - } - - /** - * Subscribe to messages / notifications - * @param listener - * @returns {*} - */ - onMessage(listener: Object => any): () => any { - const rnListener = SharedEventEmitter.addListener( - EVENT_TYPE.Notification, - async event => { - const data = { - ...event, - finish, - }; - await listener(data); - - if (!data._finishCalled) { - data.finish(); - } - } - ); - return () => rnListener.remove(); - } - - /** - * Subscribe to token refresh events - * @param listener - * @returns {*} - */ - onTokenRefresh(listener: string => any): () => any { - const rnListener = SharedEventEmitter.addListener( - EVENT_TYPE.RefreshToken, - listener - ); - return () => rnListener.remove(); - } - - /** - * Subscribe to a topic - * @param topic - */ subscribeToTopic(topic: string): void { getNativeModule(this).subscribeToTopic(topic); } - /** - * Unsubscribe from a topic - * @param topic - */ unsubscribeFromTopic(topic: string): void { getNativeModule(this).unsubscribeFromTopic(topic); } /** - * Send an upstream message - * @param remoteMessage + * KNOWN UNSUPPORTED METHODS */ - send(remoteMessage: RemoteMessage): Promise { - if (!(remoteMessage instanceof RemoteMessage)) { - throw new Error( - 'messaging().send requires an instance of RemoteMessage as the first argument.' - ); - } - return getNativeModule(this).send(remoteMessage.toJSON()); + deleteToken() { + throw new Error( + INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD( + 'messaging', + 'deleteToken' + ) + ); + } + + setBackgroundMessageHandler() { + throw new Error( + INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD( + 'messaging', + 'setBackgroundMessageHandler' + ) + ); + } + + useServiceWorker() { + throw new Error( + INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD( + 'messaging', + 'useServiceWorker' + ) + ); } } export const statics = { - EVENT_TYPE, - NOTIFICATION_TYPE, - REMOTE_NOTIFICATION_RESULT, - WILL_PRESENT_RESULT, - RemoteMessage, + // RemoteMessage, }; diff --git a/lib/modules/messaging/messagingIndex.js b/lib/modules/messaging/messagingIndex.js deleted file mode 100644 index 9d7f4247..00000000 --- a/lib/modules/messaging/messagingIndex.js +++ /dev/null @@ -1,186 +0,0 @@ -/** - * @flow - * Messaging (FCM) representation wrapper - */ -import { SharedEventEmitter } from '../../utils/events'; -import INTERNALS from '../../utils/internals'; -import { getLogger } from '../../utils/log'; -import ModuleBase from '../../utils/ModuleBase'; -import { getNativeModule } from '../../utils/native'; -import { isFunction, isObject } from '../../utils'; - -import type App from '../core/firebase-app'; - -type Message = { - // TODO -}; - -type OnMessage = Message => any; - -type OnMessageObserver = { - next: OnMessage, -}; - -type OnTokenRefresh = String => any; - -type OnTokenRefreshObserver = { - next: OnTokenRefresh, -}; - -type RemoteMessage = { - // TODO -}; - -const NATIVE_EVENTS = [ - 'messaging_message_received', - 'messaging_token_refreshed', -]; - -export const MODULE_NAME = 'NewRNFirebaseMessaging'; -export const NAMESPACE = 'newmessaging'; - -/** - * @class Messaging - */ -export default class Messaging extends ModuleBase { - constructor(app: App) { - super(app, { - events: NATIVE_EVENTS, - moduleName: MODULE_NAME, - multiApp: false, - namespace: NAMESPACE, - }); - - SharedEventEmitter.addListener( - // sub to internal native event - this fans out to - // public event name: onMessage - 'messaging_message_received', - (message: Message) => { - SharedEventEmitter.emit('onMessage', message); - } - ); - - SharedEventEmitter.addListener( - // sub to internal native event - this fans out to - // public event name: onMessage - 'messaging_token_refreshed', - (token: string) => { - SharedEventEmitter.emit('onTokenRefresh', token); - } - ); - } - - deleteToken(token: string): Promise { - return getNativeModule(this).deleteToken(token); - } - - getToken(): Promise { - return getNativeModule(this).getToken(); - } - - onMessage(nextOrObserver: OnMessage | OnMessageObserver): () => any { - let listener; - if (isFunction(nextOrObserver)) { - listener = nextOrObserver; - } else if (isObject(nextOrObserver) && isFunction(nextOrObserver.next)) { - listener = nextOrObserver.next; - } else { - throw new Error( - 'Messaging.onMessage failed: First argument must be a function or observer object with a `next` function.' - ); - } - - // TODO: iOS finish - getLogger(this).info('Creating onMessage listener'); - SharedEventEmitter.addListener('onMessage', listener); - - return () => { - getLogger(this).info('Removing onMessage listener'); - SharedEventEmitter.removeListener('onMessage', listener); - }; - } - - onTokenRefresh( - nextOrObserver: OnTokenRefresh | OnTokenRefreshObserver - ): () => any { - let listener; - if (isFunction(nextOrObserver)) { - listener = nextOrObserver; - } else if (isObject(nextOrObserver) && isFunction(nextOrObserver.next)) { - listener = nextOrObserver.next; - } else { - throw new Error( - 'Messaging.OnTokenRefresh failed: First argument must be a function or observer object with a `next` function.' - ); - } - - getLogger(this).info('Creating onTokenRefresh listener'); - SharedEventEmitter.addListener('onTokenRefresh', listener); - - return () => { - getLogger(this).info('Removing onTokenRefresh listener'); - SharedEventEmitter.removeListener('onTokenRefresh', listener); - }; - } - - requestPermission(): Promise { - return getNativeModule(this).requestPermission(); - } - - /** - * NON WEB-SDK METHODS - */ - deleteInstanceId(): Promise { - return getNativeModule(this).deleteInstanceId(); - } - - getBadgeNumber(): Promise { - return getNativeModule(this).getBadgeNumber(); - } - - getInitialMessage(): Promise { - return getNativeModule(this).getInitialMessage(); - } - - sendMessage(remoteMessage: RemoteMessage): Promise { - return getNativeModule(this).send(remoteMessage); - } - - setBadgeNumber(badge: number): void { - getNativeModule(this).setBadgeNumber(badge); - } - - subscribeToTopic(topic: string): void { - getNativeModule(this).subscribeToTopic(topic); - } - - unsubscribeFromTopic(topic: string): void { - getNativeModule(this).unsubscribeFromTopic(topic); - } - - /** - * KNOWN UNSUPPORTED METHODS - */ - - setBackgroundMessageHandler() { - throw new Error( - INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD( - 'messaging', - 'setBackgroundMessageHandler' - ) - ); - } - - useServiceWorker() { - throw new Error( - INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD( - 'messaging', - 'useServiceWorker' - ) - ); - } -} - -export const statics = { - // RemoteMessage, -}; diff --git a/lib/modules/notifications/notificationsIndex.js b/lib/modules/notifications/index.js similarity index 93% rename from lib/modules/notifications/notificationsIndex.js rename to lib/modules/notifications/index.js index 9620292f..94b1753c 100644 --- a/lib/modules/notifications/notificationsIndex.js +++ b/lib/modules/notifications/index.js @@ -108,19 +108,24 @@ export default class Notifications extends ModuleBase { } /** - * Remove a delivered notification - using '*' will remove - * all delivered notifications. + * Remove a delivered notification. * @param id * @returns {*} */ removeDeliveredNotification(id: string): Promise { if (!id) return Promise.reject(new Error('Missing notification id')); - if (id === '*') { - return getNativeModule(this).removeAllDeliveredNotifications(); - } return getNativeModule(this).removeDeliveredNotification(id); } + /** + * Remove all delivered notifications. + * @param id + * @returns {*} + */ + removeDeliveredNotifications(): Promise { + return getNativeModule(this).removeDeliveredNotifications(); + } + /** * * @param notification @@ -136,3 +141,5 @@ export default class Notifications extends ModuleBase { return getNativeModule(this).scheduleLocalNotification(_notification); } } + +export const statics = {}; diff --git a/lib/types/index.js b/lib/types/index.js index ce894079..3b94966d 100644 --- a/lib/types/index.js +++ b/lib/types/index.js @@ -15,12 +15,14 @@ import type Database from '../modules/database'; import { typeof statics as DatabaseStatics } from '../modules/database'; import type Firestore from '../modules/firestore'; import { typeof statics as FirestoreStatics } from '../modules/firestore'; +import type InstanceId from '../modules/instanceid'; +import { typeof statics as InstanceIdStatics } from '../modules/instanceid'; import type Links from '../modules/links'; import { typeof statics as LinksStatics } from '../modules/links'; import type Messaging from '../modules/messaging'; import { typeof statics as MessagingStatics } from '../modules/messaging'; -import type NewMessaging from '../modules/messaging/messagingIndex'; -import { typeof statics as NewMessagingStatics } from '../modules/messaging/messagingIndex'; +import type Notifications from '../modules/notifications'; +import { typeof statics as NotificationsStatics } from '../modules/notifications'; import type ModuleBase from '../utils/ModuleBase'; import type Performance from '../modules/perf'; import { typeof statics as PerformanceStatics } from '../modules/perf'; @@ -58,9 +60,9 @@ export type FirebaseModuleName = | 'RNFirebaseCrashlytics' | 'RNFirebaseDatabase' | 'RNFirebaseFirestore' + | 'RNFirebaseInstanceId' | 'RNFirebaseLinks' | 'RNFirebaseMessaging' - | 'NewRNFirebaseMessaging' | 'RNFirebaseNotifications' | 'RNFirebasePerformance' | 'RNFirebaseStorage' @@ -75,9 +77,9 @@ export type FirebaseNamespace = | 'crashlytics' | 'database' | 'firestore' + | 'instanceid' | 'links' | 'messaging' - | 'newmessaging' | 'notifications' | 'perf' | 'storage' @@ -209,6 +211,13 @@ export type FirestoreWriteOptions = { merge?: boolean, }; +/* InstanceId types */ + +export type InstanceIdModule = { + (): InstanceId, + nativeModuleExists: boolean, +} & InstanceIdStatics; + /* Links types */ export type LinksModule = { @@ -223,10 +232,12 @@ export type MessagingModule = { nativeModuleExists: boolean, } & MessagingStatics; -export type NewMessagingModule = { - (): NewMessaging, +/* Notifications types */ + +export type NotificationsModule = { + (): Notifications, nativeModuleExists: boolean, -} & NewMessagingStatics; +} & NotificationsStatics; /* Performance types */ diff --git a/tests/android/app/src/main/AndroidManifest.xml b/tests/android/app/src/main/AndroidManifest.xml index d055d381..0d75e277 100644 --- a/tests/android/app/src/main/AndroidManifest.xml +++ b/tests/android/app/src/main/AndroidManifest.xml @@ -13,10 +13,10 @@ android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" - android:launchMode="singleTop" + android:launchMode="singleTask" android:theme="@style/AppTheme"> @@ -24,7 +24,7 @@ - + @@ -34,6 +34,7 @@ android:name=".MainActivity" android:configChanges="keyboard|keyboardHidden|orientation|screenSize" android:label="@string/app_name" + android:launchMode="singleTop" android:windowSoftInputMode="adjustResize"> @@ -42,5 +43,4 @@ - diff --git a/tests/android/app/src/main/java/com/reactnativefirebasedemo/MainApplication.java b/tests/android/app/src/main/java/com/reactnativefirebasedemo/MainApplication.java index e1b6fe51..ae9de9cd 100644 --- a/tests/android/app/src/main/java/com/reactnativefirebasedemo/MainApplication.java +++ b/tests/android/app/src/main/java/com/reactnativefirebasedemo/MainApplication.java @@ -12,6 +12,7 @@ import io.invertase.firebase.crash.RNFirebaseCrashPackage; import io.invertase.firebase.database.RNFirebaseDatabasePackage; import io.invertase.firebase.fabric.crashlytics.RNFirebaseCrashlyticsPackage; import io.invertase.firebase.firestore.RNFirebaseFirestorePackage; +import io.invertase.firebase.instanceid.RNFirebaseInstanceIdPackage; import io.invertase.firebase.links.RNFirebaseLinksPackage; import io.invertase.firebase.messaging.RNFirebaseMessagingPackage; import io.invertase.firebase.perf.RNFirebasePerformancePackage; @@ -47,6 +48,7 @@ public class MainApplication extends Application implements ReactApplication { new RNFirebaseCrashlyticsPackage(), new RNFirebaseDatabasePackage(), new RNFirebaseFirestorePackage(), + new RNFirebaseInstanceIdPackage(), new RNFirebaseLinksPackage(), new RNFirebaseMessagingPackage(), new RNFirebasePerformancePackage(), diff --git a/tests/src/firebase.js b/tests/src/firebase.js index f78cfe1d..fcdad95f 100644 --- a/tests/src/firebase.js +++ b/tests/src/firebase.js @@ -7,19 +7,30 @@ import DatabaseContents from './tests/support/DatabaseContents'; RNfirebase.database.enableLogging(true); RNfirebase.firestore.enableLogging(true); -RNfirebase.newmessaging() - .requestPermission() - .then(response => { - console.log('requestPermission:', response); - RNfirebase.newmessaging() - .getToken() - .then(token => { - console.log('token: ', token); - }); - }) - .catch(error => { - console.error('requestPermission:', error); - }); +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(); + const instanceid = await RNfirebase.instanceid().get(); + 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); + // RNfirebase.instanceid().delete(); + } catch (error) { + console.error('messaging init error:', error); + } +}; + +init(); const config = { apiKey: 'AIzaSyDnVqNhxU0Biit9nCo4RorAh5ulQQwko3E',