Page MenuHomePhabricator

D9144.diff
No OneTemporary

D9144.diff

diff --git a/native/ios/NotificationService/NotificationService.mm b/native/ios/NotificationService/NotificationService.mm
--- a/native/ios/NotificationService/NotificationService.mm
+++ b/native/ios/NotificationService/NotificationService.mm
@@ -9,10 +9,15 @@
NSString *const messageInfosKey = @"messageInfos";
NSString *const encryptedPayloadKey = @"encryptedPayload";
NSString *const encryptionFailureKey = @"encryptionFailure";
+NSString *const collapseIDKey = @"collapseID";
const std::string callingProcessName = "NSE";
// The context for this constant can be found here:
// https://linear.app/comm/issue/ENG-3074#comment-bd2f5e28
int64_t const notificationRemovalDelay = (int64_t)(0.1 * NSEC_PER_SEC);
+// Apple gives us about 30 seconds to process single notification,
+// se we let any semaphore wait for at most 20 seconds
+int64_t const semaphoreAwaitTimeLimit = (int64_t)(20 * NSEC_PER_SEC);
+
CFStringRef newMessageInfosDarwinNotification =
CFSTR("app.comm.darwin_new_message_infos");
@@ -130,9 +135,11 @@
std::string rescindErrorMessage;
try {
@try {
- [self
- removeNotificationWithIdentifier:content
- .userInfo[@"notificationId"]];
+ [self removeNotificationsWithCondition:^BOOL(
+ UNNotification *_Nonnull notif) {
+ return [content.userInfo[@"notificationId"]
+ isEqualToString:notif.request.content.userInfo[@"id"]];
+ }];
} @catch (NSException *e) {
rescindErrorMessage =
"Obj-C exception: " + std::string([e.name UTF8String]) +
@@ -152,6 +159,38 @@
publicUserContent = [[UNNotificationContent alloc] init];
}
+ // Step 4: (optional) execute notification coalescing
+ if ([self isCollapsible:content.userInfo]) {
+ std::string coalescingErrorMessage;
+ try {
+ @try {
+ [self displayLocalNotificationFromContent:content
+ forCollapseKey:content
+ .userInfo[collapseIDKey]];
+ } @catch (NSException *e) {
+ coalescingErrorMessage =
+ "Obj-C exception: " + std::string([e.name UTF8String]) +
+ " during notification coalescing.";
+ }
+ } catch (const std::exception &e) {
+ coalescingErrorMessage = "C++ exception: " + std::string(e.what()) +
+ " during notification coalescing.";
+ }
+
+ if (coalescingErrorMessage.size()) {
+ [errorMessages
+ addObject:[NSString
+ stringWithUTF8String:coalescingErrorMessage.c_str()]];
+ // Even if we fail to execute coalescing then public users
+ // should still see the original message.
+ publicUserContent = content;
+ } else {
+ publicUserContent = [[UNNotificationContent alloc] init];
+ }
+ }
+
+ // Step 5: (optional) create empty notification that
+ // only provides badge count.
if ([self isBadgeOnly:content.userInfo]) {
UNMutableNotificationContent *badgeOnlyContent =
[[UNMutableNotificationContent alloc] init];
@@ -222,6 +261,23 @@
continue;
}
+ if ([self isCollapsible:content.userInfo]) {
+ // If we get to this place it means we were unable to
+ // execute notification coalescing with local notification
+ // mechanism in time given to NSE to process notification.
+ if (!comm::StaffUtils::isStaffRelease()) {
+ handler(content);
+ continue;
+ }
+
+ NSString *errorMessage =
+ @"NSE: Exceeded time limit to collapse a notitication.";
+ UNNotificationContent *errorContent =
+ [self buildContentForError:errorMessage];
+ handler(errorContent);
+ continue;
+ }
+
if ([self shouldBeDecrypted:content.userInfo] &&
!content.userInfo[@"succesfullyDecrypted"]) {
// If we get to this place it means we were unable to
@@ -254,7 +310,8 @@
}
}
-- (void)removeNotificationWithIdentifier:(NSString *)identifier {
+- (void)removeNotificationsWithCondition:
+ (BOOL (^)(UNNotification *_Nonnull))condition {
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
void (^delayedSemaphorePostCallback)() = ^() {
@@ -268,18 +325,62 @@
[UNUserNotificationCenter.currentNotificationCenter
getDeliveredNotificationsWithCompletionHandler:^(
NSArray<UNNotification *> *_Nonnull notifications) {
+ NSMutableArray<NSString *> *notificationsToRemove =
+ [[NSMutableArray alloc] init];
for (UNNotification *notif in notifications) {
- if ([identifier isEqual:notif.request.content.userInfo[@"id"]]) {
- [UNUserNotificationCenter.currentNotificationCenter
- removeDeliveredNotificationsWithIdentifiers:@[
- notif.request.identifier
- ]];
+ if (condition(notif)) {
+ [notificationsToRemove addObject:notif.request.identifier];
}
}
+ [UNUserNotificationCenter.currentNotificationCenter
+ removeDeliveredNotificationsWithIdentifiers:notificationsToRemove];
delayedSemaphorePostCallback();
}];
- dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
+ dispatch_semaphore_wait(
+ semaphore, dispatch_time(DISPATCH_TIME_NOW, semaphoreAwaitTimeLimit));
+}
+
+- (void)displayLocalNotificationFromContent:(UNNotificationContent *)content
+ forCollapseKey:(NSString *)collapseKey {
+ UNMutableNotificationContent *localNotifContent =
+ [[UNMutableNotificationContent alloc] init];
+
+ localNotifContent.title = content.title;
+ localNotifContent.body = content.body;
+ localNotifContent.badge = content.badge;
+ localNotifContent.userInfo = content.userInfo;
+
+ UNNotificationRequest *localNotifRequest =
+ [UNNotificationRequest requestWithIdentifier:collapseKey
+ content:localNotifContent
+ trigger:nil];
+
+ // We must wait until local notif display completion
+ // handler returns. Context:
+ // https://developer.apple.com/forums/thread/108340?answerId=331640022#331640022
+
+ dispatch_semaphore_t localNotifDisplaySemaphore =
+ dispatch_semaphore_create(0);
+
+ __block NSError *localNotifDisplayError = nil;
+ [UNUserNotificationCenter.currentNotificationCenter
+ addNotificationRequest:localNotifRequest
+ withCompletionHandler:^(NSError *_Nullable error) {
+ if (error) {
+ localNotifDisplayError = error;
+ }
+ dispatch_semaphore_signal(localNotifDisplaySemaphore);
+ }];
+
+ dispatch_semaphore_wait(
+ localNotifDisplaySemaphore,
+ dispatch_time(DISPATCH_TIME_NOW, semaphoreAwaitTimeLimit));
+
+ if (localNotifDisplayError) {
+ throw std::runtime_error(
+ std::string([localNotifDisplayError.localizedDescription UTF8String]));
+ }
}
- (void)persistMessagePayload:(NSDictionary *)payload {
@@ -326,6 +427,10 @@
return !payload[@"threadID"];
}
+- (BOOL)isCollapsible:(NSDictionary *)payload {
+ return payload[collapseIDKey];
+}
+
- (UNNotificationContent *)getBadgeOnlyContentFor:
(UNNotificationContent *)content {
UNMutableNotificationContent *badgeOnlyContent =

File Metadata

Mime Type
text/plain
Expires
Wed, Nov 27, 8:31 AM (21 h, 53 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2589838
Default Alt Text
D9144.diff (7 KB)

Event Timeline