diff --git a/AUTHORS.rst b/AUTHORS.rst index b68079ea7..5253de51c 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -38,4 +38,8 @@ Evan Tang Joey Watts * PR #1777 Add support for interactive notifications + +Arash Tabrizian + * PR #1828 Fix issue #1793 Confirmation popup when leaving room + * PR #1824 Fix issue #1816 Support specifying kick and ban msgs \ No newline at end of file diff --git a/CHANGES.rst b/CHANGES.rst index 36f107dba..4271f0686 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,28 @@ +Changes in 0.6.13 (2018-04-20) +=============================================== + +Improvements: + * Upgrade MatrixKit version (v0.7.10). + * The minimal iOS version is now 9.0. + * Render stickers in the timeline (#1819). + * Support specifying kick and ban msgs (#1816), thanks to @atabrizian (PR #1824). + * Confirmation popup when leaving room (#1793), thanks to @atabrizian (PR #1828). + + +Bug fixes: + * Global Messages search: some search results are missing. + * Crash on URL like https://riot.im/#/app/register?hs_url=... (#1838). + +Changes in 0.6.13 (2018-03-30) +=============================================== + +Improvements: + * Upgrade MatrixKit version (v0.7.9). + * Make state event redaction handling gentler with homeserver (vector-im/riot-ios#1823). + +Bug fixes: + * Room summary is not updated after redaction of the room display name (vector-im/riot-ios#1822). + Changes in 0.6.12 (2018-03-12) =============================================== diff --git a/Podfile b/Podfile index 39c47abfc..143db2a15 100644 --- a/Podfile +++ b/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -platform :ios, "8.0" +platform :ios, "9.0" # Use frameforks to allow usage of pod written in Swift (like PiwikTracker) use_frameworks! @@ -9,7 +9,7 @@ source 'https://github.com/CocoaPods/Specs.git' # Different flavours of pods to MatrixKit # The current MatrixKit pod version -$matrixKitVersion = '0.7.8' +$matrixKitVersion = '0.7.9' # The develop branch version #$matrixKitVersion = 'develop' diff --git a/Podfile.lock b/Podfile.lock index 09f676b66..c2d2efea2 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -43,34 +43,34 @@ PODS: - GZIP (1.2.1) - HPGrowingTextView (1.1) - libPhoneNumber-iOS (0.9.13) - - MatrixKit (0.7.8): + - MatrixKit (0.7.9): - cmark (~> 0.24.1) - DTCoreText (~> 1.6.21) - HPGrowingTextView (~> 1.1) - libPhoneNumber-iOS (~> 0.9.13) - - MatrixKit/Core (= 0.7.8) - - MatrixSDK (= 0.10.6) - - MatrixKit/AppExtension (0.7.8): + - MatrixKit/Core (= 0.7.9) + - MatrixSDK (= 0.10.7) + - MatrixKit/AppExtension (0.7.9): - cmark (~> 0.24.1) - DTCoreText (~> 1.6.21) - DTCoreText/Extension - HPGrowingTextView (~> 1.1) - libPhoneNumber-iOS (~> 0.9.13) - - MatrixSDK (= 0.10.6) - - MatrixKit/Core (0.7.8): + - MatrixSDK (= 0.10.7) + - MatrixKit/Core (0.7.9): - cmark (~> 0.24.1) - DTCoreText (~> 1.6.21) - HPGrowingTextView (~> 1.1) - libPhoneNumber-iOS (~> 0.9.13) - - MatrixSDK (= 0.10.6) - - MatrixSDK (0.10.6): - - MatrixSDK/Core (= 0.10.6) - - MatrixSDK/Core (0.10.6): + - MatrixSDK (= 0.10.7) + - MatrixSDK (0.10.7): + - MatrixSDK/Core (= 0.10.7) + - MatrixSDK/Core (0.10.7): - AFNetworking (~> 3.2.0) - GZIP (~> 1.2.1) - OLMKit (~> 2.2.2) - Realm (~> 3.1.1) - - MatrixSDK/JingleCallStack (0.10.6): + - MatrixSDK/JingleCallStack (0.10.7): - MatrixSDK/Core - WebRTC (= 63.11.20455) - OLMKit (2.2.2): @@ -90,8 +90,8 @@ DEPENDENCIES: - cmark - DTCoreText - GBDeviceInfo (~> 5.1.0) - - MatrixKit (= 0.7.8) - - MatrixKit/AppExtension (= 0.7.8) + - MatrixKit (= 0.7.9) + - MatrixKit/AppExtension (= 0.7.9) - MatrixSDK/JingleCallStack - OLMKit - PiwikTracker (from `https://github.com/manuroe/matomo-sdk-ios.git`, branch `feature/CustomVariables`) @@ -115,13 +115,13 @@ SPEC CHECKSUMS: GZIP: 7ee835f989fb3c6ea79005fc90b8fa6af710a70d HPGrowingTextView: 88a716d97fb853bcb08a4a08e4727da17efc9b19 libPhoneNumber-iOS: e444379ac18bbfbdefad571da735b2cd7e096caa - MatrixKit: 4a44fc7c77b312d921604af249c891144b228d5c - MatrixSDK: 9d2edb30d020fb8478e03d5b69097b7ce273bc63 + MatrixKit: 1945e24b20e0ba0853798c347404cdf0efb780e3 + MatrixSDK: a08af4179df86082affa188b5b2099def9eb2843 OLMKit: b9d8c0ffee9ea8c45bc0aaa9afb47f93fba7efbd PiwikTracker: 42862c7b13028065c3dfd36b4dc38db8a5765acf Realm: 42d1c38a5b1bbcc828b48a7ce702cb86fc68adf4 WebRTC: f2a6203584745fe53532633397557876b5d71640 -PODFILE CHECKSUM: 4a8ff7d0d54b2e3369a8e4d32f2814e24404033f +PODFILE CHECKSUM: 7abb83aba898f3755b58b5859fd52321f7d97bb2 COCOAPODS: 1.4.0 diff --git a/Riot.xcodeproj/project.pbxproj b/Riot.xcodeproj/project.pbxproj index 0a5a4b186..c16ad9b65 100755 --- a/Riot.xcodeproj/project.pbxproj +++ b/Riot.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + 02543BAE206E6C59001CDAB2 /* RoomSelectedStickerBubbleCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 02543BAB206E6C58001CDAB2 /* RoomSelectedStickerBubbleCell.m */; }; + 02543BAF206E6C59001CDAB2 /* RoomSelectedStickerBubbleCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 02543BAC206E6C58001CDAB2 /* RoomSelectedStickerBubbleCell.xib */; }; 2435179C1F375B9400D0683E /* Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 2466B7551F2F80B800AE27B0 /* Info.plist */; }; 2435179F1F375C0F00D0683E /* Vector.strings in Resources */ = {isa = PBXBuildFile; fileRef = 327382C01F276AED00356143 /* Vector.strings */; }; 2439DD621F6BBE760090F42D /* RecentRoomTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 2439DD611F6BBE760090F42D /* RecentRoomTableViewCell.m */; }; @@ -656,6 +658,9 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 02543BAB206E6C58001CDAB2 /* RoomSelectedStickerBubbleCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RoomSelectedStickerBubbleCell.m; sourceTree = ""; }; + 02543BAC206E6C58001CDAB2 /* RoomSelectedStickerBubbleCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = RoomSelectedStickerBubbleCell.xib; sourceTree = ""; }; + 02543BAD206E6C59001CDAB2 /* RoomSelectedStickerBubbleCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RoomSelectedStickerBubbleCell.h; sourceTree = ""; }; 12AA0005C8B3D8D8162584C5 /* Pods-RiotShareExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RiotShareExtension.debug.xcconfig"; path = "Pods/Target Support Files/Pods-RiotShareExtension/Pods-RiotShareExtension.debug.xcconfig"; sourceTree = ""; }; 22D76C11C202B6BC5917A049 /* Pods-RiotPods-RiotShareExtension.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RiotPods-RiotShareExtension.release.xcconfig"; path = "Pods/Target Support Files/Pods-RiotPods-RiotShareExtension/Pods-RiotPods-RiotShareExtension.release.xcconfig"; sourceTree = ""; }; 23D7292481328A48B8D5D4ED /* Pods_RiotPods_RiotShareExtension.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RiotPods_RiotShareExtension.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -2428,6 +2433,9 @@ F083BCD51E7009EC00A9B29C /* RoomOutgoingTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.h */, F083BCD61E7009EC00A9B29C /* RoomOutgoingTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.m */, F083BCD71E7009EC00A9B29C /* RoomOutgoingTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.xib */, + 02543BAD206E6C59001CDAB2 /* RoomSelectedStickerBubbleCell.h */, + 02543BAB206E6C58001CDAB2 /* RoomSelectedStickerBubbleCell.m */, + 02543BAC206E6C58001CDAB2 /* RoomSelectedStickerBubbleCell.xib */, ); path = RoomBubbleList; sourceTree = ""; @@ -3217,6 +3225,7 @@ F083BD3A1E7009ED00A9B29C /* call_chat_icon@2x.png in Resources */, F083BE711E7009ED00A9B29C /* RoomOutgoingTextMsgBubbleCell.xib in Resources */, F083BD5E1E7009ED00A9B29C /* create_room@3x.png in Resources */, + 02543BAF206E6C59001CDAB2 /* RoomSelectedStickerBubbleCell.xib in Resources */, F083BE6B1E7009ED00A9B29C /* RoomOutgoingAttachmentBubbleCell.xib in Resources */, F083BD4C1E7009ED00A9B29C /* camera_capture@2x.png in Resources */, F083BD8F1E7009ED00A9B29C /* file_video_icon.png in Resources */, @@ -3635,6 +3644,7 @@ F083BE601E7009ED00A9B29C /* RoomIncomingTextMsgBubbleCell.m in Sources */, F083BE2F1E7009ED00A9B29C /* ContactTableViewCell.m in Sources */, F083BE901E7009ED00A9B29C /* RoomTitleView.m in Sources */, + 02543BAE206E6C59001CDAB2 /* RoomSelectedStickerBubbleCell.m in Sources */, F083BE6E1E7009ED00A9B29C /* RoomOutgoingAttachmentWithPaginationTitleBubbleCell.m in Sources */, 3233F7311F31F4BF006ACA81 /* JitsiViewController.m in Sources */, F083BE2A1E7009ED00A9B29C /* UsersDevicesViewController.m in Sources */, @@ -4009,7 +4019,7 @@ DEVELOPMENT_TEAM = 7J4U792NQT; ENABLE_BITCODE = NO; INFOPLIST_FILE = RiotShareExtension/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = im.vector.app.shareExtension; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -4032,7 +4042,7 @@ DEVELOPMENT_TEAM = 7J4U792NQT; ENABLE_BITCODE = NO; INFOPLIST_FILE = RiotShareExtension/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = im.vector.app.shareExtension; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -4193,7 +4203,7 @@ "\"$(PROJECT_DIR)/Riot/libs/jitsi-meet\"", ); INFOPLIST_FILE = Riot/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = im.vector.app; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -4227,7 +4237,7 @@ "\"$(PROJECT_DIR)/Riot/libs/jitsi-meet\"", ); INFOPLIST_FILE = Riot/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = im.vector.app; PRODUCT_NAME = "$(TARGET_NAME)"; diff --git a/Riot/AppDelegate.h b/Riot/AppDelegate.h index c50209be9..69ae2addd 100644 --- a/Riot/AppDelegate.h +++ b/Riot/AppDelegate.h @@ -101,7 +101,14 @@ extern NSString *const kAppDelegateNetworkStatusDidChangeNotification; // Reload all running matrix sessions - (void)reloadMatrixSessions:(BOOL)clearCache; -- (void)logout; +/** + Log out all the accounts after asking for a potential confirmation. + Show the authentication screen on successful logout. + + @param askConfirmation tell whether a confirmation is required before logging out. + @param completion the block to execute at the end of the operation. + */ +- (void)logoutWithConfirmation:(BOOL)askConfirmation completion:(void (^)(BOOL isLoggedOut))completion; #pragma mark - Matrix Accounts handling diff --git a/Riot/AppDelegate.m b/Riot/AppDelegate.m index 195b7a4d2..2d06f62cb 100644 --- a/Riot/AppDelegate.m +++ b/Riot/AppDelegate.m @@ -1,6 +1,7 @@ /* Copyright 2014 OpenMarket Ltd Copyright 2017 Vector Creations Ltd + Copyright 2018 New Vector Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -207,6 +208,8 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN @property (strong, nonatomic) UIAlertController *mxInAppNotification; +@property (strong, nonatomic) UIAlertController *logoutConfirmation; + @property (nonatomic, nullable, copy) void (^registrationForRemoteNotificationsCompletion)(NSError *); @@ -1587,6 +1590,15 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN else notificationBody = [NSString stringWithFormat:NSLocalizedString(@"USER_INVITE_TO_CHAT", nil), eventSenderName]; } + else if (event.eventType == MXEventTypeSticker) + { + NSString *roomDisplayName = room.summary.displayname; + + if (roomDisplayName.length && ![roomDisplayName isEqualToString:eventSenderName]) + notificationBody = [NSString stringWithFormat:NSLocalizedString(@"MSG_FROM_USER_IN_ROOM", nil), eventSenderName, roomDisplayName]; + else + notificationBody = [NSString stringWithFormat:NSLocalizedString(@"MSG_FROM_USER", nil), eventSenderName]; + } return notificationBody; } @@ -2074,6 +2086,7 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN }]; } } + // Check whether this is a registration links. else if ([pathParams[0] isEqualToString:@"register"]) { NSLog(@"[AppDelegate] Universal link with registration parameters"); @@ -2385,7 +2398,7 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN // Logout the app when there is no available account if (![MXKAccountManager sharedManager].accounts.count) { - [self logout]; + [self logoutWithConfirmation:NO completion:nil]; } }]; @@ -2557,8 +2570,90 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN } } -- (void)logout +- (void)logoutWithConfirmation:(BOOL)askConfirmation completion:(void (^)(BOOL isLoggedOut))completion { + // Check whether we have to ask confirmation before logging out. + if (askConfirmation) + { + if (self.logoutConfirmation) + { + [self.logoutConfirmation dismissViewControllerAnimated:NO completion:nil]; + self.logoutConfirmation = nil; + } + + __weak typeof(self) weakSelf = self; + + NSString *message = NSLocalizedStringFromTable(@"settings_sign_out_confirmation", @"Vector", nil); + + // If the user has encrypted rooms, warn he will lose his e2e keys + MXSession *session = self.mxSessions.firstObject; + for (MXRoom *room in session.rooms) + { + if (room.state.isEncrypted) + { + message = [message stringByAppendingString:[NSString stringWithFormat:@"\n\n%@", NSLocalizedStringFromTable(@"settings_sign_out_e2e_warn", @"Vector", nil)]]; + break; + } + } + + // Ask confirmation + self.logoutConfirmation = [UIAlertController alertControllerWithTitle:NSLocalizedStringFromTable(@"settings_sign_out", @"Vector", nil) message:message preferredStyle:UIAlertControllerStyleAlert]; + + [self.logoutConfirmation addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"settings_sign_out", @"Vector", nil) + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * action) { + + if (weakSelf) + { + typeof(self) self = weakSelf; + self.logoutConfirmation = nil; + + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.3 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ + + [self logoutWithConfirmation:NO completion:completion]; + + }); + } + + }]]; + + [self.logoutConfirmation addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"cancel"] + style:UIAlertActionStyleCancel + handler:^(UIAlertAction * action) { + + if (weakSelf) + { + typeof(self) self = weakSelf; + self.logoutConfirmation = nil; + + if (completion) + { + completion(NO); + } + } + + }]]; + + [self.logoutConfirmation mxk_setAccessibilityIdentifier: @"AppDelegateLogoutConfirmationAlert"]; + [self showNotificationAlert:self.logoutConfirmation]; + return; + } + + // Display a loading wheel during the logout process + id topVC; + if (_masterTabBarController && _masterTabBarController == _masterNavigationController.visibleViewController) + { + topVC = _masterTabBarController.selectedViewController; + } + else + { + topVC = _masterNavigationController.visibleViewController; + } + if (topVC && [topVC respondsToSelector:@selector(startActivityIndicator)]) + { + [topVC startActivityIndicator]; + } + self.pushRegistry = nil; isPushRegistered = NO; @@ -2577,15 +2672,22 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN #endif // Logout all matrix account - [[MXKAccountManager sharedManager] logout]; - - // Return to authentication screen - [_masterTabBarController showAuthenticationScreen]; - - // Note: Keep App settings - - // Reset the contact manager - [[MXKContactManager sharedManager] reset]; + [[MXKAccountManager sharedManager] logoutWithCompletion:^{ + + if (completion) + { + completion (YES); + } + + // Return to authentication screen + [_masterTabBarController showAuthenticationScreen]; + + // Note: Keep App settings + + // Reset the contact manager + [[MXKContactManager sharedManager] reset]; + + }]; } - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context @@ -2907,7 +3009,7 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN typeof(self) self = weakSelf; self->_errorNotification = nil; - [self logout]; + [self logoutWithConfirmation:NO completion:nil]; } }]]; diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index f8fe3a6a5..d17a8f452 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -256,6 +256,8 @@ "room_event_action_view_source" = "View Source"; "room_event_action_report" = "Report content"; "room_event_action_report_prompt_reason" = "Reason for reporting this content"; +"room_event_action_kick_prompt_reason" = "Reason for kicking this user"; +"room_event_action_ban_prompt_reason" = "Reason for banning this user"; "room_event_action_report_prompt_ignore_user" = "Do you want to hide all messages from this user?"; "room_event_action_save" = "Save"; "room_event_action_resend" = "Resend"; diff --git a/Riot/Categories/MXRoom+Riot.m b/Riot/Categories/MXRoom+Riot.m index d6e070c07..cdb616247 100644 --- a/Riot/Categories/MXRoom+Riot.m +++ b/Riot/Categories/MXRoom+Riot.m @@ -1,6 +1,7 @@ /* Copyright 2015 OpenMarket Ltd Copyright 2017 Vector Creations Ltd + Copyright 2018 New Vector Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -57,9 +58,12 @@ } failure:^(NSError *error) { NSLog(@"[MXRoom+Riot] Failed to update the tag %@ of room (%@)", tag, self.state.roomId); + NSString *userId = self.mxSession.myUser.userId; // Notify user - [[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification object:error]; + [[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification + object:error + userInfo:userId ? @{kMXKErrorUserIdKey: userId} : nil]; if (completion) { diff --git a/Riot/Info.plist b/Riot/Info.plist index 70f7e130d..ce962784d 100644 --- a/Riot/Info.plist +++ b/Riot/Info.plist @@ -17,11 +17,11 @@ CFBundlePackageType APPL CFBundleShortVersionString - 0.6.12 + 0.6.13 CFBundleSignature ???? CFBundleVersion - 0.6.12 + 0.6.13 ITSAppUsesNonExemptEncryption ITSEncryptionExportComplianceCode diff --git a/Riot/Model/Room/RoomDataSource.m b/Riot/Model/Room/RoomDataSource.m index a09745a8f..e6401e54d 100644 --- a/Riot/Model/Room/RoomDataSource.m +++ b/Riot/Model/Room/RoomDataSource.m @@ -391,6 +391,9 @@ } } } + + // Auto animate the sticker in case of animated gif + bubbleCell.isAutoAnimatedGif = (cellData.attachment && cellData.attachment.type == MXKAttachmentTypeSticker); } return cell; diff --git a/Riot/Model/RoomList/HomeMessagesSearchDataSource.m b/Riot/Model/RoomList/HomeMessagesSearchDataSource.m index b8dd97a71..0d2787c60 100644 --- a/Riot/Model/RoomList/HomeMessagesSearchDataSource.m +++ b/Riot/Model/RoomList/HomeMessagesSearchDataSource.m @@ -1,6 +1,7 @@ /* Copyright 2016 OpenMarket Ltd Copyright 2017 Vector Creations Ltd + Copyright 2018 New Vector Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -46,8 +47,8 @@ MXKRoomDataSource *roomDataSource; if (roomId) { - roomDataSource = [roomDataSourceManager roomDataSourceForRoom:roomId create:NO]; - + // Check whether the user knows this room to create the room data source if it doesn't exist. + roomDataSource = [roomDataSourceManager roomDataSourceForRoom:roomId create:([self.mxSession roomWithRoomId:roomId])]; if (roomDataSource) { // Prepare text font used to highlight the search pattern. diff --git a/Riot/Model/Search/FilesSearchCellData.m b/Riot/Model/Search/FilesSearchCellData.m index 3eff018a7..c95adeb96 100644 --- a/Riot/Model/Search/FilesSearchCellData.m +++ b/Riot/Model/Search/FilesSearchCellData.m @@ -1,6 +1,7 @@ /* Copyright 2016 OpenMarket Ltd Copyright 2017 Vector Creations Ltd + Copyright 2018 New Vector Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -39,7 +40,7 @@ // Check attachment if any if ([searchDataSource.eventFormatter isSupportedAttachment:event]) { - // Note: event.eventType is equal here to MXEventTypeRoomMessage + // Note: event.eventType may be equal here to MXEventTypeRoomMessage or MXEventTypeSticker attachment = [[MXKAttachment alloc] initWithEvent:event andMatrixSession:searchDataSource.mxSession]; } @@ -101,7 +102,7 @@ - (BOOL)isAttachmentWithThumbnail { - return (attachment && (attachment.type == MXKAttachmentTypeImage || attachment.type == MXKAttachmentTypeVideo)); + return (attachment && (attachment.type == MXKAttachmentTypeImage || attachment.type == MXKAttachmentTypeVideo || attachment.type == MXKAttachmentTypeSticker)); } - (UIImage*)attachmentIcon diff --git a/Riot/ViewController/MasterTabBarController.m b/Riot/ViewController/MasterTabBarController.m index 38f3ba6ad..6a1a6c667 100644 --- a/Riot/ViewController/MasterTabBarController.m +++ b/Riot/ViewController/MasterTabBarController.m @@ -372,13 +372,19 @@ } else { - NSLog(@"[MasterTabBarController] Universal link: Logout current sessions and open AuthViewController to complete the registration"); + NSLog(@"[MasterTabBarController] Universal link: Prompt to logout current sessions and open AuthViewController to complete the registration"); // Keep a ref on the params authViewControllerRegistrationParameters = parameters; - // And do a logout out. It will then display AuthViewController - [[AppDelegate theDelegate] logout]; + // Prompt to logout. It will then display AuthViewController if the user is logged out. + [[AppDelegate theDelegate] logoutWithConfirmation:YES completion:^(BOOL isLoggedOut) { + if (!isLoggedOut) + { + // Reset temporary params + authViewControllerRegistrationParameters = nil; + } + }]; } } diff --git a/Riot/ViewController/RecentsViewController.m b/Riot/ViewController/RecentsViewController.m index 60e64e640..24b8d97db 100644 --- a/Riot/ViewController/RecentsViewController.m +++ b/Riot/ViewController/RecentsViewController.m @@ -1,6 +1,7 @@ /* Copyright 2015 OpenMarket Ltd Copyright 2017 Vector Creations Ltd + Copyright 2018 New Vector Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -950,45 +951,92 @@ { if (editedRoomId) { - // Check whether the user didn't leave the room yet - MXRoom *room = [self.mainSession roomWithRoomId:editedRoomId]; - if (room) - { - [self startActivityIndicator]; - - // cancel pending uploads/downloads - // they are useless by now - [MXMediaManager cancelDownloadsInCacheFolder:room.state.roomId]; - - // TODO GFO cancel pending uploads related to this room - - NSLog(@"[RecentsViewController] Leave room (%@)", room.state.roomId); - - [room leave:^{ - - [self stopActivityIndicator]; - - // Force table refresh - [self cancelEditionMode:YES]; - - } failure:^(NSError *error) { - - NSLog(@"[RecentsViewController] Failed to leave room (%@)", room.state.roomId); - - // Notify MatrixKit user - [[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification object:error]; - - [self stopActivityIndicator]; - - // Leave editing mode - [self cancelEditionMode:isRefreshPending]; - }]; - } - else - { - // Leave editing mode - [self cancelEditionMode:isRefreshPending]; - } + NSString *currentRoomId = editedRoomId; + + __weak typeof(self) weakSelf = self; + + // confirm leave + NSString *promptMessage = NSLocalizedStringFromTable(@"room_participants_leave_prompt_msg", @"Vector", nil); + currentAlert = [UIAlertController alertControllerWithTitle:NSLocalizedStringFromTable(@"room_participants_leave_prompt_title", @"Vector", nil) + message:promptMessage + preferredStyle:UIAlertControllerStyleAlert]; + + [currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"cancel"] + style:UIAlertActionStyleCancel + handler:^(UIAlertAction * action) { + + if (weakSelf) + { + typeof(self) self = weakSelf; + self->currentAlert = nil; + } + + }]]; + + [currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"leave", @"Vector", nil) + style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { + + if (weakSelf) + { + typeof(self) self = weakSelf; + self->currentAlert = nil; + + // Check whether the user didn't leave the room yet + // TODO: Handle multi-account + MXRoom *room = [self.mainSession roomWithRoomId:currentRoomId]; + if (room) + { + [self startActivityIndicator]; + + // cancel pending uploads/downloads + // they are useless by now + [MXMediaManager cancelDownloadsInCacheFolder:room.state.roomId]; + + // TODO GFO cancel pending uploads related to this room + + NSLog(@"[RecentsViewController] Leave room (%@)", room.state.roomId); + + [room leave:^{ + + if (weakSelf) + { + typeof(self) self = weakSelf; + [self stopActivityIndicator]; + // Force table refresh + [self cancelEditionMode:YES]; + } + + } failure:^(NSError *error) { + + NSLog(@"[RecentsViewController] Failed to leave room"); + if (weakSelf) + { + typeof(self) self = weakSelf; + // Notify the end user + NSString *userId = room.mxSession.myUser.userId; + [[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification + object:error + userInfo:userId ? @{kMXKErrorUserIdKey: userId} : nil]; + + [self stopActivityIndicator]; + + // Leave editing mode + [self cancelEditionMode:isRefreshPending]; + } + + }]; + } + else + { + // Leave editing mode + [self cancelEditionMode:isRefreshPending]; + } + } + + }]]; + + [currentAlert mxk_setAccessibilityIdentifier:@"LeaveEditedRoomAlert"]; + [self presentViewController:currentAlert animated:YES completion:nil]; } } @@ -1023,7 +1071,10 @@ { if (editedRoomId) { + __weak typeof(self) weakSelf = self; + // Check whether the user didn't leave the room + // TODO: handle multi-account MXRoom *room = [self.mainSession roomWithRoomId:editedRoomId]; if (room) { @@ -1031,23 +1082,32 @@ [room setIsDirect:isDirect withUserId:nil success:^{ - [self stopActivityIndicator]; - - // Leave editing mode - [self cancelEditionMode:isRefreshPending]; - + if (weakSelf) + { + typeof(self) self = weakSelf; + [self stopActivityIndicator]; + // Leave editing mode + [self cancelEditionMode:isRefreshPending]; + } } failure:^(NSError *error) { - [self stopActivityIndicator]; - - NSLog(@"[RecentsViewController] Failed to update direct tag of the room (%@)", editedRoomId); - - // Notify MatrixKit user - [[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification object:error]; - - // Leave editing mode - [self cancelEditionMode:isRefreshPending]; + if (weakSelf) + { + typeof(self) self = weakSelf; + [self stopActivityIndicator]; + + NSLog(@"[RecentsViewController] Failed to update direct tag of the room (%@)", editedRoomId); + + // Notify the end user + NSString *userId = self.mainSession.myUser.userId; // TODO: handle multi-account + [[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification + object:error + userInfo:userId ? @{kMXKErrorUserIdKey: userId} : nil]; + + // Leave editing mode + [self cancelEditionMode:isRefreshPending]; + } }]; } diff --git a/Riot/ViewController/RoomMemberDetailsViewController.m b/Riot/ViewController/RoomMemberDetailsViewController.m index 0f61f22fa..d7b9ecf94 100644 --- a/Riot/ViewController/RoomMemberDetailsViewController.m +++ b/Riot/ViewController/RoomMemberDetailsViewController.m @@ -1,6 +1,7 @@ /* Copyright 2016 OpenMarket Ltd Copyright 2017 Vector Creations Ltd + Copyright 2018 New Vector Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -415,9 +416,7 @@ { // Restore the status bar typeof(self) self = weakSelf; - self->devicesArray = usersDevicesInfoMap.map[userId].allValues; - // Reload the full table to take into account a potential change on a device status. [super updateMemberInfo]; } @@ -425,9 +424,15 @@ } failure:^(NSError *error) { NSLog(@"[RoomMemberDetailsVC] Crypto failed to download device info for user: %@", userId); - - // Notify MatrixKit user - [[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification object:error]; + if (weakSelf) + { + // Restore the status bar + typeof(self) self = weakSelf; + // Notify the end user + NSString *myUserId = self.mainSession.myUser.userId; + [[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification object:error userInfo:myUserId ? @{kMXKErrorUserIdKey: myUserId} : nil]; + } + }]; } @@ -938,6 +943,130 @@ [self setPowerLevel:kRiotRoomAdminLevel promptUser:YES]; break; } + case MXKRoomMemberDetailsActionBan: + { + __weak typeof(self) weakSelf = self; + + // Ban + currentAlert = [UIAlertController alertControllerWithTitle:NSLocalizedStringFromTable(@"room_event_action_ban_prompt_reason", @"Vector", nil) + message:nil + preferredStyle:UIAlertControllerStyleAlert]; + + [currentAlert addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) { + textField.secureTextEntry = NO; + textField.placeholder = nil; + textField.keyboardType = UIKeyboardTypeDefault; + }]; + + [currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"cancel"] + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * action) { + + if (weakSelf) + { + typeof(self) self = weakSelf; + self->currentAlert = nil; + } + + }]]; + + [currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"ban", @"Vector", nil) + style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { + + if (weakSelf) + { + typeof(self) self = weakSelf; + self->currentAlert = nil; + + [self startActivityIndicator]; + + // kick user + UITextField *textField = [self->currentAlert textFields].firstObject; + [self.mxRoom banUser:self.mxRoomMember.userId reason:textField.text success:^{ + + __strong __typeof(weakSelf)self = weakSelf; + [self stopActivityIndicator]; + + } failure:^(NSError *error) { + + __strong __typeof(weakSelf)self = weakSelf; + [self stopActivityIndicator]; + + NSLog(@"[RoomMemberDetailVC] Ban user (%@) failed", self.mxRoomMember.userId); + //Alert user + [[AppDelegate theDelegate] showErrorAsAlert:error]; + + }]; + } + + }]]; + + [currentAlert mxk_setAccessibilityIdentifier:@"RoomMemberDetailsVCBanAlert"]; + [self presentViewController:currentAlert animated:YES completion:nil]; + break; + } + case MXKRoomMemberDetailsActionKick: + { + __weak typeof(self) weakSelf = self; + + // Kick + currentAlert = [UIAlertController alertControllerWithTitle:NSLocalizedStringFromTable(@"room_event_action_kick_prompt_reason", @"Vector", nil) + message:nil + preferredStyle:UIAlertControllerStyleAlert]; + + [currentAlert addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) { + textField.secureTextEntry = NO; + textField.placeholder = nil; + textField.keyboardType = UIKeyboardTypeDefault; + }]; + + [currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"cancel"] + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * action) { + + if (weakSelf) + { + typeof(self) self = weakSelf; + self->currentAlert = nil; + } + + }]]; + + [currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"remove", @"Vector", nil) + style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { + + if (weakSelf) + { + typeof(self) self = weakSelf; + self->currentAlert = nil; + + [self startActivityIndicator]; + + // kick user + UITextField *textField = [self->currentAlert textFields].firstObject; + [self.mxRoom kickUser:self.mxRoomMember.userId reason:textField.text success:^{ + + __strong __typeof(weakSelf)self = weakSelf; + [self stopActivityIndicator]; + + } failure:^(NSError *error) { + + __strong __typeof(weakSelf)self = weakSelf; + [self stopActivityIndicator]; + + NSLog(@"[RoomMemberDetailVC] Removing user (%@) failed", self.mxRoomMember.userId); + //Alert user + [[AppDelegate theDelegate] showErrorAsAlert:error]; + + }]; + } + + }]]; + + [currentAlert mxk_setAccessibilityIdentifier:@"RoomMemberDetailsVCKickAlert"]; + [self presentViewController:currentAlert animated:YES completion:nil]; + break; + } default: { [super onActionButtonPressed:sender]; diff --git a/Riot/ViewController/RoomViewController.m b/Riot/ViewController/RoomViewController.m index b434af949..8c1306325 100644 --- a/Riot/ViewController/RoomViewController.m +++ b/Riot/ViewController/RoomViewController.m @@ -1,6 +1,7 @@ /* Copyright 2014 OpenMarket Ltd Copyright 2017 Vector Creations Ltd + Copyright 2018 New Vector Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -97,6 +98,8 @@ #import "RoomMembershipExpandedBubbleCell.h" #import "RoomMembershipExpandedWithPaginationTitleBubbleCell.h" +#import "RoomSelectedStickerBubbleCell.h" + #import "MXKRoomBubbleTableViewCell+Riot.h" #import "AvatarGenerator.h" @@ -310,6 +313,8 @@ [self.bubblesTableView registerClass:RoomMembershipExpandedBubbleCell.class forCellReuseIdentifier:RoomMembershipExpandedBubbleCell.defaultReuseIdentifier]; [self.bubblesTableView registerClass:RoomMembershipExpandedWithPaginationTitleBubbleCell.class forCellReuseIdentifier:RoomMembershipExpandedWithPaginationTitleBubbleCell.defaultReuseIdentifier]; + [self.bubblesTableView registerClass:RoomSelectedStickerBubbleCell.class forCellReuseIdentifier:RoomSelectedStickerBubbleCell.defaultReuseIdentifier]; + // Prepare expanded header expandedHeader = [ExpandedRoomTitleView roomTitleView]; expandedHeader.delegate = self; @@ -1760,7 +1765,12 @@ { if (bubbleData.isAttachmentWithThumbnail) { - if (bubbleData.isPaginationFirstBubble) + // Check whether the provided celldata corresponds to a selected sticker + if (customizedRoomDataSource.selectedEventId && (bubbleData.attachment.type == MXKAttachmentTypeSticker) && [bubbleData.attachment.eventId isEqualToString:customizedRoomDataSource.selectedEventId]) + { + cellViewClass = RoomSelectedStickerBubbleCell.class; + } + else if (bubbleData.isPaginationFirstBubble) { cellViewClass = isEncryptedRoom ? RoomIncomingEncryptedAttachmentWithPaginationTitleBubbleCell.class : RoomIncomingAttachmentWithPaginationTitleBubbleCell.class; } @@ -1805,7 +1815,12 @@ // Handle here outgoing bubbles if (bubbleData.isAttachmentWithThumbnail) { - if (bubbleData.isPaginationFirstBubble) + // Check whether the provided celldata corresponds to a selected sticker + if (customizedRoomDataSource.selectedEventId && (bubbleData.attachment.type == MXKAttachmentTypeSticker) && [bubbleData.attachment.eventId isEqualToString:customizedRoomDataSource.selectedEventId]) + { + cellViewClass = RoomSelectedStickerBubbleCell.class; + } + else if (bubbleData.isPaginationFirstBubble) { cellViewClass = isEncryptedRoom ? RoomOutgoingEncryptedAttachmentWithPaginationTitleBubbleCell.class :RoomOutgoingAttachmentWithPaginationTitleBubbleCell.class; } @@ -1909,12 +1924,38 @@ [self showEditButtonAlertMenuForEvent:selectedEvent inCell:cell level:0]; } } - else if ([actionIdentifier isEqualToString:kMXKRoomBubbleCellTapOnAttachmentView] - && ((MXKRoomBubbleTableViewCell*)cell).bubbleData.attachment.eventSentState == MXEventSentStateFailed) + else if ([actionIdentifier isEqualToString:kMXKRoomBubbleCellTapOnAttachmentView]) { - // Shortcut: when clicking on an unsent media, show the action sheet to resend it - MXEvent *selectedEvent = [self.roomDataSource eventWithEventId:((MXKRoomBubbleTableViewCell*)cell).bubbleData.attachment.eventId]; - [self dataSource:dataSource didRecognizeAction:kMXKRoomBubbleCellRiotEditButtonPressed inCell:cell userInfo:@{kMXKRoomBubbleCellEventKey:selectedEvent}]; + if (((MXKRoomBubbleTableViewCell*)cell).bubbleData.attachment.eventSentState == MXEventSentStateFailed) + { + // Shortcut: when clicking on an unsent media, show the action sheet to resend it + MXEvent *selectedEvent = [self.roomDataSource eventWithEventId:((MXKRoomBubbleTableViewCell*)cell).bubbleData.attachment.eventId]; + [self dataSource:dataSource didRecognizeAction:kMXKRoomBubbleCellRiotEditButtonPressed inCell:cell userInfo:@{kMXKRoomBubbleCellEventKey:selectedEvent}]; + } + else if (((MXKRoomBubbleTableViewCell*)cell).bubbleData.attachment.type == MXKAttachmentTypeSticker) + { + // We don't open the attachments viewer when the user taps on a sticker. + // We consider this tap like a selection. + + // Check whether a selection already exist or not + if (customizedRoomDataSource.selectedEventId) + { + [self cancelEventSelection]; + } + else + { + // Highlight this event in displayed message + customizedRoomDataSource.selectedEventId = ((MXKRoomBubbleTableViewCell*)cell).bubbleData.attachment.eventId; + } + + // Force table refresh + [self dataSource:self.roomDataSource didCellChange:nil]; + } + else + { + // Keep default implementation + [super dataSource:dataSource didRecognizeAction:actionIdentifier inCell:cell userInfo:userInfo]; + } } else if ([actionIdentifier isEqualToString:kRoomEncryptedDataBubbleCellTapOnEncryptionIcon]) { @@ -2156,38 +2197,41 @@ }]]; } - [currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"room_event_action_copy", @"Vector", nil) - style:UIAlertActionStyleDefault - handler:^(UIAlertAction * action) { - - if (weakSelf) - { - typeof(self) self = weakSelf; + if (attachment.type != MXKAttachmentTypeSticker) + { + [currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"room_event_action_copy", @"Vector", nil) + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * action) { - [self cancelEventSelection]; + if (weakSelf) + { + typeof(self) self = weakSelf; + + [self cancelEventSelection]; + + [self startActivityIndicator]; + + [attachment copy:^{ + + __strong __typeof(weakSelf)self = weakSelf; + [self stopActivityIndicator]; + + } failure:^(NSError *error) { + + __strong __typeof(weakSelf)self = weakSelf; + [self stopActivityIndicator]; + + //Alert user + [[AppDelegate theDelegate] showErrorAsAlert:error]; + + }]; + + // Start animation in case of download during attachment preparing + [roomBubbleTableViewCell startProgressUI]; + } - [self startActivityIndicator]; - - [attachment copy:^{ - - __strong __typeof(weakSelf)self = weakSelf; - [self stopActivityIndicator]; - - } failure:^(NSError *error) { - - __strong __typeof(weakSelf)self = weakSelf; - [self stopActivityIndicator]; - - //Alert user - [[AppDelegate theDelegate] showErrorAsAlert:error]; - - }]; - - // Start animation in case of download during attachment preparing - [roomBubbleTableViewCell startProgressUI]; - } - - }]]; + }]]; + } // Check status of the selected event if (selectedEvent.sentState == MXEventSentStatePreparing || @@ -2234,7 +2278,7 @@ } } - if (level == 1) + if (level == 1 && (attachment.type != MXKAttachmentTypeSticker)) { [currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"room_event_action_share", @"Vector", nil) style:UIAlertActionStyleDefault diff --git a/Riot/ViewController/SettingsViewController.m b/Riot/ViewController/SettingsViewController.m index d196e052d..acb4c8126 100644 --- a/Riot/ViewController/SettingsViewController.m +++ b/Riot/ViewController/SettingsViewController.m @@ -1,6 +1,7 @@ /* Copyright 2015 OpenMarket Ltd Copyright 2017 Vector Creations Ltd + Copyright 2018 New Vector Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -707,7 +708,8 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(); [self stopActivityIndicator]; // Notify user - [[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification object:error]; + NSString *myUserId = self.mainSession.myUser.userId; // TODO: Hanlde multi-account + [[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification object:error userInfo:myUserId ? @{kMXKErrorUserIdKey: myUserId} : nil]; } } } @@ -817,8 +819,8 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(); { [self stopActivityIndicator]; - // Notify user - [[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification object:error]; + NSString *myUserId = self.mainSession.myUser.userId; // TODO: Hanlde multi-account + [[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification object:error userInfo:myUserId ? @{kMXKErrorUserIdKey: myUserId} : nil]; } } @@ -2446,8 +2448,8 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(); NSLog(@"[SettingsViewController] Unignore %@ failed", ignoredUserId); - // Notify user - [[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification object:error]; + NSString *myUserId = session.myUser.userId; + [[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification object:error userInfo:myUserId ? @{kMXKErrorUserIdKey: myUserId} : nil]; }]; } @@ -2570,63 +2572,24 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(); - (void)onSignout:(id)sender { - [currentAlert dismissViewControllerAnimated:NO completion:nil]; - - __weak typeof(self) weakSelf = self; - - NSString *message = NSLocalizedStringFromTable(@"settings_sign_out_confirmation", @"Vector", nil); - - // If the user has encrypted rooms, warn he will lose his e2e keys - MXSession* session = [[AppDelegate theDelegate].mxSessions objectAtIndex:0]; - for (MXRoom *room in session.rooms) - { - if (room.state.isEncrypted) + // Feedback: disable button and run activity indicator + UIButton *button = (UIButton*)sender; + button.enabled = NO; + [self startActivityIndicator]; + + __weak typeof(self) weakSelf = self; + + [[AppDelegate theDelegate] logoutWithConfirmation:YES completion:^(BOOL isLoggedOut) { + + if (!isLoggedOut && weakSelf) { - message = [message stringByAppendingString:[NSString stringWithFormat:@"\n\n%@", NSLocalizedStringFromTable(@"settings_sign_out_e2e_warn", @"Vector", nil)]]; - break; + typeof(self) self = weakSelf; + + // Enable the button and stop activity indicator + button.enabled = YES; + [self stopActivityIndicator]; } - } - - // Ask confirmation - currentAlert = [UIAlertController alertControllerWithTitle:NSLocalizedStringFromTable(@"settings_sign_out", @"Vector", nil) message:message preferredStyle:UIAlertControllerStyleAlert]; - - [currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"settings_sign_out", @"Vector", nil) - style:UIAlertActionStyleDefault - handler:^(UIAlertAction * action) { - - if (weakSelf) - { - typeof(self) self = weakSelf; - self->currentAlert = nil; - - // Feedback: disable button and run activity indicator - UIButton *button = (UIButton*)sender; - button.enabled = NO; - [self startActivityIndicator]; - - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.3 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ - - [[MXKAccountManager sharedManager] logout]; - - }); - } - - }]]; - - [currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"cancel"] - style:UIAlertActionStyleCancel - handler:^(UIAlertAction * action) { - - if (weakSelf) - { - typeof(self) self = weakSelf; - self->currentAlert = nil; - } - - }]]; - - [currentAlert mxk_setAccessibilityIdentifier: @"SettingsVCSignoutAlert"]; - [self presentViewController:currentAlert animated:YES completion:nil]; + }]; } - (void)onRemove3PID:(NSIndexPath*)path @@ -2724,8 +2687,8 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(); [self stopActivityIndicator]; - // Notify user - [[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification object:error]; + NSString *myUserId = self.mainSession.myUser.userId; // TODO: Hanlde multi-account + [[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification object:error userInfo:myUserId ? @{kMXKErrorUserIdKey: myUserId} : nil]; } }]; } @@ -2930,7 +2893,7 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(); dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.3 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ - [[MXKAccountManager sharedManager] logout]; + [[AppDelegate theDelegate] logoutWithConfirmation:NO completion:nil]; }); } @@ -3413,7 +3376,8 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(); } // Notify user - [[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification object:error]; + NSString *myUserId = session.myUser.userId; // TODO: Hanlde multi-account + [[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification object:error userInfo:myUserId ? @{kMXKErrorUserIdKey: myUserId} : nil]; }]; } @@ -3515,7 +3479,8 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(); } // Notify user - [[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification object:error]; + NSString *myUserId = session.myUser.userId; + [[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification object:error userInfo:myUserId ? @{kMXKErrorUserIdKey: myUserId} : nil]; }]; } diff --git a/Riot/Views/RoomBubbleList/RoomOutgoingAttachmentBubbleCell.m b/Riot/Views/RoomBubbleList/RoomOutgoingAttachmentBubbleCell.m index 0e46d607d..d5e7d5cfa 100644 --- a/Riot/Views/RoomBubbleList/RoomOutgoingAttachmentBubbleCell.m +++ b/Riot/Views/RoomBubbleList/RoomOutgoingAttachmentBubbleCell.m @@ -21,12 +21,6 @@ @implementation RoomOutgoingAttachmentBubbleCell -- (void)awakeFromNib -{ - [super awakeFromNib]; - self.readReceiptsAlignment = ReadReceiptAlignmentRight; -} - - (void)customizeTableViewCellRendering { [super customizeTableViewCellRendering]; diff --git a/Riot/Views/RoomBubbleList/RoomOutgoingAttachmentWithPaginationTitleBubbleCell.m b/Riot/Views/RoomBubbleList/RoomOutgoingAttachmentWithPaginationTitleBubbleCell.m index e25096a2d..9ec7644e1 100644 --- a/Riot/Views/RoomBubbleList/RoomOutgoingAttachmentWithPaginationTitleBubbleCell.m +++ b/Riot/Views/RoomBubbleList/RoomOutgoingAttachmentWithPaginationTitleBubbleCell.m @@ -21,12 +21,6 @@ @implementation RoomOutgoingAttachmentWithPaginationTitleBubbleCell -- (void)awakeFromNib -{ - [super awakeFromNib]; - self.readReceiptsAlignment = ReadReceiptAlignmentRight; -} - - (void)customizeTableViewCellRendering { [super customizeTableViewCellRendering]; diff --git a/Riot/Views/RoomBubbleList/RoomOutgoingAttachmentWithoutSenderInfoBubbleCell.m b/Riot/Views/RoomBubbleList/RoomOutgoingAttachmentWithoutSenderInfoBubbleCell.m index 1e3667b4a..a106baa47 100644 --- a/Riot/Views/RoomBubbleList/RoomOutgoingAttachmentWithoutSenderInfoBubbleCell.m +++ b/Riot/Views/RoomBubbleList/RoomOutgoingAttachmentWithoutSenderInfoBubbleCell.m @@ -21,13 +21,6 @@ @implementation RoomOutgoingAttachmentWithoutSenderInfoBubbleCell -- (void)awakeFromNib -{ - [super awakeFromNib]; - - self.readReceiptsAlignment = ReadReceiptAlignmentRight; -} - - (void)customizeTableViewCellRendering { [super customizeTableViewCellRendering]; diff --git a/Riot/Views/RoomBubbleList/RoomOutgoingTextMsgBubbleCell.m b/Riot/Views/RoomBubbleList/RoomOutgoingTextMsgBubbleCell.m index b623bf719..9446e5bb2 100644 --- a/Riot/Views/RoomBubbleList/RoomOutgoingTextMsgBubbleCell.m +++ b/Riot/Views/RoomBubbleList/RoomOutgoingTextMsgBubbleCell.m @@ -21,12 +21,6 @@ @implementation RoomOutgoingTextMsgBubbleCell -- (void)awakeFromNib -{ - [super awakeFromNib]; - self.readReceiptsAlignment = ReadReceiptAlignmentRight; -} - - (void)customizeTableViewCellRendering { [super customizeTableViewCellRendering]; diff --git a/Riot/Views/RoomBubbleList/RoomOutgoingTextMsgWithoutSenderInfoBubbleCell.m b/Riot/Views/RoomBubbleList/RoomOutgoingTextMsgWithoutSenderInfoBubbleCell.m index 9c00a8b82..1a3b829b1 100644 --- a/Riot/Views/RoomBubbleList/RoomOutgoingTextMsgWithoutSenderInfoBubbleCell.m +++ b/Riot/Views/RoomBubbleList/RoomOutgoingTextMsgWithoutSenderInfoBubbleCell.m @@ -21,13 +21,6 @@ @implementation RoomOutgoingTextMsgWithoutSenderInfoBubbleCell -- (void)awakeFromNib -{ - [super awakeFromNib]; - - self.readReceiptsAlignment = ReadReceiptAlignmentRight; -} - - (void)customizeTableViewCellRendering { [super customizeTableViewCellRendering]; diff --git a/Riot/Views/RoomBubbleList/RoomSelectedStickerBubbleCell.h b/Riot/Views/RoomBubbleList/RoomSelectedStickerBubbleCell.h new file mode 100644 index 000000000..689b39403 --- /dev/null +++ b/Riot/Views/RoomBubbleList/RoomSelectedStickerBubbleCell.h @@ -0,0 +1,33 @@ +/* + Copyright 2018 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "RoomIncomingEncryptedAttachmentWithPaginationTitleBubbleCell.h" + +/** + `RoomSelectedStickerBubbleCell` is used to display the current selected sticker if any + */ +@interface RoomSelectedStickerBubbleCell : RoomIncomingEncryptedAttachmentWithPaginationTitleBubbleCell + +@property (weak, nonatomic) IBOutlet UIView *descriptionContainerView; +@property (weak, nonatomic) IBOutlet UIView *arrowView; +@property (weak, nonatomic) IBOutlet UIView *descriptionView; +@property (weak, nonatomic) IBOutlet UILabel *descriptionLabel; + +@property (weak, nonatomic) IBOutlet NSLayoutConstraint *userNameLabelTopConstraint; +@property (weak, nonatomic) IBOutlet NSLayoutConstraint *attachViewLeadingConstraint; +@property (weak, nonatomic) IBOutlet NSLayoutConstraint *descriptionContainerViewBottomConstraint; + +@end diff --git a/Riot/Views/RoomBubbleList/RoomSelectedStickerBubbleCell.m b/Riot/Views/RoomBubbleList/RoomSelectedStickerBubbleCell.m new file mode 100644 index 000000000..6df642c36 --- /dev/null +++ b/Riot/Views/RoomBubbleList/RoomSelectedStickerBubbleCell.m @@ -0,0 +1,242 @@ +/* + Copyright 2018 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "RoomSelectedStickerBubbleCell.h" + +#import "RoomEncryptedDataBubbleCell.h" + +#import "RoomIncomingAttachmentBubbleCell.h" +#import "RoomIncomingAttachmentWithoutSenderInfoBubbleCell.h" +#import "RoomIncomingAttachmentWithPaginationTitleBubbleCell.h" +#import "RoomIncomingEncryptedAttachmentBubbleCell.h" +#import "RoomIncomingEncryptedAttachmentWithoutSenderInfoBubbleCell.h" + +#import "RiotDesignValues.h" + +@implementation RoomSelectedStickerBubbleCell + +- (void)awakeFromNib +{ + [super awakeFromNib]; + + // define arrow mask + CAShapeLayer *arrowMaskLayer = [[CAShapeLayer alloc] init]; + arrowMaskLayer.frame = self.arrowView.bounds; + CGSize viewSize = self.arrowView.frame.size; + UIBezierPath *path = [[UIBezierPath alloc] init]; + [path moveToPoint:CGPointMake(0, viewSize.height)]; // arrow left bottom point + [path addLineToPoint:CGPointMake(viewSize.width / 2, 0)]; // arrow head + [path addLineToPoint:CGPointMake(viewSize.width, viewSize.height)]; // arrow right bottom point + [path closePath]; // arrow top side + arrowMaskLayer.path = path.CGPath; + self.arrowView.layer.mask = arrowMaskLayer; + + self.arrowView.backgroundColor = kRiotSecondaryBgColor; + self.descriptionView.backgroundColor = kRiotSecondaryBgColor; + [self.descriptionView.layer setCornerRadius:10]; +} + +- (void)render:(MXKCellData *)cellData +{ + [self prepareRender:cellData]; + + if (bubbleData) + { + // Retrieve the component which stores the sticker (Only one component is handled by the bubble in case of sticker). + MXKRoomBubbleComponent *component = bubbleData.bubbleComponents.firstObject; + + // Handle the pagination and the sender information + // Look for the original cell class to extract the constraints value. + Class modelCellViewClass = nil; + if (bubbleData.shouldHideSenderInformation) + { + modelCellViewClass = bubbleData.isEncryptedRoom ? RoomIncomingEncryptedAttachmentWithoutSenderInfoBubbleCell.class : RoomIncomingAttachmentWithoutSenderInfoBubbleCell.class; + + self.paginationTitleView.hidden = YES; + self.pictureView.hidden = YES; + self.userNameLabel.hidden = YES; + self.userNameTapGestureMaskView.userInteractionEnabled = NO; + } + else + { + if (bubbleData.isPaginationFirstBubble) + { + modelCellViewClass = bubbleData.isEncryptedRoom ? RoomIncomingEncryptedAttachmentWithPaginationTitleBubbleCell.class : RoomIncomingAttachmentWithPaginationTitleBubbleCell.class; + + self.paginationTitleView.hidden = NO; + self.paginationLabel.text = [[bubbleData.eventFormatter dateStringFromDate:bubbleData.date withTime:NO] uppercaseString]; + } + else + { + modelCellViewClass = bubbleData.isEncryptedRoom ? RoomIncomingEncryptedAttachmentBubbleCell.class : RoomIncomingAttachmentBubbleCell.class; + + self.paginationTitleView.hidden = YES; + } + + // Hanlde sender avatar + self.pictureView.hidden = NO; + NSString *avatarThumbURL = nil; + if (bubbleData.senderAvatarUrl) + { + // Suppose this url is a matrix content uri, we use SDK to get the well adapted thumbnail from server + avatarThumbURL = [bubbleData.mxSession.matrixRestClient urlOfContentThumbnail:bubbleData.senderAvatarUrl toFitViewSize:self.pictureView.frame.size withMethod:MXThumbnailingMethodCrop]; + } + self.pictureView.enableInMemoryCache = YES; + [self.pictureView setImageURL:avatarThumbURL withType:nil andImageOrientation:UIImageOrientationUp previewImage: bubbleData.senderAvatarPlaceholder ? bubbleData.senderAvatarPlaceholder : self.picturePlaceholder]; + + // Display sender's name except if the name appears in the displayed text (see emote and membership events) + if (bubbleData.shouldHideSenderName == NO) + { + if (bubbleData.senderFlair) + { + [self renderSenderFlair]; + } + else + { + self.userNameLabel.text = bubbleData.senderDisplayName; + } + + self.userNameLabel.hidden = NO; + self.userNameTapGestureMaskView.userInteractionEnabled = YES; + } + else + { + self.userNameLabel.hidden = YES; + self.userNameTapGestureMaskView.userInteractionEnabled = NO; + } + } + + // Retrieve the suitable content size for the attachment thumbnail + CGSize contentSize = bubbleData.contentSize; + // Update image view frame in order to center loading wheel (if any) + CGRect frame = self.attachmentView.frame; + frame.size.width = contentSize.width; + frame.size.height = contentSize.height; + self.attachmentView.frame = frame; + // Retrieve the MIME type + NSString *mimetype = nil; + if (bubbleData.attachment.thumbnailInfo) + { + mimetype = bubbleData.attachment.thumbnailInfo[@"mimetype"]; + } + else if (bubbleData.attachment.contentInfo) + { + mimetype = bubbleData.attachment.contentInfo[@"mimetype"]; + } + + // Display the sticker + self.attachmentView.backgroundColor = [UIColor clearColor]; + self.attachmentView.mediaFolder = bubbleData.roomId; + self.attachmentView.enableInMemoryCache = YES; + [self.attachmentView setAttachmentThumb:bubbleData.attachment]; + + // Set the description + NSAttributedString *description = component.attributedTextMessage; + if (description.length) + { + self.descriptionContainerView.hidden = NO; + self.descriptionLabel.attributedText = description; + } + else + { + self.descriptionContainerView.hidden = YES; + } + + // Adjust Attachment width constant + self.attachViewWidthConstraint.constant = contentSize.width; + + // Handle the encryption view + if (bubbleData.isEncryptedRoom) + { + // Set the right device info icon + self.encryptionStatusView.hidden = NO; + self.encryptionStatusView.image = [RoomEncryptedDataBubbleCell encryptionIconForEvent:component.event andSession:bubbleData.mxSession]; + } + else + { + self.encryptionStatusView.hidden = YES; + } + + // Hide by default the info container + self.bubbleInfoContainer.hidden = YES; + + // Adjust the layout according to the original cell, the one used to display the sticker unselected. + if ([modelCellViewClass nib]) + { + MXKRoomBubbleTableViewCell* cell= (MXKRoomBubbleTableViewCell*)[[modelCellViewClass nib] instantiateWithOwner:nil options:nil].firstObject; + + if (cell.userNameLabel) + { + frame = cell.userNameLabel.frame; + self.userNameLabelTopConstraint.constant = frame.origin.y; + } + frame = cell.attachmentView.frame; + self.attachViewLeadingConstraint.constant = frame.origin.x; + self.attachViewTopConstraint.constant = cell.attachViewTopConstraint.constant; + self.attachViewBottomConstraint.constant = cell.attachViewBottomConstraint.constant; + self.bubbleInfoContainerTopConstraint.constant = cell.bubbleInfoContainerTopConstraint.constant; + } + } +} + ++ (CGFloat)heightForCellData:(MXKCellData*)cellData withMaximumWidth:(CGFloat)maxWidth +{ + // Sanity check: accept only object of MXKRoomBubbleCellData classes or sub-classes + NSParameterAssert([cellData isKindOfClass:[MXKRoomBubbleCellData class]]); + MXKRoomBubbleCellData *bubbleData = (MXKRoomBubbleCellData*)cellData; + + // Look for the original cell class to extract the constraints value. + Class modelCellViewClass = nil; + if (bubbleData.shouldHideSenderInformation) + { + modelCellViewClass = bubbleData.isEncryptedRoom ? RoomIncomingEncryptedAttachmentWithoutSenderInfoBubbleCell.class : RoomIncomingAttachmentWithoutSenderInfoBubbleCell.class; + } + else + { + if (bubbleData.isPaginationFirstBubble) + { + modelCellViewClass = bubbleData.isEncryptedRoom ? RoomIncomingEncryptedAttachmentWithPaginationTitleBubbleCell.class : RoomIncomingAttachmentWithPaginationTitleBubbleCell.class; + } + else + { + modelCellViewClass = bubbleData.isEncryptedRoom ? RoomIncomingEncryptedAttachmentBubbleCell.class : RoomIncomingAttachmentBubbleCell.class; + } + } + + CGFloat rowHeight = [modelCellViewClass heightForCellData:cellData withMaximumWidth:maxWidth]; + + // Finalize the cell height by adding the height of the description. + // Retrieve the component which stores the sticker (Only one component is handled by the bubble in case of sticker). + MXKRoomBubbleComponent *component = bubbleData.bubbleComponents.firstObject; + NSAttributedString *description = component.attributedTextMessage; + if (description.length) + { + RoomSelectedStickerBubbleCell* cell = (RoomSelectedStickerBubbleCell*)[self cellWithOriginalXib]; + CGRect frame = cell.frame; + frame.size.width = maxWidth; + frame.size.height = 300; + cell.frame = frame; + + cell.descriptionLabel.attributedText = description; + [cell layoutIfNeeded]; + + rowHeight += cell.descriptionContainerView.frame.size.height + cell.descriptionContainerViewBottomConstraint.constant; + } + + return rowHeight; +} + +@end diff --git a/Riot/Views/RoomBubbleList/RoomSelectedStickerBubbleCell.xib b/Riot/Views/RoomBubbleList/RoomSelectedStickerBubbleCell.xib new file mode 100644 index 000000000..3f1198ef3 --- /dev/null +++ b/Riot/Views/RoomBubbleList/RoomSelectedStickerBubbleCell.xib @@ -0,0 +1,209 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/RiotShareExtension/Info.plist b/RiotShareExtension/Info.plist index 6c3a51574..cceea2141 100644 --- a/RiotShareExtension/Info.plist +++ b/RiotShareExtension/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType XPC! CFBundleShortVersionString - 0.6.12 + 0.6.13 CFBundleVersion 1 NSExtension diff --git a/SiriIntents/Info.plist b/SiriIntents/Info.plist index 682251e2e..09a3e79a6 100644 --- a/SiriIntents/Info.plist +++ b/SiriIntents/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType XPC! CFBundleShortVersionString - 0.6.12 + 0.6.13 CFBundleVersion 1 NSExtension diff --git a/checkipa.sh b/checkipa.sh index 71e5f702c..30d18778b 100755 --- a/checkipa.sh +++ b/checkipa.sh @@ -30,7 +30,9 @@ rm -r "$tmp" if [ -z "$xcent" ] then echo "$1 has no archived-expanded-entitlements.xcent." - exit 2 + + # It seems that since Xcode 9.3, this file is no more present (https://forums.developer.apple.com/thread/99923) + #exit 2 fi # Check the aps-environment embedded in the binary.