diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index 7985cbeb5..c78ecc45e 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -277,6 +277,9 @@ "room_replacement_link" = "The conversation continues here."; "room_predecessor_information" = "This room is a continuation of another conversation."; "room_predecessor_link" = "Click here to see older messages."; +"room_resource_limit_exceeded_message_contact_1" = " Please "; +"room_resource_limit_exceeded_message_contact_2_link" = "contact your service administrator"; +"room_resource_limit_exceeded_message_contact_3" = " to continue using this service."; // Unknown devices "unknown_devices_alert_title" = "Room contains unknown devices"; diff --git a/Riot/Modules/Room/RoomViewController.m b/Riot/Modules/Room/RoomViewController.m index d4d31dde3..9c886ebda 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -176,7 +176,10 @@ // Observe kAppDelegateNetworkStatusDidChangeNotification to handle network status change. id kAppDelegateNetworkStatusDidChangeNotificationObserver; - + + // Observers to manage MXSession state (and sync errors) + id kMXSessionStateDidChangeObserver; + // Observers to manage ongoing conference call banner id kMXCallStateDidChangeObserver; id kMXCallManagerConferenceStartedObserver; @@ -470,6 +473,7 @@ [self listenCallNotifications]; [self listenWidgetNotifications]; [self listenTombstoneEventNotifications]; + [self listenMXSessionStateChangeNotifications]; if (self.showExpandedHeader) { @@ -519,6 +523,7 @@ [self removeCallNotificationsListeners]; [self removeWidgetNotificationsListeners]; [self removeTombstoneEventNotificationsListener]; + [self removeMXSessionStateChangeNotificationsListener]; // Re-enable the read marker display, and disable its update. self.roomDataSource.showReadMarker = YES; @@ -1156,6 +1161,7 @@ [self removeCallNotificationsListeners]; [self removeWidgetNotificationsListeners]; [self removeTombstoneEventNotificationsListener]; + [self removeMXSessionStateChangeNotificationsListener]; if (previewHeader || (self.expandedHeaderContainer.isHidden == NO)) { @@ -3872,8 +3878,21 @@ } Widget *jitsiWidget = [customizedRoomDataSource jitsiWidget]; - - if ([AppDelegate theDelegate].isOffline) + + if ([self.roomDataSource.mxSession.syncError.errcode isEqualToString:kMXErrCodeStringResourceLimitExceeded]) + { + [roomActivitiesView showResourceLimitExceededError:self.roomDataSource.mxSession.syncError.userInfo onAdminContactTapped:^(NSURL *adminContact) { + if ([[UIApplication sharedApplication] canOpenURL:adminContact]) + { + [[UIApplication sharedApplication] openURL:adminContact]; + } + else + { + NSLog(@"[RoomVC] refreshActivitiesViewDisplay: adminContact(%@) cannot be opened", adminContact); + } + }]; + } + else if ([AppDelegate theDelegate].isOffline) { [roomActivitiesView displayNetworkErrorNotification:NSLocalizedStringFromTable(@"room_offline_notification", @"Vector", nil)]; } @@ -4810,5 +4829,29 @@ } } +#pragma mark MXSession state change + +- (void)listenMXSessionStateChangeNotifications +{ + kMXSessionStateDidChangeObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kMXSessionStateDidChangeNotification object:self.roomDataSource.mxSession queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { + + if (self.roomDataSource.mxSession.state == MXSessionStateSyncError + || self.roomDataSource.mxSession.state == MXSessionStateRunning) + { + [self refreshActivitiesViewDisplay]; + [self refreshRoomInputToolbar]; + } + }]; +} + +- (void)removeMXSessionStateChangeNotificationsListener +{ + if (kMXSessionStateDidChangeObserver) + { + [[NSNotificationCenter defaultCenter] removeObserver:kMXSessionStateDidChangeObserver]; + kMXSessionStateDidChangeObserver = nil; + } +} + @end diff --git a/Riot/Modules/Room/Views/Activities/RoomActivitiesView.h b/Riot/Modules/Room/Views/Activities/RoomActivitiesView.h index fc695bea6..df8b66581 100644 --- a/Riot/Modules/Room/Views/Activities/RoomActivitiesView.h +++ b/Riot/Modules/Room/Views/Activities/RoomActivitiesView.h @@ -85,6 +85,14 @@ */ - (void)displayRoomReplacementWithRoomLinkTappedHandler:(void (^)(void))onRoomReplacementLinkTapped; +/** + Display a kMXErrCodeStringResourceLimitExceeded error received during a /sync request. + + @param errorDict the error data. + @param onAdminContactTapped a callback indicating if the user wants to contact their admin. + */ +- (void)showResourceLimitExceededError:(NSDictionary *)errorDict onAdminContactTapped:(void (^)(NSURL *adminContact))onAdminContactTapped; + /** Remove any displayed information. */ diff --git a/Riot/Modules/Room/Views/Activities/RoomActivitiesView.m b/Riot/Modules/Room/Views/Activities/RoomActivitiesView.m index 56dc9b1b7..2634ed57a 100644 --- a/Riot/Modules/Room/Views/Activities/RoomActivitiesView.m +++ b/Riot/Modules/Room/Views/Activities/RoomActivitiesView.m @@ -374,6 +374,78 @@ [self checkHeight:YES]; } +- (void)showResourceLimitExceededError:(NSDictionary *)errorDict onAdminContactTapped:(void (^)(NSURL *adminContact))onAdminContactTapped +{ + [self reset]; + + CGFloat fontSize = 15; + + // Parse error data + NSString *limitType, *adminContactString; + NSURL *adminContact; + + MXJSONModelSetString(limitType, errorDict[kMXErrorResourceLimitExceededLimitTypeKey]); + MXJSONModelSetString(adminContactString, errorDict[kMXErrorResourceLimitExceededAdminContactKey]); + + if (adminContactString) + { + adminContact = [NSURL URLWithString:adminContactString]; + } + + // Build the message content + // Reuse MatrixKit as is for the beginning + NSMutableString *message = [NSMutableString new]; + if ([limitType isEqualToString:kMXErrorResourceLimitExceededLimitTypeMonthlyActiveUserValue]) + { + [message appendString:[NSBundle mxk_localizedStringForKey:@"login_error_resource_limit_exceeded_message_monthly_active_user"]]; + } + else + { + [message appendString:[NSBundle mxk_localizedStringForKey:@"login_error_resource_limit_exceeded_message_default"]]; + } + + NSDictionary *messageContact2LinkAttributes; + if (adminContact && onAdminContactTapped) + { + void (^onAdminContactTappedLink)(void) = ^() { + onAdminContactTapped(adminContact); + }; + + objc_setAssociatedObject(self.messageTextView, "onAdminContactTappedLink", [onAdminContactTappedLink copy], OBJC_ASSOCIATION_RETAIN_NONATOMIC); + messageContact2LinkAttributes = @{ + NSFontAttributeName : [UIFont systemFontOfSize:fontSize], + NSUnderlineStyleAttributeName : @(NSUnderlineStyleSingle), + NSLinkAttributeName : @"onAdminContactTappedLink", + }; + } + + NSDictionary *attributes = @{ + NSFontAttributeName: [UIFont systemFontOfSize:fontSize], + NSForegroundColorAttributeName: kRiotPrimaryBgColor + }; + + NSAttributedString *messageContact1 = [[NSAttributedString alloc] initWithString:NSLocalizedStringFromTable(@"room_resource_limit_exceeded_message_contact_1", @"Vector", nil) attributes:attributes]; + NSAttributedString *messageContact2Link = [[NSAttributedString alloc] initWithString:NSLocalizedStringFromTable(@"room_resource_limit_exceeded_message_contact_2_link", @"Vector", nil) attributes:messageContact2LinkAttributes]; + NSAttributedString *messageContact3 = [[NSAttributedString alloc] initWithString:NSLocalizedStringFromTable(@"room_resource_limit_exceeded_message_contact_3", @"Vector", nil) attributes:attributes]; + + NSMutableAttributedString *attributedText = [[NSMutableAttributedString alloc] initWithString:message attributes:attributes]; + [attributedText appendAttributedString:messageContact1]; + [attributedText appendAttributedString:messageContact2Link]; + [attributedText appendAttributedString:messageContact3]; + + self.messageTextView.attributedText = attributedText; + self.messageTextView.tintColor = kRiotPrimaryBgColor; + self.messageTextView.hidden = NO; + + self.backgroundColor = kRiotColorPinkRed; + self.messageTextView.backgroundColor = kRiotColorPinkRed; + + // Hide the separator to display correctly the banner + self.separatorView.hidden = YES; + + [self checkHeight:YES]; +} + - (void)reset { self.separatorView.hidden = NO; @@ -476,6 +548,16 @@ return NO; } + else if ([[URL absoluteString] isEqualToString:@"onAdminContactTappedLink"]) + { + void (^onAdminContactTappedLink)(void) = objc_getAssociatedObject(self.messageTextView, "onAdminContactTappedLink"); + if (onAdminContactTappedLink) + { + onAdminContactTappedLink(); + } + + return NO; + } return YES; }