diff --git a/native/ios/Comm/AppDelegate.mm b/native/ios/Comm/AppDelegate.mm --- a/native/ios/Comm/AppDelegate.mm +++ b/native/ios/Comm/AppDelegate.mm @@ -26,8 +26,8 @@ @end #endif +#import "CommIOSNotifications.h" #import "Orientation.h" -#import "RNNotifications.h" #import #import @@ -158,13 +158,13 @@ - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { - [RNNotifications + [CommIOSNotifications didRegisterForRemoteNotificationsWithDeviceToken:deviceToken]; } - (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error { - [RNNotifications didFailToRegisterForRemoteNotificationsWithError:error]; + [CommIOSNotifications didFailToRegisterForRemoteNotificationsWithError:error]; } // Required for the notification event. You must call the completion handler @@ -184,8 +184,8 @@ return; } - [RNNotifications didReceiveRemoteNotification:notification - fetchCompletionHandler:completionHandler]; + [CommIOSNotifications didReceiveRemoteNotification:notification + fetchCompletionHandler:completionHandler]; } - (BOOL)handleBackgroundNotification:(NSDictionary *)notification @@ -202,33 +202,14 @@ comm::ThreadOperations::updateSQLiteUnreadStatus(threadID, false); }); } - [[UNUserNotificationCenter currentNotificationCenter] - getDeliveredNotificationsWithCompletionHandler:^( - NSArray *notifications) { - for (UNNotification *notif in notifications) { - if ([notification[@"notificationId"] - isEqual:notif.request.content.userInfo[@"id"]]) { - NSArray *identifiers = - [NSArray arrayWithObjects:notif.request.identifier, nil]; - [[UNUserNotificationCenter currentNotificationCenter] - removeDeliveredNotificationsWithIdentifiers:identifiers]; - } - } - dispatch_async(dispatch_get_main_queue(), ^{ - completionHandler(UIBackgroundFetchResultNewData); - }); - }]; + [CommIOSNotifications + clearNotificationFromNotificationsCenter:notification[@"notificationId"] + completionHandler:completionHandler]; return YES; } return NO; } -// Required for the localNotification event. -- (void)application:(UIApplication *)application - didReceiveLocalNotification:(UILocalNotification *)notification { - [RNNotifications didReceiveLocalNotification:notification]; -} - - (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window { return [Orientation getOrientation]; diff --git a/native/push/ios.js b/native/push/ios.js --- a/native/push/ios.js +++ b/native/push/ios.js @@ -1,9 +1,37 @@ // @flow -import NotificationsIOS from 'react-native-notifications'; +import { NativeModules, NativeEventEmitter } from 'react-native'; + +import type { + CoreIOSNotificationData, + CoreIOSNotificationDataWithRequestIdentifier, +} from './comm-ios-notification'; type PushPermissions = { alert?: boolean, badge?: boolean, sound?: boolean }; +type CommIOSNotificationsModuleType = { + +requestPermissions: () => void, + +checkPermissions: () => PushPermissions, + +consumeBackgroundQueue: () => void, + +setBadgesCount: (count: number) => void, + +removeAllDeliveredNotifications: () => void, + +removeDeliveredNotifications: (identifiers: $ReadOnlyArray) => void, + +getDeliveredNotifications: ( + callback: ( + notifications: $ReadOnlyArray, + ) => void, + ) => void, + +completeNotif: (id: string, fetchResult: string) => void, + +getConstants: () => { [string]: string }, + // required since CommIOSNotifications subclasses RCTEventEmitter + +addListener: (eventName: string) => void, + +removeListeners: (count: number) => void, + ... +}; + +const CommIOSNotifications: CommIOSNotificationsModuleType = + NativeModules.CommIOSNotifications; + let currentlyActive = false; let firstRun = true; @@ -12,7 +40,7 @@ firstRun = false; if (!permissionNeeded) { - const permissions: PushPermissions = await NotificationsIOS.checkPermissions(); + const permissions: PushPermissions = await CommIOSNotifications.checkPermissions(); permissionNeeded = permissionMissing(permissions); } @@ -21,10 +49,10 @@ return; } currentlyActive = true; - await NotificationsIOS.requestPermissions(); + await CommIOSNotifications.requestPermissions(); } - NotificationsIOS.consumeBackgroundQueue(); + CommIOSNotifications.consumeBackgroundQueue(); } function iosPushPermissionResponseReceived() { @@ -35,4 +63,20 @@ return !permissions.alert || !permissions.badge || !permissions.sound; } -export { requestIOSPushPermissions, iosPushPermissionResponseReceived }; +function getCommIOSNotificationsEventEmitter(): NativeEventEmitter< + $ReadOnly<{ + remoteNotificationsRegistered: [{ +deviceToken: ?string }], + remoteNotificationsRegistrationFailed: [void], + notificationReceivedForeground: [CoreIOSNotificationData], + notificationOpened: [CoreIOSNotificationData], + }>, +> { + return new NativeEventEmitter(CommIOSNotifications); +} + +export { + requestIOSPushPermissions, + iosPushPermissionResponseReceived, + CommIOSNotifications, + getCommIOSNotificationsEventEmitter, +}; diff --git a/native/push/push-handler.react.js b/native/push/push-handler.react.js --- a/native/push/push-handler.react.js +++ b/native/push/push-handler.react.js @@ -55,11 +55,16 @@ handleAndroidMessage, androidBackgroundMessageTask, } from './android'; +import { + CommIOSNotification, + type CoreIOSNotificationData, +} from './comm-ios-notification'; import { getFirebase } from './firebase'; import InAppNotif from './in-app-notif.react'; import { requestIOSPushPermissions, iosPushPermissionResponseReceived, + getCommIOSNotificationsEventEmitter, } from './ios'; LogBox.ignoreLogs([ @@ -103,6 +108,7 @@ +onPress: () => void, }, }; + class PushHandler extends React.PureComponent { state: State = { inAppNotifProps: null, @@ -115,6 +121,7 @@ initialAndroidNotifHandled = false; openThreadOnceReceived: Set = new Set(); lifecycleSubscription: ?EventSubscription; + iosNotificationEventSubscriptions: Array = []; componentDidMount() { this.appStarted = Date.now(); @@ -123,21 +130,25 @@ ); this.onForeground(); if (Platform.OS === 'ios') { - NotificationsIOS.addEventListener( - 'remoteNotificationsRegistered', - this.registerPushPermissions, - ); - NotificationsIOS.addEventListener( - 'remoteNotificationsRegistrationFailed', - this.failedToRegisterPushPermissions, - ); - NotificationsIOS.addEventListener( - 'notificationReceivedForeground', - this.iosForegroundNotificationReceived, - ); - NotificationsIOS.addEventListener( - 'notificationOpened', - this.iosNotificationOpened, + const commIOSNotificationsEventEmitter = getCommIOSNotificationsEventEmitter(); + this.iosNotificationEventSubscriptions.push( + commIOSNotificationsEventEmitter.addListener( + 'remoteNotificationsRegistered', + registration => + this.registerPushPermissions(registration?.deviceToken), + ), + commIOSNotificationsEventEmitter.addListener( + 'remoteNotificationsRegistrationFailed', + this.failedToRegisterPushPermissions, + ), + commIOSNotificationsEventEmitter.addListener( + 'notificationReceivedForeground', + this.iosForegroundNotificationReceived, + ), + commIOSNotificationsEventEmitter.addListener( + 'notificationOpened', + this.iosNotificationOpened, + ), ); } else if (Platform.OS === 'android') { const firebase = getFirebase(); @@ -168,22 +179,10 @@ this.lifecycleSubscription.remove(); } if (Platform.OS === 'ios') { - NotificationsIOS.removeEventListener( - 'remoteNotificationsRegistered', - this.registerPushPermissions, - ); - NotificationsIOS.removeEventListener( - 'remoteNotificationsRegistrationFailed', - this.failedToRegisterPushPermissions, - ); - NotificationsIOS.removeEventListener( - 'notificationReceivedForeground', - this.iosForegroundNotificationReceived, - ); - NotificationsIOS.removeEventListener( - 'notificationOpened', - this.iosNotificationOpened, - ); + for (const iosNotificationEventSubscription of this + .iosNotificationEventSubscriptions) { + iosNotificationEventSubscription.remove(); + } } else if (Platform.OS === 'android') { if (this.androidTokenListener) { this.androidTokenListener(); @@ -457,15 +456,10 @@ }); } - iosForegroundNotificationReceived = notification => { - if ( - notification.getData() && - notification.getData().managedAps && - notification.getData().managedAps.action === 'CLEAR' - ) { - notification.finish(NotificationsIOS.FetchResult.NoData); - return; - } + iosForegroundNotificationReceived = ( + rawNotification: CoreIOSNotificationData, + ) => { + const notification = new CommIOSNotification(rawNotification); if (Date.now() < this.appStarted + 1500) { // On iOS, when the app is opened from a notif press, for some reason this // callback gets triggered before iosNotificationOpened. In fact this @@ -476,19 +470,25 @@ return; } const threadID = notification.getData().threadID; - if (!threadID) { - console.log('Notification with missing threadID received!'); - notification.finish(NotificationsIOS.FetchResult.NoData); - return; - } const messageInfos = notification.getData().messageInfos; this.saveMessageInfos(messageInfos); - let title = null; - let body = notification.getMessage(); - if (notification.getData().title) { - ({ title, body } = mergePrefixIntoBody(notification.getData())); + + let title = notification.getData().title; + let body = notification.getData().body; + + if (title && body) { + ({ title, body } = mergePrefixIntoBody({ title, body })); + } else { + body = notification.getMessage(); + } + + if (body) { + this.showInAppNotification(threadID, body, title); + } else { + console.log( + 'Non-rescind foreground notification without alert received!', + ); } - this.showInAppNotification(threadID, body, title); notification.finish(NotificationsIOS.FetchResult.NewData); }; @@ -501,14 +501,10 @@ } } - iosNotificationOpened = notification => { + iosNotificationOpened = (rawNotification: CoreIOSNotificationData) => { + const notification = new CommIOSNotification(rawNotification); this.onPushNotifBootsApp(); const threadID = notification.getData().threadID; - if (!threadID) { - console.log('Notification with missing threadID received!'); - notification.finish(NotificationsIOS.FetchResult.NoData); - return; - } const messageInfos = notification.getData().messageInfos; this.saveMessageInfos(messageInfos); this.onPressNotificationForThread(threadID, true);