mirror of
https://github.com/element-hq/element-x-ios.git
synced 2025-03-10 21:39:12 +00:00
Handle alias room permalinks
This commit is contained in:
parent
8ba544bc44
commit
69ffd3be46
@ -190,16 +190,21 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
|
||||
}
|
||||
case .userProfile(let userID):
|
||||
if isExternalURL {
|
||||
userSessionFlowCoordinator?.handleAppRoute(route, animated: true)
|
||||
handleAppRoute(route)
|
||||
} else {
|
||||
userSessionFlowCoordinator?.handleAppRoute(.roomMemberDetails(userID: userID), animated: true)
|
||||
handleAppRoute(.roomMemberDetails(userID: userID))
|
||||
}
|
||||
case .room(let roomID):
|
||||
// check that the room is joined here, if not use a joinRoom route.
|
||||
if isExternalURL {
|
||||
userSessionFlowCoordinator?.handleAppRoute(route, animated: true)
|
||||
handleAppRoute(route)
|
||||
} else {
|
||||
userSessionFlowCoordinator?.handleAppRoute(.childRoom(roomID: roomID), animated: true)
|
||||
handleAppRoute(.childRoom(roomID: roomID))
|
||||
}
|
||||
case .roomAlias(let alias):
|
||||
if isExternalURL {
|
||||
handleAppRoute(route)
|
||||
} else {
|
||||
handleAppRoute(.childRoomAlias(alias))
|
||||
}
|
||||
default:
|
||||
break
|
||||
@ -255,7 +260,6 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
|
||||
return
|
||||
}
|
||||
|
||||
// Handle here the account switching when available
|
||||
handleAppRoute(.room(roomID: roomID))
|
||||
}
|
||||
|
||||
|
@ -24,8 +24,12 @@ enum AppRoute: Equatable {
|
||||
case roomList
|
||||
/// A room, shown as the root of the stack (popping any child rooms).
|
||||
case room(roomID: String)
|
||||
/// A room, shown as the root of the stack (popping any child rooms).
|
||||
case roomAlias(String)
|
||||
/// A room, pushed as a child of any existing rooms on the stack.
|
||||
case childRoom(roomID: String)
|
||||
/// A room, pushed as a child of any existing rooms on the stack.
|
||||
case childRoomAlias(String)
|
||||
/// The information about a particular room.
|
||||
case roomDetails(roomID: String)
|
||||
/// The profile of a member within the current room.
|
||||
@ -125,6 +129,8 @@ struct MatrixPermalinkParser: URLParser {
|
||||
switch parseMatrixEntityFrom(uri: url.absoluteString)?.id {
|
||||
case .room(let id):
|
||||
return .room(roomID: id)
|
||||
case .roomAlias(let alias):
|
||||
return .roomAlias(alias)
|
||||
case .user(let id):
|
||||
return .userProfile(userID: id)
|
||||
default:
|
||||
|
@ -138,7 +138,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
||||
} else {
|
||||
stateMachine.tryEvent(.presentRoomMemberDetails(userID: userID), userInfo: EventUserInfo(animated: animated))
|
||||
}
|
||||
case .roomList, .userProfile, .genericCallLink, .oidcCallback, .settings, .chatBackupSettings:
|
||||
case .roomAlias, .childRoomAlias, .roomList, .userProfile, .genericCallLink, .oidcCallback, .settings, .chatBackupSettings:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
@ -175,11 +175,33 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
|
||||
// MARK: - FlowCoordinatorProtocol
|
||||
|
||||
func handleAppRoute(_ appRoute: AppRoute, animated: Bool) {
|
||||
clearPresentedSheets(animated: animated) { [weak self] in
|
||||
guard let self else { return }
|
||||
Task {
|
||||
await asyncHandleAppRoute(appRoute, animated: animated)
|
||||
}
|
||||
}
|
||||
|
||||
func clearRoute(animated: Bool) {
|
||||
roomFlowCoordinator?.clearRoute(animated: animated)
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
func asyncHandleAppRoute(_ appRoute: AppRoute, animated: Bool) async {
|
||||
showLoadingIndicator(delay: .seconds(0.25))
|
||||
defer {
|
||||
hideLoadingIndicator()
|
||||
}
|
||||
|
||||
await clearPresentedSheets(animated: animated)
|
||||
|
||||
switch appRoute {
|
||||
case .room(let roomID):
|
||||
stateMachine.processEvent(.selectRoom(roomID: roomID, showingRoomDetails: false), userInfo: .init(animated: animated))
|
||||
case .roomAlias(let alias):
|
||||
guard let roomID = await userSession.clientProxy.resolveRoomAlias(alias) else {
|
||||
return
|
||||
}
|
||||
|
||||
stateMachine.processEvent(.selectRoom(roomID: roomID, showingRoomDetails: false), userInfo: .init(animated: animated))
|
||||
case .childRoom(let roomID):
|
||||
if let roomFlowCoordinator {
|
||||
@ -187,6 +209,16 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
|
||||
} else {
|
||||
stateMachine.processEvent(.selectRoom(roomID: roomID, showingRoomDetails: false), userInfo: .init(animated: animated))
|
||||
}
|
||||
case .childRoomAlias(let alias):
|
||||
guard let roomID = await userSession.clientProxy.resolveRoomAlias(alias) else {
|
||||
return
|
||||
}
|
||||
|
||||
if let roomFlowCoordinator {
|
||||
roomFlowCoordinator.handleAppRoute(.childRoom(roomID: roomID), animated: animated)
|
||||
} else {
|
||||
stateMachine.processEvent(.selectRoom(roomID: roomID, showingRoomDetails: false), userInfo: .init(animated: animated))
|
||||
}
|
||||
case .roomDetails(let roomID):
|
||||
if stateMachine.state.selectedRoomID == roomID {
|
||||
roomFlowCoordinator?.handleAppRoute(appRoute, animated: animated)
|
||||
@ -207,13 +239,6 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
|
||||
settingsFlowCoordinator.handleAppRoute(appRoute, animated: animated)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func clearRoute(animated: Bool) {
|
||||
roomFlowCoordinator?.clearRoute(animated: animated)
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
func attemptStartingOnboarding() {
|
||||
if onboardingFlowCoordinator.shouldStart {
|
||||
@ -222,18 +247,15 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
|
||||
}
|
||||
}
|
||||
|
||||
private func clearPresentedSheets(animated: Bool, completion: @escaping () -> Void) {
|
||||
private func clearPresentedSheets(animated: Bool) async {
|
||||
if navigationSplitCoordinator.sheetCoordinator == nil {
|
||||
completion()
|
||||
return
|
||||
}
|
||||
|
||||
navigationSplitCoordinator.setSheetCoordinator(nil, animated: animated)
|
||||
|
||||
// Prevents system crashes when presenting a sheet if another one was already shown
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.25) {
|
||||
completion()
|
||||
}
|
||||
try? await Task.sleep(for: .seconds(0.25))
|
||||
}
|
||||
|
||||
private func setupStateMachine() {
|
||||
@ -675,4 +697,20 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
|
||||
self?.stateMachine.processEvent(.dismissedUserProfileScreen)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Toasts and loading indicators
|
||||
|
||||
private static let loadingIndicatorIdentifier = "\(UserSessionFlowCoordinator.self)-Loading"
|
||||
|
||||
private func showLoadingIndicator(delay: Duration? = nil) {
|
||||
ServiceLocator.shared.userIndicatorController.submitIndicator(UserIndicator(id: Self.loadingIndicatorIdentifier,
|
||||
type: .modal,
|
||||
title: L10n.commonLoading,
|
||||
persistent: true),
|
||||
delay: delay)
|
||||
}
|
||||
|
||||
private func hideLoadingIndicator() {
|
||||
ServiceLocator.shared.userIndicatorController.retractIndicatorWithId(Self.loadingIndicatorIdentifier)
|
||||
}
|
||||
}
|
||||
|
@ -3361,6 +3361,74 @@ class ClientProxyMock: ClientProxyProtocol {
|
||||
return roomDirectorySearchProxyReturnValue
|
||||
}
|
||||
}
|
||||
//MARK: - resolveRoomAlias
|
||||
|
||||
var resolveRoomAliasUnderlyingCallsCount = 0
|
||||
var resolveRoomAliasCallsCount: Int {
|
||||
get {
|
||||
if Thread.isMainThread {
|
||||
return resolveRoomAliasUnderlyingCallsCount
|
||||
} else {
|
||||
var returnValue: Int? = nil
|
||||
DispatchQueue.main.sync {
|
||||
returnValue = resolveRoomAliasUnderlyingCallsCount
|
||||
}
|
||||
|
||||
return returnValue!
|
||||
}
|
||||
}
|
||||
set {
|
||||
if Thread.isMainThread {
|
||||
resolveRoomAliasUnderlyingCallsCount = newValue
|
||||
} else {
|
||||
DispatchQueue.main.sync {
|
||||
resolveRoomAliasUnderlyingCallsCount = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var resolveRoomAliasCalled: Bool {
|
||||
return resolveRoomAliasCallsCount > 0
|
||||
}
|
||||
var resolveRoomAliasReceivedAlias: String?
|
||||
var resolveRoomAliasReceivedInvocations: [String] = []
|
||||
|
||||
var resolveRoomAliasUnderlyingReturnValue: String?
|
||||
var resolveRoomAliasReturnValue: String? {
|
||||
get {
|
||||
if Thread.isMainThread {
|
||||
return resolveRoomAliasUnderlyingReturnValue
|
||||
} else {
|
||||
var returnValue: String?? = nil
|
||||
DispatchQueue.main.sync {
|
||||
returnValue = resolveRoomAliasUnderlyingReturnValue
|
||||
}
|
||||
|
||||
return returnValue!
|
||||
}
|
||||
}
|
||||
set {
|
||||
if Thread.isMainThread {
|
||||
resolveRoomAliasUnderlyingReturnValue = newValue
|
||||
} else {
|
||||
DispatchQueue.main.sync {
|
||||
resolveRoomAliasUnderlyingReturnValue = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var resolveRoomAliasClosure: ((String) async -> String?)?
|
||||
|
||||
func resolveRoomAlias(_ alias: String) async -> String? {
|
||||
resolveRoomAliasCallsCount += 1
|
||||
resolveRoomAliasReceivedAlias = alias
|
||||
resolveRoomAliasReceivedInvocations.append(alias)
|
||||
if let resolveRoomAliasClosure = resolveRoomAliasClosure {
|
||||
return await resolveRoomAliasClosure(alias)
|
||||
} else {
|
||||
return resolveRoomAliasReturnValue
|
||||
}
|
||||
}
|
||||
//MARK: - ignoreUser
|
||||
|
||||
var ignoreUserUnderlyingCallsCount = 0
|
||||
|
@ -597,6 +597,15 @@ class ClientProxy: ClientProxyProtocol {
|
||||
RoomDirectorySearchProxy(roomDirectorySearch: client.roomDirectorySearch())
|
||||
}
|
||||
|
||||
func resolveRoomAlias(_ alias: String) async -> String? {
|
||||
do {
|
||||
return try await client.resolveRoomAlias(roomAlias: alias)
|
||||
} catch {
|
||||
MXLog.error("Failed resolving room alias: \(alias) with error: \(error)")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Ignored users
|
||||
|
||||
func ignoreUser(_ userID: String) async -> Result<Void, ClientProxyError> {
|
||||
|
@ -161,6 +161,8 @@ protocol ClientProxyProtocol: AnyObject, MediaLoaderProtocol {
|
||||
|
||||
func roomDirectorySearchProxy() -> RoomDirectorySearchProxyProtocol
|
||||
|
||||
func resolveRoomAlias(_ alias: String) async -> String?
|
||||
|
||||
// MARK: - Ignored users
|
||||
|
||||
func ignoreUser(_ userID: String) async -> Result<Void, ClientProxyError>
|
||||
|
@ -21,6 +21,7 @@ import Combine
|
||||
|
||||
@MainActor
|
||||
class UserSessionFlowCoordinatorTests: XCTestCase {
|
||||
var clientProxy: ClientProxyMock!
|
||||
var userSessionFlowCoordinator: UserSessionFlowCoordinator!
|
||||
var navigationRootCoordinator: NavigationRootCoordinator!
|
||||
var notificationManager: NotificationManagerMock!
|
||||
@ -33,7 +34,7 @@ class UserSessionFlowCoordinatorTests: XCTestCase {
|
||||
|
||||
override func setUp() async throws {
|
||||
cancellables.removeAll()
|
||||
let clientProxy = ClientProxyMock(.init(userID: "hi@bob", roomSummaryProvider: RoomSummaryProviderMock(.init(state: .loaded(.mockRooms)))))
|
||||
clientProxy = ClientProxyMock(.init(userID: "hi@bob", roomSummaryProvider: RoomSummaryProviderMock(.init(state: .loaded(.mockRooms)))))
|
||||
let mediaProvider = MockMediaProvider()
|
||||
let voiceMessageMediaManager = VoiceMessageMediaManagerMock()
|
||||
let userSession = MockUserSession(clientProxy: clientProxy,
|
||||
@ -84,6 +85,34 @@ class UserSessionFlowCoordinatorTests: XCTestCase {
|
||||
XCTAssertEqual(notificationManager.removeDeliveredMessageNotificationsForReceivedInvocations, ["1", "1", "2"])
|
||||
}
|
||||
|
||||
func testRoomAliasPresentation() async throws {
|
||||
clientProxy.resolveRoomAliasReturnValue = "1"
|
||||
|
||||
try await process(route: .roomAlias("#alias:matrix.org"), expectedState: .roomList(selectedRoomID: "1"))
|
||||
XCTAssertTrue(detailNavigationStack?.rootCoordinator is RoomScreenCoordinator)
|
||||
XCTAssertNotNil(detailCoordinator)
|
||||
|
||||
try await process(route: .roomList, expectedState: .roomList(selectedRoomID: nil))
|
||||
XCTAssertNil(detailNavigationStack?.rootCoordinator)
|
||||
XCTAssertNil(detailCoordinator)
|
||||
|
||||
try await process(route: .room(roomID: "1"), expectedState: .roomList(selectedRoomID: "1"))
|
||||
XCTAssertTrue(detailNavigationStack?.rootCoordinator is RoomScreenCoordinator)
|
||||
XCTAssertNotNil(detailCoordinator)
|
||||
|
||||
clientProxy.resolveRoomAliasReturnValue = "2"
|
||||
|
||||
try await process(route: .room(roomID: "2"), expectedState: .roomList(selectedRoomID: "2"))
|
||||
XCTAssertTrue(detailNavigationStack?.rootCoordinator is RoomScreenCoordinator)
|
||||
XCTAssertNotNil(detailCoordinator)
|
||||
|
||||
try await process(route: .roomList, expectedState: .roomList(selectedRoomID: nil))
|
||||
XCTAssertNil(detailNavigationStack?.rootCoordinator)
|
||||
XCTAssertNil(detailCoordinator)
|
||||
|
||||
XCTAssertEqual(notificationManager.removeDeliveredMessageNotificationsForReceivedInvocations, ["1", "1", "2"])
|
||||
}
|
||||
|
||||
func testRoomDetailsPresentation() async throws {
|
||||
try await process(route: .roomDetails(roomID: "1"), expectedState: .roomList(selectedRoomID: "1"))
|
||||
XCTAssertTrue(detailNavigationStack?.rootCoordinator is RoomDetailsScreenCoordinator)
|
||||
|
Loading…
x
Reference in New Issue
Block a user