Space Settings: Leave Room (#4700)

* Implementation for all navigations inside the space settings aside the left space action

# Conflicts:
#	ElementX/Sources/FlowCoordinators/SpaceSettingsFlowCoordinator.swift

* refactored the leave space view to use its own view model

# Conflicts:
#	ElementX.xcodeproj/project.pbxproj

* implemented the leave space view model also in the settings screen, and corrected some tests

* reusing the details coordinator for the space settings screen

* leave space from settings implemented

* fix project

* minor pr fixes

* code improvements
This commit is contained in:
Mauro
2025-11-07 12:11:21 +01:00
committed by GitHub
parent 8974a4584a
commit 096e2d1713
22 changed files with 407 additions and 277 deletions
@@ -43,6 +43,7 @@ class SpaceFlowCoordinator: FlowCoordinatorProtocol {
private var roomFlowCoordinator: RoomFlowCoordinator?
private var membersFlowCoordinator: RoomMembersFlowCoordinator?
private var settingsFlowCoordinator: SpaceSettingsFlowCoordinator?
private var rolesAndPermissionsFlowCoordinator: RoomRolesAndPermissionsFlowCoordinator?
indirect enum State: StateType {
/// The state machine hasn't started.
@@ -60,6 +61,8 @@ class SpaceFlowCoordinator: FlowCoordinatorProtocol {
/// A space settings flow is in progress
case settingsFlow
case rolesAndPermissionsFlow
case leftSpace
}
@@ -89,6 +92,9 @@ class SpaceFlowCoordinator: FlowCoordinatorProtocol {
case startSettingsFlow
case stopSettingsFlow
case startRolesAndPermissionsFlow
case stopRolesAndPermissionsFlow
}
private let stateMachine: StateMachine<State, Event>
@@ -151,6 +157,9 @@ class SpaceFlowCoordinator: FlowCoordinatorProtocol {
case .settingsFlow:
settingsFlowCoordinator?.clearRoute(animated: animated)
clearRoute(animated: animated) // Re-run with the state machine back in the .space state.
case .rolesAndPermissionsFlow:
rolesAndPermissionsFlowCoordinator?.clearRoute(animated: animated)
clearRoute(animated: animated) // Re-run with the state machine back in the .space state.
}
}
@@ -248,6 +257,22 @@ class SpaceFlowCoordinator: FlowCoordinatorProtocol {
settingsFlowCoordinator = nil
}
stateMachine.addRouteMapping { event, fromState, _ in
guard event == .startRolesAndPermissionsFlow, case .space = fromState else { return nil }
return .rolesAndPermissionsFlow
} handler: { [weak self] context in
guard let self, let roomProxy = context.userInfo as? JoinedRoomProxyProtocol else { return }
startRolesAndPermissionsFlow(roomProxy: roomProxy)
}
stateMachine.addRouteMapping { event, fromState, _ in
guard event == .stopRolesAndPermissionsFlow, case .rolesAndPermissionsFlow = fromState else { return nil }
return .space
} handler: { [weak self] _ in
guard let self else { return }
rolesAndPermissionsFlowCoordinator = nil
}
stateMachine.addErrorHandler { context in
fatalError("Unexpected transition: \(context)")
}
@@ -279,6 +304,8 @@ class SpaceFlowCoordinator: FlowCoordinatorProtocol {
stateMachine.tryEvent(.startMembersFlow, userInfo: roomProxy)
case .displaySpaceSettings(let roomProxy):
stateMachine.tryEvent(.startSettingsFlow, userInfo: roomProxy)
case .displayRolesAndPermissions(let roomProxy):
stateMachine.tryEvent(.startRolesAndPermissionsFlow, userInfo: roomProxy)
}
}
.store(in: &cancellables)
@@ -430,8 +457,11 @@ class SpaceFlowCoordinator: FlowCoordinatorProtocol {
flowCoordinator.actions.sink { [weak self] actions in
guard let self else { return }
switch actions {
case .finished:
case .finished(let leftRoom):
stateMachine.tryEvent(.stopSettingsFlow)
if leftRoom {
stateMachine.tryEvent(.leftSpace)
}
case .presentCallScreen(let roomProxy):
actionsSubject.send(.presentCallScreen(roomProxy: roomProxy))
case .verifyUser(userID: let userID):
@@ -443,4 +473,23 @@ class SpaceFlowCoordinator: FlowCoordinatorProtocol {
settingsFlowCoordinator = flowCoordinator
flowCoordinator.start()
}
private func startRolesAndPermissionsFlow(roomProxy: JoinedRoomProxyProtocol) {
let flowCoordinator = RoomRolesAndPermissionsFlowCoordinator(parameters: .init(roomProxy: roomProxy,
mediaProvider: flowParameters.userSession.mediaProvider,
navigationStackCoordinator: navigationStackCoordinator,
userIndicatorController: flowParameters.userIndicatorController,
analytics: flowParameters.analytics))
flowCoordinator.actionsPublisher.sink { [weak self] action in
guard let self else { return }
switch action {
case .complete:
stateMachine.tryEvent(.stopRolesAndPermissionsFlow)
}
}
.store(in: &cancellables)
rolesAndPermissionsFlowCoordinator = flowCoordinator
flowCoordinator.start()
}
}
@@ -10,7 +10,7 @@ import Foundation
import SwiftState
enum SpaceSettingsFlowCoordinatorAction {
case finished
case finished(leftRoom: Bool)
case presentCallScreen(roomProxy: JoinedRoomProxyProtocol)
case verifyUser(userID: String)
}
@@ -65,6 +65,9 @@ final class SpaceSettingsFlowCoordinator: FlowCoordinatorProtocol {
private let stateMachine: StateMachine<State, Event>
private var cancellables = Set<AnyCancellable>()
private var membersFlowCoordinator: RoomMembersFlowCoordinator?
private var rolesAndPermissionsFlowCoordinator: RoomRolesAndPermissionsFlowCoordinator?
private var childFlowCoordinator: FlowCoordinatorProtocol?
private let actionsSubject: PassthroughSubject<SpaceSettingsFlowCoordinatorAction, Never> = .init()
@@ -178,31 +181,40 @@ final class SpaceSettingsFlowCoordinator: FlowCoordinatorProtocol {
}
private func presentSpaceSettings(animated: Bool) {
let coordinator = SpaceSettingsScreenCoordinator(parameters: .init(roomProxy: roomProxy,
userSession: flowParameters.userSession,
analyticsService: flowParameters.analytics,
userIndicator: flowParameters.userIndicatorController,
notificationSettingsProxy: flowParameters.userSession.clientProxy.notificationSettings,
attributedStringBuilder: AttributedStringBuilder(mentionBuilder: MentionBuilder()),
appSettings: flowParameters.appSettings))
let coordinator = RoomDetailsScreenCoordinator(parameters: .init(roomProxy: roomProxy,
userSession: flowParameters.userSession,
analyticsService: flowParameters.analytics,
userIndicatorController: flowParameters.userIndicatorController,
notificationSettings: flowParameters.userSession.clientProxy.notificationSettings,
attributedStringBuilder: AttributedStringBuilder(mentionBuilder: MentionBuilder()),
appSettings: flowParameters.appSettings))
coordinator.actionsPublisher.sink { [weak self] action in
var leftRoom = false
coordinator.actions.sink { [weak self] action in
guard let self else { return }
switch action {
case .presentEditDetailsScreen:
case .presentRoomDetailsEditScreen:
stateMachine.tryEvent(.presentEditDetailsScreen)
case .presentSecurityAndPrivacyScreen:
stateMachine.tryEvent(.presentSecurityAndPrivacyScreen)
case .presentMembersListScreen:
case .presentRoomMembersList:
stateMachine.tryEvent(.startMembersListFlow)
case .presentRolesAndPermissionsScreen:
stateMachine.tryEvent(.startRolesAndPermissionsFlow)
case .leftRoom:
leftRoom = true
navigationStackCoordinator.pop()
case .presentRecipientDetails, .presentNotificationSettingsScreen, .transferOwnership,
.presentInviteUsersScreen, .presentPollsHistory, .presentCall,
.presentPinnedEventsTimeline, .presentMediaEventsTimeline, .presentKnockingRequestsListScreen,
.presentReportRoomScreen:
fatalError("Not handled in the space context")
}
}
.store(in: &cancellables)
navigationStackCoordinator.push(coordinator, animated: animated) { [weak self] in
self?.actionsSubject.send(.finished)
self?.actionsSubject.send(.finished(leftRoom: leftRoom))
}
}