mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-04 10:33:45 +00:00
Squashed commits: Add unit tests and move the state machine into the FlowCoordinator [bb686861] Replace the RoomFlowCoordinator's public interface with just `handleAppRoute` [0d9a4f8d] Remove the navigationStackCoordinator dependency from the roomScreenCoordinator [4b5fbdf2] Allow rooms to be selected from any other state [41dbd127] Move all missing coordinators to the RoomFlowCoordinator and state machines [f32431b7] The UserSessionFlowCoordinator does not need to conform to the CoordinatorProtocol [0f07e87d] Fix leaving a room dismissing the currently selected one when different [138385a2] Rewind the navigation stack when re-selecting the same room (iPad) [0727eb93] Fix presenting different room details from the side menu on iPads [faf4cc60] Fix selecting the same room multiple times [fb3391da] Move room details presentation responsibility to the RoomFlowCoordinator. Fixed invitation flows. [fa2a68d9] Rename RoomTimelineFlowCoordinator -> RoomFlowCoordinator [0c9c06b5] Start moving things away from the RoomScreenCoordinator and into the RoomTimelineFlowCoordinator [86cbbdcc] Introduce a RoomTimelineFlowCoordinator to deal with timeline related operations [9b2381be] Introduce the FlowCoordinatorProtocol
This commit is contained in:
committed by
Stefan Ceriu
parent
f2b7faa183
commit
0082aba9d4
@@ -27,6 +27,7 @@
|
|||||||
072BA9DBA932374CCA300125 /* MessageComposerTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE6C10032A77AE7DC5AA4C50 /* MessageComposerTextField.swift */; };
|
072BA9DBA932374CCA300125 /* MessageComposerTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE6C10032A77AE7DC5AA4C50 /* MessageComposerTextField.swift */; };
|
||||||
08248D02BACA75CDC3B39A96 /* UserNotificationCenterSpy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69219A908D7C22E6EE6689AE /* UserNotificationCenterSpy.swift */; };
|
08248D02BACA75CDC3B39A96 /* UserNotificationCenterSpy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69219A908D7C22E6EE6689AE /* UserNotificationCenterSpy.swift */; };
|
||||||
095C0ACFC234E0550A6404C5 /* AppCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FC803282F9268D49F4ABF14 /* AppCoordinator.swift */; };
|
095C0ACFC234E0550A6404C5 /* AppCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FC803282F9268D49F4ABF14 /* AppCoordinator.swift */; };
|
||||||
|
095D3906CF2F940C2D2D17CC /* RoomFlowCoordinatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FCB2126C091EEF2454B4D56 /* RoomFlowCoordinatorTests.swift */; };
|
||||||
09713669577CDA8D012EE380 /* MatrixRustSDK in Frameworks */ = {isa = PBXBuildFile; productRef = 6647C55D93508C7CE9D954A5 /* MatrixRustSDK */; };
|
09713669577CDA8D012EE380 /* MatrixRustSDK in Frameworks */ = {isa = PBXBuildFile; productRef = 6647C55D93508C7CE9D954A5 /* MatrixRustSDK */; };
|
||||||
09AAF04B27732046C755D914 /* SoftLogoutViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32C5DAA1773F57653BF1C4F9 /* SoftLogoutViewModelTests.swift */; };
|
09AAF04B27732046C755D914 /* SoftLogoutViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32C5DAA1773F57653BF1C4F9 /* SoftLogoutViewModelTests.swift */; };
|
||||||
09C83DDDB07C28364F325209 /* MockRoomTimelineController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52D7074991B3267B26D89B22 /* MockRoomTimelineController.swift */; };
|
09C83DDDB07C28364F325209 /* MockRoomTimelineController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52D7074991B3267B26D89B22 /* MockRoomTimelineController.swift */; };
|
||||||
@@ -111,7 +112,6 @@
|
|||||||
2AA684867C20F62CF03E8698 /* MockUserIndicatorController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13673F95EBA78D40C09CCE35 /* MockUserIndicatorController.swift */; };
|
2AA684867C20F62CF03E8698 /* MockUserIndicatorController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13673F95EBA78D40C09CCE35 /* MockUserIndicatorController.swift */; };
|
||||||
2ABF11717C64054CEF2819A3 /* RoomTimelineController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F85164F9475FF2867F71AAA /* RoomTimelineController.swift */; };
|
2ABF11717C64054CEF2819A3 /* RoomTimelineController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F85164F9475FF2867F71AAA /* RoomTimelineController.swift */; };
|
||||||
2AD59AD5B09498EF8B3B04EC /* InvitesScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8E1BBA73B611EDEEA6E20E05 /* InvitesScreenModels.swift */; };
|
2AD59AD5B09498EF8B3B04EC /* InvitesScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8E1BBA73B611EDEEA6E20E05 /* InvitesScreenModels.swift */; };
|
||||||
2B9AEEC12B1BBE5BD61D0F5E /* UserSessionFlowCoordinatorStateMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3429142FE11930422E7CC1A0 /* UserSessionFlowCoordinatorStateMachine.swift */; };
|
|
||||||
2BA59D0AEFB4B82A2EC2A326 /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = 50009897F60FAE7D63EF5E5B /* Kingfisher */; };
|
2BA59D0AEFB4B82A2EC2A326 /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = 50009897F60FAE7D63EF5E5B /* Kingfisher */; };
|
||||||
2BAA5B222856068158D0B3C6 /* MatrixRustSDK in Frameworks */ = {isa = PBXBuildFile; productRef = B1E8B697DF78FE7F61FC6CA4 /* MatrixRustSDK */; };
|
2BAA5B222856068158D0B3C6 /* MatrixRustSDK in Frameworks */ = {isa = PBXBuildFile; productRef = B1E8B697DF78FE7F61FC6CA4 /* MatrixRustSDK */; };
|
||||||
2C4C750D0039AFABDF24236C /* TemplateScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 342BEBC3C5FC3F9943C41C4C /* TemplateScreenViewModelProtocol.swift */; };
|
2C4C750D0039AFABDF24236C /* TemplateScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 342BEBC3C5FC3F9943C41C4C /* TemplateScreenViewModelProtocol.swift */; };
|
||||||
@@ -125,6 +125,7 @@
|
|||||||
3097A0A867D2B19CE32DAE58 /* UIKitBackgroundTaskService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DF1FFC3336EB23374BBBFCC /* UIKitBackgroundTaskService.swift */; };
|
3097A0A867D2B19CE32DAE58 /* UIKitBackgroundTaskService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DF1FFC3336EB23374BBBFCC /* UIKitBackgroundTaskService.swift */; };
|
||||||
30CC1DB7CE357659C82AA115 /* MediaProviderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85EB16E7FE59A947CA441531 /* MediaProviderProtocol.swift */; };
|
30CC1DB7CE357659C82AA115 /* MediaProviderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85EB16E7FE59A947CA441531 /* MediaProviderProtocol.swift */; };
|
||||||
30CC4F796B27BE8B1DFDBF5A /* NSEUserSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEAA2832D93EC7D2608703FB /* NSEUserSession.swift */; };
|
30CC4F796B27BE8B1DFDBF5A /* NSEUserSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEAA2832D93EC7D2608703FB /* NSEUserSession.swift */; };
|
||||||
|
3113065AABBC14CEAE6843FA /* UserSessionFlowCoordinatorStateMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8774CF614849664B5B3C2A1 /* UserSessionFlowCoordinatorStateMachine.swift */; };
|
||||||
33CAC1226DFB8B5D8447D286 /* GZIP in Frameworks */ = {isa = PBXBuildFile; productRef = 1BCD21310B997A6837B854D6 /* GZIP */; };
|
33CAC1226DFB8B5D8447D286 /* GZIP in Frameworks */ = {isa = PBXBuildFile; productRef = 1BCD21310B997A6837B854D6 /* GZIP */; };
|
||||||
340D39DB87F3800D53A6A621 /* EmojiPickerScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00245D40CD90FD71D6A05239 /* EmojiPickerScreen.swift */; };
|
340D39DB87F3800D53A6A621 /* EmojiPickerScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00245D40CD90FD71D6A05239 /* EmojiPickerScreen.swift */; };
|
||||||
3471204F2CC05D4821C35F23 /* landscape_test_image.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 7A5D2323D7B6BF4913EB7EED /* landscape_test_image.jpg */; };
|
3471204F2CC05D4821C35F23 /* landscape_test_image.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 7A5D2323D7B6BF4913EB7EED /* landscape_test_image.jpg */; };
|
||||||
@@ -175,6 +176,7 @@
|
|||||||
492274DA6691EE985C2FCCAA /* Sentry in Frameworks */ = {isa = PBXBuildFile; productRef = 67E7A6F388D3BF85767609D9 /* Sentry */; };
|
492274DA6691EE985C2FCCAA /* Sentry in Frameworks */ = {isa = PBXBuildFile; productRef = 67E7A6F388D3BF85767609D9 /* Sentry */; };
|
||||||
496CC9D59ACFAB84FD9B3B5F /* AnalyticsPromptScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 840E86A67DB2C92C09771EAD /* AnalyticsPromptScreenModels.swift */; };
|
496CC9D59ACFAB84FD9B3B5F /* AnalyticsPromptScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 840E86A67DB2C92C09771EAD /* AnalyticsPromptScreenModels.swift */; };
|
||||||
49F2E7DD8CAACE09CEECE3E6 /* SeparatorRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6390A6DC140CA3D6865A66FF /* SeparatorRoomTimelineView.swift */; };
|
49F2E7DD8CAACE09CEECE3E6 /* SeparatorRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6390A6DC140CA3D6865A66FF /* SeparatorRoomTimelineView.swift */; };
|
||||||
|
4A618590DEB72C4F186BFED4 /* UserSessionFlowCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C99FDEEB71173C4C6FA2734C /* UserSessionFlowCoordinator.swift */; };
|
||||||
4A85928E27D4C1A548A06EE9 /* StartChatScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 052B2F924572AFD70B5F500E /* StartChatScreenViewModel.swift */; };
|
4A85928E27D4C1A548A06EE9 /* StartChatScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 052B2F924572AFD70B5F500E /* StartChatScreenViewModel.swift */; };
|
||||||
4AAA8606FBA290E23D15422E /* AvatarHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC743C7A85E3171BCBF0A653 /* AvatarHeaderView.swift */; };
|
4AAA8606FBA290E23D15422E /* AvatarHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC743C7A85E3171BCBF0A653 /* AvatarHeaderView.swift */; };
|
||||||
4B978C09567387EF4366BD7A /* MediaLoaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3EF1AC723C2609C7705569CA /* MediaLoaderTests.swift */; };
|
4B978C09567387EF4366BD7A /* MediaLoaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3EF1AC723C2609C7705569CA /* MediaLoaderTests.swift */; };
|
||||||
@@ -335,7 +337,6 @@
|
|||||||
8B41D0357B91CD3B6F6A3BCA /* EmoteRoomTimelineItemContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE378083653EF0C9B5E9D580 /* EmoteRoomTimelineItemContent.swift */; };
|
8B41D0357B91CD3B6F6A3BCA /* EmoteRoomTimelineItemContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE378083653EF0C9B5E9D580 /* EmoteRoomTimelineItemContent.swift */; };
|
||||||
8B76191B9DDD1AC90A6E3A35 /* MediaFileHandleProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEC1D382565A4E9CAC2F14EA /* MediaFileHandleProxy.swift */; };
|
8B76191B9DDD1AC90A6E3A35 /* MediaFileHandleProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEC1D382565A4E9CAC2F14EA /* MediaFileHandleProxy.swift */; };
|
||||||
8B7771E319436E542412A22C /* SlidingSyncListProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 074DA547928E85183066DB4A /* SlidingSyncListProxy.swift */; };
|
8B7771E319436E542412A22C /* SlidingSyncListProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 074DA547928E85183066DB4A /* SlidingSyncListProxy.swift */; };
|
||||||
8B807DC963D1D4155A241BCC /* UserSessionFlowCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F9E67AAB66638C69626866C /* UserSessionFlowCoordinator.swift */; };
|
|
||||||
8BC8EF6705A78946C1F22891 /* SoftLogoutScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71A7D4DDEEE5D2CA0C8D63CD /* SoftLogoutScreen.swift */; };
|
8BC8EF6705A78946C1F22891 /* SoftLogoutScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71A7D4DDEEE5D2CA0C8D63CD /* SoftLogoutScreen.swift */; };
|
||||||
8C454500B8073E1201F801A9 /* MXLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = A34A814CBD56230BC74FFCF4 /* MXLogger.swift */; };
|
8C454500B8073E1201F801A9 /* MXLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = A34A814CBD56230BC74FFCF4 /* MXLogger.swift */; };
|
||||||
8CC12086CBF91A7E10CDC205 /* HomeScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D653265D006E708E4E51AD64 /* HomeScreenCoordinator.swift */; };
|
8CC12086CBF91A7E10CDC205 /* HomeScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D653265D006E708E4E51AD64 /* HomeScreenCoordinator.swift */; };
|
||||||
@@ -539,6 +540,7 @@
|
|||||||
D415764645491F10344FC6AC /* Publisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60F18AECC9D38C2B6D85F99C /* Publisher.swift */; };
|
D415764645491F10344FC6AC /* Publisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60F18AECC9D38C2B6D85F99C /* Publisher.swift */; };
|
||||||
D4ACF3276F5D0DA28D4028C9 /* AnalyticsPromptScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8196D64EB9CF2AF1F43E4ED1 /* AnalyticsPromptScreenViewModelProtocol.swift */; };
|
D4ACF3276F5D0DA28D4028C9 /* AnalyticsPromptScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8196D64EB9CF2AF1F43E4ED1 /* AnalyticsPromptScreenViewModelProtocol.swift */; };
|
||||||
D53B80EF02C1062E68659EDD /* ReportContentViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 086C19086DD16E9B38E25954 /* ReportContentViewModelTests.swift */; };
|
D53B80EF02C1062E68659EDD /* ReportContentViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 086C19086DD16E9B38E25954 /* ReportContentViewModelTests.swift */; };
|
||||||
|
D55AF9B5B55FEED04771A461 /* RoomFlowCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A008E57D52B07B78DFAD1BB /* RoomFlowCoordinator.swift */; };
|
||||||
D5C805F49B2C75DC3793E780 /* EmojiItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A243E04B58DC6E41FDCD82 /* EmojiItem.swift */; };
|
D5C805F49B2C75DC3793E780 /* EmojiItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A243E04B58DC6E41FDCD82 /* EmojiItem.swift */; };
|
||||||
D5EA4C6C80579279770D5804 /* ImageRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A45283CF1DB96E583BECA6 /* ImageRoomTimelineView.swift */; };
|
D5EA4C6C80579279770D5804 /* ImageRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A45283CF1DB96E583BECA6 /* ImageRoomTimelineView.swift */; };
|
||||||
D7CDBAE82782BD0529DECB5F /* AttributedString.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52BD6ED18E2EB61E28C340AD /* AttributedString.swift */; };
|
D7CDBAE82782BD0529DECB5F /* AttributedString.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52BD6ED18E2EB61E28C340AD /* AttributedString.swift */; };
|
||||||
@@ -615,6 +617,7 @@
|
|||||||
F508683B76EF7B23BB2CBD6D /* TimelineItemPlainStylerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94BCC8A9C73C1F838122C645 /* TimelineItemPlainStylerView.swift */; };
|
F508683B76EF7B23BB2CBD6D /* TimelineItemPlainStylerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94BCC8A9C73C1F838122C645 /* TimelineItemPlainStylerView.swift */; };
|
||||||
F519DE17A3A0F760307B2E6D /* InviteUsersScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02D155E09BF961BBA8F85263 /* InviteUsersScreenViewModel.swift */; };
|
F519DE17A3A0F760307B2E6D /* InviteUsersScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02D155E09BF961BBA8F85263 /* InviteUsersScreenViewModel.swift */; };
|
||||||
F587A9AF25A262DE5A7B0369 /* ProgressTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = F28551E81CE3700E5F1EC9B5 /* ProgressTracker.swift */; };
|
F587A9AF25A262DE5A7B0369 /* ProgressTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = F28551E81CE3700E5F1EC9B5 /* ProgressTracker.swift */; };
|
||||||
|
F5D2270B5021D521C0D22E11 /* FlowCoordinatorProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B9FCA1CFD07B8CF9BD21266 /* FlowCoordinatorProtocol.swift */; };
|
||||||
F656F92A63D3DC1978D79427 /* Compound in Frameworks */ = {isa = PBXBuildFile; productRef = 07FEEEDB11543A7DED420F04 /* Compound */; };
|
F656F92A63D3DC1978D79427 /* Compound in Frameworks */ = {isa = PBXBuildFile; productRef = 07FEEEDB11543A7DED420F04 /* Compound */; };
|
||||||
F6F49E37272AD7397CD29A01 /* HomeScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 505208F28007C0FEC14E1FF0 /* HomeScreenViewModelTests.swift */; };
|
F6F49E37272AD7397CD29A01 /* HomeScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 505208F28007C0FEC14E1FF0 /* HomeScreenViewModelTests.swift */; };
|
||||||
F7567DD6635434E8C563BF85 /* AnalyticsClientProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3B97591B2D3D4D67553506D /* AnalyticsClientProtocol.swift */; };
|
F7567DD6635434E8C563BF85 /* AnalyticsClientProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3B97591B2D3D4D67553506D /* AnalyticsClientProtocol.swift */; };
|
||||||
@@ -808,7 +811,6 @@
|
|||||||
33649299575BADC34924ABC6 /* InvitesScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InvitesScreenCoordinator.swift; sourceTree = "<group>"; };
|
33649299575BADC34924ABC6 /* InvitesScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InvitesScreenCoordinator.swift; sourceTree = "<group>"; };
|
||||||
33E49C5C6F802B4D94CA78D1 /* ro */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ro; path = ro.lproj/Localizable.strings; sourceTree = "<group>"; };
|
33E49C5C6F802B4D94CA78D1 /* ro */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ro; path = ro.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
340179A0FC1AD4AEDA7FC134 /* CreateRoomViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateRoomViewModelProtocol.swift; sourceTree = "<group>"; };
|
340179A0FC1AD4AEDA7FC134 /* CreateRoomViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateRoomViewModelProtocol.swift; sourceTree = "<group>"; };
|
||||||
3429142FE11930422E7CC1A0 /* UserSessionFlowCoordinatorStateMachine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSessionFlowCoordinatorStateMachine.swift; sourceTree = "<group>"; };
|
|
||||||
342BEBC3C5FC3F9943C41C4C /* TemplateScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemplateScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
342BEBC3C5FC3F9943C41C4C /* TemplateScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemplateScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
||||||
351E89CE2ED9B73C5CC47955 /* TimelineReactionsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineReactionsView.swift; sourceTree = "<group>"; };
|
351E89CE2ED9B73C5CC47955 /* TimelineReactionsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineReactionsView.swift; sourceTree = "<group>"; };
|
||||||
3558A15CFB934F9229301527 /* RestorationToken.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RestorationToken.swift; sourceTree = "<group>"; };
|
3558A15CFB934F9229301527 /* RestorationToken.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RestorationToken.swift; sourceTree = "<group>"; };
|
||||||
@@ -835,7 +837,6 @@
|
|||||||
3EF1AC723C2609C7705569CA /* MediaLoaderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaLoaderTests.swift; sourceTree = "<group>"; };
|
3EF1AC723C2609C7705569CA /* MediaLoaderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaLoaderTests.swift; sourceTree = "<group>"; };
|
||||||
3F40F48279322E504153AB0D /* MockClientProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockClientProxy.swift; sourceTree = "<group>"; };
|
3F40F48279322E504153AB0D /* MockClientProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockClientProxy.swift; sourceTree = "<group>"; };
|
||||||
3F684BDD23ECEADB3053BA5A /* DeveloperOptionsScreenUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeveloperOptionsScreenUITests.swift; sourceTree = "<group>"; };
|
3F684BDD23ECEADB3053BA5A /* DeveloperOptionsScreenUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeveloperOptionsScreenUITests.swift; sourceTree = "<group>"; };
|
||||||
3F9E67AAB66638C69626866C /* UserSessionFlowCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSessionFlowCoordinator.swift; sourceTree = "<group>"; };
|
|
||||||
3FFDA99C98BE05F43A92343B /* test_pdf.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = test_pdf.pdf; sourceTree = "<group>"; };
|
3FFDA99C98BE05F43A92343B /* test_pdf.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = test_pdf.pdf; sourceTree = "<group>"; };
|
||||||
40B21E611DADDEF00307E7AC /* String.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = String.swift; sourceTree = "<group>"; };
|
40B21E611DADDEF00307E7AC /* String.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = String.swift; sourceTree = "<group>"; };
|
||||||
4132F882A984ED971338EE9D /* ReportContentScreenUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportContentScreenUITests.swift; sourceTree = "<group>"; };
|
4132F882A984ED971338EE9D /* ReportContentScreenUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportContentScreenUITests.swift; sourceTree = "<group>"; };
|
||||||
@@ -871,6 +872,7 @@
|
|||||||
4E47F18A9A077E351CEA10D4 /* TextBasedRoomTimelineViewProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextBasedRoomTimelineViewProtocol.swift; sourceTree = "<group>"; };
|
4E47F18A9A077E351CEA10D4 /* TextBasedRoomTimelineViewProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextBasedRoomTimelineViewProtocol.swift; sourceTree = "<group>"; };
|
||||||
4F0CB536D1C3CC15AA740CC6 /* AuthenticationServiceProxyProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationServiceProxyProtocol.swift; sourceTree = "<group>"; };
|
4F0CB536D1C3CC15AA740CC6 /* AuthenticationServiceProxyProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationServiceProxyProtocol.swift; sourceTree = "<group>"; };
|
||||||
4F1DFE6E746539F33042D3A9 /* FormSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormSection.swift; sourceTree = "<group>"; };
|
4F1DFE6E746539F33042D3A9 /* FormSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormSection.swift; sourceTree = "<group>"; };
|
||||||
|
4FCB2126C091EEF2454B4D56 /* RoomFlowCoordinatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomFlowCoordinatorTests.swift; sourceTree = "<group>"; };
|
||||||
4FD6E621CC5E6D4830D96D2D /* MockMediaProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockMediaProvider.swift; sourceTree = "<group>"; };
|
4FD6E621CC5E6D4830D96D2D /* MockMediaProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockMediaProvider.swift; sourceTree = "<group>"; };
|
||||||
505208F28007C0FEC14E1FF0 /* HomeScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreenViewModelTests.swift; sourceTree = "<group>"; };
|
505208F28007C0FEC14E1FF0 /* HomeScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreenViewModelTests.swift; sourceTree = "<group>"; };
|
||||||
5098DA7799946A61E34A2373 /* FileRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileRoomTimelineItem.swift; sourceTree = "<group>"; };
|
5098DA7799946A61E34A2373 /* FileRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileRoomTimelineItem.swift; sourceTree = "<group>"; };
|
||||||
@@ -961,6 +963,7 @@
|
|||||||
7B04BD3874D736127A8156B8 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Localizable.strings; sourceTree = "<group>"; };
|
7B04BD3874D736127A8156B8 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
7B25F959A434BB9923A3223F /* ExpiringTaskRunner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExpiringTaskRunner.swift; sourceTree = "<group>"; };
|
7B25F959A434BB9923A3223F /* ExpiringTaskRunner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExpiringTaskRunner.swift; sourceTree = "<group>"; };
|
||||||
7B849D2FF2CC12BA411A1651 /* CreateRoomModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateRoomModels.swift; sourceTree = "<group>"; };
|
7B849D2FF2CC12BA411A1651 /* CreateRoomModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateRoomModels.swift; sourceTree = "<group>"; };
|
||||||
|
7B9FCA1CFD07B8CF9BD21266 /* FlowCoordinatorProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FlowCoordinatorProtocol.swift; sourceTree = "<group>"; };
|
||||||
7D0CBC76C80E04345E11F2DB /* Application.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Application.swift; sourceTree = "<group>"; };
|
7D0CBC76C80E04345E11F2DB /* Application.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Application.swift; sourceTree = "<group>"; };
|
||||||
7D25A35764C7B3DB78954AB5 /* RoomTimelineItemFactoryProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineItemFactoryProtocol.swift; sourceTree = "<group>"; };
|
7D25A35764C7B3DB78954AB5 /* RoomTimelineItemFactoryProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineItemFactoryProtocol.swift; sourceTree = "<group>"; };
|
||||||
7D379E13DD9D987470A3C70C /* LoginServerInfoSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginServerInfoSection.swift; sourceTree = "<group>"; };
|
7D379E13DD9D987470A3C70C /* LoginServerInfoSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginServerInfoSection.swift; sourceTree = "<group>"; };
|
||||||
@@ -1025,6 +1028,7 @@
|
|||||||
981663D961C94270FA035FD0 /* Alert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Alert.swift; sourceTree = "<group>"; };
|
981663D961C94270FA035FD0 /* Alert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Alert.swift; sourceTree = "<group>"; };
|
||||||
9873076F224E4CE09D8BD47D /* TemplateScreenUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemplateScreenUITests.swift; sourceTree = "<group>"; };
|
9873076F224E4CE09D8BD47D /* TemplateScreenUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemplateScreenUITests.swift; sourceTree = "<group>"; };
|
||||||
98A2932515EA11D3DD8A3506 /* TimelineItemBubbledStylerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineItemBubbledStylerView.swift; sourceTree = "<group>"; };
|
98A2932515EA11D3DD8A3506 /* TimelineItemBubbledStylerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineItemBubbledStylerView.swift; sourceTree = "<group>"; };
|
||||||
|
9A008E57D52B07B78DFAD1BB /* RoomFlowCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomFlowCoordinator.swift; sourceTree = "<group>"; };
|
||||||
9A22A05E472533ED3C5A31B3 /* NavigationModule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationModule.swift; sourceTree = "<group>"; };
|
9A22A05E472533ED3C5A31B3 /* NavigationModule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationModule.swift; sourceTree = "<group>"; };
|
||||||
9A68BCE6438873D2661D93D0 /* BugReportServiceProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BugReportServiceProtocol.swift; sourceTree = "<group>"; };
|
9A68BCE6438873D2661D93D0 /* BugReportServiceProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BugReportServiceProtocol.swift; sourceTree = "<group>"; };
|
||||||
9B65A314DF40B6BBF775C2BC /* AnalyticsPromptScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsPromptScreenCoordinator.swift; sourceTree = "<group>"; };
|
9B65A314DF40B6BBF775C2BC /* AnalyticsPromptScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsPromptScreenCoordinator.swift; sourceTree = "<group>"; };
|
||||||
@@ -1138,6 +1142,7 @@
|
|||||||
C830A64609CBD152F06E0457 /* NotificationConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationConstants.swift; sourceTree = "<group>"; };
|
C830A64609CBD152F06E0457 /* NotificationConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationConstants.swift; sourceTree = "<group>"; };
|
||||||
C843CF833BF6485B64AC87E1 /* AppRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppRouter.swift; sourceTree = "<group>"; };
|
C843CF833BF6485B64AC87E1 /* AppRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppRouter.swift; sourceTree = "<group>"; };
|
||||||
C8F2A7A4E3F5060F52ACFFB0 /* RedactedRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RedactedRoomTimelineView.swift; sourceTree = "<group>"; };
|
C8F2A7A4E3F5060F52ACFFB0 /* RedactedRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RedactedRoomTimelineView.swift; sourceTree = "<group>"; };
|
||||||
|
C99FDEEB71173C4C6FA2734C /* UserSessionFlowCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSessionFlowCoordinator.swift; sourceTree = "<group>"; };
|
||||||
CA28F29C9F93E93CC3C2C715 /* NavigationRootCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationRootCoordinator.swift; sourceTree = "<group>"; };
|
CA28F29C9F93E93CC3C2C715 /* NavigationRootCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationRootCoordinator.swift; sourceTree = "<group>"; };
|
||||||
CA2A71915C1F075E403F559C /* InvitesScreenCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InvitesScreenCell.swift; sourceTree = "<group>"; };
|
CA2A71915C1F075E403F559C /* InvitesScreenCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InvitesScreenCell.swift; sourceTree = "<group>"; };
|
||||||
CA89A2DD51B6BBE1DA55E263 /* Application.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Application.swift; sourceTree = "<group>"; };
|
CA89A2DD51B6BBE1DA55E263 /* Application.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Application.swift; sourceTree = "<group>"; };
|
||||||
@@ -1208,6 +1213,7 @@
|
|||||||
E6E6BDF9D26DB05C88901416 /* RedactedRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RedactedRoomTimelineItem.swift; sourceTree = "<group>"; };
|
E6E6BDF9D26DB05C88901416 /* RedactedRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RedactedRoomTimelineItem.swift; sourceTree = "<group>"; };
|
||||||
E6FCC416A3BFE73DF7B3E6BF /* RoomTimelineControllerFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineControllerFactory.swift; sourceTree = "<group>"; };
|
E6FCC416A3BFE73DF7B3E6BF /* RoomTimelineControllerFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineControllerFactory.swift; sourceTree = "<group>"; };
|
||||||
E80F9E9B93B6ECE9A937B1C6 /* FormRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormRow.swift; sourceTree = "<group>"; };
|
E80F9E9B93B6ECE9A937B1C6 /* FormRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormRow.swift; sourceTree = "<group>"; };
|
||||||
|
E8774CF614849664B5B3C2A1 /* UserSessionFlowCoordinatorStateMachine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSessionFlowCoordinatorStateMachine.swift; sourceTree = "<group>"; };
|
||||||
E8AE4B3273BA189FDCD4055C /* UserIndicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserIndicator.swift; sourceTree = "<group>"; };
|
E8AE4B3273BA189FDCD4055C /* UserIndicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserIndicator.swift; sourceTree = "<group>"; };
|
||||||
E8CA187FE656EE5A3F6C7DE5 /* UIFont+AttributedStringBuilder.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "UIFont+AttributedStringBuilder.m"; sourceTree = "<group>"; };
|
E8CA187FE656EE5A3F6C7DE5 /* UIFont+AttributedStringBuilder.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "UIFont+AttributedStringBuilder.m"; sourceTree = "<group>"; };
|
||||||
E96ED747FF90332EA1333C22 /* RoomTimelineItemFixtures.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineItemFixtures.swift; sourceTree = "<group>"; };
|
E96ED747FF90332EA1333C22 /* RoomTimelineItemFixtures.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineItemFixtures.swift; sourceTree = "<group>"; };
|
||||||
@@ -1960,6 +1966,16 @@
|
|||||||
path = Other;
|
path = Other;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
593C7129C5927E25AD8B688F /* FlowCoordinators */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
9A008E57D52B07B78DFAD1BB /* RoomFlowCoordinator.swift */,
|
||||||
|
C99FDEEB71173C4C6FA2734C /* UserSessionFlowCoordinator.swift */,
|
||||||
|
E8774CF614849664B5B3C2A1 /* UserSessionFlowCoordinatorStateMachine.swift */,
|
||||||
|
);
|
||||||
|
path = FlowCoordinators;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
595B8797ED6A7489ABDCE384 /* ErrorHandling */ = {
|
595B8797ED6A7489ABDCE384 /* ErrorHandling */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@@ -2203,6 +2219,7 @@
|
|||||||
086C19086DD16E9B38E25954 /* ReportContentViewModelTests.swift */,
|
086C19086DD16E9B38E25954 /* ReportContentViewModelTests.swift */,
|
||||||
00E5B2CBEF8F96424F095508 /* RoomDetailsEditScreenViewModelTests.swift */,
|
00E5B2CBEF8F96424F095508 /* RoomDetailsEditScreenViewModelTests.swift */,
|
||||||
2EFE1922F39398ABFB36DF3F /* RoomDetailsViewModelTests.swift */,
|
2EFE1922F39398ABFB36DF3F /* RoomDetailsViewModelTests.swift */,
|
||||||
|
4FCB2126C091EEF2454B4D56 /* RoomFlowCoordinatorTests.swift */,
|
||||||
EC589E641AE46EFB2962534D /* RoomMemberDetailsViewModelTests.swift */,
|
EC589E641AE46EFB2962534D /* RoomMemberDetailsViewModelTests.swift */,
|
||||||
69B63F817FE305548DB4B512 /* RoomMembersListViewModelTests.swift */,
|
69B63F817FE305548DB4B512 /* RoomMembersListViewModelTests.swift */,
|
||||||
93CF7B19FFCF8EFBE0A8696A /* RoomScreenViewModelTests.swift */,
|
93CF7B19FFCF8EFBE0A8696A /* RoomScreenViewModelTests.swift */,
|
||||||
@@ -2626,6 +2643,7 @@
|
|||||||
CA89A2DD51B6BBE1DA55E263 /* Application.swift */,
|
CA89A2DD51B6BBE1DA55E263 /* Application.swift */,
|
||||||
AC3F82523D6F48B926D6AF68 /* AppSettings.swift */,
|
AC3F82523D6F48B926D6AF68 /* AppSettings.swift */,
|
||||||
B251F5B4511D1CA0BA8361FE /* CoordinatorProtocol.swift */,
|
B251F5B4511D1CA0BA8361FE /* CoordinatorProtocol.swift */,
|
||||||
|
7B9FCA1CFD07B8CF9BD21266 /* FlowCoordinatorProtocol.swift */,
|
||||||
57F95CADD0A5DBD76B990FCB /* ServiceLocator.swift */,
|
57F95CADD0A5DBD76B990FCB /* ServiceLocator.swift */,
|
||||||
780F74C73E826685A9DB289B /* Navigation */,
|
780F74C73E826685A9DB289B /* Navigation */,
|
||||||
);
|
);
|
||||||
@@ -2848,8 +2866,6 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
3558A15CFB934F9229301527 /* RestorationToken.swift */,
|
3558A15CFB934F9229301527 /* RestorationToken.swift */,
|
||||||
3F9E67AAB66638C69626866C /* UserSessionFlowCoordinator.swift */,
|
|
||||||
3429142FE11930422E7CC1A0 /* UserSessionFlowCoordinatorStateMachine.swift */,
|
|
||||||
0E8BDC092D817B68CD9040C5 /* UserSessionStore.swift */,
|
0E8BDC092D817B68CD9040C5 /* UserSessionStore.swift */,
|
||||||
BEBA759D1347CFFB3D84ED1F /* UserSessionStoreProtocol.swift */,
|
BEBA759D1347CFFB3D84ED1F /* UserSessionStoreProtocol.swift */,
|
||||||
);
|
);
|
||||||
@@ -2992,6 +3008,7 @@
|
|||||||
children = (
|
children = (
|
||||||
A78C2592419CA4C76FBA8FD2 /* Application */,
|
A78C2592419CA4C76FBA8FD2 /* Application */,
|
||||||
0787F81684E503024BD0C051 /* Services */,
|
0787F81684E503024BD0C051 /* Services */,
|
||||||
|
593C7129C5927E25AD8B688F /* FlowCoordinators */,
|
||||||
E59565F441830B19DBAE567C /* Screens */,
|
E59565F441830B19DBAE567C /* Screens */,
|
||||||
C0937E3B06A8F0E2DB7C8241 /* Other */,
|
C0937E3B06A8F0E2DB7C8241 /* Other */,
|
||||||
2ECFF6B05DAA37EB10DBF7E8 /* UITests */,
|
2ECFF6B05DAA37EB10DBF7E8 /* UITests */,
|
||||||
@@ -3601,6 +3618,7 @@
|
|||||||
D53B80EF02C1062E68659EDD /* ReportContentViewModelTests.swift in Sources */,
|
D53B80EF02C1062E68659EDD /* ReportContentViewModelTests.swift in Sources */,
|
||||||
9DD84E014ADFB2DD813022D5 /* RoomDetailsEditScreenViewModelTests.swift in Sources */,
|
9DD84E014ADFB2DD813022D5 /* RoomDetailsEditScreenViewModelTests.swift in Sources */,
|
||||||
EA974337FA7D040E7C74FE6E /* RoomDetailsViewModelTests.swift in Sources */,
|
EA974337FA7D040E7C74FE6E /* RoomDetailsViewModelTests.swift in Sources */,
|
||||||
|
095D3906CF2F940C2D2D17CC /* RoomFlowCoordinatorTests.swift in Sources */,
|
||||||
6B31508C6334C617360C2EAB /* RoomMemberDetailsViewModelTests.swift in Sources */,
|
6B31508C6334C617360C2EAB /* RoomMemberDetailsViewModelTests.swift in Sources */,
|
||||||
CAF8755E152204F55F8D6B5B /* RoomMembersListViewModelTests.swift in Sources */,
|
CAF8755E152204F55F8D6B5B /* RoomMembersListViewModelTests.swift in Sources */,
|
||||||
46562110EE202E580A5FFD9C /* RoomScreenViewModelTests.swift in Sources */,
|
46562110EE202E580A5FFD9C /* RoomScreenViewModelTests.swift in Sources */,
|
||||||
@@ -3762,6 +3780,7 @@
|
|||||||
D33AC79A50DFC26D2498DD28 /* FileRoomTimelineItem.swift in Sources */,
|
D33AC79A50DFC26D2498DD28 /* FileRoomTimelineItem.swift in Sources */,
|
||||||
37D789F24199B32E3FD1AA7B /* FileRoomTimelineItemContent.swift in Sources */,
|
37D789F24199B32E3FD1AA7B /* FileRoomTimelineItemContent.swift in Sources */,
|
||||||
1F04C63D4FA95948E3F52147 /* FileRoomTimelineView.swift in Sources */,
|
1F04C63D4FA95948E3F52147 /* FileRoomTimelineView.swift in Sources */,
|
||||||
|
F5D2270B5021D521C0D22E11 /* FlowCoordinatorProtocol.swift in Sources */,
|
||||||
B3EDDEC1839BB5A3747624BB /* FormButtonStyles.swift in Sources */,
|
B3EDDEC1839BB5A3747624BB /* FormButtonStyles.swift in Sources */,
|
||||||
5CE74302A0725F56F1E9D2A0 /* FormRow.swift in Sources */,
|
5CE74302A0725F56F1E9D2A0 /* FormRow.swift in Sources */,
|
||||||
4166A7DD2A4E2EFF0EB9369B /* FormRowLabelStyle.swift in Sources */,
|
4166A7DD2A4E2EFF0EB9369B /* FormRowLabelStyle.swift in Sources */,
|
||||||
@@ -3894,6 +3913,7 @@
|
|||||||
A5D551E5691749066E0E0C44 /* RoomDetailsScreenViewModel.swift in Sources */,
|
A5D551E5691749066E0E0C44 /* RoomDetailsScreenViewModel.swift in Sources */,
|
||||||
E9560744F7B0292E20ECE5F2 /* RoomDetailsScreenViewModelProtocol.swift in Sources */,
|
E9560744F7B0292E20ECE5F2 /* RoomDetailsScreenViewModelProtocol.swift in Sources */,
|
||||||
42F1C8731166633E35A6D7E6 /* RoomEventStringBuilder.swift in Sources */,
|
42F1C8731166633E35A6D7E6 /* RoomEventStringBuilder.swift in Sources */,
|
||||||
|
D55AF9B5B55FEED04771A461 /* RoomFlowCoordinator.swift in Sources */,
|
||||||
04A16B45228F7678A027C079 /* RoomHeaderView.swift in Sources */,
|
04A16B45228F7678A027C079 /* RoomHeaderView.swift in Sources */,
|
||||||
FA4296218444C48BC890F46B /* RoomMemberDetails.swift in Sources */,
|
FA4296218444C48BC890F46B /* RoomMemberDetails.swift in Sources */,
|
||||||
19FE025AE9BA2959B6589B0D /* RoomMemberDetailsScreen.swift in Sources */,
|
19FE025AE9BA2959B6589B0D /* RoomMemberDetailsScreen.swift in Sources */,
|
||||||
@@ -4047,8 +4067,8 @@
|
|||||||
F94000E3D91B11C527DA8807 /* UserProfileCell.swift in Sources */,
|
F94000E3D91B11C527DA8807 /* UserProfileCell.swift in Sources */,
|
||||||
9CCC77C31CB399661A034739 /* UserProperties+Element.swift in Sources */,
|
9CCC77C31CB399661A034739 /* UserProperties+Element.swift in Sources */,
|
||||||
8AB8ED1051216546CB35FA0E /* UserSession.swift in Sources */,
|
8AB8ED1051216546CB35FA0E /* UserSession.swift in Sources */,
|
||||||
8B807DC963D1D4155A241BCC /* UserSessionFlowCoordinator.swift in Sources */,
|
4A618590DEB72C4F186BFED4 /* UserSessionFlowCoordinator.swift in Sources */,
|
||||||
2B9AEEC12B1BBE5BD61D0F5E /* UserSessionFlowCoordinatorStateMachine.swift in Sources */,
|
3113065AABBC14CEAE6843FA /* UserSessionFlowCoordinatorStateMachine.swift in Sources */,
|
||||||
978BB24F2A5D31EE59EEC249 /* UserSessionProtocol.swift in Sources */,
|
978BB24F2A5D31EE59EEC249 /* UserSessionProtocol.swift in Sources */,
|
||||||
7E91BAC17963ED41208F489B /* UserSessionStore.swift in Sources */,
|
7E91BAC17963ED41208F489B /* UserSessionStore.swift in Sources */,
|
||||||
AC69B6DF15FC451AB2945036 /* UserSessionStoreProtocol.swift in Sources */,
|
AC69B6DF15FC451AB2945036 /* UserSessionStoreProtocol.swift in Sources */,
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
//
|
||||||
|
// Copyright 2023 New Vector Ltd
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
@MainActor
|
||||||
|
protocol FlowCoordinatorProtocol {
|
||||||
|
func handleAppRoute(_ appRoute: AppRoute, animated: Bool)
|
||||||
|
}
|
||||||
@@ -19,7 +19,9 @@ import Foundation
|
|||||||
import URLRouting
|
import URLRouting
|
||||||
|
|
||||||
enum AppRoute {
|
enum AppRoute {
|
||||||
|
case roomList
|
||||||
case room(roomID: String)
|
case room(roomID: String)
|
||||||
|
case roomDetails(roomID: String)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct AppRouterManager {
|
struct AppRouterManager {
|
||||||
|
|||||||
@@ -0,0 +1,480 @@
|
|||||||
|
//
|
||||||
|
// Copyright 2023 New Vector Ltd
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Combine
|
||||||
|
import Foundation
|
||||||
|
import SwiftState
|
||||||
|
|
||||||
|
enum RoomFlowCoordinatorAction: Equatable {
|
||||||
|
case presentedRoom(String)
|
||||||
|
case dismissedRoom
|
||||||
|
}
|
||||||
|
|
||||||
|
class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
||||||
|
private let userSession: UserSessionProtocol
|
||||||
|
private let roomTimelineControllerFactory: RoomTimelineControllerFactoryProtocol
|
||||||
|
private let navigationStackCoordinator: NavigationStackCoordinator
|
||||||
|
private let navigationSplitCoordinator: NavigationSplitCoordinator
|
||||||
|
private let emojiProvider: EmojiProviderProtocol
|
||||||
|
|
||||||
|
private let stateMachine: StateMachine<State, Event> = .init(state: .initial)
|
||||||
|
|
||||||
|
private var cancellables: Set<AnyCancellable> = .init()
|
||||||
|
|
||||||
|
private let actionsSubject: PassthroughSubject<RoomFlowCoordinatorAction, Never> = .init()
|
||||||
|
var actions: AnyPublisher<RoomFlowCoordinatorAction, Never> {
|
||||||
|
actionsSubject.eraseToAnyPublisher()
|
||||||
|
}
|
||||||
|
|
||||||
|
private var roomProxy: RoomProxyProtocol?
|
||||||
|
private var timelineController: RoomTimelineControllerProtocol?
|
||||||
|
|
||||||
|
init(userSession: UserSessionProtocol,
|
||||||
|
roomTimelineControllerFactory: RoomTimelineControllerFactoryProtocol,
|
||||||
|
navigationStackCoordinator: NavigationStackCoordinator,
|
||||||
|
navigationSplitCoordinator: NavigationSplitCoordinator,
|
||||||
|
emojiProvider: EmojiProviderProtocol) {
|
||||||
|
self.userSession = userSession
|
||||||
|
self.roomTimelineControllerFactory = roomTimelineControllerFactory
|
||||||
|
self.navigationStackCoordinator = navigationStackCoordinator
|
||||||
|
self.navigationSplitCoordinator = navigationSplitCoordinator
|
||||||
|
self.emojiProvider = emojiProvider
|
||||||
|
|
||||||
|
setupStateMachine()
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - FlowCoordinatorProtocol
|
||||||
|
|
||||||
|
func handleAppRoute(_ appRoute: AppRoute, animated: Bool) {
|
||||||
|
switch appRoute {
|
||||||
|
case .room(let roomID):
|
||||||
|
if case .room(let identifier) = stateMachine.state,
|
||||||
|
roomID == identifier {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
stateMachine.tryEvent(.presentRoom(roomID: roomID), userInfo: EventUserInfo(animated: animated))
|
||||||
|
case .roomDetails(let roomID):
|
||||||
|
stateMachine.tryEvent(.presentRoomDetails(roomID: roomID), userInfo: EventUserInfo(animated: animated))
|
||||||
|
case .roomList:
|
||||||
|
stateMachine.tryEvent(.dismissRoom, userInfo: EventUserInfo(animated: animated))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Private
|
||||||
|
|
||||||
|
// swiftlint:disable:next cyclomatic_complexity function_body_length
|
||||||
|
private func setupStateMachine() {
|
||||||
|
stateMachine.addRouteMapping { event, fromState, _ in
|
||||||
|
switch (event, fromState) {
|
||||||
|
case (.presentRoom(let roomID), _):
|
||||||
|
return .room(roomID: roomID)
|
||||||
|
case (.dismissRoom, .room):
|
||||||
|
return .initial
|
||||||
|
|
||||||
|
case (.presentRoomDetails(let roomID), .initial):
|
||||||
|
return .roomDetails(roomID: roomID)
|
||||||
|
case (.presentRoomDetails(let roomID), .room):
|
||||||
|
return .roomDetails(roomID: roomID)
|
||||||
|
case (.presentRoomDetails(let roomID), .roomDetails):
|
||||||
|
return .roomDetails(roomID: roomID)
|
||||||
|
case (.dismissRoomDetails, .roomDetails(let roomID)):
|
||||||
|
return .room(roomID: roomID)
|
||||||
|
case (.dismissRoom, .roomDetails):
|
||||||
|
return .initial
|
||||||
|
|
||||||
|
case (.presentMediaViewer(let file, let title), .room(let roomID)):
|
||||||
|
return .mediaViewer(roomID: roomID, file: file, title: title)
|
||||||
|
case (.dismissMediaViewer, .mediaViewer(let roomID, _, _)):
|
||||||
|
return .room(roomID: roomID)
|
||||||
|
|
||||||
|
case (.presentReportContent(let itemID, let senderID), .room(let roomID)):
|
||||||
|
return .reportContent(roomID: roomID, itemID: itemID, senderID: senderID)
|
||||||
|
case (.dismissReportContent, .reportContent(let roomID, _, _)):
|
||||||
|
return .room(roomID: roomID)
|
||||||
|
|
||||||
|
case (.presentMediaUploadPicker(let source), .room(let roomID)):
|
||||||
|
return .mediaUploadPicker(roomID: roomID, source: source)
|
||||||
|
case (.dismissMediaUploadPicker, .mediaUploadPicker(let roomID, _)):
|
||||||
|
return .room(roomID: roomID)
|
||||||
|
|
||||||
|
case (.presentMediaUploadPreview(let fileURL), .mediaUploadPicker(let roomID, _)):
|
||||||
|
return .mediaUploadPreview(roomID: roomID, fileURL: fileURL)
|
||||||
|
case (.presentMediaUploadPreview(let fileURL), .room(let roomID)):
|
||||||
|
return .mediaUploadPreview(roomID: roomID, fileURL: fileURL)
|
||||||
|
case (.dismissMediaUploadPreview, .mediaUploadPreview(let roomID, _)):
|
||||||
|
return .room(roomID: roomID)
|
||||||
|
|
||||||
|
case (.presentEmojiPicker(let itemID), .room(let roomID)):
|
||||||
|
return .emojiPicker(roomID: roomID, itemID: itemID)
|
||||||
|
case (.dismissEmojiPicker, .emojiPicker(let roomID, _)):
|
||||||
|
return .room(roomID: roomID)
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stateMachine.addAnyHandler(.any => .any) { [weak self] context in
|
||||||
|
guard let self else { return }
|
||||||
|
|
||||||
|
let animated = (context.userInfo as? EventUserInfo)?.animated ?? true
|
||||||
|
|
||||||
|
switch (context.fromState, context.event, context.toState) {
|
||||||
|
case (_, .presentRoom(let roomID), .room):
|
||||||
|
presentRoom(roomID, animated: animated)
|
||||||
|
case (.room, .dismissRoom, .initial):
|
||||||
|
dismissRoom(animated: animated)
|
||||||
|
|
||||||
|
case (.initial, .presentRoomDetails, .roomDetails(let roomID)),
|
||||||
|
(.room, .presentRoomDetails, .roomDetails(let roomID)),
|
||||||
|
(.roomDetails, .presentRoomDetails, .roomDetails(let roomID)):
|
||||||
|
Task {
|
||||||
|
await self.presentRoomDetails(roomID: roomID, animated: animated)
|
||||||
|
}
|
||||||
|
case (.roomDetails, .dismissRoomDetails, .room):
|
||||||
|
break
|
||||||
|
case (.roomDetails, .dismissRoom, .initial):
|
||||||
|
dismissRoom(animated: animated)
|
||||||
|
|
||||||
|
case (.room, .presentMediaViewer, .mediaViewer(_, let file, let title)):
|
||||||
|
presentMediaViewer(file, title: title)
|
||||||
|
case (.mediaViewer, .dismissMediaViewer, .room):
|
||||||
|
break
|
||||||
|
|
||||||
|
case (.room, .presentReportContent, .reportContent(_, let itemID, let senderID)):
|
||||||
|
presentReportContent(for: itemID, from: senderID)
|
||||||
|
case (.reportContent, .dismissReportContent, .room):
|
||||||
|
break
|
||||||
|
|
||||||
|
case (.room, .presentMediaUploadPicker, .mediaUploadPicker(_, let source)):
|
||||||
|
presentMediaUploadPickerWithSource(source)
|
||||||
|
case (.mediaUploadPicker, .dismissMediaUploadPicker, .room):
|
||||||
|
break
|
||||||
|
|
||||||
|
case (.mediaUploadPicker, .presentMediaUploadPreview, .mediaUploadPreview(_, let fileURL)):
|
||||||
|
presentMediaUploadPreviewScreen(for: fileURL)
|
||||||
|
case (.room, .presentMediaUploadPreview, .mediaUploadPreview(_, let fileURL)):
|
||||||
|
presentMediaUploadPreviewScreen(for: fileURL)
|
||||||
|
case (.mediaUploadPreview, .dismissMediaUploadPreview, .room):
|
||||||
|
break
|
||||||
|
|
||||||
|
case (.room, .presentEmojiPicker, .emojiPicker(_, let itemID)):
|
||||||
|
presentEmojiPicker(for: itemID)
|
||||||
|
case (.emojiPicker, .dismissEmojiPicker, .room):
|
||||||
|
break
|
||||||
|
|
||||||
|
default:
|
||||||
|
fatalError("Unknown transition: \(context)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stateMachine.addAnyHandler(.any => .any) { context in
|
||||||
|
if let event = context.event {
|
||||||
|
MXLog.info("Transitioning from `\(context.fromState)` to `\(context.toState)` with event `\(event)`")
|
||||||
|
} else {
|
||||||
|
MXLog.info("Transitioning from \(context.fromState)` to `\(context.toState)`")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stateMachine.addErrorHandler { context in
|
||||||
|
fatalError("Failed transition with context: \(context)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func presentRoom(_ roomID: String, animated: Bool) {
|
||||||
|
Task {
|
||||||
|
await asyncPresentRoom(roomID, animated: animated)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// swiftlint:disable:next cyclomatic_complexity function_body_length
|
||||||
|
private func asyncPresentRoom(_ roomID: String, animated: Bool) async {
|
||||||
|
if let roomProxy, roomProxy.id == roomID {
|
||||||
|
navigationStackCoordinator.popToRoot()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let roomProxy = await userSession.clientProxy.roomForIdentifier(roomID) else {
|
||||||
|
MXLog.error("Invalid room identifier: \(roomID)")
|
||||||
|
stateMachine.tryEvent(.dismissRoom)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
actionsSubject.send(.presentedRoom(roomID))
|
||||||
|
|
||||||
|
self.roomProxy = roomProxy
|
||||||
|
|
||||||
|
let userId = userSession.clientProxy.userID
|
||||||
|
|
||||||
|
let timelineItemFactory = RoomTimelineItemFactory(userID: userId,
|
||||||
|
mediaProvider: userSession.mediaProvider,
|
||||||
|
attributedStringBuilder: AttributedStringBuilder(),
|
||||||
|
stateEventStringBuilder: RoomStateEventStringBuilder(userID: userId))
|
||||||
|
|
||||||
|
let timelineController = roomTimelineControllerFactory.buildRoomTimelineController(userId: userId,
|
||||||
|
roomProxy: roomProxy,
|
||||||
|
timelineItemFactory: timelineItemFactory,
|
||||||
|
mediaProvider: userSession.mediaProvider)
|
||||||
|
self.timelineController = timelineController
|
||||||
|
|
||||||
|
let parameters = RoomScreenCoordinatorParameters(roomProxy: roomProxy,
|
||||||
|
timelineController: timelineController,
|
||||||
|
mediaProvider: userSession.mediaProvider,
|
||||||
|
emojiProvider: emojiProvider)
|
||||||
|
let coordinator = RoomScreenCoordinator(parameters: parameters)
|
||||||
|
coordinator.actions
|
||||||
|
.sink { [weak self] action in
|
||||||
|
guard let self else { return }
|
||||||
|
|
||||||
|
switch action {
|
||||||
|
case .presentRoomDetails:
|
||||||
|
stateMachine.tryEvent(.presentRoomDetails(roomID: roomID))
|
||||||
|
case .presentMediaViewer(let file, let title):
|
||||||
|
stateMachine.tryEvent(.presentMediaViewer(file: file, title: title))
|
||||||
|
case .presentReportContent(let itemID, let senderID):
|
||||||
|
stateMachine.tryEvent(.presentReportContent(itemID: itemID, senderID: senderID))
|
||||||
|
case .presentMediaUploadPicker(let source):
|
||||||
|
stateMachine.tryEvent(.presentMediaUploadPicker(source: source))
|
||||||
|
case .presentMediaUploadPreviewScreen(let url):
|
||||||
|
stateMachine.tryEvent(.presentMediaUploadPreview(fileURL: url))
|
||||||
|
case .presentEmojiPicker(let itemID):
|
||||||
|
stateMachine.tryEvent(.presentEmojiPicker(itemID: itemID))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.store(in: &cancellables)
|
||||||
|
|
||||||
|
navigationStackCoordinator.setRootCoordinator(coordinator, animated: animated) { [weak self] in
|
||||||
|
// Move the state machine to no room selected if the room currently being dismissed
|
||||||
|
// is the same as the one selected in the state machine.
|
||||||
|
// This generally happens when popping the room screen while in a compact layout
|
||||||
|
switch self?.stateMachine.state {
|
||||||
|
case let .room(selectedRoomID) where selectedRoomID == roomID:
|
||||||
|
self?.stateMachine.tryEvent(.dismissRoom)
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if navigationSplitCoordinator.detailCoordinator == nil {
|
||||||
|
navigationSplitCoordinator.setDetailCoordinator(navigationStackCoordinator, animated: animated)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func dismissRoom(animated: Bool) {
|
||||||
|
navigationStackCoordinator.popToRoot(animated: true)
|
||||||
|
navigationSplitCoordinator.setDetailCoordinator(nil)
|
||||||
|
roomProxy = nil
|
||||||
|
|
||||||
|
actionsSubject.send(.dismissedRoom)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func presentRoomDetails(roomID: String, animated: Bool) async {
|
||||||
|
if roomProxy?.id != roomID {
|
||||||
|
await asyncPresentRoom(roomID, animated: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let roomProxy else {
|
||||||
|
fatalError()
|
||||||
|
}
|
||||||
|
|
||||||
|
let params = RoomDetailsScreenCoordinatorParameters(navigationStackCoordinator: navigationStackCoordinator,
|
||||||
|
roomProxy: roomProxy,
|
||||||
|
mediaProvider: userSession.mediaProvider,
|
||||||
|
userDiscoveryService: UserDiscoveryService(clientProxy: userSession.clientProxy))
|
||||||
|
let coordinator = RoomDetailsScreenCoordinator(parameters: params)
|
||||||
|
coordinator.callback = { [weak self] action in
|
||||||
|
switch action {
|
||||||
|
case .cancel:
|
||||||
|
self?.navigationStackCoordinator.pop()
|
||||||
|
case .leftRoom:
|
||||||
|
self?.dismissRoom(animated: animated)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
navigationStackCoordinator.push(coordinator) { [weak self] in
|
||||||
|
guard let self else { return }
|
||||||
|
|
||||||
|
if case .roomDetails = stateMachine.state {
|
||||||
|
stateMachine.tryEvent(.dismissRoomDetails)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func presentMediaViewer(_ file: MediaFileHandleProxy, title: String?) {
|
||||||
|
let params = FilePreviewScreenCoordinatorParameters(mediaFile: file, title: title)
|
||||||
|
let coordinator = FilePreviewScreenCoordinator(parameters: params)
|
||||||
|
coordinator.callback = { [weak self] action in
|
||||||
|
switch action {
|
||||||
|
case .cancel:
|
||||||
|
self?.navigationStackCoordinator.pop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
navigationStackCoordinator.push(coordinator) { [weak self] in
|
||||||
|
self?.stateMachine.tryEvent(.dismissMediaViewer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func presentReportContent(for itemID: String, from senderID: String) {
|
||||||
|
guard let roomProxy else {
|
||||||
|
fatalError()
|
||||||
|
}
|
||||||
|
|
||||||
|
let navigationCoordinator = NavigationStackCoordinator()
|
||||||
|
let userIndicatorController = UserIndicatorController(rootCoordinator: navigationCoordinator)
|
||||||
|
let parameters = ReportContentScreenCoordinatorParameters(itemID: itemID,
|
||||||
|
senderID: senderID,
|
||||||
|
roomProxy: roomProxy,
|
||||||
|
userIndicatorController: userIndicatorController)
|
||||||
|
let coordinator = ReportContentScreenCoordinator(parameters: parameters)
|
||||||
|
coordinator.callback = { [weak self] completion in
|
||||||
|
self?.navigationStackCoordinator.setSheetCoordinator(nil)
|
||||||
|
|
||||||
|
switch completion {
|
||||||
|
case .cancel:
|
||||||
|
break
|
||||||
|
case .finish:
|
||||||
|
self?.showSuccess(label: L10n.commonReportSubmitted)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
navigationCoordinator.setRootCoordinator(coordinator)
|
||||||
|
navigationStackCoordinator.setSheetCoordinator(userIndicatorController) { [weak self] in
|
||||||
|
self?.stateMachine.tryEvent(.dismissReportContent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func presentMediaUploadPickerWithSource(_ source: MediaPickerScreenSource) {
|
||||||
|
let stackCoordinator = NavigationStackCoordinator()
|
||||||
|
let userIndicatorController = UserIndicatorController(rootCoordinator: stackCoordinator)
|
||||||
|
|
||||||
|
let mediaPickerCoordinator = MediaPickerScreenCoordinator(userIndicatorController: userIndicatorController, source: source) { [weak self] action in
|
||||||
|
switch action {
|
||||||
|
case .cancel:
|
||||||
|
self?.navigationStackCoordinator.setSheetCoordinator(nil)
|
||||||
|
case .selectMediaAtURL(let url):
|
||||||
|
self?.stateMachine.tryEvent(.presentMediaUploadPreview(fileURL: url))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stackCoordinator.setRootCoordinator(mediaPickerCoordinator)
|
||||||
|
|
||||||
|
navigationStackCoordinator.setSheetCoordinator(userIndicatorController) { [weak self] in
|
||||||
|
if case .mediaUploadPicker = self?.stateMachine.state {
|
||||||
|
self?.stateMachine.tryEvent(.dismissMediaUploadPicker)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func presentMediaUploadPreviewScreen(for url: URL) {
|
||||||
|
guard let roomProxy else {
|
||||||
|
fatalError()
|
||||||
|
}
|
||||||
|
|
||||||
|
let stackCoordinator = NavigationStackCoordinator()
|
||||||
|
let userIndicatorController = UserIndicatorController(rootCoordinator: stackCoordinator)
|
||||||
|
|
||||||
|
let parameters = MediaUploadPreviewScreenCoordinatorParameters(userIndicatorController: userIndicatorController,
|
||||||
|
roomProxy: roomProxy,
|
||||||
|
mediaUploadingPreprocessor: MediaUploadingPreprocessor(),
|
||||||
|
title: url.lastPathComponent,
|
||||||
|
url: url)
|
||||||
|
|
||||||
|
let mediaUploadPreviewScreenCoordinator = MediaUploadPreviewScreenCoordinator(parameters: parameters) { [weak self] action in
|
||||||
|
switch action {
|
||||||
|
case .dismiss:
|
||||||
|
self?.navigationStackCoordinator.setSheetCoordinator(nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stackCoordinator.setRootCoordinator(mediaUploadPreviewScreenCoordinator)
|
||||||
|
|
||||||
|
navigationStackCoordinator.setSheetCoordinator(userIndicatorController) { [weak self] in
|
||||||
|
self?.stateMachine.tryEvent(.dismissMediaUploadPreview)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func presentEmojiPicker(for itemId: String) {
|
||||||
|
let emojiPickerNavigationStackCoordinator = NavigationStackCoordinator()
|
||||||
|
|
||||||
|
let params = EmojiPickerScreenCoordinatorParameters(emojiProvider: emojiProvider,
|
||||||
|
itemId: itemId)
|
||||||
|
let coordinator = EmojiPickerScreenCoordinator(parameters: params)
|
||||||
|
coordinator.callback = { [weak self] action in
|
||||||
|
switch action {
|
||||||
|
case let .emojiSelected(emoji: emoji, itemId: itemId):
|
||||||
|
MXLog.debug("Selected \(emoji) for \(itemId)")
|
||||||
|
self?.navigationStackCoordinator.setSheetCoordinator(nil)
|
||||||
|
Task {
|
||||||
|
await self?.timelineController?.sendReaction(emoji, to: itemId)
|
||||||
|
}
|
||||||
|
case .dismiss:
|
||||||
|
self?.navigationStackCoordinator.setSheetCoordinator(nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
emojiPickerNavigationStackCoordinator.setRootCoordinator(coordinator)
|
||||||
|
emojiPickerNavigationStackCoordinator.presentationDetents = [.medium, .large]
|
||||||
|
|
||||||
|
navigationStackCoordinator.setSheetCoordinator(emojiPickerNavigationStackCoordinator) { [weak self] in
|
||||||
|
self?.stateMachine.tryEvent(.dismissEmojiPicker)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func showSuccess(label: String) {
|
||||||
|
ServiceLocator.shared.userIndicatorController.submitIndicator(UserIndicator(title: label, iconName: "checkmark"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension RoomFlowCoordinator {
|
||||||
|
enum State: StateType {
|
||||||
|
case initial
|
||||||
|
case room(roomID: String)
|
||||||
|
case mediaViewer(roomID: String, file: MediaFileHandleProxy, title: String?)
|
||||||
|
case reportContent(roomID: String, itemID: String, senderID: String)
|
||||||
|
case roomDetails(roomID: String)
|
||||||
|
case mediaUploadPicker(roomID: String, source: MediaPickerScreenSource)
|
||||||
|
case mediaUploadPreview(roomID: String, fileURL: URL)
|
||||||
|
case emojiPicker(roomID: String, itemID: String)
|
||||||
|
}
|
||||||
|
|
||||||
|
struct EventUserInfo {
|
||||||
|
let animated: Bool
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Event: EventType {
|
||||||
|
case presentRoom(roomID: String)
|
||||||
|
case dismissRoom
|
||||||
|
|
||||||
|
case presentMediaViewer(file: MediaFileHandleProxy, title: String?)
|
||||||
|
case dismissMediaViewer
|
||||||
|
|
||||||
|
case presentReportContent(itemID: String, senderID: String)
|
||||||
|
case dismissReportContent
|
||||||
|
|
||||||
|
case presentRoomDetails(roomID: String)
|
||||||
|
case dismissRoomDetails
|
||||||
|
|
||||||
|
case presentMediaUploadPicker(source: MediaPickerScreenSource)
|
||||||
|
case dismissMediaUploadPicker
|
||||||
|
|
||||||
|
case presentMediaUploadPreview(fileURL: URL)
|
||||||
|
case dismissMediaUploadPreview
|
||||||
|
|
||||||
|
case presentEmojiPicker(itemID: String)
|
||||||
|
case dismissEmojiPicker
|
||||||
|
}
|
||||||
|
}
|
||||||
+49
-142
@@ -22,15 +22,16 @@ enum UserSessionFlowCoordinatorAction {
|
|||||||
case clearCache
|
case clearCache
|
||||||
}
|
}
|
||||||
|
|
||||||
class UserSessionFlowCoordinator: CoordinatorProtocol {
|
class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
|
||||||
private let stateMachine: UserSessionFlowCoordinatorStateMachine
|
|
||||||
private var cancellables: Set<AnyCancellable> = .init()
|
|
||||||
|
|
||||||
private let userSession: UserSessionProtocol
|
private let userSession: UserSessionProtocol
|
||||||
private let navigationSplitCoordinator: NavigationSplitCoordinator
|
private let navigationSplitCoordinator: NavigationSplitCoordinator
|
||||||
private let bugReportService: BugReportServiceProtocol
|
private let bugReportService: BugReportServiceProtocol
|
||||||
private let roomTimelineControllerFactory: RoomTimelineControllerFactoryProtocol
|
private let roomTimelineControllerFactory: RoomTimelineControllerFactoryProtocol
|
||||||
private let emojiProvider: EmojiProviderProtocol = EmojiProvider()
|
|
||||||
|
private let stateMachine: UserSessionFlowCoordinatorStateMachine
|
||||||
|
private let roomFlowCoordinator: RoomFlowCoordinator
|
||||||
|
|
||||||
|
private var cancellables: Set<AnyCancellable> = .init()
|
||||||
|
|
||||||
private let sidebarNavigationStackCoordinator: NavigationStackCoordinator
|
private let sidebarNavigationStackCoordinator: NavigationStackCoordinator
|
||||||
private let detailNavigationStackCoordinator: NavigationStackCoordinator
|
private let detailNavigationStackCoordinator: NavigationStackCoordinator
|
||||||
@@ -52,7 +53,23 @@ class UserSessionFlowCoordinator: CoordinatorProtocol {
|
|||||||
|
|
||||||
navigationSplitCoordinator.setSidebarCoordinator(sidebarNavigationStackCoordinator)
|
navigationSplitCoordinator.setSidebarCoordinator(sidebarNavigationStackCoordinator)
|
||||||
|
|
||||||
|
roomFlowCoordinator = RoomFlowCoordinator(userSession: userSession,
|
||||||
|
roomTimelineControllerFactory: roomTimelineControllerFactory,
|
||||||
|
navigationStackCoordinator: detailNavigationStackCoordinator,
|
||||||
|
navigationSplitCoordinator: navigationSplitCoordinator,
|
||||||
|
emojiProvider: EmojiProvider())
|
||||||
|
|
||||||
setupStateMachine()
|
setupStateMachine()
|
||||||
|
|
||||||
|
roomFlowCoordinator.actions.sink { action in
|
||||||
|
switch action {
|
||||||
|
case .presentedRoom(let roomID):
|
||||||
|
self.stateMachine.processEvent(.selectRoom(roomId: roomID))
|
||||||
|
case .dismissedRoom:
|
||||||
|
self.stateMachine.processEvent(.deselectRoom)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.store(in: &cancellables)
|
||||||
}
|
}
|
||||||
|
|
||||||
func start() {
|
func start() {
|
||||||
@@ -65,6 +82,8 @@ class UserSessionFlowCoordinator: CoordinatorProtocol {
|
|||||||
stateMachine.isDisplayingRoomScreen(withRoomId: roomId)
|
stateMachine.isDisplayingRoomScreen(withRoomId: roomId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - FlowCoordinatorProtocol
|
||||||
|
|
||||||
func handleAppRoute(_ appRoute: AppRoute, animated: Bool) {
|
func handleAppRoute(_ appRoute: AppRoute, animated: Bool) {
|
||||||
switch stateMachine.state {
|
switch stateMachine.state {
|
||||||
case .feedbackScreen, .sessionVerificationScreen, .settingsScreen, .startChatScreen, .invitesScreen:
|
case .feedbackScreen, .sessionVerificationScreen, .settingsScreen, .startChatScreen, .invitesScreen:
|
||||||
@@ -72,9 +91,10 @@ class UserSessionFlowCoordinator: CoordinatorProtocol {
|
|||||||
case .roomList, .initial:
|
case .roomList, .initial:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
switch appRoute {
|
switch appRoute {
|
||||||
case .room(let roomID):
|
case .room, .roomDetails, .roomList:
|
||||||
stateMachine.processEvent(.selectRoom(roomId: roomID), userInfo: .init(animated: animated))
|
roomFlowCoordinator.handleAppRoute(appRoute, animated: true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,21 +104,21 @@ class UserSessionFlowCoordinator: CoordinatorProtocol {
|
|||||||
private func setupStateMachine() {
|
private func setupStateMachine() {
|
||||||
stateMachine.addTransitionHandler { [weak self] context in
|
stateMachine.addTransitionHandler { [weak self] context in
|
||||||
guard let self else { return }
|
guard let self else { return }
|
||||||
let animated = (context.userInfo as? EventUserInfo)?.animated ?? true
|
let animated = (context.userInfo as? UserSessionFlowCoordinatorStateMachine.EventUserInfo)?.animated ?? true
|
||||||
switch (context.fromState, context.event, context.toState) {
|
switch (context.fromState, context.event, context.toState) {
|
||||||
case (.initial, .start, .roomList):
|
case (.initial, .start, .roomList):
|
||||||
self.presentHomeScreen()
|
self.presentHomeScreen()
|
||||||
|
|
||||||
case(.roomList(let currentRoomId), .selectRoom, .roomList(let selectedRoomId)):
|
case(.roomList, .selectRoom, .roomList):
|
||||||
guard let selectedRoomId,
|
break
|
||||||
selectedRoomId != currentRoomId else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
self.presentRoomWithIdentifier(selectedRoomId, animated: animated)
|
|
||||||
case(.roomList, .deselectRoom, .roomList):
|
case(.roomList, .deselectRoom, .roomList):
|
||||||
break
|
break
|
||||||
|
|
||||||
|
case (.invitesScreen, .selectRoom, .invitesScreen):
|
||||||
|
break
|
||||||
|
case (.invitesScreen, .deselectRoom, .invitesScreen):
|
||||||
|
break
|
||||||
|
|
||||||
case (.roomList, .showSessionVerificationScreen, .sessionVerificationScreen):
|
case (.roomList, .showSessionVerificationScreen, .sessionVerificationScreen):
|
||||||
self.presentSessionVerification(animated: animated)
|
self.presentSessionVerification(animated: animated)
|
||||||
case (.sessionVerificationScreen, .dismissedSessionVerificationScreen, .roomList):
|
case (.sessionVerificationScreen, .dismissedSessionVerificationScreen, .roomList):
|
||||||
@@ -123,15 +143,6 @@ class UserSessionFlowCoordinator: CoordinatorProtocol {
|
|||||||
self.presentInvitesList(animated: animated)
|
self.presentInvitesList(animated: animated)
|
||||||
case (.invitesScreen, .closedInvitesScreen, .roomList):
|
case (.invitesScreen, .closedInvitesScreen, .roomList):
|
||||||
break
|
break
|
||||||
case (.invitesScreen, .selectRoom(let roomId), .invitesScreen(let selectedRoomId)) where roomId == selectedRoomId:
|
|
||||||
self.presentRoomWithIdentifier(roomId, animated: animated)
|
|
||||||
case (.invitesScreen, .deselectRoom, .invitesScreen):
|
|
||||||
break
|
|
||||||
|
|
||||||
case (.roomList(let currentRoomId), .selectRoomDetails(let roomId), .roomList) where currentRoomId == roomId:
|
|
||||||
break
|
|
||||||
case (.roomList, .selectRoomDetails(let roomId), .roomList(let selectedRoomId)) where roomId == selectedRoomId:
|
|
||||||
self.presentRoomDetails(roomIdentifier: roomId, animated: animated)
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
fatalError("Unknown transition: \(context)")
|
fatalError("Unknown transition: \(context)")
|
||||||
@@ -143,6 +154,7 @@ class UserSessionFlowCoordinator: CoordinatorProtocol {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// swiftlint:disable:next cyclomatic_complexity
|
||||||
private func presentHomeScreen() {
|
private func presentHomeScreen() {
|
||||||
let parameters = HomeScreenCoordinatorParameters(userSession: userSession,
|
let parameters = HomeScreenCoordinatorParameters(userSession: userSession,
|
||||||
attributedStringBuilder: AttributedStringBuilder(),
|
attributedStringBuilder: AttributedStringBuilder(),
|
||||||
@@ -154,12 +166,15 @@ class UserSessionFlowCoordinator: CoordinatorProtocol {
|
|||||||
guard let self else { return }
|
guard let self else { return }
|
||||||
|
|
||||||
switch action {
|
switch action {
|
||||||
case .presentRoom(let roomIdentifier):
|
case .presentRoom(let roomID):
|
||||||
self.stateMachine.processEvent(.selectRoom(roomId: roomIdentifier))
|
self.roomFlowCoordinator.handleAppRoute(.room(roomID: roomID), animated: true)
|
||||||
case .presentRoomDetails(let roomIdentifier):
|
case .presentRoomDetails(let roomID):
|
||||||
self.stateMachine.processEvent(.selectRoomDetails(roomId: roomIdentifier))
|
self.roomFlowCoordinator.handleAppRoute(.roomDetails(roomID: roomID), animated: true)
|
||||||
case .roomLeft(let roomIdentifier):
|
case .roomLeft(let roomID):
|
||||||
self.deselectRoomIfNeeded(roomIdentifier: roomIdentifier)
|
if case .roomList(selectedRoomId: let selectedRoomId) = stateMachine.state,
|
||||||
|
selectedRoomId == roomID {
|
||||||
|
self.roomFlowCoordinator.handleAppRoute(.roomList, animated: true)
|
||||||
|
}
|
||||||
case .presentSettingsScreen:
|
case .presentSettingsScreen:
|
||||||
self.stateMachine.processEvent(.showSettingsScreen)
|
self.stateMachine.processEvent(.showSettingsScreen)
|
||||||
case .presentFeedbackScreen:
|
case .presentFeedbackScreen:
|
||||||
@@ -178,114 +193,6 @@ class UserSessionFlowCoordinator: CoordinatorProtocol {
|
|||||||
sidebarNavigationStackCoordinator.setRootCoordinator(coordinator)
|
sidebarNavigationStackCoordinator.setRootCoordinator(coordinator)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Rooms
|
|
||||||
|
|
||||||
private func presentRoomWithIdentifier(_ roomIdentifier: String, animated: Bool = true) {
|
|
||||||
Task { @MainActor in
|
|
||||||
guard let roomProxy = await userSession.clientProxy.roomForIdentifier(roomIdentifier) else {
|
|
||||||
MXLog.error("Invalid room identifier: \(roomIdentifier)")
|
|
||||||
stateMachine.processEvent(.deselectRoom)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
let userId = userSession.clientProxy.userID
|
|
||||||
|
|
||||||
let timelineItemFactory = RoomTimelineItemFactory(userID: userId,
|
|
||||||
mediaProvider: userSession.mediaProvider,
|
|
||||||
attributedStringBuilder: AttributedStringBuilder(),
|
|
||||||
stateEventStringBuilder: RoomStateEventStringBuilder(userID: userId))
|
|
||||||
|
|
||||||
let timelineController = roomTimelineControllerFactory.buildRoomTimelineController(userId: userId,
|
|
||||||
roomProxy: roomProxy,
|
|
||||||
timelineItemFactory: timelineItemFactory,
|
|
||||||
mediaProvider: userSession.mediaProvider)
|
|
||||||
|
|
||||||
let parameters = RoomScreenCoordinatorParameters(navigationStackCoordinator: detailNavigationStackCoordinator,
|
|
||||||
roomProxy: roomProxy,
|
|
||||||
timelineController: timelineController,
|
|
||||||
mediaProvider: userSession.mediaProvider,
|
|
||||||
emojiProvider: emojiProvider,
|
|
||||||
userDiscoveryService: UserDiscoveryService(clientProxy: userSession.clientProxy))
|
|
||||||
let coordinator = RoomScreenCoordinator(parameters: parameters)
|
|
||||||
coordinator.callback = { [weak self] action in
|
|
||||||
switch action {
|
|
||||||
case .leftRoom:
|
|
||||||
self?.dismissRoom()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
detailNavigationStackCoordinator.setRootCoordinator(coordinator, animated: animated) { [weak self, roomIdentifier] in
|
|
||||||
guard let self else { return }
|
|
||||||
|
|
||||||
// Move the state machine to no room selected if the room currently being dismissed
|
|
||||||
// is the same as the one selected in the state machine.
|
|
||||||
// This generally happens when popping the room screen while in a compact layout
|
|
||||||
switch self.stateMachine.state {
|
|
||||||
case
|
|
||||||
let .roomList(selectedRoomId) where selectedRoomId == roomIdentifier,
|
|
||||||
let .invitesScreen(selectedRoomId) where selectedRoomId == roomIdentifier:
|
|
||||||
|
|
||||||
self.stateMachine.processEvent(.deselectRoom)
|
|
||||||
self.detailNavigationStackCoordinator.setRootCoordinator(nil)
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if navigationSplitCoordinator.detailCoordinator == nil {
|
|
||||||
navigationSplitCoordinator.setDetailCoordinator(detailNavigationStackCoordinator, animated: animated)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func deselectRoomIfNeeded(roomIdentifier: String) {
|
|
||||||
guard
|
|
||||||
case .roomList(selectedRoomId: let selectedRoomId) = stateMachine.state,
|
|
||||||
selectedRoomId == roomIdentifier
|
|
||||||
else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
stateMachine.processEvent(.deselectRoom)
|
|
||||||
navigationSplitCoordinator.setDetailCoordinator(nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
private func dismissRoom() {
|
|
||||||
detailNavigationStackCoordinator.popToRoot(animated: true)
|
|
||||||
navigationSplitCoordinator.setDetailCoordinator(nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
private func presentRoomDetails(roomIdentifier: String, animated: Bool = true) {
|
|
||||||
Task {
|
|
||||||
guard let roomProxy = await userSession.clientProxy.roomForIdentifier(roomIdentifier) else {
|
|
||||||
MXLog.error("Invalid room identifier: \(roomIdentifier)")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
let params = RoomDetailsScreenCoordinatorParameters(navigationStackCoordinator: detailNavigationStackCoordinator,
|
|
||||||
roomProxy: roomProxy,
|
|
||||||
mediaProvider: userSession.mediaProvider,
|
|
||||||
userDiscoveryService: UserDiscoveryService(clientProxy: userSession.clientProxy))
|
|
||||||
|
|
||||||
let coordinator = RoomDetailsScreenCoordinator(parameters: params)
|
|
||||||
|
|
||||||
coordinator.callback = { [weak self] action in
|
|
||||||
switch action {
|
|
||||||
case .cancel, .leftRoom:
|
|
||||||
self?.stateMachine.processEvent(.deselectRoom)
|
|
||||||
self?.detailNavigationStackCoordinator.setRootCoordinator(nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
detailNavigationStackCoordinator.setRootCoordinator(coordinator, animated: animated) { [weak self, roomIdentifier] in
|
|
||||||
self?.deselectRoomIfNeeded(roomIdentifier: roomIdentifier)
|
|
||||||
}
|
|
||||||
|
|
||||||
if navigationSplitCoordinator.detailCoordinator == nil {
|
|
||||||
navigationSplitCoordinator.setDetailCoordinator(detailNavigationStackCoordinator, animated: animated)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: Settings
|
// MARK: Settings
|
||||||
|
|
||||||
private func presentSettingsScreen(animated: Bool) {
|
private func presentSettingsScreen(animated: Bool) {
|
||||||
@@ -352,9 +259,9 @@ class UserSessionFlowCoordinator: CoordinatorProtocol {
|
|||||||
switch action {
|
switch action {
|
||||||
case .close:
|
case .close:
|
||||||
self.navigationSplitCoordinator.setSheetCoordinator(nil)
|
self.navigationSplitCoordinator.setSheetCoordinator(nil)
|
||||||
case .openRoom(let identifier):
|
case .openRoom(let roomID):
|
||||||
self.navigationSplitCoordinator.setSheetCoordinator(nil)
|
self.navigationSplitCoordinator.setSheetCoordinator(nil)
|
||||||
self.stateMachine.processEvent(.selectRoom(roomId: identifier))
|
self.roomFlowCoordinator.handleAppRoute(.room(roomID: roomID), animated: true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
@@ -400,8 +307,8 @@ class UserSessionFlowCoordinator: CoordinatorProtocol {
|
|||||||
coordinator.actions
|
coordinator.actions
|
||||||
.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))
|
self?.roomFlowCoordinator.handleAppRoute(.room(roomID: roomID), animated: true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
+8
-14
@@ -42,6 +42,10 @@ class UserSessionFlowCoordinatorStateMachine {
|
|||||||
case invitesScreen(selectedRoomId: String?)
|
case invitesScreen(selectedRoomId: String?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct EventUserInfo {
|
||||||
|
let animated: Bool
|
||||||
|
}
|
||||||
|
|
||||||
/// Events that can be triggered on the AppCoordinator state machine
|
/// Events that can be triggered on the AppCoordinator state machine
|
||||||
enum Event: EventType {
|
enum Event: EventType {
|
||||||
/// Start the user session flows
|
/// Start the user session flows
|
||||||
@@ -77,9 +81,6 @@ class UserSessionFlowCoordinatorStateMachine {
|
|||||||
case showInvitesScreen
|
case showInvitesScreen
|
||||||
/// The invites screen has been dismissed
|
/// The invites screen has been dismissed
|
||||||
case closedInvitesScreen
|
case closedInvitesScreen
|
||||||
|
|
||||||
/// Request presentation of the settings of a specific room
|
|
||||||
case selectRoomDetails(roomId: String)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private let stateMachine: StateMachine<State, Event>
|
private let stateMachine: StateMachine<State, Event>
|
||||||
@@ -101,8 +102,12 @@ class UserSessionFlowCoordinatorStateMachine {
|
|||||||
switch (event, fromState) {
|
switch (event, fromState) {
|
||||||
case (.selectRoom(let roomId), .roomList):
|
case (.selectRoom(let roomId), .roomList):
|
||||||
return .roomList(selectedRoomId: roomId)
|
return .roomList(selectedRoomId: roomId)
|
||||||
|
case (.selectRoom(let roomId), .invitesScreen):
|
||||||
|
return .invitesScreen(selectedRoomId: roomId)
|
||||||
case (.deselectRoom, .roomList):
|
case (.deselectRoom, .roomList):
|
||||||
return .roomList(selectedRoomId: nil)
|
return .roomList(selectedRoomId: nil)
|
||||||
|
case (.deselectRoom, .invitesScreen):
|
||||||
|
return .invitesScreen(selectedRoomId: nil)
|
||||||
|
|
||||||
case (.showSettingsScreen, .roomList(let selectedRoomId)):
|
case (.showSettingsScreen, .roomList(let selectedRoomId)):
|
||||||
return .settingsScreen(selectedRoomId: selectedRoomId)
|
return .settingsScreen(selectedRoomId: selectedRoomId)
|
||||||
@@ -128,13 +133,6 @@ class UserSessionFlowCoordinatorStateMachine {
|
|||||||
return .invitesScreen(selectedRoomId: selectedRoomId)
|
return .invitesScreen(selectedRoomId: selectedRoomId)
|
||||||
case (.closedInvitesScreen, .invitesScreen(let selectedRoomId)):
|
case (.closedInvitesScreen, .invitesScreen(let selectedRoomId)):
|
||||||
return .roomList(selectedRoomId: selectedRoomId)
|
return .roomList(selectedRoomId: selectedRoomId)
|
||||||
case (.selectRoom(let roomId), .invitesScreen):
|
|
||||||
return .invitesScreen(selectedRoomId: roomId)
|
|
||||||
case (.deselectRoom, .invitesScreen):
|
|
||||||
return .invitesScreen(selectedRoomId: nil)
|
|
||||||
|
|
||||||
case (.selectRoomDetails(let roomId), .roomList):
|
|
||||||
return .roomList(selectedRoomId: roomId)
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return nil
|
return nil
|
||||||
@@ -176,7 +174,3 @@ class UserSessionFlowCoordinatorStateMachine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct EventUserInfo {
|
|
||||||
let animated: Bool
|
|
||||||
}
|
|
||||||
@@ -14,30 +14,34 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
import Combine
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct RoomScreenCoordinatorParameters {
|
struct RoomScreenCoordinatorParameters {
|
||||||
let navigationStackCoordinator: NavigationStackCoordinator
|
|
||||||
let roomProxy: RoomProxyProtocol
|
let roomProxy: RoomProxyProtocol
|
||||||
let timelineController: RoomTimelineControllerProtocol
|
let timelineController: RoomTimelineControllerProtocol
|
||||||
let mediaProvider: MediaProviderProtocol
|
let mediaProvider: MediaProviderProtocol
|
||||||
let emojiProvider: EmojiProviderProtocol
|
let emojiProvider: EmojiProviderProtocol
|
||||||
let userDiscoveryService: UserDiscoveryServiceProtocol
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum RoomScreenCoordinatorAction {
|
enum RoomScreenCoordinatorAction {
|
||||||
case leftRoom
|
case presentMediaViewer(file: MediaFileHandleProxy, title: String?)
|
||||||
|
case presentReportContent(itemID: String, senderID: String)
|
||||||
|
case presentMediaUploadPicker(MediaPickerScreenSource)
|
||||||
|
case presentMediaUploadPreviewScreen(URL)
|
||||||
|
case presentRoomDetails
|
||||||
|
case presentEmojiPicker(itemID: String)
|
||||||
}
|
}
|
||||||
|
|
||||||
final class RoomScreenCoordinator: CoordinatorProtocol {
|
final class RoomScreenCoordinator: CoordinatorProtocol {
|
||||||
private var parameters: RoomScreenCoordinatorParameters
|
private var parameters: RoomScreenCoordinatorParameters
|
||||||
|
|
||||||
private var viewModel: RoomScreenViewModelProtocol
|
private var viewModel: RoomScreenViewModelProtocol
|
||||||
private var navigationStackCoordinator: NavigationStackCoordinator {
|
|
||||||
parameters.navigationStackCoordinator
|
|
||||||
}
|
|
||||||
|
|
||||||
var callback: ((RoomScreenCoordinatorAction) -> Void)?
|
private let actionsSubject: PassthroughSubject<RoomScreenCoordinatorAction, Never> = .init()
|
||||||
|
var actions: AnyPublisher<RoomScreenCoordinatorAction, Never> {
|
||||||
|
actionsSubject.eraseToAnyPublisher()
|
||||||
|
}
|
||||||
|
|
||||||
init(parameters: RoomScreenCoordinatorParameters) {
|
init(parameters: RoomScreenCoordinatorParameters) {
|
||||||
self.parameters = parameters
|
self.parameters = parameters
|
||||||
@@ -55,21 +59,21 @@ final class RoomScreenCoordinator: CoordinatorProtocol {
|
|||||||
|
|
||||||
switch action {
|
switch action {
|
||||||
case .displayRoomDetails:
|
case .displayRoomDetails:
|
||||||
self.displayRoomDetails()
|
actionsSubject.send(.presentRoomDetails)
|
||||||
case .displayMediaFile(let file, let title):
|
case .displayMediaViewer(let file, let title):
|
||||||
self.displayFilePreview(for: file, with: title)
|
actionsSubject.send(.presentMediaViewer(file: file, title: title))
|
||||||
case .displayEmojiPicker(let itemID):
|
case .displayEmojiPicker(let itemID):
|
||||||
self.displayEmojiPickerScreen(for: itemID)
|
actionsSubject.send(.presentEmojiPicker(itemID: itemID))
|
||||||
case .displayReportContent(let itemID, let senderID):
|
case .displayReportContent(let itemID, let senderID):
|
||||||
self.displayReportContent(for: itemID, from: senderID)
|
actionsSubject.send(.presentReportContent(itemID: itemID, senderID: senderID))
|
||||||
case .displayCameraPicker:
|
case .displayCameraPicker:
|
||||||
self.displayMediaPickerWithSource(.camera)
|
actionsSubject.send(.presentMediaUploadPicker(.camera))
|
||||||
case .displayMediaPicker:
|
case .displayMediaPicker:
|
||||||
self.displayMediaPickerWithSource(.photoLibrary)
|
actionsSubject.send(.presentMediaUploadPicker(.photoLibrary))
|
||||||
case .displayDocumentPicker:
|
case .displayDocumentPicker:
|
||||||
self.displayMediaPickerWithSource(.documents)
|
actionsSubject.send(.presentMediaUploadPicker(.documents))
|
||||||
case .displayMediaUploadPreviewScreen(let url):
|
case .displayMediaUploadPreviewScreen(let url):
|
||||||
self.displayMediaUploadPreviewScreenForFile(at: url)
|
actionsSubject.send(.presentMediaUploadPreviewScreen(url))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -81,123 +85,4 @@ final class RoomScreenCoordinator: CoordinatorProtocol {
|
|||||||
func toPresentable() -> AnyView {
|
func toPresentable() -> AnyView {
|
||||||
AnyView(RoomScreen(context: viewModel.context))
|
AnyView(RoomScreen(context: viewModel.context))
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Private
|
|
||||||
|
|
||||||
private func displayMediaPickerWithSource(_ source: MediaPickerScreenSource) {
|
|
||||||
let stackCoordinator = NavigationStackCoordinator()
|
|
||||||
let userIndicatorController = UserIndicatorController(rootCoordinator: stackCoordinator)
|
|
||||||
|
|
||||||
let mediaPickerCoordinator = MediaPickerScreenCoordinator(userIndicatorController: userIndicatorController, source: source) { [weak self] action in
|
|
||||||
switch action {
|
|
||||||
case .cancel:
|
|
||||||
self?.navigationStackCoordinator.setSheetCoordinator(nil)
|
|
||||||
case .selectMediaAtURL(let url):
|
|
||||||
self?.displayMediaUploadPreviewScreenForFile(at: url)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stackCoordinator.setRootCoordinator(mediaPickerCoordinator)
|
|
||||||
|
|
||||||
navigationStackCoordinator.setSheetCoordinator(userIndicatorController)
|
|
||||||
}
|
|
||||||
|
|
||||||
private func displayMediaUploadPreviewScreenForFile(at url: URL) {
|
|
||||||
let stackCoordinator = NavigationStackCoordinator()
|
|
||||||
let userIndicatorController = UserIndicatorController(rootCoordinator: stackCoordinator)
|
|
||||||
|
|
||||||
let parameters = MediaUploadPreviewScreenCoordinatorParameters(userIndicatorController: userIndicatorController,
|
|
||||||
roomProxy: parameters.roomProxy,
|
|
||||||
mediaUploadingPreprocessor: MediaUploadingPreprocessor(),
|
|
||||||
title: url.lastPathComponent,
|
|
||||||
url: url)
|
|
||||||
|
|
||||||
let mediaUploadPreviewScreenCoordinator = MediaUploadPreviewScreenCoordinator(parameters: parameters) { [weak self] action in
|
|
||||||
switch action {
|
|
||||||
case .dismiss:
|
|
||||||
self?.navigationStackCoordinator.setSheetCoordinator(nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stackCoordinator.setRootCoordinator(mediaUploadPreviewScreenCoordinator)
|
|
||||||
|
|
||||||
navigationStackCoordinator.setSheetCoordinator(userIndicatorController)
|
|
||||||
}
|
|
||||||
|
|
||||||
private func displayFilePreview(for file: MediaFileHandleProxy, with title: String?) {
|
|
||||||
let params = FilePreviewScreenCoordinatorParameters(mediaFile: file, title: title)
|
|
||||||
let coordinator = FilePreviewScreenCoordinator(parameters: params)
|
|
||||||
coordinator.callback = { [weak self] _ in
|
|
||||||
self?.navigationStackCoordinator.pop()
|
|
||||||
}
|
|
||||||
|
|
||||||
navigationStackCoordinator.push(coordinator)
|
|
||||||
}
|
|
||||||
|
|
||||||
private func displayEmojiPickerScreen(for itemId: String) {
|
|
||||||
let emojiPickerNavigationStackCoordinator = NavigationStackCoordinator()
|
|
||||||
|
|
||||||
let params = EmojiPickerScreenCoordinatorParameters(emojiProvider: parameters.emojiProvider,
|
|
||||||
itemId: itemId)
|
|
||||||
let coordinator = EmojiPickerScreenCoordinator(parameters: params)
|
|
||||||
coordinator.callback = { [weak self] action in
|
|
||||||
switch action {
|
|
||||||
case let .emojiSelected(emoji: emoji, itemId: itemId):
|
|
||||||
MXLog.debug("Selected \(emoji) for \(itemId)")
|
|
||||||
self?.navigationStackCoordinator.setSheetCoordinator(nil)
|
|
||||||
Task {
|
|
||||||
await self?.parameters.timelineController.sendReaction(emoji, to: itemId)
|
|
||||||
}
|
|
||||||
case .dismiss:
|
|
||||||
self?.navigationStackCoordinator.setSheetCoordinator(nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
emojiPickerNavigationStackCoordinator.setRootCoordinator(coordinator)
|
|
||||||
emojiPickerNavigationStackCoordinator.presentationDetents = [.medium, .large]
|
|
||||||
|
|
||||||
navigationStackCoordinator.setSheetCoordinator(emojiPickerNavigationStackCoordinator)
|
|
||||||
}
|
|
||||||
|
|
||||||
private func displayRoomDetails() {
|
|
||||||
let params = RoomDetailsScreenCoordinatorParameters(navigationStackCoordinator: navigationStackCoordinator,
|
|
||||||
roomProxy: parameters.roomProxy,
|
|
||||||
mediaProvider: parameters.mediaProvider,
|
|
||||||
userDiscoveryService: parameters.userDiscoveryService)
|
|
||||||
let coordinator = RoomDetailsScreenCoordinator(parameters: params)
|
|
||||||
coordinator.callback = { [weak self] action in
|
|
||||||
switch action {
|
|
||||||
case .cancel:
|
|
||||||
self?.navigationStackCoordinator.pop()
|
|
||||||
case .leftRoom:
|
|
||||||
self?.callback?(.leftRoom)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
navigationStackCoordinator.push(coordinator)
|
|
||||||
}
|
|
||||||
|
|
||||||
private func displayReportContent(for itemID: String, from senderID: String) {
|
|
||||||
let navigationCoordinator = NavigationStackCoordinator()
|
|
||||||
let userIndicatorController = UserIndicatorController(rootCoordinator: navigationCoordinator)
|
|
||||||
let parameters = ReportContentScreenCoordinatorParameters(itemID: itemID,
|
|
||||||
senderID: senderID,
|
|
||||||
roomProxy: parameters.roomProxy,
|
|
||||||
userIndicatorController: userIndicatorController)
|
|
||||||
let coordinator = ReportContentScreenCoordinator(parameters: parameters)
|
|
||||||
coordinator.callback = { [weak self] completion in
|
|
||||||
self?.navigationStackCoordinator.setSheetCoordinator(nil)
|
|
||||||
switch completion {
|
|
||||||
case .cancel: break
|
|
||||||
case .finish:
|
|
||||||
self?.showSuccess(label: L10n.commonReportSubmitted)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
navigationCoordinator.setRootCoordinator(coordinator)
|
|
||||||
navigationStackCoordinator.setSheetCoordinator(userIndicatorController)
|
|
||||||
}
|
|
||||||
|
|
||||||
private func showSuccess(label: String) {
|
|
||||||
ServiceLocator.shared.userIndicatorController.submitIndicator(UserIndicator(title: label, iconName: "checkmark"))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import UIKit
|
|||||||
|
|
||||||
enum RoomScreenViewModelAction {
|
enum RoomScreenViewModelAction {
|
||||||
case displayRoomDetails
|
case displayRoomDetails
|
||||||
case displayMediaFile(file: MediaFileHandleProxy, title: String?)
|
case displayMediaViewer(file: MediaFileHandleProxy, title: String?)
|
||||||
case displayEmojiPicker(itemID: String)
|
case displayEmojiPicker(itemID: String)
|
||||||
case displayReportContent(itemID: String, senderID: String)
|
case displayReportContent(itemID: String, senderID: String)
|
||||||
case displayCameraPicker
|
case displayCameraPicker
|
||||||
|
|||||||
@@ -154,7 +154,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
|||||||
|
|
||||||
switch action {
|
switch action {
|
||||||
case .displayMediaFile(let file, let title):
|
case .displayMediaFile(let file, let title):
|
||||||
callback?(.displayMediaFile(file: file, title: title))
|
callback?(.displayMediaViewer(file: file, title: title))
|
||||||
case .none:
|
case .none:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -159,23 +159,19 @@ class MockScreen: Identifiable {
|
|||||||
return OnboardingCoordinator()
|
return OnboardingCoordinator()
|
||||||
case .roomPlainNoAvatar:
|
case .roomPlainNoAvatar:
|
||||||
let navigationStackCoordinator = NavigationStackCoordinator()
|
let navigationStackCoordinator = NavigationStackCoordinator()
|
||||||
let parameters = RoomScreenCoordinatorParameters(navigationStackCoordinator: navigationStackCoordinator,
|
let parameters = RoomScreenCoordinatorParameters(roomProxy: RoomProxyMock(with: .init(displayName: "Some room name", avatarURL: nil)),
|
||||||
roomProxy: RoomProxyMock(with: .init(displayName: "Some room name", avatarURL: nil)),
|
|
||||||
timelineController: MockRoomTimelineController(),
|
timelineController: MockRoomTimelineController(),
|
||||||
mediaProvider: MockMediaProvider(),
|
mediaProvider: MockMediaProvider(),
|
||||||
emojiProvider: EmojiProvider(),
|
emojiProvider: EmojiProvider())
|
||||||
userDiscoveryService: UserDiscoveryServiceMock())
|
|
||||||
let coordinator = RoomScreenCoordinator(parameters: parameters)
|
let coordinator = RoomScreenCoordinator(parameters: parameters)
|
||||||
navigationStackCoordinator.setRootCoordinator(coordinator)
|
navigationStackCoordinator.setRootCoordinator(coordinator)
|
||||||
return navigationStackCoordinator
|
return navigationStackCoordinator
|
||||||
case .roomEncryptedWithAvatar:
|
case .roomEncryptedWithAvatar:
|
||||||
let navigationStackCoordinator = NavigationStackCoordinator()
|
let navigationStackCoordinator = NavigationStackCoordinator()
|
||||||
let parameters = RoomScreenCoordinatorParameters(navigationStackCoordinator: navigationStackCoordinator,
|
let parameters = RoomScreenCoordinatorParameters(roomProxy: RoomProxyMock(with: .init(displayName: "Some room name", avatarURL: URL.picturesDirectory)),
|
||||||
roomProxy: RoomProxyMock(with: .init(displayName: "Some room name", avatarURL: URL.picturesDirectory)),
|
|
||||||
timelineController: MockRoomTimelineController(),
|
timelineController: MockRoomTimelineController(),
|
||||||
mediaProvider: MockMediaProvider(),
|
mediaProvider: MockMediaProvider(),
|
||||||
emojiProvider: EmojiProvider(),
|
emojiProvider: EmojiProvider())
|
||||||
userDiscoveryService: UserDiscoveryServiceMock())
|
|
||||||
let coordinator = RoomScreenCoordinator(parameters: parameters)
|
let coordinator = RoomScreenCoordinator(parameters: parameters)
|
||||||
navigationStackCoordinator.setRootCoordinator(coordinator)
|
navigationStackCoordinator.setRootCoordinator(coordinator)
|
||||||
return navigationStackCoordinator
|
return navigationStackCoordinator
|
||||||
@@ -183,12 +179,10 @@ class MockScreen: Identifiable {
|
|||||||
let navigationStackCoordinator = NavigationStackCoordinator()
|
let navigationStackCoordinator = NavigationStackCoordinator()
|
||||||
let timelineController = MockRoomTimelineController()
|
let timelineController = MockRoomTimelineController()
|
||||||
timelineController.timelineItems = RoomTimelineItemFixtures.smallChunk
|
timelineController.timelineItems = RoomTimelineItemFixtures.smallChunk
|
||||||
let parameters = RoomScreenCoordinatorParameters(navigationStackCoordinator: navigationStackCoordinator,
|
let parameters = RoomScreenCoordinatorParameters(roomProxy: RoomProxyMock(with: .init(displayName: "New room", avatarURL: URL.picturesDirectory)),
|
||||||
roomProxy: RoomProxyMock(with: .init(displayName: "New room", avatarURL: URL.picturesDirectory)),
|
|
||||||
timelineController: timelineController,
|
timelineController: timelineController,
|
||||||
mediaProvider: MockMediaProvider(),
|
mediaProvider: MockMediaProvider(),
|
||||||
emojiProvider: EmojiProvider(),
|
emojiProvider: EmojiProvider())
|
||||||
userDiscoveryService: UserDiscoveryServiceMock())
|
|
||||||
let coordinator = RoomScreenCoordinator(parameters: parameters)
|
let coordinator = RoomScreenCoordinator(parameters: parameters)
|
||||||
navigationStackCoordinator.setRootCoordinator(coordinator)
|
navigationStackCoordinator.setRootCoordinator(coordinator)
|
||||||
return navigationStackCoordinator
|
return navigationStackCoordinator
|
||||||
@@ -199,12 +193,10 @@ class MockScreen: Identifiable {
|
|||||||
timelineController.timelineItems = RoomTimelineItemFixtures.smallChunk
|
timelineController.timelineItems = RoomTimelineItemFixtures.smallChunk
|
||||||
timelineController.backPaginationResponses = [RoomTimelineItemFixtures.singleMessageChunk]
|
timelineController.backPaginationResponses = [RoomTimelineItemFixtures.singleMessageChunk]
|
||||||
timelineController.incomingItems = [RoomTimelineItemFixtures.incomingMessage]
|
timelineController.incomingItems = [RoomTimelineItemFixtures.incomingMessage]
|
||||||
let parameters = RoomScreenCoordinatorParameters(navigationStackCoordinator: navigationStackCoordinator,
|
let parameters = RoomScreenCoordinatorParameters(roomProxy: RoomProxyMock(with: .init(displayName: "Small timeline", avatarURL: URL.picturesDirectory)),
|
||||||
roomProxy: RoomProxyMock(with: .init(displayName: "Small timeline", avatarURL: URL.picturesDirectory)),
|
|
||||||
timelineController: timelineController,
|
timelineController: timelineController,
|
||||||
mediaProvider: MockMediaProvider(),
|
mediaProvider: MockMediaProvider(),
|
||||||
emojiProvider: EmojiProvider(),
|
emojiProvider: EmojiProvider())
|
||||||
userDiscoveryService: UserDiscoveryServiceMock())
|
|
||||||
let coordinator = RoomScreenCoordinator(parameters: parameters)
|
let coordinator = RoomScreenCoordinator(parameters: parameters)
|
||||||
|
|
||||||
navigationStackCoordinator.setRootCoordinator(coordinator)
|
navigationStackCoordinator.setRootCoordinator(coordinator)
|
||||||
@@ -215,12 +207,10 @@ class MockScreen: Identifiable {
|
|||||||
let timelineController = MockRoomTimelineController(listenForSignals: true)
|
let timelineController = MockRoomTimelineController(listenForSignals: true)
|
||||||
timelineController.timelineItems = RoomTimelineItemFixtures.smallChunk
|
timelineController.timelineItems = RoomTimelineItemFixtures.smallChunk
|
||||||
timelineController.backPaginationResponses = [RoomTimelineItemFixtures.largeChunk]
|
timelineController.backPaginationResponses = [RoomTimelineItemFixtures.largeChunk]
|
||||||
let parameters = RoomScreenCoordinatorParameters(navigationStackCoordinator: navigationStackCoordinator,
|
let parameters = RoomScreenCoordinatorParameters(roomProxy: RoomProxyMock(with: .init(displayName: "Small timeline, paginating", avatarURL: URL.picturesDirectory)),
|
||||||
roomProxy: RoomProxyMock(with: .init(displayName: "Small timeline, paginating", avatarURL: URL.picturesDirectory)),
|
|
||||||
timelineController: timelineController,
|
timelineController: timelineController,
|
||||||
mediaProvider: MockMediaProvider(),
|
mediaProvider: MockMediaProvider(),
|
||||||
emojiProvider: EmojiProvider(),
|
emojiProvider: EmojiProvider())
|
||||||
userDiscoveryService: UserDiscoveryServiceMock())
|
|
||||||
let coordinator = RoomScreenCoordinator(parameters: parameters)
|
let coordinator = RoomScreenCoordinator(parameters: parameters)
|
||||||
|
|
||||||
navigationStackCoordinator.setRootCoordinator(coordinator)
|
navigationStackCoordinator.setRootCoordinator(coordinator)
|
||||||
@@ -231,12 +221,10 @@ class MockScreen: Identifiable {
|
|||||||
let timelineController = MockRoomTimelineController(listenForSignals: true)
|
let timelineController = MockRoomTimelineController(listenForSignals: true)
|
||||||
timelineController.timelineItems = RoomTimelineItemFixtures.largeChunk
|
timelineController.timelineItems = RoomTimelineItemFixtures.largeChunk
|
||||||
timelineController.backPaginationResponses = [RoomTimelineItemFixtures.largeChunk]
|
timelineController.backPaginationResponses = [RoomTimelineItemFixtures.largeChunk]
|
||||||
let parameters = RoomScreenCoordinatorParameters(navigationStackCoordinator: navigationStackCoordinator,
|
let parameters = RoomScreenCoordinatorParameters(roomProxy: RoomProxyMock(with: .init(displayName: "Large timeline", avatarURL: URL.picturesDirectory)),
|
||||||
roomProxy: RoomProxyMock(with: .init(displayName: "Large timeline", avatarURL: URL.picturesDirectory)),
|
|
||||||
timelineController: timelineController,
|
timelineController: timelineController,
|
||||||
mediaProvider: MockMediaProvider(),
|
mediaProvider: MockMediaProvider(),
|
||||||
emojiProvider: EmojiProvider(),
|
emojiProvider: EmojiProvider())
|
||||||
userDiscoveryService: UserDiscoveryServiceMock())
|
|
||||||
let coordinator = RoomScreenCoordinator(parameters: parameters)
|
let coordinator = RoomScreenCoordinator(parameters: parameters)
|
||||||
|
|
||||||
navigationStackCoordinator.setRootCoordinator(coordinator)
|
navigationStackCoordinator.setRootCoordinator(coordinator)
|
||||||
@@ -248,12 +236,10 @@ class MockScreen: Identifiable {
|
|||||||
timelineController.timelineItems = RoomTimelineItemFixtures.largeChunk
|
timelineController.timelineItems = RoomTimelineItemFixtures.largeChunk
|
||||||
timelineController.backPaginationResponses = [RoomTimelineItemFixtures.largeChunk]
|
timelineController.backPaginationResponses = [RoomTimelineItemFixtures.largeChunk]
|
||||||
timelineController.incomingItems = [RoomTimelineItemFixtures.incomingMessage]
|
timelineController.incomingItems = [RoomTimelineItemFixtures.incomingMessage]
|
||||||
let parameters = RoomScreenCoordinatorParameters(navigationStackCoordinator: navigationStackCoordinator,
|
let parameters = RoomScreenCoordinatorParameters(roomProxy: RoomProxyMock(with: .init(displayName: "Large timeline", avatarURL: URL.picturesDirectory)),
|
||||||
roomProxy: RoomProxyMock(with: .init(displayName: "Large timeline", avatarURL: URL.picturesDirectory)),
|
|
||||||
timelineController: timelineController,
|
timelineController: timelineController,
|
||||||
mediaProvider: MockMediaProvider(),
|
mediaProvider: MockMediaProvider(),
|
||||||
emojiProvider: EmojiProvider(),
|
emojiProvider: EmojiProvider())
|
||||||
userDiscoveryService: UserDiscoveryServiceMock())
|
|
||||||
let coordinator = RoomScreenCoordinator(parameters: parameters)
|
let coordinator = RoomScreenCoordinator(parameters: parameters)
|
||||||
|
|
||||||
navigationStackCoordinator.setRootCoordinator(coordinator)
|
navigationStackCoordinator.setRootCoordinator(coordinator)
|
||||||
@@ -264,12 +250,10 @@ class MockScreen: Identifiable {
|
|||||||
let timelineController = MockRoomTimelineController(listenForSignals: true)
|
let timelineController = MockRoomTimelineController(listenForSignals: true)
|
||||||
timelineController.timelineItems = RoomTimelineItemFixtures.largeChunk
|
timelineController.timelineItems = RoomTimelineItemFixtures.largeChunk
|
||||||
timelineController.incomingItems = [RoomTimelineItemFixtures.incomingMessage]
|
timelineController.incomingItems = [RoomTimelineItemFixtures.incomingMessage]
|
||||||
let parameters = RoomScreenCoordinatorParameters(navigationStackCoordinator: navigationStackCoordinator,
|
let parameters = RoomScreenCoordinatorParameters(roomProxy: RoomProxyMock(with: .init(displayName: "Large timeline", avatarURL: URL.picturesDirectory)),
|
||||||
roomProxy: RoomProxyMock(with: .init(displayName: "Large timeline", avatarURL: URL.picturesDirectory)),
|
|
||||||
timelineController: timelineController,
|
timelineController: timelineController,
|
||||||
mediaProvider: MockMediaProvider(),
|
mediaProvider: MockMediaProvider(),
|
||||||
emojiProvider: EmojiProvider(),
|
emojiProvider: EmojiProvider())
|
||||||
userDiscoveryService: UserDiscoveryServiceMock())
|
|
||||||
let coordinator = RoomScreenCoordinator(parameters: parameters)
|
let coordinator = RoomScreenCoordinator(parameters: parameters)
|
||||||
|
|
||||||
navigationStackCoordinator.setRootCoordinator(coordinator)
|
navigationStackCoordinator.setRootCoordinator(coordinator)
|
||||||
|
|||||||
@@ -0,0 +1,87 @@
|
|||||||
|
//
|
||||||
|
// Copyright 2023 New Vector Ltd
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
//
|
||||||
|
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
import Combine
|
||||||
|
@testable import ElementX
|
||||||
|
|
||||||
|
@MainActor
|
||||||
|
class RoomFlowCoordinatorTests: XCTestCase {
|
||||||
|
var roomFlowCoordinator: RoomFlowCoordinator!
|
||||||
|
var navigationStackCoordinator: NavigationStackCoordinator!
|
||||||
|
private var cancellables: Set<AnyCancellable> = .init()
|
||||||
|
|
||||||
|
override func setUp() async throws {
|
||||||
|
let clientProxy = MockClientProxy(userID: "hi@bob", roomSummaryProvider: MockRoomSummaryProvider(state: .loaded(.mockRooms)))
|
||||||
|
let mediaProvider = MockMediaProvider()
|
||||||
|
let userSession = MockUserSession(clientProxy: clientProxy, mediaProvider: mediaProvider)
|
||||||
|
|
||||||
|
let navigationSplitCoordinator = NavigationSplitCoordinator(placeholderCoordinator: SplashScreenCoordinator())
|
||||||
|
navigationStackCoordinator = NavigationStackCoordinator()
|
||||||
|
navigationSplitCoordinator.setDetailCoordinator(navigationStackCoordinator)
|
||||||
|
|
||||||
|
roomFlowCoordinator = RoomFlowCoordinator(userSession: userSession,
|
||||||
|
roomTimelineControllerFactory: MockRoomTimelineControllerFactory(),
|
||||||
|
navigationStackCoordinator: navigationStackCoordinator,
|
||||||
|
navigationSplitCoordinator: navigationSplitCoordinator,
|
||||||
|
emojiProvider: EmojiProvider())
|
||||||
|
}
|
||||||
|
|
||||||
|
func testRoomPresentation() async {
|
||||||
|
await process(route: .room(roomID: "1"), expectedAction: .presentedRoom("1"))
|
||||||
|
XCTAssert(navigationStackCoordinator.rootCoordinator is RoomScreenCoordinator)
|
||||||
|
|
||||||
|
await process(route: .roomList, expectedAction: .dismissedRoom)
|
||||||
|
XCTAssertNil(navigationStackCoordinator.rootCoordinator)
|
||||||
|
|
||||||
|
await process(route: .room(roomID: "1"), expectedAction: .presentedRoom("1"))
|
||||||
|
XCTAssert(navigationStackCoordinator.rootCoordinator is RoomScreenCoordinator)
|
||||||
|
|
||||||
|
await process(route: .room(roomID: "2"), expectedAction: .presentedRoom("2"))
|
||||||
|
XCTAssert(navigationStackCoordinator.rootCoordinator is RoomScreenCoordinator)
|
||||||
|
|
||||||
|
await process(route: .roomList, expectedAction: .dismissedRoom)
|
||||||
|
XCTAssertNil(navigationStackCoordinator.rootCoordinator)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testRoomDetailsPresentation() async {
|
||||||
|
await process(route: .roomDetails(roomID: "1"), expectedAction: .presentedRoom("1"))
|
||||||
|
XCTAssert(navigationStackCoordinator.rootCoordinator is RoomScreenCoordinator)
|
||||||
|
XCTAssertEqual(navigationStackCoordinator.stackCoordinators.count, 1)
|
||||||
|
XCTAssert(navigationStackCoordinator.stackCoordinators.first is RoomDetailsScreenCoordinator)
|
||||||
|
|
||||||
|
await process(route: .roomList, expectedAction: .dismissedRoom)
|
||||||
|
XCTAssertNil(navigationStackCoordinator.rootCoordinator)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testStackUnwinding() async {
|
||||||
|
await process(route: .roomDetails(roomID: "1"), expectedAction: .presentedRoom("1"))
|
||||||
|
XCTAssert(navigationStackCoordinator.rootCoordinator is RoomScreenCoordinator)
|
||||||
|
XCTAssertEqual(navigationStackCoordinator.stackCoordinators.count, 1)
|
||||||
|
XCTAssert(navigationStackCoordinator.stackCoordinators.first is RoomDetailsScreenCoordinator)
|
||||||
|
|
||||||
|
await process(route: .room(roomID: "2"), expectedAction: .presentedRoom("2"))
|
||||||
|
XCTAssert(navigationStackCoordinator.rootCoordinator is RoomScreenCoordinator)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Private
|
||||||
|
|
||||||
|
func process(route: AppRoute, expectedAction: RoomFlowCoordinatorAction) async {
|
||||||
|
Task { try? await Task.sleep(for: .seconds(0.1)); roomFlowCoordinator.handleAppRoute(route, animated: true) }
|
||||||
|
_ = await roomFlowCoordinator.actions.values.first(where: { $0 == expectedAction })
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -16,7 +16,7 @@ options:
|
|||||||
- pattern: ElementX
|
- pattern: ElementX
|
||||||
order: [Sources, Resources, SupportingFiles]
|
order: [Sources, Resources, SupportingFiles]
|
||||||
- pattern: Sources
|
- pattern: Sources
|
||||||
order: [Application, UserSession, Services, Screens, Other, UITests]
|
order: [Application, UserSession, Services, FlowCoordinators, Screens, Other, UITests]
|
||||||
postGenCommand: cd Tools/XcodeGen && sh postGenCommand.sh
|
postGenCommand: cd Tools/XcodeGen && sh postGenCommand.sh
|
||||||
|
|
||||||
settings:
|
settings:
|
||||||
|
|||||||
Reference in New Issue
Block a user