mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-04 19:44:05 +00:00
Add Event Routes to the flows (#2743)
This commit is contained in:
@@ -206,6 +206,18 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
|
|||||||
} else {
|
} else {
|
||||||
handleAppRoute(.childRoomAlias(alias))
|
handleAppRoute(.childRoomAlias(alias))
|
||||||
}
|
}
|
||||||
|
case .event(let roomID, let eventID):
|
||||||
|
if isExternalURL {
|
||||||
|
handleAppRoute(route)
|
||||||
|
} else {
|
||||||
|
handleAppRoute(.childEvent(roomID: roomID, eventID: eventID))
|
||||||
|
}
|
||||||
|
case .eventOnRoomAlias(let alias, let eventID):
|
||||||
|
if isExternalURL {
|
||||||
|
handleAppRoute(route)
|
||||||
|
} else {
|
||||||
|
handleAppRoute(.childEventOnRoomAlias(alias: alias, eventID: eventID))
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,16 +24,24 @@ enum AppRoute: Equatable {
|
|||||||
case roomList
|
case roomList
|
||||||
/// A room, shown as the root of the stack (popping any child rooms).
|
/// A room, shown as the root of the stack (popping any child rooms).
|
||||||
case room(roomID: String)
|
case room(roomID: String)
|
||||||
/// A room, shown as the root of the stack (popping any child rooms).
|
/// The same as ``room`` but using a room alias.
|
||||||
case roomAlias(String)
|
case roomAlias(String)
|
||||||
/// A room, pushed as a child of any existing rooms on the stack.
|
/// A room, pushed as a child of any existing rooms on the stack.
|
||||||
case childRoom(roomID: String)
|
case childRoom(roomID: String)
|
||||||
/// A room, pushed as a child of any existing rooms on the stack.
|
/// The same as ``childRoom`` but using a room alias.
|
||||||
case childRoomAlias(String)
|
case childRoomAlias(String)
|
||||||
/// The information about a particular room.
|
/// The information about a particular room.
|
||||||
case roomDetails(roomID: String)
|
case roomDetails(roomID: String)
|
||||||
/// The profile of a member within the current room.
|
/// The profile of a member within the current room.
|
||||||
case roomMemberDetails(userID: String)
|
case roomMemberDetails(userID: String)
|
||||||
|
/// An event within a room, shown as the root of the stack (popping any child rooms).
|
||||||
|
case event(roomID: String, eventID: String)
|
||||||
|
/// The same as ``event`` but using a room alias.
|
||||||
|
case eventOnRoomAlias(alias: String, eventID: String)
|
||||||
|
/// An event within a room, either within the last child on the stack or pushing a new child if needed.
|
||||||
|
case childEvent(roomID: String, eventID: String)
|
||||||
|
/// The same as ``childEvent`` but using a room alias.
|
||||||
|
case childEventOnRoomAlias(alias: String, eventID: String)
|
||||||
/// The profile of a matrix user (outside of a room).
|
/// The profile of a matrix user (outside of a room).
|
||||||
case userProfile(userID: String)
|
case userProfile(userID: String)
|
||||||
/// An Element Call link generated outside of a chat room.
|
/// An Element Call link generated outside of a chat room.
|
||||||
@@ -133,6 +141,12 @@ struct MatrixPermalinkParser: URLParser {
|
|||||||
return .roomAlias(alias)
|
return .roomAlias(alias)
|
||||||
case .user(let id):
|
case .user(let id):
|
||||||
return .userProfile(userID: id)
|
return .userProfile(userID: id)
|
||||||
|
case .eventOnRoomId(let roomID, let eventID):
|
||||||
|
// return .event(roomID: roomID, eventID: eventID)
|
||||||
|
return nil
|
||||||
|
case .eventOnRoomAlias(let alias, let eventID):
|
||||||
|
// return .eventOnRoomAlias(alias: alias, eventID: eventID)
|
||||||
|
return nil
|
||||||
default:
|
default:
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,16 @@ enum RoomFlowCoordinatorAction: Equatable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A value that represents where the room flow will be started.
|
||||||
|
enum RoomFlowCoordinatorEntryPoint: Hashable {
|
||||||
|
/// The flow will start by showing the room directly.
|
||||||
|
case room
|
||||||
|
/// The flow will start by showing the room, focussing on the supplied event ID.
|
||||||
|
case eventID(String)
|
||||||
|
/// The flow will start by showing the room's details.
|
||||||
|
case roomDetails
|
||||||
|
}
|
||||||
|
|
||||||
// swiftlint:disable:next type_body_length
|
// swiftlint:disable:next type_body_length
|
||||||
class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
||||||
private let roomID: String
|
private let roomID: String
|
||||||
@@ -54,6 +64,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
|
|
||||||
private var roomProxy: RoomProxyProtocol!
|
private var roomProxy: RoomProxyProtocol!
|
||||||
|
|
||||||
|
private var roomScreenCoordinator: RoomScreenCoordinator?
|
||||||
private weak var joinRoomScreenCoordinator: JoinRoomScreenCoordinator?
|
private weak var joinRoomScreenCoordinator: JoinRoomScreenCoordinator?
|
||||||
|
|
||||||
// periphery:ignore - used to avoid deallocation
|
// periphery:ignore - used to avoid deallocation
|
||||||
@@ -118,7 +129,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
if case .presentingChild = stateMachine.state, let childRoomFlowCoordinator {
|
if case .presentingChild = stateMachine.state, let childRoomFlowCoordinator {
|
||||||
childRoomFlowCoordinator.handleAppRoute(appRoute, animated: animated)
|
childRoomFlowCoordinator.handleAppRoute(appRoute, animated: animated)
|
||||||
} else if roomID != roomProxy.id {
|
} else if roomID != roomProxy.id {
|
||||||
stateMachine.tryEvent(.startChildFlow(roomID: roomID), userInfo: EventUserInfo(animated: animated))
|
stateMachine.tryEvent(.startChildFlow(roomID: roomID, entryPoint: .room), userInfo: EventUserInfo(animated: animated))
|
||||||
} else {
|
} else {
|
||||||
MXLog.info("Ignoring presentation of the same room as a child of this one.")
|
MXLog.info("Ignoring presentation of the same room as a child of this one.")
|
||||||
}
|
}
|
||||||
@@ -143,12 +154,24 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
} else {
|
} else {
|
||||||
stateMachine.tryEvent(.presentRoomMemberDetails(userID: userID), userInfo: EventUserInfo(animated: animated))
|
stateMachine.tryEvent(.presentRoomMemberDetails(userID: userID), userInfo: EventUserInfo(animated: animated))
|
||||||
}
|
}
|
||||||
case .roomAlias, .childRoomAlias, .roomList, .userProfile, .genericCallLink, .oidcCallback, .settings, .chatBackupSettings:
|
case .event(let roomID, let eventID):
|
||||||
break
|
Task { await handleRoomRoute(roomID: roomID, focussedEventID: eventID, animated: animated) }
|
||||||
|
case .childEvent(let roomID, let eventID):
|
||||||
|
if case .presentingChild = stateMachine.state, let childRoomFlowCoordinator {
|
||||||
|
childRoomFlowCoordinator.handleAppRoute(appRoute, animated: animated)
|
||||||
|
} else if roomID != roomProxy.id {
|
||||||
|
stateMachine.tryEvent(.startChildFlow(roomID: roomID, entryPoint: .eventID(eventID)), userInfo: EventUserInfo(animated: animated))
|
||||||
|
} else {
|
||||||
|
roomScreenCoordinator?.focusOnEvent(eventID: eventID)
|
||||||
|
}
|
||||||
|
case .roomAlias, .childRoomAlias, .eventOnRoomAlias, .childEventOnRoomAlias:
|
||||||
|
break // These are converted to a room ID route one level above.
|
||||||
|
case .roomList, .userProfile, .genericCallLink, .oidcCallback, .settings, .chatBackupSettings:
|
||||||
|
break // These routes can't be handled.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func handleRoomRoute(roomID: String, animated: Bool) async {
|
private func handleRoomRoute(roomID: String, focussedEventID: String? = nil, animated: Bool) async {
|
||||||
guard roomID == self.roomID else { fatalError("Navigation route doesn't belong to this room flow.") }
|
guard roomID == self.roomID else { fatalError("Navigation route doesn't belong to this room flow.") }
|
||||||
|
|
||||||
guard let roomProxy = await userSession.clientProxy.roomForIdentifier(roomID) else {
|
guard let roomProxy = await userSession.clientProxy.roomForIdentifier(roomID) else {
|
||||||
@@ -159,7 +182,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
switch roomProxy.membership {
|
switch roomProxy.membership {
|
||||||
case .joined:
|
case .joined:
|
||||||
await storeAndSubscribeToRoomProxy(roomProxy)
|
await storeAndSubscribeToRoomProxy(roomProxy)
|
||||||
stateMachine.tryEvent(.presentRoom, userInfo: EventUserInfo(animated: animated))
|
stateMachine.tryEvent(.presentRoom(focussedEventID: focussedEventID), userInfo: EventUserInfo(animated: animated))
|
||||||
default:
|
default:
|
||||||
stateMachine.tryEvent(.presentJoinRoomScreen, userInfo: EventUserInfo(animated: animated))
|
stateMachine.tryEvent(.presentJoinRoomScreen, userInfo: EventUserInfo(animated: animated))
|
||||||
}
|
}
|
||||||
@@ -307,7 +330,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
|
|
||||||
// Child flow
|
// Child flow
|
||||||
|
|
||||||
case (_, .startChildFlow(let roomID)):
|
case (_, .startChildFlow(let roomID, _)):
|
||||||
return .presentingChild(childRoomID: roomID, previousState: fromState)
|
return .presentingChild(childRoomID: roomID, previousState: fromState)
|
||||||
case (.presentingChild(_, let previousState), .dismissChildFlow):
|
case (.presentingChild(_, let previousState), .dismissChildFlow):
|
||||||
return previousState
|
return previousState
|
||||||
@@ -328,8 +351,8 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
case (_, .dismissJoinRoomScreen, .complete):
|
case (_, .dismissJoinRoomScreen, .complete):
|
||||||
dismissFlow(animated: animated)
|
dismissFlow(animated: animated)
|
||||||
|
|
||||||
case (_, .presentRoom, .room):
|
case (_, .presentRoom(let focussedEventID), .room):
|
||||||
Task { await self.presentRoom(fromState: context.fromState, animated: animated) }
|
Task { await self.presentRoom(fromState: context.fromState, focussedEventID: focussedEventID, animated: animated) }
|
||||||
case (_, .dismissFlow, .complete):
|
case (_, .dismissFlow, .complete):
|
||||||
dismissFlow(animated: animated)
|
dismissFlow(animated: animated)
|
||||||
|
|
||||||
@@ -438,8 +461,8 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
rolesAndPermissionsFlowCoordinator = nil
|
rolesAndPermissionsFlowCoordinator = nil
|
||||||
|
|
||||||
// Child flow
|
// Child flow
|
||||||
case (_, .startChildFlow(let roomID), .presentingChild):
|
case (_, .startChildFlow(let roomID, let entryPoint), .presentingChild):
|
||||||
Task { await self.startChildFlow(for: roomID) }
|
Task { await self.startChildFlow(for: roomID, entryPoint: entryPoint) }
|
||||||
case (.presentingChild, .dismissChildFlow, _):
|
case (.presentingChild, .dismissChildFlow, _):
|
||||||
childRoomFlowCoordinator = nil
|
childRoomFlowCoordinator = nil
|
||||||
|
|
||||||
@@ -467,8 +490,10 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
|
|
||||||
/// Updates the navigation stack so it displays the timeline for the given room
|
/// Updates the navigation stack so it displays the timeline for the given room
|
||||||
/// - Parameters:
|
/// - Parameters:
|
||||||
|
/// - fromState: The state that asked for the room presentation.
|
||||||
|
/// - focussedEventID: An (optional) event ID that the timeline should be focussed around.
|
||||||
/// - animated: whether it should animate the transition
|
/// - animated: whether it should animate the transition
|
||||||
private func presentRoom(fromState: State, animated: Bool) async {
|
private func presentRoom(fromState: State, focussedEventID: String? = nil, animated: Bool) async {
|
||||||
// If any sheets are presented dismiss them, rely on their dismissal callbacks to transition the state machine
|
// If any sheets are presented dismiss them, rely on their dismissal callbacks to transition the state machine
|
||||||
// through the correct states before presenting the room
|
// through the correct states before presenting the room
|
||||||
navigationStackCoordinator.setSheetCoordinator(nil)
|
navigationStackCoordinator.setSheetCoordinator(nil)
|
||||||
@@ -507,6 +532,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
let completionSuggestionService = CompletionSuggestionService(roomProxy: roomProxy)
|
let completionSuggestionService = CompletionSuggestionService(roomProxy: roomProxy)
|
||||||
|
|
||||||
let parameters = RoomScreenCoordinatorParameters(roomProxy: roomProxy,
|
let parameters = RoomScreenCoordinatorParameters(roomProxy: roomProxy,
|
||||||
|
focussedEventID: focussedEventID,
|
||||||
timelineController: timelineController,
|
timelineController: timelineController,
|
||||||
mediaProvider: userSession.mediaProvider,
|
mediaProvider: userSession.mediaProvider,
|
||||||
mediaPlayerProvider: MediaPlayerProvider(),
|
mediaPlayerProvider: MediaPlayerProvider(),
|
||||||
@@ -548,6 +574,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
}
|
}
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
|
|
||||||
|
roomScreenCoordinator = coordinator
|
||||||
if !isChildFlow {
|
if !isChildFlow {
|
||||||
let animated = UIDevice.current.userInterfaceIdiom == .phone ? animated : false
|
let animated = UIDevice.current.userInterfaceIdiom == .phone ? animated : false
|
||||||
navigationStackCoordinator.setRootCoordinator(coordinator, animated: animated) { [weak self] in
|
navigationStackCoordinator.setRootCoordinator(coordinator, animated: animated) { [weak self] in
|
||||||
@@ -584,7 +611,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
|
|
||||||
if let roomProxy = await userSession.clientProxy.roomForIdentifier(roomID) {
|
if let roomProxy = await userSession.clientProxy.roomForIdentifier(roomID) {
|
||||||
await storeAndSubscribeToRoomProxy(roomProxy)
|
await storeAndSubscribeToRoomProxy(roomProxy)
|
||||||
stateMachine.tryEvent(.presentRoom, userInfo: EventUserInfo(animated: animated))
|
stateMachine.tryEvent(.presentRoom(focussedEventID: nil), userInfo: EventUserInfo(animated: animated))
|
||||||
|
|
||||||
analytics.trackJoinedRoom(isDM: roomProxy.isDirect, isSpace: roomProxy.isSpace, activeMemberCount: UInt(roomProxy.activeMembersCount))
|
analytics.trackJoinedRoom(isDM: roomProxy.isDirect, isSpace: roomProxy.isSpace, activeMemberCount: UInt(roomProxy.activeMembersCount))
|
||||||
} else {
|
} else {
|
||||||
@@ -1022,7 +1049,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
case .openUserProfile:
|
case .openUserProfile:
|
||||||
stateMachine.tryEvent(.presentUserProfile(userID: userID))
|
stateMachine.tryEvent(.presentUserProfile(userID: userID))
|
||||||
case .openDirectChat(let roomID):
|
case .openDirectChat(let roomID):
|
||||||
stateMachine.tryEvent(.startChildFlow(roomID: roomID))
|
stateMachine.tryEvent(.startChildFlow(roomID: roomID, entryPoint: .room))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
@@ -1045,7 +1072,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
|
|
||||||
switch action {
|
switch action {
|
||||||
case .openDirectChat(let roomID):
|
case .openDirectChat(let roomID):
|
||||||
stateMachine.tryEvent(.startChildFlow(roomID: roomID))
|
stateMachine.tryEvent(.startChildFlow(roomID: roomID, entryPoint: .room))
|
||||||
case .dismiss:
|
case .dismiss:
|
||||||
break // Not supported when pushed.
|
break // Not supported when pushed.
|
||||||
}
|
}
|
||||||
@@ -1115,7 +1142,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
|
|
||||||
// We don't need to worry about passing in the room proxy as timelines are
|
// We don't need to worry about passing in the room proxy as timelines are
|
||||||
// cached. The local echo will be visible when fetching the room by its ID.
|
// cached. The local echo will be visible when fetching the room by its ID.
|
||||||
stateMachine.tryEvent(.startChildFlow(roomID: roomID))
|
stateMachine.tryEvent(.startChildFlow(roomID: roomID, entryPoint: .room))
|
||||||
}
|
}
|
||||||
|
|
||||||
private func presentNotificationSettingsScreen() {
|
private func presentNotificationSettingsScreen() {
|
||||||
@@ -1249,7 +1276,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
|
|
||||||
// MARK: - Child Flow
|
// MARK: - Child Flow
|
||||||
|
|
||||||
private func startChildFlow(for roomID: String) async {
|
private func startChildFlow(for roomID: String, entryPoint: RoomFlowCoordinatorEntryPoint) async {
|
||||||
let coordinator = await RoomFlowCoordinator(roomID: roomID,
|
let coordinator = await RoomFlowCoordinator(roomID: roomID,
|
||||||
userSession: userSession,
|
userSession: userSession,
|
||||||
isChildFlow: true,
|
isChildFlow: true,
|
||||||
@@ -1275,7 +1302,14 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
|
|
||||||
childRoomFlowCoordinator = coordinator
|
childRoomFlowCoordinator = coordinator
|
||||||
coordinator.handleAppRoute(.room(roomID: roomID), animated: true)
|
switch entryPoint {
|
||||||
|
case .room:
|
||||||
|
coordinator.handleAppRoute(.room(roomID: roomID), animated: true)
|
||||||
|
case .eventID(let eventID):
|
||||||
|
coordinator.handleAppRoute(.event(roomID: roomID, eventID: eventID), animated: true)
|
||||||
|
case .roomDetails:
|
||||||
|
coordinator.handleAppRoute(.roomDetails(roomID: roomID), animated: true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1329,7 +1363,7 @@ private extension RoomFlowCoordinator {
|
|||||||
case presentJoinRoomScreen
|
case presentJoinRoomScreen
|
||||||
case dismissJoinRoomScreen
|
case dismissJoinRoomScreen
|
||||||
|
|
||||||
case presentRoom
|
case presentRoom(focussedEventID: String?)
|
||||||
case dismissFlow
|
case dismissFlow
|
||||||
|
|
||||||
case presentReportContent(itemID: TimelineItemIdentifier, senderID: String)
|
case presentReportContent(itemID: TimelineItemIdentifier, senderID: String)
|
||||||
@@ -1384,7 +1418,7 @@ private extension RoomFlowCoordinator {
|
|||||||
case dismissRolesAndPermissionsScreen
|
case dismissRolesAndPermissionsScreen
|
||||||
|
|
||||||
// Child room flow events
|
// Child room flow events
|
||||||
case startChildFlow(roomID: String)
|
case startChildFlow(roomID: String, entryPoint: RoomFlowCoordinatorEntryPoint)
|
||||||
case dismissChildFlow
|
case dismissChildFlow
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -196,39 +196,39 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
|
|
||||||
switch appRoute {
|
switch appRoute {
|
||||||
case .room(let roomID):
|
case .room(let roomID):
|
||||||
stateMachine.processEvent(.selectRoom(roomID: roomID, showingRoomDetails: false), userInfo: .init(animated: animated))
|
stateMachine.processEvent(.selectRoom(roomID: roomID, entryPoint: .room), userInfo: .init(animated: animated))
|
||||||
case .roomAlias(let alias):
|
case .roomAlias(let alias):
|
||||||
guard let roomID = await userSession.clientProxy.resolveRoomAlias(alias) else {
|
guard let roomID = await userSession.clientProxy.resolveRoomAlias(alias) else { return }
|
||||||
return
|
await asyncHandleAppRoute(.room(roomID: roomID), animated: animated)
|
||||||
}
|
|
||||||
|
|
||||||
stateMachine.processEvent(.selectRoom(roomID: roomID, showingRoomDetails: false), userInfo: .init(animated: animated))
|
|
||||||
case .childRoom(let roomID):
|
case .childRoom(let roomID):
|
||||||
if let roomFlowCoordinator {
|
if let roomFlowCoordinator {
|
||||||
roomFlowCoordinator.handleAppRoute(appRoute, animated: animated)
|
roomFlowCoordinator.handleAppRoute(appRoute, animated: animated)
|
||||||
} else {
|
} else {
|
||||||
stateMachine.processEvent(.selectRoom(roomID: roomID, showingRoomDetails: false), userInfo: .init(animated: animated))
|
stateMachine.processEvent(.selectRoom(roomID: roomID, entryPoint: .room), userInfo: .init(animated: animated))
|
||||||
}
|
}
|
||||||
case .childRoomAlias(let alias):
|
case .childRoomAlias(let alias):
|
||||||
guard let roomID = await userSession.clientProxy.resolveRoomAlias(alias) else {
|
guard let roomID = await userSession.clientProxy.resolveRoomAlias(alias) else { return }
|
||||||
return
|
await asyncHandleAppRoute(.childRoom(roomID: roomID), animated: animated)
|
||||||
}
|
|
||||||
|
|
||||||
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):
|
case .roomDetails(let roomID):
|
||||||
if stateMachine.state.selectedRoomID == roomID {
|
if stateMachine.state.selectedRoomID == roomID {
|
||||||
roomFlowCoordinator?.handleAppRoute(appRoute, animated: animated)
|
roomFlowCoordinator?.handleAppRoute(appRoute, animated: animated)
|
||||||
} else {
|
} else {
|
||||||
stateMachine.processEvent(.selectRoom(roomID: roomID, showingRoomDetails: true), userInfo: .init(animated: animated))
|
stateMachine.processEvent(.selectRoom(roomID: roomID, entryPoint: .roomDetails), userInfo: .init(animated: animated))
|
||||||
}
|
}
|
||||||
case .roomList:
|
case .roomList:
|
||||||
roomFlowCoordinator?.clearRoute(animated: animated)
|
roomFlowCoordinator?.clearRoute(animated: animated)
|
||||||
case .roomMemberDetails:
|
case .roomMemberDetails:
|
||||||
roomFlowCoordinator?.handleAppRoute(appRoute, animated: animated)
|
roomFlowCoordinator?.handleAppRoute(appRoute, animated: animated)
|
||||||
|
case .event(let roomID, let eventID):
|
||||||
|
stateMachine.processEvent(.selectRoom(roomID: roomID, entryPoint: .eventID(eventID)), userInfo: .init(animated: animated))
|
||||||
|
case .eventOnRoomAlias(let alias, let eventID):
|
||||||
|
guard let roomID = await userSession.clientProxy.resolveRoomAlias(alias) else { return }
|
||||||
|
await asyncHandleAppRoute(.event(roomID: roomID, eventID: eventID), animated: animated)
|
||||||
|
case .childEvent:
|
||||||
|
roomFlowCoordinator?.handleAppRoute(appRoute, animated: animated)
|
||||||
|
case .childEventOnRoomAlias(let alias, let eventID):
|
||||||
|
guard let roomID = await userSession.clientProxy.resolveRoomAlias(alias) else { return }
|
||||||
|
await asyncHandleAppRoute(.childEvent(roomID: roomID, eventID: eventID), animated: animated)
|
||||||
case .userProfile(let userID):
|
case .userProfile(let userID):
|
||||||
stateMachine.processEvent(.showUserProfileScreen(userID: userID), userInfo: .init(animated: animated))
|
stateMachine.processEvent(.showUserProfileScreen(userID: userID), userInfo: .init(animated: animated))
|
||||||
case .genericCallLink(let url):
|
case .genericCallLink(let url):
|
||||||
@@ -266,14 +266,18 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
case (.initial, .start, .roomList):
|
case (.initial, .start, .roomList):
|
||||||
presentHomeScreen()
|
presentHomeScreen()
|
||||||
attemptStartingOnboarding()
|
attemptStartingOnboarding()
|
||||||
|
case(.roomList(let selectedRoomID), .selectRoom(let roomID, let entryPoint), .roomList):
|
||||||
case(.roomList(let selectedRoomID), .selectRoom(let roomID, let showingRoomDetails), .roomList):
|
|
||||||
if selectedRoomID == roomID {
|
if selectedRoomID == roomID {
|
||||||
if let roomFlowCoordinator {
|
if let roomFlowCoordinator {
|
||||||
roomFlowCoordinator.handleAppRoute(.room(roomID: roomID), animated: animated)
|
let route: AppRoute = switch entryPoint {
|
||||||
|
case .room: .room(roomID: roomID)
|
||||||
|
case .eventID(let eventID): .event(roomID: roomID, eventID: eventID)
|
||||||
|
case .roomDetails: .roomDetails(roomID: roomID)
|
||||||
|
}
|
||||||
|
roomFlowCoordinator.handleAppRoute(route, animated: animated)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Task { await self.startRoomFlow(roomID: roomID, showingRoomDetails: showingRoomDetails, animated: animated) }
|
Task { await self.startRoomFlow(roomID: roomID, entryPoint: entryPoint, animated: animated) }
|
||||||
}
|
}
|
||||||
case(.roomList, .deselectRoom, .roomList):
|
case(.roomList, .deselectRoom, .roomList):
|
||||||
dismissRoomFlow(animated: animated)
|
dismissRoomFlow(animated: animated)
|
||||||
@@ -442,7 +446,9 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
|
|
||||||
// MARK: Room Flow
|
// MARK: Room Flow
|
||||||
|
|
||||||
private func startRoomFlow(roomID: String, showingRoomDetails: Bool, animated: Bool) async {
|
private func startRoomFlow(roomID: String,
|
||||||
|
entryPoint: RoomFlowCoordinatorEntryPoint,
|
||||||
|
animated: Bool) async {
|
||||||
let coordinator = await RoomFlowCoordinator(roomID: roomID,
|
let coordinator = await RoomFlowCoordinator(roomID: roomID,
|
||||||
userSession: userSession,
|
userSession: userSession,
|
||||||
isChildFlow: false,
|
isChildFlow: false,
|
||||||
@@ -461,7 +467,7 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
case .finished:
|
case .finished:
|
||||||
stateMachine.processEvent(.deselectRoom)
|
stateMachine.processEvent(.deselectRoom)
|
||||||
case .presentRoom(let roomID):
|
case .presentRoom(let roomID):
|
||||||
stateMachine.processEvent(.selectRoom(roomID: roomID, showingRoomDetails: false))
|
stateMachine.processEvent(.selectRoom(roomID: roomID, entryPoint: .room))
|
||||||
case .presentCallScreen(let roomProxy):
|
case .presentCallScreen(let roomProxy):
|
||||||
presentCallScreen(roomProxy: roomProxy)
|
presentCallScreen(roomProxy: roomProxy)
|
||||||
}
|
}
|
||||||
@@ -474,10 +480,13 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
navigationSplitCoordinator.setDetailCoordinator(detailNavigationStackCoordinator, animated: animated)
|
navigationSplitCoordinator.setDetailCoordinator(detailNavigationStackCoordinator, animated: animated)
|
||||||
}
|
}
|
||||||
|
|
||||||
if showingRoomDetails {
|
switch entryPoint {
|
||||||
coordinator.handleAppRoute(.roomDetails(roomID: roomID), animated: animated)
|
case .room:
|
||||||
} else {
|
|
||||||
coordinator.handleAppRoute(.room(roomID: roomID), animated: animated)
|
coordinator.handleAppRoute(.room(roomID: roomID), animated: animated)
|
||||||
|
case .eventID(let eventID):
|
||||||
|
coordinator.handleAppRoute(.event(roomID: roomID, eventID: eventID), animated: animated)
|
||||||
|
case .roomDetails:
|
||||||
|
coordinator.handleAppRoute(.roomDetails(roomID: roomID), animated: animated)
|
||||||
}
|
}
|
||||||
|
|
||||||
let availableInvitesCount = userSession.clientProxy.inviteSummaryProvider?.roomListPublisher.value.count ?? 0
|
let availableInvitesCount = userSession.clientProxy.inviteSummaryProvider?.roomListPublisher.value.count ?? 0
|
||||||
@@ -518,7 +527,7 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
self.navigationSplitCoordinator.setSheetCoordinator(nil)
|
self.navigationSplitCoordinator.setSheetCoordinator(nil)
|
||||||
case .openRoom(let roomID):
|
case .openRoom(let roomID):
|
||||||
self.navigationSplitCoordinator.setSheetCoordinator(nil)
|
self.navigationSplitCoordinator.setSheetCoordinator(nil)
|
||||||
self.stateMachine.processEvent(.selectRoom(roomID: roomID, showingRoomDetails: false))
|
self.stateMachine.processEvent(.selectRoom(roomID: roomID, entryPoint: .room))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
@@ -540,7 +549,7 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
.sink { [weak self] action in
|
.sink { [weak self] action in
|
||||||
switch action {
|
switch action {
|
||||||
case .openRoom(let roomID):
|
case .openRoom(let roomID):
|
||||||
self?.stateMachine.processEvent(.selectRoom(roomID: roomID, showingRoomDetails: false))
|
self?.stateMachine.processEvent(.selectRoom(roomID: roomID, entryPoint: .room))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
@@ -691,7 +700,7 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
switch action {
|
switch action {
|
||||||
case .openDirectChat(let roomID):
|
case .openDirectChat(let roomID):
|
||||||
navigationSplitCoordinator.setSheetCoordinator(nil)
|
navigationSplitCoordinator.setSheetCoordinator(nil)
|
||||||
stateMachine.processEvent(.selectRoom(roomID: roomID, showingRoomDetails: false))
|
stateMachine.processEvent(.selectRoom(roomID: roomID, entryPoint: .room))
|
||||||
case .dismiss:
|
case .dismiss:
|
||||||
navigationSplitCoordinator.setSheetCoordinator(nil)
|
navigationSplitCoordinator.setSheetCoordinator(nil)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,8 +75,9 @@ class UserSessionFlowCoordinatorStateMachine {
|
|||||||
case start
|
case start
|
||||||
|
|
||||||
/// Request presentation for a particular room
|
/// Request presentation for a particular room
|
||||||
/// - Parameter roomID:the room identifier
|
/// - Parameter roomID: The room identifier
|
||||||
case selectRoom(roomID: String, showingRoomDetails: Bool)
|
/// - Parameter entryPoint: The starting point for the presented room.
|
||||||
|
case selectRoom(roomID: String, entryPoint: RoomFlowCoordinatorEntryPoint)
|
||||||
/// The room screen has been dismissed
|
/// The room screen has been dismissed
|
||||||
case deselectRoom
|
case deselectRoom
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import WysiwygComposer
|
|||||||
|
|
||||||
struct RoomScreenCoordinatorParameters {
|
struct RoomScreenCoordinatorParameters {
|
||||||
let roomProxy: RoomProxyProtocol
|
let roomProxy: RoomProxyProtocol
|
||||||
|
var focussedEventID: String?
|
||||||
let timelineController: RoomTimelineControllerProtocol
|
let timelineController: RoomTimelineControllerProtocol
|
||||||
let mediaProvider: MediaProviderProtocol
|
let mediaProvider: MediaProviderProtocol
|
||||||
let mediaPlayerProvider: MediaPlayerProviderProtocol
|
let mediaPlayerProvider: MediaPlayerProviderProtocol
|
||||||
@@ -59,6 +60,7 @@ final class RoomScreenCoordinator: CoordinatorProtocol {
|
|||||||
|
|
||||||
init(parameters: RoomScreenCoordinatorParameters) {
|
init(parameters: RoomScreenCoordinatorParameters) {
|
||||||
viewModel = RoomScreenViewModel(roomProxy: parameters.roomProxy,
|
viewModel = RoomScreenViewModel(roomProxy: parameters.roomProxy,
|
||||||
|
focussedEventID: parameters.focussedEventID,
|
||||||
timelineController: parameters.timelineController,
|
timelineController: parameters.timelineController,
|
||||||
mediaProvider: parameters.mediaProvider,
|
mediaProvider: parameters.mediaProvider,
|
||||||
mediaPlayerProvider: parameters.mediaPlayerProvider,
|
mediaPlayerProvider: parameters.mediaPlayerProvider,
|
||||||
@@ -129,6 +131,10 @@ final class RoomScreenCoordinator: CoordinatorProtocol {
|
|||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func focusOnEvent(eventID: String) {
|
||||||
|
Task { await viewModel.focusOnEvent(eventID: eventID) }
|
||||||
|
}
|
||||||
|
|
||||||
func stop() {
|
func stop() {
|
||||||
viewModel.stop()
|
viewModel.stop()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
|||||||
private var paginateBackwardsTask: Task<Void, Never>?
|
private var paginateBackwardsTask: Task<Void, Never>?
|
||||||
|
|
||||||
init(roomProxy: RoomProxyProtocol,
|
init(roomProxy: RoomProxyProtocol,
|
||||||
|
focussedEventID: String? = nil,
|
||||||
timelineController: RoomTimelineControllerProtocol,
|
timelineController: RoomTimelineControllerProtocol,
|
||||||
mediaProvider: MediaProviderProtocol,
|
mediaProvider: MediaProviderProtocol,
|
||||||
mediaPlayerProvider: MediaPlayerProviderProtocol,
|
mediaPlayerProvider: MediaPlayerProviderProtocol,
|
||||||
@@ -90,6 +91,11 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
|||||||
bindings: .init(reactionsCollapsed: [:])),
|
bindings: .init(reactionsCollapsed: [:])),
|
||||||
imageProvider: mediaProvider)
|
imageProvider: mediaProvider)
|
||||||
|
|
||||||
|
// This may change to load the detached timeline directly.
|
||||||
|
if let focussedEventID {
|
||||||
|
Task { await focusOnEvent(eventID: focussedEventID) }
|
||||||
|
}
|
||||||
|
|
||||||
setupSubscriptions()
|
setupSubscriptions()
|
||||||
setupDirectRoomSubscriptionsIfNeeded()
|
setupDirectRoomSubscriptionsIfNeeded()
|
||||||
|
|
||||||
@@ -208,6 +214,8 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func focusOnEvent(eventID: String) async { }
|
||||||
|
|
||||||
// MARK: - Private
|
// MARK: - Private
|
||||||
|
|
||||||
private func attach(_ attachment: ComposerAttachmentType) {
|
private func attach(_ attachment: ComposerAttachmentType) {
|
||||||
|
|||||||
@@ -23,5 +23,7 @@ protocol RoomScreenViewModelProtocol {
|
|||||||
var actions: AnyPublisher<RoomScreenViewModelAction, Never> { get }
|
var actions: AnyPublisher<RoomScreenViewModelAction, Never> { get }
|
||||||
var context: RoomScreenViewModelType.Context { get }
|
var context: RoomScreenViewModelType.Context { get }
|
||||||
func process(composerAction: ComposerToolbarViewModelAction)
|
func process(composerAction: ComposerToolbarViewModelAction)
|
||||||
|
/// Updates the timeline to show and highlight the item with the corresponding event ID.
|
||||||
|
func focusOnEvent(eventID: String) async
|
||||||
func stop()
|
func stop()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user