diff --git a/ios/RNFirebase/notifications/RNFirebaseNotifications.m b/ios/RNFirebase/notifications/RNFirebaseNotifications.m index fb602149..ed8b7fa5 100644 --- a/ios/RNFirebase/notifications/RNFirebaseNotifications.m +++ b/ios/RNFirebase/notifications/RNFirebaseNotifications.m @@ -38,7 +38,7 @@ RCT_EXPORT_MODULE(); - (void)configure { // If we're on iOS 10 then we need to set this as a delegate for the UNUserNotificationCenter #if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 - // [UNUserNotificationCenter currentNotificationCenter].delegate = self; + [UNUserNotificationCenter currentNotificationCenter].delegate = self; #endif // Set static instance for use from AppDelegate @@ -51,20 +51,22 @@ RCT_EXPORT_MODULE(); // ******************************************************* - (void)didReceiveLocalNotification:(nonnull UILocalNotification *)notification { - NSString *event; - if (RCTSharedApplication().applicationState == UIApplicationStateBackground) { - // notification_displayed - event = NOTIFICATIONS_NOTIFICATION_DISPLAYED; - } else if (RCTSharedApplication().applicationState == UIApplicationStateInactive) { - // notification_displayed - event = NOTIFICATIONS_NOTIFICATION_PRESSED; - } else { - // notification_received - event = NOTIFICATIONS_NOTIFICATION_RECEIVED; - } + #if !defined(__IPHONE_10_0) || __IPHONE_OS_VERSION_MAX_ALLOWED < __IPHONE_10_0 + NSString *event; + if (RCTSharedApplication().applicationState == UIApplicationStateBackground) { + // notification_displayed + event = NOTIFICATIONS_NOTIFICATION_DISPLAYED; + } else if (RCTSharedApplication().applicationState == UIApplicationStateInactive) { + // notification_displayed + event = NOTIFICATIONS_NOTIFICATION_PRESSED; + } else { + // notification_received + event = NOTIFICATIONS_NOTIFICATION_RECEIVED; + } - NSDictionary *message = [self parseUILocalNotification:notification]; - [RNFirebaseUtil sendJSEvent:self name:event body:message]; + NSDictionary *message = [self parseUILocalNotification:notification]; + [RNFirebaseUtil sendJSEvent:self name:event body:message]; + #endif } // Listen for background messages @@ -75,7 +77,7 @@ RCT_EXPORT_MODULE(); [[RNFirebaseMessaging instance] didReceiveRemoteNotification:userInfo]; return; } - + NSString *event; if (RCTSharedApplication().applicationState == UIApplicationStateBackground) { // notification_displayed @@ -85,12 +87,17 @@ RCT_EXPORT_MODULE(); event = NOTIFICATIONS_NOTIFICATION_PRESSED; } else { // notification_received - event = NOTIFICATIONS_NOTIFICATION_RECEIVED; + // On IOS 10, foreground notifications also go through willPresentNotification + // This prevents duplicate messages from hitting the JS app + #if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 + return; + #else + event = NOTIFICATIONS_NOTIFICATION_RECEIVED; + #endif } - - // TODO: Proper notification structure - NSDictionary *message = [self parseUserInfo:userInfo messageType:@"RemoteNotification" clickAction:nil]; - + + NSDictionary *message = [self parseUserInfo:userInfo messageType:@"RemoteNotification" category:nil]; + [RNFirebaseUtil sendJSEvent:self name:event body:message]; } @@ -104,6 +111,7 @@ RCT_EXPORT_MODULE(); return; } + NSString *event; if (RCTSharedApplication().applicationState == UIApplicationStateBackground) { // notification_displayed @@ -113,16 +121,19 @@ RCT_EXPORT_MODULE(); event = NOTIFICATIONS_NOTIFICATION_PRESSED; } else { // notification_received - event = NOTIFICATIONS_NOTIFICATION_RECEIVED; + // On IOS 10, foreground notifications also go through willPresentNotification + // This prevents duplicate messages from hitting the JS app + #if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 + return; + #else + event = NOTIFICATIONS_NOTIFICATION_RECEIVED; + #endif } - - // TODO: Proper notification structure - NSDictionary *message = [self parseUserInfo:userInfo messageType:@"RemoteNotificationHandler" clickAction:nil]; - - // TODO: Callback handler - // [_callbackHandlers setObject:[completionHandler copy] forKey:message[@"messageId"]]; - + + NSDictionary *message = [self parseUserInfo:userInfo messageType:@"RemoteNotificationHandler" category:nil]; + [RNFirebaseUtil sendJSEvent:self name:event body:message]; + completionHandler(UIBackgroundFetchResultNoData); } // ******************************************************* @@ -181,7 +192,7 @@ didReceiveNotificationResponse:(UNNotificationResponse *)response #else withCompletionHandler:(void(^)())completionHandler { #endif - NSDictionary *message = [self parseUNNotification:response.notification messageType:@"NotificationResponse"]; + NSDictionary *message = [self parseUNNotificationResponse:response messageType:@"NotificationResponse"]; [RNFirebaseUtil sendJSEvent:self name:NOTIFICATIONS_NOTIFICATION_PRESSED body:message]; completionHandler(); @@ -253,7 +264,7 @@ RCT_EXPORT_METHOD(getInitialNotification:(RCTPromiseResolveBlock)resolve rejecte } else { NSDictionary *remoteNotification = [self bridge].launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey]; if (remoteNotification) { - NSDictionary *message = [self parseUserInfo:remoteNotification messageType:@"InitialMessage" clickAction:nil]; + NSDictionary *message = [self parseUserInfo:remoteNotification messageType:@"InitialMessage" category:nil]; resolve(message); } else { resolve(nil); @@ -560,17 +571,27 @@ RCT_EXPORT_METHOD(scheduleNotification:(NSDictionary*) notification return notification; } +- (NSDictionary*)parseUNNotificationResponse:(UNNotificationResponse *)response + messageType:(NSString *)messageType { + NSMutableDictionary *notificationResponse = [[NSMutableDictionary alloc] init]; + NSDictionary *notification = [self parseUNNotification:response.notification messageType:messageType]; + notificationResponse[@"notification"] = notification; + notificationResponse[@"action"] = response.actionIdentifier; + + return notificationResponse; +} + - (NSDictionary*)parseUNNotification:(UNNotification *)notification messageType:(NSString *)messageType { NSDictionary *userInfo = notification.request.content.userInfo; - NSString *clickAction = notification.request.content.categoryIdentifier; + NSString *category = notification.request.content.categoryIdentifier; - return [self parseUserInfo:userInfo messageType:messageType clickAction:clickAction]; + return [self parseUserInfo:userInfo messageType:messageType category:category]; } - (NSDictionary*)parseUserInfo:(NSDictionary *)userInfo messageType:(NSString *) messageType - clickAction:(NSString *) clickAction { + category:(NSString *) category { NSMutableDictionary *notification = [[NSMutableDictionary alloc] init]; NSMutableDictionary *data = [[NSMutableDictionary alloc] init]; @@ -585,22 +606,15 @@ RCT_EXPORT_METHOD(scheduleNotification:(NSDictionary*) notification for (id k3 in alert) { if ([k3 isEqualToString:@"body"]) { notification[@"body"] = alert[k3]; - } else if ([k3 isEqualToString:@"loc-args"]) { - // TODO: What to do with this? - // notif[@"bodyLocalizationArgs"] = alert[k3]; - } else if ([k3 isEqualToString:@"loc-key"]) { - // TODO: What to do with this? - // notif[@"bodyLocalizationKey"] = alert[k3]; } else if ([k3 isEqualToString:@"subtitle"]) { notification[@"subtitle"] = alert[k3]; } else if ([k3 isEqualToString:@"title"]) { notification[@"title"] = alert[k3]; - } else if ([k3 isEqualToString:@"title-loc-args"]) { - // TODO: What to do with this? - // notif[@"titleLocalizationArgs"] = alert[k3]; - } else if ([k3 isEqualToString:@"title-loc-key"]) { - // TODO: What to do with this? - // notif[@"titleLocalizationKey"] = alert[k3]; + } else if ([k3 isEqualToString:@"loc-args"] + || [k3 isEqualToString:@"loc-key"] + || [k3 isEqualToString:@"title-loc-args"] + || [k3 isEqualToString:@"title-loc-key"]) { + // Ignore known keys } else { NSLog(@"Unknown alert key: %@", k2); } @@ -617,21 +631,23 @@ RCT_EXPORT_METHOD(scheduleNotification:(NSDictionary*) notification } } else if ([k1 isEqualToString:@"gcm.message_id"]) { notification[@"notificationId"] = userInfo[k1]; - } else if ([k1 isEqualToString:@"google.c.a.ts"]) { - // TODO: What to do with this? - // message[@"sentTime"] = userInfo[k1]; } else if ([k1 isEqualToString:@"gcm.n.e"] || [k1 isEqualToString:@"gcm.notification.sound2"] || [k1 isEqualToString:@"google.c.a.c_id"] || [k1 isEqualToString:@"google.c.a.c_l"] || [k1 isEqualToString:@"google.c.a.e"] - || [k1 isEqualToString:@"google.c.a.udt"]) { + || [k1 isEqualToString:@"google.c.a.udt"] + || [k1 isEqualToString:@"google.c.a.ts"]) { // Ignore known keys } else { // Assume custom data data[k1] = userInfo[k1]; } } + + if (!ios[@"category"]) { + ios[@"category"] = category; + } // TODO: What to do with this? // message[@"messageType"] = messageType; diff --git a/lib/modules/notifications/Notification.js b/lib/modules/notifications/Notification.js index 50ec6b5d..1ad7d2cd 100644 --- a/lib/modules/notifications/Notification.js +++ b/lib/modules/notifications/Notification.js @@ -9,6 +9,11 @@ import { generatePushID, isObject } from '../../utils'; import type { NativeNotification } from './types'; +export type NotificationPressed = { + action: string, + notification: Notification, +}; + export default class Notification { // iOS 8/9 | 10+ | Android _android: AndroidNotification; diff --git a/lib/modules/notifications/index.js b/lib/modules/notifications/index.js index 6b9f3e24..60e1dc61 100644 --- a/lib/modules/notifications/index.js +++ b/lib/modules/notifications/index.js @@ -18,7 +18,12 @@ import { } from './types'; import type App from '../core/app'; -import type { NativeNotification, Schedule } from './types'; +import type { NotificationPressed } from './Notification'; +import type { + NativeNotification, + NativeNotificationPressed, + Schedule, +} from './types'; type OnNotification = Notification => any; @@ -26,6 +31,12 @@ type OnNotificationObserver = { next: OnNotification, }; +type OnNotificationPressed = NotificationPressed => any; + +type OnNotificationPressedObserver = { + next: OnNotificationPressed, +}; + const NATIVE_EVENTS = [ 'notifications_notification_displayed', 'notifications_notification_pressed', @@ -61,18 +72,6 @@ export default class Notifications extends ModuleBase { namespace: NAMESPACE, }); - SharedEventEmitter.addListener( - // sub to internal native event - this fans out to - // public event name: onNotificationPressed - 'notifications_notification_pressed', - (notification: NativeNotification) => { - SharedEventEmitter.emit( - 'onNotificationPressed', - new Notification(notification) - ); - } - ); - SharedEventEmitter.addListener( // sub to internal native event - this fans out to // public event name: onNotificationDisplayed @@ -85,6 +84,18 @@ export default class Notifications extends ModuleBase { } ); + SharedEventEmitter.addListener( + // sub to internal native event - this fans out to + // public event name: onNotificationPressed + 'notifications_notification_pressed', + (notificationPressed: NativeNotificationPressed) => { + SharedEventEmitter.emit('onNotificationPressed', { + action: notificationPressed.action, + notification: new Notification(notificationPressed.notification), + }); + } + ); + SharedEventEmitter.addListener( // sub to internal native event - this fans out to // public event name: onNotification @@ -193,11 +204,10 @@ export default class Notifications extends ModuleBase { } onNotificationPressed( - nextOrObserver: OnNotification | OnNotificationObserver + nextOrObserver: OnNotificationPressed | OnNotificationPressedObserver ): () => any { - let listener: Notification => any; + let listener; if (isFunction(nextOrObserver)) { - // $FlowBug: Not coping with the overloaded method signature listener = nextOrObserver; } else if (isObject(nextOrObserver) && isFunction(nextOrObserver.next)) { listener = nextOrObserver.next; diff --git a/lib/modules/notifications/types.js b/lib/modules/notifications/types.js index eec87988..9c2abe10 100644 --- a/lib/modules/notifications/types.js +++ b/lib/modules/notifications/types.js @@ -160,3 +160,8 @@ export type NativeNotification = {| subtitle?: string, title: string, |}; + +export type NativeNotificationPressed = {| + action: string, + notification: NativeNotification, +|};