diff --git a/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift index 7799a0260..21d56c61b 100644 --- a/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift +++ b/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift @@ -79,6 +79,8 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol { private var mediaEventsTimelineFlowCoordinator: MediaEventsTimelineFlowCoordinator? // periphery:ignore - used to avoid deallocation private var childRoomFlowCoordinator: RoomFlowCoordinator? + // periphery:ignore - retaining purpose + private var spaceFlowCoordinator: SpaceFlowCoordinator? private let stateMachine: StateMachine = .init(state: .initial) @@ -257,24 +259,36 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol { switch room { case .joined(let roomProxy): - await storeAndSubscribeToRoomProxy(roomProxy) - guard case let .eventFocus(focusEvent) = presentationAction else { - // If is not a focus event just handle the presentation action directly in `presentRoom` - stateMachine.tryEvent(.presentRoom(presentationAction: presentationAction), userInfo: EventUserInfo(animated: animated)) - return - } - // Otherwise check if the focussed event exists to handle a possible error or theaded event. - switch await roomProxy.loadOrFetchEventDetails(for: focusEvent.eventID) { - case .success(let event): - if flowParameters.appSettings.threadsEnabled, let threadRootEventID = event.threadRootEventId() { - stateMachine.tryEvent(.presentRoom(presentationAction: .eventFocus(.init(eventID: threadRootEventID, shouldSetPin: false))), userInfo: EventUserInfo(animated: animated)) - stateMachine.tryEvent(.presentThread(threadRootEventID: threadRootEventID, focusEventID: focusEvent.eventID), userInfo: EventUserInfo(animated: false)) - } else { - stateMachine.tryEvent(.presentRoom(presentationAction: presentationAction), userInfo: EventUserInfo(animated: animated)) + if roomProxy.infoPublisher.value.isSpace { + switch await userSession.clientProxy.spaceService.spaceRoomList(spaceID: roomProxy.id, parent: nil) { + case .success(let spaceRoomListProxy): + actionsSubject.send(.continueWithSpaceFlow(spaceRoomListProxy)) + case .failure: + showErrorIndicator() + stateMachine.tryEvent(.dismissFlow) + } + } else { + await storeAndSubscribeToRoomProxy(roomProxy) + + guard case let .eventFocus(focusEvent) = presentationAction else { + // If is not a focus event just handle the presentation action directly in `presentRoom` + stateMachine.tryEvent(.presentRoom(presentationAction: presentationAction), userInfo: EventUserInfo(animated: animated)) + return + } + + // Otherwise check if the focussed event exists to handle a possible error or theaded event. + switch await roomProxy.loadOrFetchEventDetails(for: focusEvent.eventID) { + case .success(let event): + if flowParameters.appSettings.threadsEnabled, let threadRootEventID = event.threadRootEventId() { + stateMachine.tryEvent(.presentRoom(presentationAction: .eventFocus(.init(eventID: threadRootEventID, shouldSetPin: false))), userInfo: EventUserInfo(animated: animated)) + stateMachine.tryEvent(.presentThread(threadRootEventID: threadRootEventID, focusEventID: focusEvent.eventID), userInfo: EventUserInfo(animated: false)) + } else { + stateMachine.tryEvent(.presentRoom(presentationAction: presentationAction), userInfo: EventUserInfo(animated: animated)) + } + case .failure: + showErrorIndicator() + stateMachine.tryEvent(.presentRoom(presentationAction: nil), userInfo: EventUserInfo(animated: animated)) } - case .failure: - showErrorIndicator() - stateMachine.tryEvent(.presentRoom(presentationAction: nil), userInfo: EventUserInfo(animated: animated)) } default: stateMachine.tryEvent(.presentJoinRoomScreen(via: via), userInfo: EventUserInfo(animated: animated)) @@ -322,7 +336,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol { .store(in: &cancellables) } - // swiftlint:disable:next function_body_length + // swiftlint:disable:next function_body_length cyclomatic_complexity private func setupStateMachine() { addRouteMapping(stateMachine: stateMachine) @@ -353,6 +367,14 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol { // Thread + Room + case (_, .startSpaceFlow, .spaceFlow): + guard let spaceRoomListProxy = (context.userInfo as? EventUserInfo)?.spaceRoomListProxy else { + fatalError("The space room list proxy is required to present a space.") + } + startSpaceFlow(spaceRoomListProxy: spaceRoomListProxy, animated: animated) + case (.spaceFlow, .finishedSpaceFlow, _): + spaceFlowCoordinator = nil + case (_, .presentReportContent, .reportContent(let itemID, let senderID, _)): presentReportContent(for: itemID, from: senderID) @@ -1521,7 +1543,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol { case .verifyUser(let userID): actionsSubject.send(.verifyUser(userID: userID)) case .continueWithSpaceFlow(let spaceRoomListProxy): - #warning("Present the space as a child.") + stateMachine.tryEvent(.startSpaceFlow, userInfo: EventUserInfo(animated: true, spaceRoomListProxy: spaceRoomListProxy)) case .finished: stateMachine.tryEvent(.dismissChildFlow) } @@ -1610,6 +1632,31 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol { flowCoordinator.start() } + private func startSpaceFlow(spaceRoomListProxy: SpaceRoomListProxyProtocol, animated: Bool) { + let coordinator = SpaceFlowCoordinator(entryPoint: .space(spaceRoomListProxy), + spaceServiceProxy: userSession.clientProxy.spaceService, + isChildFlow: true, + navigationStackCoordinator: navigationStackCoordinator, + flowParameters: flowParameters) + coordinator.actionsPublisher + .sink { [weak self] action in + guard let self else { return } + switch action { + case .presentCallScreen(let roomProxy): + actionsSubject.send(.presentCallScreen(roomProxy: roomProxy)) + case .verifyUser(let userID): + actionsSubject.send(.verifyUser(userID: userID)) + case .finished: + stateMachine.tryEvent(.finishedSpaceFlow) + } + } + .store(in: &cancellables) + + spaceFlowCoordinator = coordinator + + coordinator.start() + } + private static let loadingIndicatorID = "\(RoomFlowCoordinator.self)-Loading" private func showLoadingIndicator(delay: Duration? = nil, diff --git a/ElementX/Sources/FlowCoordinators/RoomFlowCoordinatorStateMachine.swift b/ElementX/Sources/FlowCoordinators/RoomFlowCoordinatorStateMachine.swift index bee112642..16fce582a 100644 --- a/ElementX/Sources/FlowCoordinators/RoomFlowCoordinatorStateMachine.swift +++ b/ElementX/Sources/FlowCoordinators/RoomFlowCoordinatorStateMachine.swift @@ -79,6 +79,9 @@ extension RoomFlowCoordinator { case presentingChild(childRoomID: String, previousState: State) /// The flow is complete and is handing control of the stack back to its parent. case complete + + /// A space flow is in progress + case spaceFlow(previousState: State) } struct EventUserInfo { @@ -98,6 +101,9 @@ extension RoomFlowCoordinator { case presentThread(threadRootEventID: String, focusEventID: String?) case dismissThread + case startSpaceFlow + case finishedSpaceFlow + case presentReportContent(itemID: TimelineItemIdentifier, senderID: String) case dismissReportContent @@ -341,6 +347,11 @@ extension RoomFlowCoordinator { return .presentingChild(childRoomID: roomID, previousState: fromState) case (.presentingChild(_, let previousState), .dismissChildFlow): return previousState + + case (.presentingChild(_, let previousState), .startSpaceFlow): + return .spaceFlow(previousState: previousState) + case (.spaceFlow(let previousState), .finishedSpaceFlow): + return previousState case (_, .presentRoomMemberDetails(userID: let userID)): return .roomMemberDetails(userID: userID, previousState: fromState)