mirror of
https://gitlab.opencode.de/bwi/bundesmessenger/clients/bundesmessenger-ios.git
synced 2026-05-14 11:49:59 +02:00
Merge pull request #1714 from vector-im/notif_for_failed_sync
Missing Push Notifications again
This commit is contained in:
+176
-8
@@ -177,6 +177,12 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
|
||||
*/
|
||||
NSMutableDictionary <NSNumber *, NSMutableArray <NSDictionary *> *> *eventsToNotify;
|
||||
|
||||
/**
|
||||
Cache for payloads received with incoming push notifications.
|
||||
The key is the event id. The value, the payload.
|
||||
*/
|
||||
NSMutableDictionary <NSString*, NSDictionary*> *incomingPushPayloads;
|
||||
|
||||
/**
|
||||
Currently displayed "Call not supported" alert.
|
||||
*/
|
||||
@@ -395,6 +401,7 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
|
||||
callEventsListeners = [NSMutableDictionary dictionary];
|
||||
notificationListenerBlocks = [NSMutableDictionary dictionary];
|
||||
eventsToNotify = [NSMutableDictionary dictionary];
|
||||
incomingPushPayloads = [NSMutableDictionary dictionary];
|
||||
|
||||
// To simplify navigation into the app, we retrieve here the main navigation controller and the tab bar controller.
|
||||
UISplitViewController *splitViewController = (UISplitViewController *)self.window.rootViewController;
|
||||
@@ -539,6 +546,7 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
|
||||
{
|
||||
[array removeAllObjects];
|
||||
}
|
||||
[incomingPushPayloads removeAllObjects];
|
||||
|
||||
// Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
|
||||
|
||||
@@ -1085,6 +1093,7 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
|
||||
if (roomId.length)
|
||||
{
|
||||
// TODO retrieve the right matrix session
|
||||
// We can use the "user_id" value in notification.userInfo
|
||||
|
||||
//**************
|
||||
// Patch consider the first session which knows the room id
|
||||
@@ -1165,6 +1174,9 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
|
||||
{
|
||||
[array addObject:eventId];
|
||||
}
|
||||
|
||||
// Cache payload for further usage
|
||||
incomingPushPayloads[eventId] = payload.dictionaryPayload;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1187,9 +1199,12 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
|
||||
{
|
||||
NSLog(@"[AppDelegate][Push] launchBackgroundSync");
|
||||
__weak typeof(self) weakSelf = self;
|
||||
|
||||
NSMutableArray<NSString *> *incomingPushEventIds = self.incomingPushEventIds[@(account.mxSession.hash)];
|
||||
NSMutableArray<NSString *> *incomingPushEventIdsCopy = [incomingPushEventIds copy];
|
||||
|
||||
// Flush all the pending push notifications for this session.
|
||||
[self.incomingPushEventIds[@(account.mxSession.hash)] removeAllObjects];
|
||||
[incomingPushEventIds removeAllObjects];
|
||||
|
||||
[account backgroundSync:20000 success:^{
|
||||
|
||||
@@ -1210,8 +1225,14 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
|
||||
|
||||
} failure:^(NSError *error) {
|
||||
|
||||
NSLog(@"[AppDelegate][Push] launchBackgroundSync: the background sync fails");
|
||||
|
||||
NSLog(@"[AppDelegate][Push] launchBackgroundSync: the background sync failed. Error: %@ (%@). incomingPushEventIdsCopy: %@ - self.incomingPushEventIds: %@", error.domain, @(error.code), incomingPushEventIdsCopy, incomingPushEventIds);
|
||||
|
||||
// Trigger limited local notifications when the sync with HS fails
|
||||
[self handleLimitedLocalNotifications:account.mxSession events:incomingPushEventIdsCopy];
|
||||
|
||||
// Update app icon badge number
|
||||
[self refreshApplicationIconBadgeNumber];
|
||||
|
||||
}];
|
||||
}
|
||||
}
|
||||
@@ -1238,6 +1259,13 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
|
||||
NSString *roomId = eventDict[@"room_id"];
|
||||
BOOL checkReadEvent = YES;
|
||||
MXEvent *event;
|
||||
|
||||
// Ignore event already notified to the user
|
||||
if ([self displayedLocalNotificationForEvent:eventId andUser:account.mxCredentials.userId type:nil])
|
||||
{
|
||||
NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Skip event already displayed in a notification. Event id: %@", eventId);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (eventId && roomId)
|
||||
{
|
||||
@@ -1249,7 +1277,7 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
|
||||
// Ignore redacted event.
|
||||
if (event.isRedactedEvent)
|
||||
{
|
||||
NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Skip redacted event");
|
||||
NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Skip redacted event. Event id: %@", event.eventId);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1259,7 +1287,7 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
|
||||
// Ignore call invite when callkit is active.
|
||||
if (isCallKitActive)
|
||||
{
|
||||
NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Skip call event");
|
||||
NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Skip call event. Event id: %@", event.eventId);
|
||||
continue;
|
||||
}
|
||||
else
|
||||
@@ -1285,7 +1313,7 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
|
||||
MXEvent *readReceiptEvent = [account.mxSession.store eventWithEventId:readReceipt.eventId inRoom:roomId];
|
||||
if (event.originServerTs <= readReceiptEvent.originServerTs)
|
||||
{
|
||||
NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Skip already read event");
|
||||
NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Skip already read event. Event id: %@", event.eventId);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -1303,7 +1331,12 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
|
||||
|
||||
UILocalNotification *eventNotification = [[UILocalNotification alloc] init];
|
||||
eventNotification.alertBody = notificationBody;
|
||||
eventNotification.userInfo = @{ @"room_id" : event.roomId };
|
||||
eventNotification.userInfo = @{
|
||||
@"type": @"full",
|
||||
@"room_id": event.roomId,
|
||||
@"event_id": event.eventId,
|
||||
@"user_id": account.mxCredentials.userId
|
||||
};
|
||||
|
||||
// Set sound name based on the value provided in action of MXPushRule
|
||||
for (MXPushRuleAction *action in rule.actions)
|
||||
@@ -1444,6 +1477,127 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
|
||||
return notificationBody;
|
||||
}
|
||||
|
||||
/**
|
||||
Display "limited" notifications for events the app was not able to get data
|
||||
(because of /sync failure).
|
||||
|
||||
In this situation, we are only able to display "You received a message in %@".
|
||||
|
||||
@param mxSession the matrix session where the /sync failed.
|
||||
@param events the list of events id we did not get data.
|
||||
*/
|
||||
- (void)handleLimitedLocalNotifications:(MXSession*)mxSession events:(NSArray<NSString *> *)events
|
||||
{
|
||||
NSString *userId = mxSession.matrixRestClient.credentials.userId;
|
||||
|
||||
NSLog(@"[AppDelegate][Push] handleLocalNotificationsForFailedSync: %@", userId);
|
||||
NSLog(@"[AppDelegate][Push] handleLocalNotificationsForFailedSync: eventsToNotify: %@", eventsToNotify[@(mxSession.hash)]);
|
||||
NSLog(@"[AppDelegate][Push] handleLocalNotificationsForFailedSync: incomingPushEventIds: %@", self.incomingPushEventIds[@(mxSession.hash)]);
|
||||
NSLog(@"[AppDelegate][Push] handleLocalNotificationsForFailedSync: events: %@", events);
|
||||
|
||||
if (!events.count)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (NSString *eventId in events)
|
||||
{
|
||||
// Ignore event already notified to the user
|
||||
if ([self displayedLocalNotificationForEvent:eventId andUser:userId type:nil])
|
||||
{
|
||||
NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Skip event already displayed in a notification. Event id: %@", eventId);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Build notification user info
|
||||
NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithDictionary:@{
|
||||
@"type": @"limited",
|
||||
@"event_id": eventId,
|
||||
@"user_id": userId
|
||||
}];
|
||||
|
||||
// Add the room_id so that user will open the room when tapping on the notif
|
||||
NSDictionary *payload = incomingPushPayloads[eventId];
|
||||
NSString *roomId = payload[@"room_id"];
|
||||
if (roomId)
|
||||
{
|
||||
userInfo[@"room_id"] = roomId;
|
||||
}
|
||||
else
|
||||
{
|
||||
NSLog(@"[AppDelegate][Push] handleLocalNotificationsForFailedSync: room_id is missing for event %@ in payload %@", eventId, payload);
|
||||
}
|
||||
|
||||
UILocalNotification *localNotificationForFailedSync = [[UILocalNotification alloc] init];
|
||||
localNotificationForFailedSync.userInfo = userInfo;
|
||||
localNotificationForFailedSync.alertBody = [self limitedNotificationBodyForEvent:eventId inMatrixSession:mxSession];
|
||||
|
||||
NSLog(@"[AppDelegate][Push] handleLocalNotificationsForFailedSync: Display notification for event %@", eventId);
|
||||
[[UIApplication sharedApplication] scheduleLocalNotification:localNotificationForFailedSync];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Build the body for the "limited" notification to display to the user.
|
||||
|
||||
@param eventId the id of the event the app failed to get data.
|
||||
@param mxSession the matrix session where the /sync failed.
|
||||
@return the string to display in the local notification.
|
||||
*/
|
||||
- (nullable NSString *)limitedNotificationBodyForEvent:(NSString *)eventId inMatrixSession:(MXSession*)mxSession
|
||||
{
|
||||
NSString *notificationBody;
|
||||
|
||||
NSString *roomDisplayName;
|
||||
|
||||
NSDictionary *payload = incomingPushPayloads[eventId];
|
||||
NSString *roomId = payload[@"room_id"];
|
||||
if (roomId)
|
||||
{
|
||||
MXRoomSummary *roomSummary = [mxSession roomSummaryWithRoomId:roomId];
|
||||
if (roomSummary)
|
||||
{
|
||||
roomDisplayName = roomSummary.displayname;
|
||||
}
|
||||
}
|
||||
|
||||
if (roomDisplayName.length)
|
||||
{
|
||||
[NSString stringWithFormat:NSLocalizedString(@"SINGLE_UNREAD_IN_ROOM", nil), roomDisplayName];
|
||||
}
|
||||
else
|
||||
{
|
||||
[NSString stringWithFormat:NSLocalizedString(@"SINGLE_UNREAD", nil), roomDisplayName];
|
||||
}
|
||||
|
||||
return notificationBody;
|
||||
}
|
||||
|
||||
/**
|
||||
Return the already displayed notification for an event.
|
||||
|
||||
@param eventId the id of the event attached to the notification to find.
|
||||
@param userId the id of the user attached to the notification to find.
|
||||
@param type the type of notification. @"full" or @"limited". nil for any type.
|
||||
@return the local notification if any.
|
||||
*/
|
||||
- (UILocalNotification*)displayedLocalNotificationForEvent:(NSString*)eventId andUser:(NSString*)userId type:(NSString*)type
|
||||
{
|
||||
UILocalNotification *limitedLocalNotification;
|
||||
for (UILocalNotification *localNotification in [[UIApplication sharedApplication] scheduledLocalNotifications])
|
||||
{
|
||||
if ([localNotification.userInfo[@"event_id"] isEqualToString:eventId]
|
||||
&& [localNotification.userInfo[@"user_id"] isEqualToString:userId]
|
||||
&& (!type || [localNotification.userInfo[@"type"] isEqualToString:type]))
|
||||
{
|
||||
limitedLocalNotification = localNotification;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return limitedLocalNotification;
|
||||
}
|
||||
|
||||
- (void)refreshApplicationIconBadgeNumber
|
||||
{
|
||||
// Consider the total number of missed discussions including the invites.
|
||||
@@ -1985,10 +2139,24 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
|
||||
// Check whether some push notifications are pending for this session.
|
||||
if (self.incomingPushEventIds[@(mxSession.hash)].count)
|
||||
{
|
||||
NSLog(@"[AppDelegate][Push] relaunch a background sync for the kMXSessionStateDidChangeNotification pending incoming push");
|
||||
NSLog(@"[AppDelegate][Push] relaunch a background sync for %tu kMXSessionStateDidChangeNotification pending incoming pushes", self.incomingPushEventIds[@(mxSession.hash)].count);
|
||||
[self launchBackgroundSync];
|
||||
}
|
||||
}
|
||||
else if (mxSession.state == MXSessionStateInitialSyncFailed)
|
||||
{
|
||||
// Display failure sync notifications for pending events if any
|
||||
if (self.incomingPushEventIds[@(mxSession.hash)].count)
|
||||
{
|
||||
NSLog(@"[AppDelegate][Push] initial sync failed with %tu pending incoming pushes", self.incomingPushEventIds[@(mxSession.hash)].count);
|
||||
|
||||
// Trigger limited local notifications when the sync with HS fails
|
||||
[self handleLimitedLocalNotifications:mxSession events:self.incomingPushEventIds[@(mxSession.hash)]];
|
||||
|
||||
// Update app icon badge number
|
||||
[self refreshApplicationIconBadgeNumber];
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ([[UIApplication sharedApplication] applicationState] == UIApplicationStateActive)
|
||||
{
|
||||
|
||||
@@ -44,6 +44,12 @@
|
||||
/* New action message from a specific person in a named room. */
|
||||
"IMAGE_FROM_USER_IN_ROOM" = "%@ posted a picture %@ in %@";
|
||||
|
||||
/* A single unread message in a room */
|
||||
"SINGLE_UNREAD_IN_ROOM" = "You received a message in %@";
|
||||
|
||||
/* A single unread message */
|
||||
"SINGLE_UNREAD" = "You received a message";
|
||||
|
||||
/** Coalesced messages **/
|
||||
|
||||
/* Multiple unread messages in a room */
|
||||
|
||||
Reference in New Issue
Block a user