diff --git a/.github/workflows/triage-move-labelled.yml b/.github/workflows/triage-move-labelled.yml index 440fc42cd..5db597239 100644 --- a/.github/workflows/triage-move-labelled.yml +++ b/.github/workflows/triage-move-labelled.yml @@ -12,7 +12,7 @@ jobs: - uses: konradpabjan/move-labeled-or-milestoned-issue@219d384e03fa4b6460cd24f9f37d19eb033a4338 with: action-token: "${{ secrets.ELEMENT_BOT_TOKEN }}" - project-url: "https://github.com/vector-im/element-android/projects/4" + project-url: "https://github.com/vector-im/element-ios/projects/12" column-name: "Need info" label-name: "X-Needs-Info" diff --git a/Riot/Managers/PushNotification/PushNotificationService.m b/Riot/Managers/PushNotification/PushNotificationService.m index 30cb7da3a..59fb9604a 100644 --- a/Riot/Managers/PushNotification/PushNotificationService.m +++ b/Riot/Managers/PushNotification/PushNotificationService.m @@ -264,6 +264,15 @@ Matrix session observer used to detect new opened sessions. - (void)configurePushKit { MXLogDebug(@"[PushNotificationService] configurePushKit") + NSData* token = [_pushRegistry pushTokenForType:PKPushTypeVoIP]; + if (token) { + // If the token is available, store it. This can happen if you sign out and back in. + // i.e We are registered, but we have cleared it from the the store on logout and the + // _pushRegistry lives through signin/signout as PushNotificationService is a singleton + // on app delegate. + _pushNotificationStore.pushKitToken = token; + MXLogDebug(@"[PushNotificationService] configurePushKit: Restored pushKit token") + } _pushRegistry.delegate = self; _pushRegistry.desiredPushTypes = [NSSet setWithObject:PKPushTypeVoIP]; diff --git a/Riot/Model/Room/RoomPreviewData.m b/Riot/Model/Room/RoomPreviewData.m index 07ece4510..f619344ed 100644 --- a/Riot/Model/Room/RoomPreviewData.m +++ b/Riot/Model/Room/RoomPreviewData.m @@ -16,7 +16,8 @@ */ #import "RoomPreviewData.h" -#import + +#import "GeneratedInterface-Swift.h" @implementation RoomPreviewData diff --git a/Riot/Modules/Application/LegacyAppDelegate.m b/Riot/Modules/Application/LegacyAppDelegate.m index b7130dafd..180cfe27a 100644 --- a/Riot/Modules/Application/LegacyAppDelegate.m +++ b/Riot/Modules/Application/LegacyAppDelegate.m @@ -2497,17 +2497,17 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni - (void)checkLocalPrivateKeysInSession:(MXSession*)mxSession { - id cryptoStore = mxSession.crypto.store; + MXRecoveryService *recoveryService = mxSession.crypto.recoveryService; NSUInteger keysCount = 0; - if ([cryptoStore secretWithSecretId:MXSecretId.keyBackup]) + if ([recoveryService hasSecretWithSecretId:MXSecretId.keyBackup]) { keysCount++; } - if ([cryptoStore secretWithSecretId:MXSecretId.crossSigningUserSigning]) + if ([recoveryService hasSecretWithSecretId:MXSecretId.crossSigningUserSigning]) { keysCount++; } - if ([cryptoStore secretWithSecretId:MXSecretId.crossSigningSelfSigning]) + if ([recoveryService hasSecretWithSecretId:MXSecretId.crossSigningSelfSigning]) { keysCount++; } diff --git a/Riot/Modules/Authentication/Views/AuthInputsView.h b/Riot/Modules/Authentication/Views/AuthInputsView.h index 8b50ed935..c0090f517 100644 --- a/Riot/Modules/Authentication/Views/AuthInputsView.h +++ b/Riot/Modules/Authentication/Views/AuthInputsView.h @@ -17,8 +17,9 @@ #import "MatrixKit.h" -@interface AuthInputsView : MXKAuthInputsView +@class TermsView; +@interface AuthInputsView : MXKAuthInputsView @property (weak, nonatomic) IBOutlet UITextField *userLoginTextField; @property (weak, nonatomic) IBOutlet UITextField *passWordTextField; @property (weak, nonatomic) IBOutlet UITextField *repeatPasswordTextField; diff --git a/Riot/Modules/Common/Recents/Service/MatrixSDK/RecentsListService.swift b/Riot/Modules/Common/Recents/Service/MatrixSDK/RecentsListService.swift index 6651fddba..32d06807f 100644 --- a/Riot/Modules/Common/Recents/Service/MatrixSDK/RecentsListService.swift +++ b/Riot/Modules/Common/Recents/Service/MatrixSDK/RecentsListService.swift @@ -430,15 +430,18 @@ public class RecentsListService: NSObject, RecentsListServiceProtocol { } private func updateDirectFetcher(_ fetcher: MXRoomListDataFetcher, for mode: RecentsDataSourceMode) { - switch mode { - case .home: - fetcher.fetchOptions.filterOptions.notDataTypes = [.invited, .favorited, .lowPriority] - case .people: - fetcher.fetchOptions.filterOptions.notDataTypes = [.lowPriority] - default: - break + var notDataTypes: MXRoomSummaryDataTypes = [.hidden, .conferenceUser, .space] + switch mode { + case .home: + notDataTypes.insert([.invited, .favorited, .lowPriority]) + fetcher.fetchOptions.filterOptions.notDataTypes = notDataTypes + case .people: + notDataTypes.insert([.lowPriority]) + fetcher.fetchOptions.filterOptions.notDataTypes = notDataTypes + default: + break + } } - } private func updateFavoritedFetcher(_ fetcher: MXRoomListDataFetcher, for mode: RecentsDataSourceMode) { switch mode { diff --git a/Riot/Modules/Communities/Home/GroupHomeViewController.m b/Riot/Modules/Communities/Home/GroupHomeViewController.m index 42825da70..018c15eb9 100644 --- a/Riot/Modules/Communities/Home/GroupHomeViewController.m +++ b/Riot/Modules/Communities/Home/GroupHomeViewController.m @@ -40,11 +40,14 @@ // The options used to load long description html content. NSDictionary *options; - NSString *sanitisedGroupLongDescription; + NSString *groupLongDescription; // The current pushed view controller UIViewController *pushedViewController; } + +@property (nonatomic, readonly) DTHTMLAttributedStringBuilderWillFlushCallback longDescriptionSanitizationCallback; + @end @implementation GroupHomeViewController @@ -75,6 +78,23 @@ // Keep visible the status bar by default. isStatusBarHidden = NO; + + // Set up sanitization for the long description + NSArray *allowedHTMLTags = @[ + @"font", // custom to matrix for IRC-style font coloring + @"del", // for markdown + @"body", // added internally by DTCoreText + @"h1", @"h2", @"h3", @"h4", @"h5", @"h6", @"blockquote", @"p", @"a", @"ul", @"ol", + @"nl", @"li", @"b", @"i", @"u", @"strong", @"em", @"strike", @"code", @"hr", @"br", @"div", + @"table", @"thead", @"caption", @"tbody", @"tr", @"th", @"td", @"pre", + @"img" + ]; + + MXWeakify(self); + _longDescriptionSanitizationCallback = ^(DTHTMLElement *element) { + MXStrongifyAndReturnIfNil(self); + [element sanitizeWith:allowedHTMLTags bodyFont:self->_groupLongDescription.font imageHandler:[self groupLongDescriptionImageHandler]]; + }; } - (void)viewDidLoad @@ -154,7 +174,7 @@ font-size: small; \ }", (unsigned long)bgColor]; - // Apply the css style + // Apply the css style with some sanitisation. options = @{ DTUseiOS6Attributes: @(YES), // Enable it to be able to display the attributed string in a UITextView DTDefaultFontFamily: _groupLongDescription.font.familyName, @@ -162,7 +182,8 @@ DTDefaultFontSize: @(_groupLongDescription.font.pointSize), DTDefaultTextColor: _groupLongDescription.textColor, DTDefaultLinkDecoration: @(NO), - DTDefaultStyleSheet: [[DTCSSStylesheet alloc] initWithStyleBlock:defaultCSS] + DTDefaultStyleSheet: [[DTCSSStylesheet alloc] initWithStyleBlock:defaultCSS], + DTWillFlushBlockCallBack: self.longDescriptionSanitizationCallback }; } @@ -489,94 +510,11 @@ { if (_group.summary.profile.longDescription.length) { - // Render this html content in a text view. - NSArray * allowedHTMLTags = @[ - @"font", // custom to matrix for IRC-style font coloring - @"del", // for markdown - @"h1", @"h2", @"h3", @"h4", @"h5", @"h6", @"blockquote", @"p", @"a", @"ul", @"ol", - @"nl", @"li", @"b", @"i", @"u", @"strong", @"em", @"strike", @"code", @"hr", @"br", @"div", - @"table", @"thead", @"caption", @"tbody", @"tr", @"th", @"td", @"pre", - @"img" - ]; - - // Do some sanitisation by handling the potential image - MXWeakify(self); - sanitisedGroupLongDescription = [MXKTools sanitiseHTML:_group.summary.profile.longDescription withAllowedHTMLTags:allowedHTMLTags imageHandler:^NSString *(NSString *sourceURL, CGFloat width, CGFloat height) { - - MXStrongifyAndReturnValueIfNil(self, nil); - NSString *localSourcePath; - - if (width != -1 && height != -1) - { - CGSize size = CGSizeMake(width, height); - // Build the cache path for the a thumbnail of this image. - NSString *cacheFilePath = [MXMediaManager thumbnailCachePathForMatrixContentURI:sourceURL - andType:nil - inFolder:kMXMediaManagerDefaultCacheFolder - toFitViewSize:size - withMethod:MXThumbnailingMethodScale]; - // Check whether the provided URL is a valid Matrix Content URI. - if (cacheFilePath) - { - // Download the thumbnail if it is not already stored in the cache. - if (![[NSFileManager defaultManager] fileExistsAtPath:cacheFilePath]) - { - MXWeakify(self); - [self.mxSession.mediaManager downloadThumbnailFromMatrixContentURI:sourceURL - withType:nil - inFolder:kMXMediaManagerDefaultCacheFolder - toFitViewSize:size - withMethod:MXThumbnailingMethodScale - success:^(NSString *outputFilePath) { - MXStrongifyAndReturnIfNil(self); - [self refreshGroupLongDescription]; - } - failure:nil]; - } - else - { - // Update the local path - localSourcePath = [NSString stringWithFormat:@"file://%@", cacheFilePath]; - } - } - } - else - { - // Build the cache path for this image. - NSString* cacheFilePath = [MXMediaManager cachePathForMatrixContentURI:sourceURL - andType:nil - inFolder:kMXMediaManagerDefaultCacheFolder]; - - // Check whether the provided URL is a valid Matrix Content URI. - if (cacheFilePath) - { - // Download the image if it is not already stored in the cache. - if (![[NSFileManager defaultManager] fileExistsAtPath:cacheFilePath]) - { - MXWeakify(self); - [self.mxSession.mediaManager downloadMediaFromMatrixContentURI:sourceURL - withType:nil - inFolder:kMXMediaManagerDefaultCacheFolder - success:^(NSString *outputFilePath) { - MXStrongifyAndReturnIfNil(self); - [self refreshGroupLongDescription]; - } - failure:nil]; - } - else - { - // Update the local path - localSourcePath = [NSString stringWithFormat:@"file://%@", cacheFilePath]; - } - } - } - return localSourcePath; - - }]; + groupLongDescription = _group.summary.profile.longDescription; } else { - sanitisedGroupLongDescription = nil; + groupLongDescription = nil; } [self renderGroupLongDescription]; @@ -584,12 +522,13 @@ - (void)renderGroupLongDescription { - if (sanitisedGroupLongDescription) + if (groupLongDescription) { // Using DTCoreText, which renders static string, helps to avoid code injection attacks // that could happen with the default HTML renderer of NSAttributedString which is a // webview. - NSAttributedString *attributedString = [[NSAttributedString alloc] initWithHTMLData:[sanitisedGroupLongDescription dataUsingEncoding:NSUTF8StringEncoding] options:options documentAttributes:NULL]; + // The supplied options include a callback to sanitize html tags and load image data. + NSAttributedString *attributedString = [[NSAttributedString alloc] initWithHTMLData:[groupLongDescription dataUsingEncoding:NSUTF8StringEncoding] options:options documentAttributes:NULL]; // Apply additional treatments NSInteger mxIdsBitMask = (MXKTOOLS_USER_IDENTIFIER_BITWISE | MXKTOOLS_ROOM_IDENTIFIER_BITWISE | MXKTOOLS_ROOM_ALIAS_BITWISE | MXKTOOLS_EVENT_IDENTIFIER_BITWISE | MXKTOOLS_GROUP_IDENTIFIER_BITWISE); @@ -605,6 +544,83 @@ } } +- (NSURL *(^)(NSString *sourceURL, CGFloat width, CGFloat height))groupLongDescriptionImageHandler +{ + MXWeakify(self); + return ^NSURL *(NSString *sourceURL, CGFloat width, CGFloat height) { + + MXStrongifyAndReturnValueIfNil(self, nil); + NSURL *localSourceURL; + + if (width != -1 && height != -1) + { + CGSize size = CGSizeMake(width, height); + // Build the cache path for the a thumbnail of this image. + NSString *cacheFilePath = [MXMediaManager thumbnailCachePathForMatrixContentURI:sourceURL + andType:nil + inFolder:kMXMediaManagerDefaultCacheFolder + toFitViewSize:size + withMethod:MXThumbnailingMethodScale]; + // Check whether the provided URL is a valid Matrix Content URI. + if (cacheFilePath) + { + // Download the thumbnail if it is not already stored in the cache. + if (![[NSFileManager defaultManager] fileExistsAtPath:cacheFilePath]) + { + MXWeakify(self); + [self.mxSession.mediaManager downloadThumbnailFromMatrixContentURI:sourceURL + withType:nil + inFolder:kMXMediaManagerDefaultCacheFolder + toFitViewSize:size + withMethod:MXThumbnailingMethodScale + success:^(NSString *outputFilePath) { + MXStrongifyAndReturnIfNil(self); + [self refreshGroupLongDescription]; + } + failure:nil]; + } + else + { + // Update the local url + localSourceURL = [NSURL fileURLWithPath:cacheFilePath]; + } + } + } + else + { + // Build the cache path for this image. + NSString* cacheFilePath = [MXMediaManager cachePathForMatrixContentURI:sourceURL + andType:nil + inFolder:kMXMediaManagerDefaultCacheFolder]; + + // Check whether the provided URL is a valid Matrix Content URI. + if (cacheFilePath) + { + // Download the image if it is not already stored in the cache. + if (![[NSFileManager defaultManager] fileExistsAtPath:cacheFilePath]) + { + MXWeakify(self); + [self.mxSession.mediaManager downloadMediaFromMatrixContentURI:sourceURL + withType:nil + inFolder:kMXMediaManagerDefaultCacheFolder + success:^(NSString *outputFilePath) { + MXStrongifyAndReturnIfNil(self); + [self refreshGroupLongDescription]; + } + failure:nil]; + } + else + { + // Update the local path + localSourceURL = [NSURL fileURLWithPath:cacheFilePath]; + } + } + } + return localSourceURL; + + }; +} + - (void)didSelectRoomId:(NSString*)roomId { // Check first if the user already joined this room. diff --git a/Riot/Modules/Settings/Security/SecurityViewController.m b/Riot/Modules/Settings/Security/SecurityViewController.m index 7b5fd7d5f..5c6d93aab 100644 --- a/Riot/Modules/Settings/Security/SecurityViewController.m +++ b/Riot/Modules/Settings/Security/SecurityViewController.m @@ -167,9 +167,8 @@ TableViewSectionsDelegate> if (self.mainSession.crypto.backup) { - MXDeviceInfo *deviceInfo = [self.mainSession.crypto.deviceList storedDevice:self.mainSession.matrixRestClient.credentials.userId - deviceId:self.mainSession.matrixRestClient.credentials.deviceId]; - + MXDeviceInfo *deviceInfo = [self.mainSession.crypto deviceWithDeviceId:self.mainSession.myDeviceId ofUser:self.mainSession.myUserId]; + if (deviceInfo) { secureBackupSection = [[SettingsSecureBackupTableViewSection alloc] initWithRecoveryService:self.mainSession.crypto.recoveryService keyBackup:self.mainSession.crypto.backup userDevice:deviceInfo]; diff --git a/Riot/Modules/TabBar/MasterTabBarController.m b/Riot/Modules/TabBar/MasterTabBarController.m index f1afa5821..4a4751d83 100644 --- a/Riot/Modules/TabBar/MasterTabBarController.m +++ b/Riot/Modules/TabBar/MasterTabBarController.m @@ -1021,7 +1021,7 @@ return; } - NSArray *devices = [session.crypto.store devicesForUser:session.myUserId].allValues; + NSArray *devices = [session.crypto devicesForUser:session.myUserId].allValues; BOOL isUserHasOneUnverifiedDevice = NO; diff --git a/Riot/Utils/EventFormatter.m b/Riot/Utils/EventFormatter.m index c7a11f1d0..d97386aaa 100644 --- a/Riot/Utils/EventFormatter.m +++ b/Riot/Utils/EventFormatter.m @@ -342,6 +342,9 @@ static NSString *const kEventFormatterTimeFormat = @"HH:mm"; white-space: pre; \ -coretext-fontname: Menlo-Regular; \ font-size: small; \ + } \ + h1,h2 { \ + font-size: 1.2em; \ }", (unsigned long)bgColor]; self.defaultTextColor = ThemeService.shared.theme.textPrimaryColor; diff --git a/RiotNSE/NotificationService.swift b/RiotNSE/NotificationService.swift index 26617c52e..9464453ca 100644 --- a/RiotNSE/NotificationService.swift +++ b/RiotNSE/NotificationService.swift @@ -65,7 +65,7 @@ class NotificationService: UNNotificationServiceExtension { }() private var pushNotificationStore: PushNotificationStore = PushNotificationStore() private let localAuthenticationService = LocalAuthenticationService(pinCodePreferences: .shared) - + private static let backgroundServiceInitQueue = DispatchQueue(label: "io.element.NotificationService.backgroundServiceInitQueue") // MARK: - Method Overrides override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) { @@ -168,14 +168,16 @@ class NotificationService: UNNotificationServiceExtension { MXKAccountManager.shared()?.forceReloadAccounts() self.userAccount = MXKAccountManager.shared()?.activeAccounts.first if let userAccount = userAccount { - if NotificationService.backgroundSyncService?.credentials != userAccount.mxCredentials { - MXLog.debug("[NotificationService] setup: MXBackgroundSyncService init: BEFORE") - self.logMemory() - NotificationService.backgroundSyncService = MXBackgroundSyncService(withCredentials: userAccount.mxCredentials) - MXLog.debug("[NotificationService] setup: MXBackgroundSyncService init: AFTER") - self.logMemory() + Self.backgroundServiceInitQueue.sync { + if NotificationService.backgroundSyncService?.credentials != userAccount.mxCredentials { + MXLog.debug("[NotificationService] setup: MXBackgroundSyncService init: BEFORE") + self.logMemory() + NotificationService.backgroundSyncService = MXBackgroundSyncService(withCredentials: userAccount.mxCredentials) + MXLog.debug("[NotificationService] setup: MXBackgroundSyncService init: AFTER") + self.logMemory() + } + completion() } - completion() } else { MXLog.debug("[NotificationService] setup: No active accounts") fallbackToBestAttemptContent(forEventId: eventId) diff --git a/changelog.d/5165.change b/changelog.d/5165.change new file mode 100644 index 000000000..59fea57b5 --- /dev/null +++ b/changelog.d/5165.change @@ -0,0 +1 @@ +Use DTCoreText's callback option to sanitise formatted messages \ No newline at end of file diff --git a/changelog.d/5185.bugfix b/changelog.d/5185.bugfix new file mode 100644 index 000000000..dfc98744d --- /dev/null +++ b/changelog.d/5185.bugfix @@ -0,0 +1 @@ +Fix rooms that should be hidden(such as virtual rooms) from showing. diff --git a/changelog.d/5194.bugfix b/changelog.d/5194.bugfix new file mode 100644 index 000000000..56d98768e --- /dev/null +++ b/changelog.d/5194.bugfix @@ -0,0 +1 @@ +Improve generated Swift header imports. diff --git a/changelog.d/5199.bugfix b/changelog.d/5199.bugfix new file mode 100644 index 000000000..d363f1449 --- /dev/null +++ b/changelog.d/5199.bugfix @@ -0,0 +1 @@ +Fix bug where VoIP calls would not connect reliably after signout/signin.