From 99c3cd787226b78ba4737417b78e7f5223a7c31f Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Wed, 28 May 2025 15:16:16 +0300 Subject: [PATCH] Move `RoomFlowCoordinator` state machine elements to a separate file. --- ElementX.xcodeproj/project.pbxproj | 4 + .../RoomFlowCoordinator.swift | 322 +---------------- .../RoomFlowCoordinatorStateMachine.swift | 334 ++++++++++++++++++ 3 files changed, 339 insertions(+), 321 deletions(-) create mode 100644 ElementX/Sources/FlowCoordinators/RoomFlowCoordinatorStateMachine.swift diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index d8dfb16b6..a4eee016c 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -745,6 +745,7 @@ 8DCA1F05C3BA6ED826F1599D /* RoomSelectionScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B4E3F1265581683E4997B8 /* RoomSelectionScreenViewModel.swift */; }; 8DCD9CC5361FF22A5B2C20F1 /* AppLockSetupSettingsScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D9FCE4D1E3A81AC1CC5CB91 /* AppLockSetupSettingsScreenCoordinator.swift */; }; 8DDC6F28C797D8685F2F8E32 /* AnalyticsConsentState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57B6B383F1FD04CC0E7B60C6 /* AnalyticsConsentState.swift */; }; + 8DF0EBD97753033C715D716E /* RoomFlowCoordinatorStateMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 407C8DD85179D2DB896FC0FA /* RoomFlowCoordinatorStateMachine.swift */; }; 8E650379587C31D7912ED67B /* UNNotification+Creator.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC0AEA686E425F86F6BA0404 /* UNNotification+Creator.swift */; }; 8E7A902CA16E24928F83646C /* ElementCallServiceMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = E321E840DCC63790049984F4 /* ElementCallServiceMock.swift */; }; 8ED8AF57A06F5EE9978ED23F /* AuthenticationStartScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FB89DC7F9A4A91020037001 /* AuthenticationStartScreenViewModelTests.swift */; }; @@ -1695,6 +1696,7 @@ 40316EFFEAC7B206EE9A55AE /* SecureBackupScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureBackupScreenViewModelTests.swift; sourceTree = ""; }; 4048547AC50ADCF201684E87 /* EditRoomAddressScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditRoomAddressScreen.swift; sourceTree = ""; }; 406C90AF8C3E98DF5D4E5430 /* ElementCallServiceConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ElementCallServiceConstants.swift; sourceTree = ""; }; + 407C8DD85179D2DB896FC0FA /* RoomFlowCoordinatorStateMachine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomFlowCoordinatorStateMachine.swift; sourceTree = ""; }; 40A66E8BC8D9AE4A08EFB2DF /* RoomInfoProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomInfoProxy.swift; sourceTree = ""; }; 40B21E611DADDEF00307E7AC /* String.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = String.swift; sourceTree = ""; }; 4100DDE6BF3C566AB66B80CC /* MentionSuggestionItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MentionSuggestionItemView.swift; sourceTree = ""; }; @@ -4000,6 +4002,7 @@ C3285BD95B564CA2A948E511 /* OnboardingFlowCoordinator.swift */, A54AAF72E821B4084B7E4298 /* PinnedEventsTimelineFlowCoordinator.swift */, 9A008E57D52B07B78DFAD1BB /* RoomFlowCoordinator.swift */, + 407C8DD85179D2DB896FC0FA /* RoomFlowCoordinatorStateMachine.swift */, 0833F51229E166BCA141D004 /* RoomRolesAndPermissionsFlowCoordinator.swift */, D28F7A6CEEA4A2815B0F0F55 /* SettingsFlowCoordinator.swift */, C99FDEEB71173C4C6FA2734C /* UserSessionFlowCoordinator.swift */, @@ -7529,6 +7532,7 @@ 1DC227816777A2F3A19657E5 /* RoomDirectorySearchScreenViewModel.swift in Sources */, 42F1C8731166633E35A6D7E6 /* RoomEventStringBuilder.swift in Sources */, D55AF9B5B55FEED04771A461 /* RoomFlowCoordinator.swift in Sources */, + 8DF0EBD97753033C715D716E /* RoomFlowCoordinatorStateMachine.swift in Sources */, 9C63171267E22FEB288EC860 /* RoomHeaderView.swift in Sources */, 5C8804B4F25903516E2DAB81 /* RoomInfoProxy.swift in Sources */, 8A83D715940378B9BA9F739E /* RoomInviterLabel.swift in Sources */, diff --git a/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift index 6dbbfe1a8..553e9c1e0 100644 --- a/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift +++ b/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift @@ -51,29 +51,6 @@ struct FocusEvent: Hashable { let shouldSetPin: Bool } -private enum PresentationAction: Hashable { - case eventFocus(FocusEvent) - case share(ShareExtensionPayload) - - var focusedEvent: FocusEvent? { - switch self { - case .eventFocus(let focusEvent): - focusEvent - default: - nil - } - } - - var sharedText: String? { - switch self { - case .share(.text(_, let text)): - text - default: - nil - } - } -} - // swiftlint:disable:next type_body_length class RoomFlowCoordinator: FlowCoordinatorProtocol { private let roomID: String @@ -290,163 +267,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol { // swiftlint:disable:next function_body_length private func setupStateMachine() { - stateMachine.addRouteMapping { event, fromState, _ in - switch (fromState, event) { - case (_, .presentJoinRoomScreen): - return .joinRoomScreen - case (_, .dismissJoinRoomScreen): - return .complete - - case (_, .presentRoom): - return .room - case (_, .dismissFlow): - return .complete - - case (_, .presentThread(let itemID)): - return .thread(itemID: itemID) - case (_, .dismissThread): - return .room - - case (.initial, .presentRoomDetails): - return .roomDetails(isRoot: true) - case (.room, .presentRoomDetails): - return .roomDetails(isRoot: false) - case (.roomDetails, .dismissRoomDetails): - return .room - - case (.roomDetails, .presentRoomDetailsEditScreen): - return .roomDetailsEditScreen - case (.roomDetailsEditScreen, .dismissRoomDetailsEditScreen): - return .roomDetails(isRoot: false) - - case (.roomDetails, .presentNotificationSettingsScreen): - return .notificationSettings - case (.notificationSettings, .dismissNotificationSettingsScreen): - return .roomDetails(isRoot: false) - - case (.notificationSettings, .presentGlobalNotificationSettingsScreen): - return .globalNotificationSettings - case (.globalNotificationSettings, .dismissGlobalNotificationSettingsScreen): - return .notificationSettings - - case (.roomDetails, .presentRoomMembersList): - return .roomMembersList - case (.roomMembersList, .dismissRoomMembersList): - return .roomDetails(isRoot: false) - - case (_, .presentRoomMemberDetails(userID: let userID)): - return .roomMemberDetails(userID: userID, previousState: fromState) - case (.roomMemberDetails(_, let previousState), .dismissRoomMemberDetails): - return previousState - - case (.roomMemberDetails(_, let previousState), .presentUserProfile(let userID)): - return .userProfile(userID: userID, previousState: previousState) - case (.userProfile(_, let previousState), .dismissUserProfile): - return previousState - - case (_, .presentInviteUsersScreen): - return .inviteUsersScreen(previousState: fromState) - case (.inviteUsersScreen(let previousState), .dismissInviteUsersScreen): - return previousState - - case (.room, .presentReportContent(let itemID, let senderID)): - return .reportContent(itemID: itemID, senderID: senderID) - case (.reportContent, .dismissReportContent): - return .room - - case (.room, .presentMediaUploadPicker(let source)): - return .mediaUploadPicker(source: source) - case (.mediaUploadPicker, .dismissMediaUploadPicker): - return .room - - case (.mediaUploadPicker, .presentMediaUploadPreview(let fileURL)): - return .mediaUploadPreview(fileURL: fileURL) - case (.room, .presentMediaUploadPreview(let fileURL)): - return .mediaUploadPreview(fileURL: fileURL) - case (.mediaUploadPreview, .dismissMediaUploadPreview): - return .room - - case (.room, .presentEmojiPicker(let itemID, let selectedEmoji)): - return .emojiPicker(itemID: itemID, selectedEmojis: selectedEmoji) - case (.emojiPicker, .dismissEmojiPicker): - return .room - - case (.room, .presentMessageForwarding(let forwardingItem)): - return .messageForwarding(forwardingItem: forwardingItem) - case (.messageForwarding, .dismissMessageForwarding): - return .room - - case (.room, .presentMapNavigator): - return .mapNavigator - case (.mapNavigator, .dismissMapNavigator): - return .room - - case (.room, .presentPollForm): - return .pollForm - case (.pollForm, .dismissPollForm): - return .room - - case (.room, .presentPinnedEventsTimeline): - return .pinnedEventsTimeline(previousState: fromState) - case (.roomDetails, .presentPinnedEventsTimeline): - return .pinnedEventsTimeline(previousState: fromState) - case (.pinnedEventsTimeline(let previousState), .dismissPinnedEventsTimeline): - return previousState - - case (.roomDetails, .presentPollsHistory): - return .pollsHistory - case (.pollsHistory, .dismissPollsHistory): - return .roomDetails(isRoot: false) - - case (.pollsHistory, .presentPollForm): - return .pollsHistoryForm - case (.pollsHistoryForm, .dismissPollForm): - return .pollsHistory - - case (.roomDetails, .presentRolesAndPermissionsScreen): - return .rolesAndPermissions - case (.rolesAndPermissions, .dismissRolesAndPermissionsScreen): - return .roomDetails(isRoot: false) - - case (.room, .presentResolveSendFailure): - return .resolveSendFailure - case (.resolveSendFailure, .dismissResolveSendFailure): - return .room - - case (_, .startChildFlow(let roomID, _, _)): - return .presentingChild(childRoomID: roomID, previousState: fromState) - case (.presentingChild(_, let previousState), .dismissChildFlow): - return previousState - - case (_, .presentKnockRequestsListScreen): - return .knockRequestsList(previousState: fromState) - case (.knockRequestsList(let previousState), .dismissKnockRequestsListScreen): - return previousState - - case (.roomDetails, .presentMediaEventsTimeline): - return .mediaEventsTimeline(previousState: fromState) - case (.mediaEventsTimeline(let previousState), .dismissMediaEventsTimeline): - return previousState - - case (.roomDetails, .presentSecurityAndPrivacyScreen): - return .securityAndPrivacy(previousState: fromState) - case (.securityAndPrivacy(let previousState), .dismissSecurityAndPrivacyScreen): - return previousState - - case (.roomDetails, .presentReportRoomScreen): - return .reportRoom(previousState: fromState) - case (.reportRoom(let previousState), .dismissReportRoomScreen): - return previousState - - case (.joinRoomScreen, .presentDeclineAndBlockScreen): - return .declineAndBlockScreen - case (.declineAndBlockScreen, .dismissDeclineAndBlockScreen): - return .joinRoomScreen - - default: - return nil - } - } + addRouteMapping(stateMachine: stateMachine) stateMachine.addAnyHandler(.any => .any) { [weak self] context in guard let self else { return } @@ -1804,144 +1625,3 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol { userIndicatorController.retractIndicatorWithId(Self.loadingIndicatorID) } } - -private extension RoomFlowCoordinator { - struct HashableRoomMemberWrapper: Hashable { - let value: RoomMemberProxyProtocol - - static func == (lhs: HashableRoomMemberWrapper, rhs: HashableRoomMemberWrapper) -> Bool { - lhs.value.userID == rhs.value.userID - } - - func hash(into hasher: inout Hasher) { - hasher.combine(value.userID) - } - } - - indirect enum State: StateType { - case initial - case joinRoomScreen - case room - case thread(itemID: TimelineItemIdentifier) - case roomDetails(isRoot: Bool) - case roomDetailsEditScreen - case notificationSettings - case globalNotificationSettings - case roomMembersList - case roomMemberDetails(userID: String, previousState: State) - case userProfile(userID: String, previousState: State) - case inviteUsersScreen(previousState: State) - case mediaUploadPicker(source: MediaPickerScreenSource) - case mediaUploadPreview(fileURL: URL) - case emojiPicker(itemID: TimelineItemIdentifier, selectedEmojis: Set) - case mapNavigator - case messageForwarding(forwardingItem: MessageForwardingItem) - case reportContent(itemID: TimelineItemIdentifier, senderID: String) - case pollForm - case pollsHistory - case pollsHistoryForm - case rolesAndPermissions - case pinnedEventsTimeline(previousState: State) - case resolveSendFailure - case knockRequestsList(previousState: State) - case mediaEventsTimeline(previousState: State) - case securityAndPrivacy(previousState: State) - case reportRoom(previousState: State) - case declineAndBlockScreen - - /// A child flow is in progress. - case presentingChild(childRoomID: String, previousState: State) - /// The flow is complete and is handing control of the stack back to its parent. - case complete - } - - struct EventUserInfo { - let animated: Bool - } - - enum Event: EventType { - case presentJoinRoomScreen(via: [String]) - case dismissJoinRoomScreen - - case presentRoom(presentationAction: PresentationAction?) - case dismissFlow - - case presentThread(itemID: TimelineItemIdentifier) - case dismissThread - - case presentReportContent(itemID: TimelineItemIdentifier, senderID: String) - case dismissReportContent - - case presentRoomDetails - case dismissRoomDetails - - case presentRoomDetailsEditScreen - case dismissRoomDetailsEditScreen - - case presentNotificationSettingsScreen - case dismissNotificationSettingsScreen - - case presentGlobalNotificationSettingsScreen - case dismissGlobalNotificationSettingsScreen - - case presentRoomMembersList - case dismissRoomMembersList - - case presentRoomMemberDetails(userID: String) - case dismissRoomMemberDetails - - case presentUserProfile(userID: String) - case dismissUserProfile - - case presentInviteUsersScreen - case dismissInviteUsersScreen - - case presentMediaUploadPicker(source: MediaPickerScreenSource) - case dismissMediaUploadPicker - - case presentMediaUploadPreview(fileURL: URL) - case dismissMediaUploadPreview - - case presentEmojiPicker(itemID: TimelineItemIdentifier, selectedEmojis: Set) - case dismissEmojiPicker - - case presentMapNavigator(interactionMode: StaticLocationInteractionMode) - case dismissMapNavigator - - case presentMessageForwarding(forwardingItem: MessageForwardingItem) - case dismissMessageForwarding - - case presentPollForm(mode: PollFormMode) - case dismissPollForm - - case presentPollsHistory - case dismissPollsHistory - - case presentRolesAndPermissionsScreen - case dismissRolesAndPermissionsScreen - - case presentPinnedEventsTimeline - case dismissPinnedEventsTimeline - - case presentResolveSendFailure(failure: TimelineItemSendFailure.VerifiedUser, sendHandle: SendHandleProxy) - case dismissResolveSendFailure - - case startChildFlow(roomID: String, via: [String], entryPoint: RoomFlowCoordinatorEntryPoint) - case dismissChildFlow - - case presentKnockRequestsListScreen - case dismissKnockRequestsListScreen - - case presentMediaEventsTimeline - case dismissMediaEventsTimeline - - case presentSecurityAndPrivacyScreen - case dismissSecurityAndPrivacyScreen - - case presentReportRoomScreen - case dismissReportRoomScreen - - case presentDeclineAndBlockScreen(userID: String) - case dismissDeclineAndBlockScreen - } -} diff --git a/ElementX/Sources/FlowCoordinators/RoomFlowCoordinatorStateMachine.swift b/ElementX/Sources/FlowCoordinators/RoomFlowCoordinatorStateMachine.swift new file mode 100644 index 000000000..d98074905 --- /dev/null +++ b/ElementX/Sources/FlowCoordinators/RoomFlowCoordinatorStateMachine.swift @@ -0,0 +1,334 @@ +// +// Copyright 2025 New Vector Ltd. +// +// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +// Please see LICENSE files in the repository root for full details. +// + +import Foundation +import SwiftState + +extension RoomFlowCoordinator { + struct HashableRoomMemberWrapper: Hashable { + let value: RoomMemberProxyProtocol + + static func == (lhs: HashableRoomMemberWrapper, rhs: HashableRoomMemberWrapper) -> Bool { + lhs.value.userID == rhs.value.userID + } + + func hash(into hasher: inout Hasher) { + hasher.combine(value.userID) + } + } + + enum PresentationAction: Hashable { + case eventFocus(FocusEvent) + case share(ShareExtensionPayload) + + var focusedEvent: FocusEvent? { + switch self { + case .eventFocus(let focusEvent): + focusEvent + default: + nil + } + } + + var sharedText: String? { + switch self { + case .share(.text(_, let text)): + text + default: + nil + } + } + } + + indirect enum State: StateType { + case initial + case joinRoomScreen + case room + case thread(itemID: TimelineItemIdentifier) + case roomDetails(isRoot: Bool) + case roomDetailsEditScreen + case notificationSettings + case globalNotificationSettings + case roomMembersList + case roomMemberDetails(userID: String, previousState: State) + case userProfile(userID: String, previousState: State) + case inviteUsersScreen(previousState: State) + case mediaUploadPicker(source: MediaPickerScreenSource) + case mediaUploadPreview(fileURL: URL) + case emojiPicker(itemID: TimelineItemIdentifier, selectedEmojis: Set) + case mapNavigator + case messageForwarding(forwardingItem: MessageForwardingItem) + case reportContent(itemID: TimelineItemIdentifier, senderID: String) + case pollForm + case pollsHistory + case pollsHistoryForm + case rolesAndPermissions + case pinnedEventsTimeline(previousState: State) + case resolveSendFailure + case knockRequestsList(previousState: State) + case mediaEventsTimeline(previousState: State) + case securityAndPrivacy(previousState: State) + case reportRoom(previousState: State) + case declineAndBlockScreen + + /// A child flow is in progress. + case presentingChild(childRoomID: String, previousState: State) + /// The flow is complete and is handing control of the stack back to its parent. + case complete + } + + struct EventUserInfo { + let animated: Bool + } + + enum Event: EventType { + case presentJoinRoomScreen(via: [String]) + case dismissJoinRoomScreen + + case presentRoom(presentationAction: PresentationAction?) + case dismissFlow + + case presentThread(itemID: TimelineItemIdentifier) + case dismissThread + + case presentReportContent(itemID: TimelineItemIdentifier, senderID: String) + case dismissReportContent + + case presentRoomDetails + case dismissRoomDetails + + case presentRoomDetailsEditScreen + case dismissRoomDetailsEditScreen + + case presentNotificationSettingsScreen + case dismissNotificationSettingsScreen + + case presentGlobalNotificationSettingsScreen + case dismissGlobalNotificationSettingsScreen + + case presentRoomMembersList + case dismissRoomMembersList + + case presentRoomMemberDetails(userID: String) + case dismissRoomMemberDetails + + case presentUserProfile(userID: String) + case dismissUserProfile + + case presentInviteUsersScreen + case dismissInviteUsersScreen + + case presentMediaUploadPicker(source: MediaPickerScreenSource) + case dismissMediaUploadPicker + + case presentMediaUploadPreview(fileURL: URL) + case dismissMediaUploadPreview + + case presentEmojiPicker(itemID: TimelineItemIdentifier, selectedEmojis: Set) + case dismissEmojiPicker + + case presentMapNavigator(interactionMode: StaticLocationInteractionMode) + case dismissMapNavigator + + case presentMessageForwarding(forwardingItem: MessageForwardingItem) + case dismissMessageForwarding + + case presentPollForm(mode: PollFormMode) + case dismissPollForm + + case presentPollsHistory + case dismissPollsHistory + + case presentRolesAndPermissionsScreen + case dismissRolesAndPermissionsScreen + + case presentPinnedEventsTimeline + case dismissPinnedEventsTimeline + + case presentResolveSendFailure(failure: TimelineItemSendFailure.VerifiedUser, sendHandle: SendHandleProxy) + case dismissResolveSendFailure + + case startChildFlow(roomID: String, via: [String], entryPoint: RoomFlowCoordinatorEntryPoint) + case dismissChildFlow + + case presentKnockRequestsListScreen + case dismissKnockRequestsListScreen + + case presentMediaEventsTimeline + case dismissMediaEventsTimeline + + case presentSecurityAndPrivacyScreen + case dismissSecurityAndPrivacyScreen + + case presentReportRoomScreen + case dismissReportRoomScreen + + case presentDeclineAndBlockScreen(userID: String) + case dismissDeclineAndBlockScreen + } + + // swiftlint:disable:next function_body_length + func addRouteMapping(stateMachine: StateMachine) { + stateMachine.addRouteMapping { event, fromState, _ in + switch (fromState, event) { + case (_, .presentJoinRoomScreen): + return .joinRoomScreen + case (_, .dismissJoinRoomScreen): + return .complete + + case (_, .presentRoom): + return .room + case (_, .dismissFlow): + return .complete + + case (_, .presentThread(let itemID)): + return .thread(itemID: itemID) + case (_, .dismissThread): + return .room + + case (.initial, .presentRoomDetails): + return .roomDetails(isRoot: true) + case (.room, .presentRoomDetails): + return .roomDetails(isRoot: false) + case (.roomDetails, .dismissRoomDetails): + return .room + + case (.roomDetails, .presentRoomDetailsEditScreen): + return .roomDetailsEditScreen + case (.roomDetailsEditScreen, .dismissRoomDetailsEditScreen): + return .roomDetails(isRoot: false) + + case (.roomDetails, .presentNotificationSettingsScreen): + return .notificationSettings + case (.notificationSettings, .dismissNotificationSettingsScreen): + return .roomDetails(isRoot: false) + + case (.notificationSettings, .presentGlobalNotificationSettingsScreen): + return .globalNotificationSettings + case (.globalNotificationSettings, .dismissGlobalNotificationSettingsScreen): + return .notificationSettings + + case (.roomDetails, .presentRoomMembersList): + return .roomMembersList + case (.roomMembersList, .dismissRoomMembersList): + return .roomDetails(isRoot: false) + + case (_, .presentRoomMemberDetails(userID: let userID)): + return .roomMemberDetails(userID: userID, previousState: fromState) + case (.roomMemberDetails(_, let previousState), .dismissRoomMemberDetails): + return previousState + + case (.roomMemberDetails(_, let previousState), .presentUserProfile(let userID)): + return .userProfile(userID: userID, previousState: previousState) + case (.userProfile(_, let previousState), .dismissUserProfile): + return previousState + + case (_, .presentInviteUsersScreen): + return .inviteUsersScreen(previousState: fromState) + case (.inviteUsersScreen(let previousState), .dismissInviteUsersScreen): + return previousState + + case (.room, .presentReportContent(let itemID, let senderID)): + return .reportContent(itemID: itemID, senderID: senderID) + case (.reportContent, .dismissReportContent): + return .room + + case (.room, .presentMediaUploadPicker(let source)): + return .mediaUploadPicker(source: source) + case (.mediaUploadPicker, .dismissMediaUploadPicker): + return .room + + case (.mediaUploadPicker, .presentMediaUploadPreview(let fileURL)): + return .mediaUploadPreview(fileURL: fileURL) + case (.room, .presentMediaUploadPreview(let fileURL)): + return .mediaUploadPreview(fileURL: fileURL) + case (.mediaUploadPreview, .dismissMediaUploadPreview): + return .room + + case (.room, .presentEmojiPicker(let itemID, let selectedEmoji)): + return .emojiPicker(itemID: itemID, selectedEmojis: selectedEmoji) + case (.emojiPicker, .dismissEmojiPicker): + return .room + + case (.room, .presentMessageForwarding(let forwardingItem)): + return .messageForwarding(forwardingItem: forwardingItem) + case (.messageForwarding, .dismissMessageForwarding): + return .room + + case (.room, .presentMapNavigator): + return .mapNavigator + case (.mapNavigator, .dismissMapNavigator): + return .room + + case (.room, .presentPollForm): + return .pollForm + case (.pollForm, .dismissPollForm): + return .room + + case (.room, .presentPinnedEventsTimeline): + return .pinnedEventsTimeline(previousState: fromState) + case (.roomDetails, .presentPinnedEventsTimeline): + return .pinnedEventsTimeline(previousState: fromState) + case (.pinnedEventsTimeline(let previousState), .dismissPinnedEventsTimeline): + return previousState + + case (.roomDetails, .presentPollsHistory): + return .pollsHistory + case (.pollsHistory, .dismissPollsHistory): + return .roomDetails(isRoot: false) + + case (.pollsHistory, .presentPollForm): + return .pollsHistoryForm + case (.pollsHistoryForm, .dismissPollForm): + return .pollsHistory + + case (.roomDetails, .presentRolesAndPermissionsScreen): + return .rolesAndPermissions + case (.rolesAndPermissions, .dismissRolesAndPermissionsScreen): + return .roomDetails(isRoot: false) + + case (.room, .presentResolveSendFailure): + return .resolveSendFailure + case (.resolveSendFailure, .dismissResolveSendFailure): + return .room + + case (_, .startChildFlow(let roomID, _, _)): + return .presentingChild(childRoomID: roomID, previousState: fromState) + case (.presentingChild(_, let previousState), .dismissChildFlow): + return previousState + + case (_, .presentKnockRequestsListScreen): + return .knockRequestsList(previousState: fromState) + case (.knockRequestsList(let previousState), .dismissKnockRequestsListScreen): + return previousState + + case (.roomDetails, .presentMediaEventsTimeline): + return .mediaEventsTimeline(previousState: fromState) + case (.mediaEventsTimeline(let previousState), .dismissMediaEventsTimeline): + return previousState + + case (.roomDetails, .presentSecurityAndPrivacyScreen): + return .securityAndPrivacy(previousState: fromState) + case (.securityAndPrivacy(let previousState), .dismissSecurityAndPrivacyScreen): + return previousState + + case (.roomDetails, .presentReportRoomScreen): + return .reportRoom(previousState: fromState) + case (.reportRoom(let previousState), .dismissReportRoomScreen): + return previousState + + case (.joinRoomScreen, .presentDeclineAndBlockScreen): + return .declineAndBlockScreen + case (.declineAndBlockScreen, .dismissDeclineAndBlockScreen): + return .joinRoomScreen + + default: + return nil + } + } + } +}