diff --git a/Riot/Modules/Application/LegacyAppDelegate.m b/Riot/Modules/Application/LegacyAppDelegate.m index 80de56871..abbeebca0 100644 --- a/Riot/Modules/Application/LegacyAppDelegate.m +++ b/Riot/Modules/Application/LegacyAppDelegate.m @@ -1487,7 +1487,7 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni // Ask the HS to resolve the room alias into a room id and then retry self->universalLinkFragmentPending = fragment; MXKAccount* account = accountManager.activeAccounts.firstObject; - [account.mxSession.matrixRestClient roomIDForRoomAlias:roomIdOrAlias success:^(NSString *roomId) { + [account.mxSession.matrixRestClient resolveRoomAlias:roomIdOrAlias success:^(MXRoomAliasResolution *resolution) { // Note: the activity indicator will not disappear if the session is not ready [homeViewController stopActivityIndicator]; @@ -1495,34 +1495,20 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni // Check that 'fragment' has not been cancelled if ([self->universalLinkFragmentPending isEqualToString:fragment]) { - // Retry opening the link but with the returned room id - NSString *newUniversalLinkFragment = - [fragment stringByReplacingOccurrencesOfString:[MXTools encodeURIComponent:roomIdOrAlias] - withString:[MXTools encodeURIComponent:roomId] - ]; - - // The previous operation can fail because of percent encoding - // TBH we are not clean on data inputs. For the moment, just give another try with no encoding - // TODO: Have a dedicated module and tests to handle universal links (matrix.to, email link, etc) - if ([newUniversalLinkFragment isEqualToString:fragment]) + NSString *newFragment = resolution.deeplinkFragment; + if (newFragment && ![newFragment isEqualToString:fragment]) { - newUniversalLinkFragment = - [fragment stringByReplacingOccurrencesOfString:roomIdOrAlias - withString:[MXTools encodeURIComponent:roomId]]; - } - - if (![newUniversalLinkFragment isEqualToString:fragment]) - { - self->universalLinkFragmentPendingRoomAlias = @{roomId: roomIdOrAlias}; - - UniversalLinkParameters *newParameters = [[UniversalLinkParameters alloc] initWithFragment:newUniversalLinkFragment universalLinkURL:universalLinkURL presentationParameters:presentationParameters]; - + self->universalLinkFragmentPendingRoomAlias = @{resolution.roomId: roomIdOrAlias}; + + UniversalLinkParameters *newParameters = [[UniversalLinkParameters alloc] initWithFragment:newFragment + universalLinkURL:universalLinkURL + presentationParameters:presentationParameters]; [self handleUniversalLinkWithParameters:newParameters]; } else { // Do not continue. Else we will loop forever - MXLogDebug(@"[AppDelegate] Universal link: Error: Cannot resolve alias in %@ to the room id %@", fragment, roomId); + MXLogDebug(@"[AppDelegate] Universal link: Error: Cannot resolve alias in %@ to the room id %@", fragment, resolution.roomId); } } diff --git a/Riot/Modules/Communities/Home/GroupHomeViewController.m b/Riot/Modules/Communities/Home/GroupHomeViewController.m index d8a9e3ed3..18971b51d 100644 --- a/Riot/Modules/Communities/Home/GroupHomeViewController.m +++ b/Riot/Modules/Communities/Home/GroupHomeViewController.m @@ -867,14 +867,13 @@ __weak typeof(self) weakSelf = self; [self startActivityIndicator]; - [self.mxSession.matrixRestClient roomIDForRoomAlias:roomIdOrAlias success:^(NSString *roomId) { - + [self.mxSession.matrixRestClient resolveRoomAlias:roomIdOrAlias success:^(MXRoomAliasResolution *resolution) { if (roomId && weakSelf) { typeof(self) self = weakSelf; [self stopActivityIndicator]; - [self didSelectRoomId:roomId]; + [self didSelectRoomId:resolution.roomId]; } } failure:^(NSError *error) { diff --git a/Riot/Modules/DeepLink/MXRoomAliasResolution+Deeplink.swift b/Riot/Modules/DeepLink/MXRoomAliasResolution+Deeplink.swift new file mode 100644 index 000000000..234f07849 --- /dev/null +++ b/Riot/Modules/DeepLink/MXRoomAliasResolution+Deeplink.swift @@ -0,0 +1,43 @@ +// +// Copyright 2022 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 Foundation +import MatrixSDK + +@objc extension MXRoomAliasResolution { + + /// Deeplink fragment using a room identifier and a list of servers aware of this identifier + /// + /// For more details see + /// https://github.com/matrix-org/matrix-spec-proposals/blob/old_master/proposals/1704-matrix.to-permalinks.md + var deeplinkFragment: String? { + guard let roomId = roomId else { + MXLog.debug("[MXRoomAliasResolution]: Missing room identifier") + return nil + } + + return MXTools.encodeURIComponent( + fragment(for: roomId) + ) + } + + private func fragment(for roomId: String) -> String { + guard let servers = servers, !servers.isEmpty else { + return roomId + } + return roomId + "?via=" + servers.joined(separator: "&via=") + } +} diff --git a/RiotTests/Modules/DeepLink/MXRoomAliasResolutionDeeplinkTests.swift b/RiotTests/Modules/DeepLink/MXRoomAliasResolutionDeeplinkTests.swift new file mode 100644 index 000000000..c465dad67 --- /dev/null +++ b/RiotTests/Modules/DeepLink/MXRoomAliasResolutionDeeplinkTests.swift @@ -0,0 +1,56 @@ +// +// Copyright 2022 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 Foundation +import XCTest +@testable import Riot + +class MXRoomAliasResolutionDeeplinkTests: XCTestCase { + func test_fragmentIsNilForInvalidResolution() { + let resolution = MXRoomAliasResolution() + XCTAssertNil(resolution.deeplinkFragment) + } + + func test_fragmentDoesNotContainServers_ifNoServers() { + let resolution = MXRoomAliasResolution() + resolution.roomId = "!abc:matrix.org" + + XCTAssertEqual(resolution.deeplinkFragment, "!abc%3Amatrix.org") + } + + func test_fragmentContainsSingleServer() { + let resolution = MXRoomAliasResolution() + resolution.roomId = "xyz:element.io" + resolution.servers = [ + "matrix.org" + ] + + XCTAssertEqual(resolution.deeplinkFragment, "xyz%3Aelement.io%3Fvia%3Dmatrix.org") + } + + func test_fragmentContainsMultipleSerivers() { + let resolution = MXRoomAliasResolution() + resolution.roomId = "mno:server.com" + resolution.servers = [ + "server.com", + "element.io", + "wikipedia.org", + "matrix.org" + ] + + XCTAssertEqual(resolution.deeplinkFragment, "mno%3Aserver.com%3Fvia%3Dserver.com%26via%3Delement.io%26via%3Dwikipedia.org%26via%3Dmatrix.org") + } +} diff --git a/changelog.d/4858.bugfix b/changelog.d/4858.bugfix new file mode 100644 index 000000000..2a7d342e8 --- /dev/null +++ b/changelog.d/4858.bugfix @@ -0,0 +1 @@ +Room: Enable joining a room via identifier from another home server