diff --git a/CHANGES.rst b/CHANGES.rst index 3be965fab..fcffdee2c 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,12 @@ +Changes in 0.6.18 () +=============================================== + +Improvements: + * Upgrade MatrixKit version (). + * RoomVC: Add a re-request keys button on message unable to decrypt (#1879). + +Bug fix: + Changes in 0.6.17 (2018-06-01) =============================================== diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index 05f7ed41f..801e1e6d7 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -518,6 +518,8 @@ "event_formatter_widget_removed" = "%@ widget removed by %@"; "event_formatter_jitsi_widget_added" = "VoIP conference added by %@"; "event_formatter_jitsi_widget_removed" = "VoIP conference removed by %@"; +"event_formatter_rerequest_keys_part1_link" = "Re-request encryption keys"; +"event_formatter_rerequest_keys_part2" = " from your other devices."; // Others "or" = "or"; @@ -622,3 +624,7 @@ "deactivate_account_password_alert_title" = "Deactivate Account"; "deactivate_account_password_alert_message" = "To continue, please enter your password"; + +// Re-request confirmation dialog +"rerequest_keys_alert_title" = "Request Sent"; +"rerequest_keys_alert_message" = "Please check your other devices for key share requests"; diff --git a/Riot/Utils/EventFormatter.h b/Riot/Utils/EventFormatter.h index ba5d6a60c..d55c03257 100644 --- a/Riot/Utils/EventFormatter.h +++ b/Riot/Utils/EventFormatter.h @@ -16,6 +16,16 @@ #import +/** + Link string used in attributed strings to mark a keys re-request action. + */ +FOUNDATION_EXPORT NSString *const kEventFormatterOnReRequestKeysLinkAction; + +/** + Parameters separator in the link string. + */ +FOUNDATION_EXPORT NSString *const kEventFormatterOnReRequestKeysLinkActionSeparator; + /** `EventFormatter` class inherits from `MXKEventFormatter` to define Vector formatting */ diff --git a/Riot/Utils/EventFormatter.m b/Riot/Utils/EventFormatter.m index ef1732e49..09f92f344 100644 --- a/Riot/Utils/EventFormatter.m +++ b/Riot/Utils/EventFormatter.m @@ -21,6 +21,13 @@ #import "WidgetManager.h" +#import "MXDecryptionResult.h" + +#pragma mark - Constants definitions + +NSString *const kEventFormatterOnReRequestKeysLinkAction = @"kEventFormatterOnReRequestKeysLinkAction"; +NSString *const kEventFormatterOnReRequestKeysLinkActionSeparator = @"/"; + @interface EventFormatter () { /** @@ -107,7 +114,40 @@ } } - return [super attributedStringFromEvent:event withRoomState:roomState error:error]; + NSAttributedString *attributedString = [super attributedStringFromEvent:event withRoomState:roomState error:error]; + + if (event.sentState == MXEventSentStateSent + && [event.decryptionError.domain isEqualToString:MXDecryptingErrorDomain] + && event.decryptionError.code == MXDecryptingErrorUnknownInboundSessionIdCode) + { + // Append to the displayed error an attibuted string with a tappable link + // so that the user can try to fix the UTC + NSMutableAttributedString *attributedStringWithRerequestMessage = [attributedString mutableCopy]; + [attributedStringWithRerequestMessage appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n"]]; + + NSString *linkActionString = [NSString stringWithFormat:@"%@%@%@", kEventFormatterOnReRequestKeysLinkAction, + kEventFormatterOnReRequestKeysLinkActionSeparator, + event.eventId]; + + [attributedStringWithRerequestMessage appendAttributedString: + [[NSAttributedString alloc] initWithString:NSLocalizedStringFromTable(@"event_formatter_rerequest_keys_part1_link", @"Vector", nil) + attributes:@{ + NSLinkAttributeName: linkActionString, + NSForegroundColorAttributeName: self.sendingTextColor, + NSFontAttributeName: self.encryptedMessagesTextFont + }]]; + + [attributedStringWithRerequestMessage appendAttributedString: + [[NSAttributedString alloc] initWithString:NSLocalizedStringFromTable(@"event_formatter_rerequest_keys_part2", @"Vector", nil) + attributes:@{ + NSForegroundColorAttributeName: self.sendingTextColor, + NSFontAttributeName: self.encryptedMessagesTextFont + }]]; + + attributedString = attributedStringWithRerequestMessage; + } + + return attributedString; } - (NSAttributedString*)attributedStringFromEvents:(NSArray*)events withRoomState:(MXRoomState*)roomState error:(MXKEventFormatterError*)error diff --git a/Riot/ViewController/RoomViewController.m b/Riot/ViewController/RoomViewController.m index 093f89e17..325e63ff1 100644 --- a/Riot/ViewController/RoomViewController.m +++ b/Riot/ViewController/RoomViewController.m @@ -117,6 +117,8 @@ #import "WidgetPickerViewController.h" #import "StickerPickerViewController.h" +#import "EventFormatter.h" + @interface RoomViewController () { // The expanded header @@ -181,6 +183,9 @@ // Observer kMXRoomSummaryDidChangeNotification to keep updated the missed discussion count id mxRoomSummaryDidChangeObserver; + + // Observer for removing the re-request explanation/waiting dialog + id mxEventDidDecryptNotificationObserver; // The table view cell in which the read marker is displayed (nil by default). MXKRoomBubbleTableViewCell *readMarkerTableViewCell; @@ -592,6 +597,12 @@ [[NSNotificationCenter defaultCenter] removeObserver:mxRoomSummaryDidChangeObserver]; mxRoomSummaryDidChangeObserver = nil; } + + if (mxEventDidDecryptNotificationObserver) + { + [[NSNotificationCenter defaultCenter] removeObserver:mxEventDidDecryptNotificationObserver]; + mxEventDidDecryptNotificationObserver = nil; + } } - (void)viewDidLayoutSubviews @@ -1103,6 +1114,11 @@ [[NSNotificationCenter defaultCenter] removeObserver:mxRoomSummaryDidChangeObserver]; mxRoomSummaryDidChangeObserver = nil; } + if (mxEventDidDecryptNotificationObserver) + { + [[NSNotificationCenter defaultCenter] removeObserver:mxEventDidDecryptNotificationObserver]; + mxEventDidDecryptNotificationObserver = nil; + } [self removeCallNotificationsListeners]; [self removeWidgetNotificationsListeners]; @@ -2680,6 +2696,20 @@ NSString *fragment = [NSString stringWithFormat:@"/group/%@", [absoluteURLString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; [[AppDelegate theDelegate] handleUniversalLinkFragment:fragment]; } + else if ([absoluteURLString hasPrefix:kEventFormatterOnReRequestKeysLinkAction]) + { + NSArray *arguments = [absoluteURLString componentsSeparatedByString:kEventFormatterOnReRequestKeysLinkActionSeparator]; + if (arguments.count > 1) + { + NSString *eventId = arguments[1]; + MXEvent *event = [self.roomDataSource eventWithEventId:eventId]; + + if (event) + { + [self reRequestKeysAndShowExplanationAlert:event]; + } + } + } } return shouldDoAction; @@ -4582,5 +4612,57 @@ [self presentViewController:currentAlert animated:YES completion:nil]; } +#pragma mark - Re-request encryption keys + +- (void)reRequestKeysAndShowExplanationAlert:(MXEvent*)event +{ + MXWeakify(self); + __block UIAlertController *alert; + + // Make the re-request + [self.mainSession.crypto reRequestRoomKeyForEvent:event]; + + // Observe kMXEventDidDecryptNotification to remove automatically the dialog + // if the user has shared the keys from another device + mxEventDidDecryptNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kMXEventDidDecryptNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { + MXStrongifyAndReturnIfNil(self); + + MXEvent *decryptedEvent = notif.object; + + if ([decryptedEvent.eventId isEqualToString:event.eventId]) + { + [[NSNotificationCenter defaultCenter] removeObserver:self->mxEventDidDecryptNotificationObserver]; + self->mxEventDidDecryptNotificationObserver = nil; + + if (self->currentAlert == alert) + { + [self->currentAlert dismissViewControllerAnimated:YES completion:nil]; + self->currentAlert = nil; + } + } + }]; + + // Show the explanation dialog + alert = [UIAlertController alertControllerWithTitle:NSLocalizedStringFromTable(@"rerequest_keys_alert_title", @"Vector", nil) + message:NSLocalizedStringFromTable(@"rerequest_keys_alert_message", @"Vector", nil) + preferredStyle:UIAlertControllerStyleAlert]; + currentAlert = alert; + + + [alert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"ok"] + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * action) + { + MXStrongifyAndReturnIfNil(self); + + [[NSNotificationCenter defaultCenter] removeObserver:self->mxEventDidDecryptNotificationObserver]; + self->mxEventDidDecryptNotificationObserver = nil; + + self->currentAlert = nil; + }]]; + + [self presentViewController:currentAlert animated:YES completion:nil]; +} + @end