refactored the flows to use a state and an event for the transfer ownership screen

This commit is contained in:
Mauro Romito
2026-01-28 14:33:28 +01:00
committed by Mauro
parent b3a4929243
commit d2fa71c37f
14 changed files with 116 additions and 27 deletions

View File

@@ -168,7 +168,7 @@ class ChatsTabFlowCoordinator: FlowCoordinatorProtocol {
if case .room(roomID) = stateMachine.state.detailState {
roomFlowCoordinator?.handleAppRoute(appRoute, animated: animated)
} else {
stateMachine.processEvent(.selectRoom(roomID: roomID, via: [], entryPoint: .transferOwnership))
stateMachine.processEvent(.presentTransferOwnershipScreen(roomID: roomID))
}
case .accountProvisioningLink, .settings, .chatBackupSettings, .call, .genericCallLink:
break // These routes cannot be handled.
@@ -251,6 +251,11 @@ class ChatsTabFlowCoordinator: FlowCoordinatorProtocol {
case (.declineAndBlockUserScreen, .dismissedDeclineAndBlockScreen, .roomList):
break
case (.roomList, .presentTransferOwnershipScreen(let roomID), .transferOwnershipScreen):
Task { await self.presentTransferOwnershipScreen(roomID: roomID) }
case (.transferOwnershipScreen, .dismissedTransferOwnershipScreen, .roomList):
break
case (.roomList(let roomListSelectedRoomID), .showShareExtensionRoomList, .shareExtensionRoomList(let sharePayload)):
Task {
if roomListSelectedRoomID != nil {
@@ -469,6 +474,34 @@ class ChatsTabFlowCoordinator: FlowCoordinatorProtocol {
}
}
private func presentTransferOwnershipScreen(roomID: String) async {
guard case let .joined(roomProxy) = await userSession.clientProxy.roomForIdentifier(roomID) else {
return
}
await roomProxy.subscribeForUpdates()
let parameters = RoomChangeRolesScreenCoordinatorParameters(mode: .owner,
roomProxy: roomProxy,
mediaProvider: userSession.mediaProvider,
userIndicatorController: flowParameters.userIndicatorController,
analytics: flowParameters.analytics)
let stackCoordinator = NavigationStackCoordinator()
let coordinator = RoomChangeRolesScreenCoordinator(parameters: parameters)
coordinator.actionsPublisher.sink { [weak self] action in
guard let self else { return }
switch action {
case .complete:
navigationSplitCoordinator.setSheetCoordinator(nil)
}
}
.store(in: &cancellables)
stackCoordinator.setRootCoordinator(coordinator)
navigationSplitCoordinator.setSheetCoordinator(stackCoordinator) { [weak self] in
self?.stateMachine.processEvent(.dismissedTransferOwnershipScreen)
}
}
// MARK: Room Flow
private func startRoomFlow(roomID: String,

View File

@@ -52,6 +52,8 @@ class ChatsTabFlowCoordinatorStateMachine {
case declineAndBlockUserScreen(detailState: DetailState?)
case transferOwnershipScreen(detailState: DetailState?)
/// The state of the currently selected room
var detailState: DetailState? {
switch self {
@@ -65,6 +67,7 @@ class ChatsTabFlowCoordinatorStateMachine {
.logoutConfirmationScreen(let detailState),
.roomDirectorySearchScreen(let detailState),
.reportRoomScreen(let detailState),
.transferOwnershipScreen(let detailState),
.declineAndBlockUserScreen(let detailState):
detailState
}
@@ -134,6 +137,9 @@ class ChatsTabFlowCoordinatorStateMachine {
case presentDeclineAndBlockScreen(userID: String, roomID: String)
case dismissedDeclineAndBlockScreen
case presentTransferOwnershipScreen(roomID: String)
case dismissedTransferOwnershipScreen
}
private let stateMachine: StateMachine<State, Event>
@@ -208,6 +214,11 @@ class ChatsTabFlowCoordinatorStateMachine {
case (.declineAndBlockUserScreen(let detailState), .dismissedDeclineAndBlockScreen):
return .roomList(detailState: detailState)
case (.roomList(let detailState), .presentTransferOwnershipScreen):
return .transferOwnershipScreen(detailState: detailState)
case (.transferOwnershipScreen(let detailState), .dismissedTransferOwnershipScreen):
return .roomList(detailState: detailState)
default:
return nil
}

View File

@@ -214,7 +214,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
await storeAndSubscribeToRoomProxy(roomProxy)
}
presentTransferOwnershipScreen()
stateMachine.tryEvent(.presentTransferOwnershipScreen)
}
}
}
@@ -274,7 +274,9 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
.store(in: &cancellables)
stackCoordinator.setRootCoordinator(coordinator)
navigationStackCoordinator.setSheetCoordinator(stackCoordinator, animated: true)
navigationStackCoordinator.setSheetCoordinator(stackCoordinator) { [weak self] in
self?.stateMachine.tryEvent(.dismissedTransferOwnershipScreen)
}
}
private func handleRoomRoute(roomID: String, via: [String], presentationAction: PresentationAction? = nil, animated: Bool) async {
@@ -531,6 +533,9 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
case (_, .presentInviteUsersScreen, .inviteUsersScreen):
presentInviteUsersScreen()
case (_, .presentTransferOwnershipScreen, .transferOwnershipScreen):
presentTransferOwnershipScreen()
default:
break
@@ -937,7 +942,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
case .presentReportRoomScreen:
stateMachine.tryEvent(.presentReportRoomScreen)
case .transferOwnership:
presentTransferOwnershipScreen()
stateMachine.tryEvent(.presentTransferOwnershipScreen)
}
}
.store(in: &cancellables)

View File

@@ -83,6 +83,7 @@ extension RoomFlowCoordinator {
case manageAuthorizedSpacesScreen(previousState: State)
case reportRoom(previousState: State)
case declineAndBlockScreen
case transferOwnershipScreen(previousState: State)
/// A child flow is in progress.
case presentingChild(childRoomID: String, previousState: State)
@@ -185,6 +186,9 @@ extension RoomFlowCoordinator {
case presentDeclineAndBlockScreen(userID: String)
case dismissDeclineAndBlockScreen
case presentTransferOwnershipScreen
case dismissedTransferOwnershipScreen
case startMembersFlow(entryPoint: RoomMembersFlowCoordinatorEntryPoint)
case stopMembersFlow
}
@@ -392,6 +396,11 @@ extension RoomFlowCoordinator {
return .inviteUsersScreen(previousState: fromState)
case (.inviteUsersScreen(let previousState), .dismissInviteUsersScreen):
return previousState
case (_, .presentTransferOwnershipScreen):
return .transferOwnershipScreen(previousState: fromState)
case (.transferOwnershipScreen(let previousState), .dismissedTransferOwnershipScreen):
return previousState
default:
return nil

View File

@@ -69,9 +69,11 @@ class SpaceFlowCoordinator: FlowCoordinatorProtocol {
case createChildRoomFlow
case leftSpace
case transferOwnership
}
enum Event: EventType {
enum Event: EventType, Equatable, Hashable {
/// The flow is being started.
case start
/// The flow is being started for an unjoined space.
@@ -108,6 +110,9 @@ class SpaceFlowCoordinator: FlowCoordinatorProtocol {
case startCreateChildRoomFlow
case stopCreateChildRoomFlow
case presentTransferOwnership
case dismissedTransferOwnership
}
private let stateMachine: StateMachine<State, Event>
@@ -179,6 +184,9 @@ class SpaceFlowCoordinator: FlowCoordinatorProtocol {
case .createChildRoomFlow:
navigationStackCoordinator.setSheetCoordinator(nil)
clearRoute(animated: animated) // Re-run with the state machine back in the .space state.
case .transferOwnership:
navigationStackCoordinator.setSheetCoordinator(nil)
clearRoute(animated: animated) // Re-run with the state machine back in the .space state.
}
}
@@ -206,6 +214,12 @@ class SpaceFlowCoordinator: FlowCoordinatorProtocol {
}
stateMachine.addRoutes(event: .dismissedAddRooms, transitions: [.addingRooms => .space])
stateMachine.addRoutes(event: .presentTransferOwnership, transitions: [.space => .transferOwnership]) { [weak self] context in
guard let self, let roomProxy = context.userInfo as? JoinedRoomProxyProtocol else { return }
self.presentTransferOwnershipScreen(roomProxy: roomProxy)
}
stateMachine.addRoutes(event: .dismissedTransferOwnership, transitions: [.transferOwnership => .space])
stateMachine.addRouteMapping { event, fromState, userInfo in
guard event == .startChildFlow else { return nil }
guard let childEntryPoint = userInfo as? SpaceFlowCoordinatorEntryPoint else { fatalError("An entry point must be provided.") }
@@ -350,7 +364,7 @@ class SpaceFlowCoordinator: FlowCoordinatorProtocol {
case .displayCreateChildRoomFlow(let space):
stateMachine.tryEvent(.startCreateChildRoomFlow, userInfo: space)
case .displayTransferOwnership(let roomProxy):
presentTransferOwnershipScreen(roomProxy: roomProxy)
stateMachine.tryEvent(.presentTransferOwnership, userInfo: roomProxy)
}
}
.store(in: &cancellables)
@@ -450,16 +464,17 @@ class SpaceFlowCoordinator: FlowCoordinatorProtocol {
let stackCoordinator = NavigationStackCoordinator()
let coordinator = RoomChangeRolesScreenCoordinator(parameters: parameters)
coordinator.actionsPublisher.sink { [weak self] action in
guard let self else { return }
switch action {
case .complete:
navigationStackCoordinator.setSheetCoordinator(nil)
self?.navigationStackCoordinator.setSheetCoordinator(nil)
}
}
.store(in: &cancellables)
stackCoordinator.setRootCoordinator(coordinator)
navigationStackCoordinator.setSheetCoordinator(stackCoordinator)
navigationStackCoordinator.setSheetCoordinator(stackCoordinator) { [weak self] in
self?.stateMachine.tryEvent(.dismissedTransferOwnership)
}
}
// MARK: - Other flows

View File

@@ -35,6 +35,8 @@ final class SpaceSettingsFlowCoordinator: FlowCoordinatorProtocol {
case membersFlow
case manageAuthorizedSpacesScreen
case transferOwnership
}
enum Event: EventType {
@@ -60,6 +62,9 @@ final class SpaceSettingsFlowCoordinator: FlowCoordinatorProtocol {
case presentManageAuthorizedSpacesScreen
case dismissedManageAuthorizedSpacesScreen
case presentTransferOwnership
case dismissedTransferOwnership
}
private let roomProxy: JoinedRoomProxyProtocol
@@ -135,6 +140,11 @@ final class SpaceSettingsFlowCoordinator: FlowCoordinatorProtocol {
return .manageAuthorizedSpacesScreen
case (.manageAuthorizedSpacesScreen, .dismissedManageAuthorizedSpacesScreen):
return .securityAndPrivacy
case (.spaceSettings, .presentTransferOwnership):
return .transferOwnership
case (.transferOwnership, .dismissedTransferOwnership):
return .spaceSettings
case (.spaceSettings, .startMembersListFlow):
return .membersFlow
@@ -181,7 +191,12 @@ final class SpaceSettingsFlowCoordinator: FlowCoordinatorProtocol {
presentManageAuthorizedSpacesScreen(selection: selection)
case (.manageAuthorizedSpacesScreen, .dismissedManageAuthorizedSpacesScreen, .securityAndPrivacy):
break
case (.spaceSettings, .presentTransferOwnership, .transferOwnership):
presentTransferOwnershipScreen()
case (.transferOwnership, .dismissedTransferOwnership, .spaceSettings):
break
case (.spaceSettings, .startMembersListFlow, .membersFlow):
startMembersListFlow()
case (.membersFlow, .stopMembersListFlow, .spaceSettings):
@@ -223,7 +238,7 @@ final class SpaceSettingsFlowCoordinator: FlowCoordinatorProtocol {
leftRoom = true
navigationStackCoordinator.pop()
case .transferOwnership:
presentTransferOwnershipScreen()
stateMachine.tryEvent(.presentTransferOwnership)
case .presentRecipientDetails, .presentNotificationSettingsScreen, .presentReportRoomScreen,
.presentInviteUsersScreen, .presentPollsHistory, .presentCall,
.presentPinnedEventsTimeline, .presentMediaEventsTimeline, .presentKnockingRequestsListScreen:
@@ -336,16 +351,17 @@ final class SpaceSettingsFlowCoordinator: FlowCoordinatorProtocol {
let stackCoordinator = NavigationStackCoordinator()
let coordinator = RoomChangeRolesScreenCoordinator(parameters: parameters)
coordinator.actionsPublisher.sink { [weak self] action in
guard let self else { return }
switch action {
case .complete:
navigationStackCoordinator.setSheetCoordinator(nil)
self?.navigationStackCoordinator.setSheetCoordinator(nil)
}
}
.store(in: &cancellables)
stackCoordinator.setRootCoordinator(coordinator)
navigationStackCoordinator.setSheetCoordinator(stackCoordinator, animated: true)
navigationStackCoordinator.setSheetCoordinator(stackCoordinator) { [weak self] in
self?.stateMachine.tryEvent(.dismissedTransferOwnership)
}
}
// MARK: - Other flows

View File

@@ -99,7 +99,7 @@ struct RoomChangeRolesScreen: View {
.disabled(!context.viewState.hasChanges)
}
if context.viewState.mode == .owner {
if context.viewState.mode == .owner || context.viewState.hasChanges {
ToolbarItem(placement: .cancellationAction) {
ToolbarButton(role: .cancel) {
context.send(viewAction: .cancel)

View File

@@ -66,7 +66,7 @@ struct LeaveSpaceView: View {
Section {
ForEach(context.viewState.leaveHandle.rooms, id: \.spaceServiceRoom.id) { room in
LeaveSpaceRoomDetailsCell(room: room,
hideSelection: !context.viewState.leaveHandle.canLeave,
hideSelection: !room.canLeave,
mediaProvider: context.mediaProvider) {
context.send(viewAction: .toggleRoom(roomID: room.spaceServiceRoom.id))
}

View File

@@ -79,7 +79,7 @@ final class SpaceScreenCoordinator: CoordinatorProtocol {
actionsSubject.send(.addExistingChildren)
case .displayCreateChildRoomFlow(let space):
actionsSubject.send(.displayCreateChildRoomFlow(space: space))
case .presentTransferOwnership(roomProxy: let roomProxy):
case .presentTransferOwnership(let roomProxy):
actionsSubject.send(.displayTransferOwnership(roomProxy: roomProxy))
}
}

View File

@@ -254,7 +254,7 @@ class SpaceScreenViewModel: SpaceScreenViewModelType, SpaceScreenViewModelProtoc
state.bindings.leaveSpaceViewModel = nil
case .presentRolesAndPermissions:
guard let roomProxy = state.roomProxy else {
fatalError("The space screen should always have a room proxy")
fatalError("There should always be a room proxy available for joined spaces.")
}
state.bindings.leaveSpaceViewModel = nil
actionsSubject.send(.presentRolesAndPermissions(roomProxy: roomProxy))
@@ -263,7 +263,7 @@ class SpaceScreenViewModel: SpaceScreenViewModelType, SpaceScreenViewModelProtoc
actionsSubject.send(.leftSpace)
case .presentTransferOwnership:
guard let roomProxy = state.roomProxy else {
fatalError("The space screen should always have a room proxy")
fatalError("There should always be a room proxy available for joined spaces.")
}
state.bindings.leaveSpaceViewModel = nil
actionsSubject.send(.presentTransferOwnership(roomProxy: roomProxy))

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c9ad306e0be48b82c3d289c1900afdf79b2efcf0403f3e86cbb09013e7705a16
size 143185
oid sha256:60e6e432817c3a55835ce744166ff3a9f9af6977109b98abb5c1103a88480ae1
size 138961

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:153e608bd51747e151a6901bfc4214ef355d4f2b422e28c16e888948306c6c70
size 166095
oid sha256:3b6a5f02f6177dd4bb2ea2897365c20339082fdbd08d0851246662937e746afd
size 162011

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7c1946343246044027e21fc27989935ee7fa80768f76542fc8a56c9eca4db792
size 93713
oid sha256:5fcd4c08581c9c01569b6297e7122067e30bf29a5c82ad142f41269d58939fe9
size 90649

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6ed5e1592778ff49f919299c9f7fc2e530827890310f9af77133acdc12633a9f
size 121020
oid sha256:d386b1c14b44d784e78d1f52ade674d4118f3aefc865f83a5c66aaeaff2f5861
size 118139