From 1ca84c8c94909f29025303b825477d36a344c2fb Mon Sep 17 00:00:00 2001 From: Doug Date: Tue, 29 Jul 2025 15:55:27 +0100 Subject: [PATCH] Re-introduce a basic UserSessionFlowCoordinator using the NavigationTabCoordinator. --- ElementX.xcodeproj/project.pbxproj | 26 +++-- .../xcshareddata/swiftpm/Package.resolved | 6 +- .../Sources/Application/AppCoordinator.swift | 26 ++--- .../ChatsFlowCoordinator.swift | 9 +- .../UserSessionFlowCoordinator.swift | 106 ++++++++++++++++++ .../UITests/UITestsAppCoordinator.swift | 30 ++--- .../Sources/ChatsFlowCoordinatorTests.swift | 7 +- project.yml | 2 +- 8 files changed, 160 insertions(+), 52 deletions(-) create mode 100644 ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index 342ae40f7..6389dea82 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -269,7 +269,6 @@ 308BD9343B95657FAA583FB7 /* SwiftState in Frameworks */ = {isa = PBXBuildFile; productRef = 19CD5B074D7DD44AF4C58BB6 /* SwiftState */; }; 30CC1DB7CE357659C82AA115 /* MediaProviderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85EB16E7FE59A947CA441531 /* MediaProviderProtocol.swift */; }; 30E5628F74AD3C27A061BF25 /* QRCodeLoginScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BC1B7CB061C9865B2B91B56 /* QRCodeLoginScreenViewModel.swift */; }; - 3113065AABBC14CEAE6843FA /* UserSessionFlowCoordinatorStateMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8774CF614849664B5B3C2A1 /* UserSessionFlowCoordinatorStateMachine.swift */; }; 311868F5E65BA61AFA6CCC2C /* CompoundHook.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B89D6C760E8CAE29CA28FB1 /* CompoundHook.swift */; }; 3118D9ABFD4BE5A3492FF88A /* ElementCallConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC437C491EA6996513B1CEAB /* ElementCallConfiguration.swift */; }; 32B7891D937377A59606EDFC /* UserFlowTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21DD8599815136EFF5B73F38 /* UserFlowTests.swift */; }; @@ -322,6 +321,7 @@ 3B5AB5CF8D8163599C5BF19B /* PillViewOnBubblePreviews.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2005B4AD70A792340E2694F1 /* PillViewOnBubblePreviews.swift */; }; 3B98049F56025726FB646ABD /* SwipeToReplyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B0E0B55E2EE75AF67029924 /* SwipeToReplyView.swift */; }; 3BEBDCB42BABFA3B456FECA7 /* MapTilerURLBuilderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D262A26713C18BB70C82CA5 /* MapTilerURLBuilderTests.swift */; }; + 3C1E27520258D4C89058839E /* ChatsFlowCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55B3BF242C93CD32C4C3E08D /* ChatsFlowCoordinator.swift */; }; 3C312A3AEDE58BB1C9BBB07C /* preview_avatar_room.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 12FD5280AF55AB7F50F8E47D /* preview_avatar_room.jpg */; }; 3C31E1A65EEB61E72E1113B4 /* AudioRecorderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBEC57C204D77908E355EF42 /* AudioRecorderProtocol.swift */; }; 3C549A0BF39F8A854D45D9FD /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = 020597E28A4BC8E1BE8EDF6E /* KeychainAccess */; }; @@ -418,6 +418,7 @@ 4DAEE2468669848B6C9F55B4 /* TimelineReadReceiptsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33035418BB35754232985871 /* TimelineReadReceiptsView.swift */; }; 4DEEFB73181C3B023DB42686 /* NetworkMonitorProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1575947B7A6FE08C57FE5EE4 /* NetworkMonitorProtocol.swift */; }; 4E0D9E09B52CEC4C0E6211A8 /* MediaPickerScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64F49FB9EE2913234F06CE68 /* MediaPickerScreenCoordinator.swift */; }; + 4E1E13E5B913D35959E6BFCC /* ChatsFlowCoordinatorStateMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 566FB9DA81607C2739D8C6A0 /* ChatsFlowCoordinatorStateMachine.swift */; }; 4E36A66E0EDA74BF3A036FD0 /* RoomChangeRolesScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AAD8C633AA57948B34EDCF7 /* RoomChangeRolesScreenViewModelProtocol.swift */; }; 4E4EF97B9F9CEFAC726BA72F /* TimelineProviderMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62EACAFB3F3E017060F9F1C5 /* TimelineProviderMock.swift */; }; 4E8A2A2CFEB212F14E49E1A1 /* AppLockSetupSettingsScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5484457C81B325660901B161 /* AppLockSetupSettingsScreen.swift */; }; @@ -515,7 +516,6 @@ 61941DEE5F3834765770BE01 /* InviteUsersScreenSelectedItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 10F32E0B4B83D2A11EE8D011 /* InviteUsersScreenSelectedItem.swift */; }; 61A36B9BB2ADE36CEFF5E98C /* Array.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E93A1BE7D8A2EBCAD51EEB4 /* Array.swift */; }; 62684AECDFC5C7DC989CBD9E /* SnapshotTesting in Frameworks */ = {isa = PBXBuildFile; productRef = 7B6BC3219ADD8AA0311D2B86 /* SnapshotTesting */; }; - 627139A3D79F032BA81E3A53 /* UserSessionFlowCoordinatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FA29BAE9B0F2D90E57B261C /* UserSessionFlowCoordinatorTests.swift */; }; 6298AB0906DDD3525CD78C6B /* KZFileWatchers in Frameworks */ = {isa = PBXBuildFile; productRef = 81DB3AB6CE996AB3954F4F03 /* KZFileWatchers */; }; 62A7FC3A0191BC7181AA432B /* AudioRecorder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 907FA4DE17DEA1A3738EFB83 /* AudioRecorder.swift */; }; 62C5876C4254C58C2086F0DE /* HomeScreenContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = A3B4B58B79A6FA250B24A1EC /* HomeScreenContent.swift */; }; @@ -674,6 +674,7 @@ 7E43FBB918AAC136034F2758 /* test_image.png in Resources */ = {isa = PBXBuildFile; fileRef = 810133CF215075C285FC6F3A /* test_image.png */; }; 7E91BAC17963ED41208F489B /* UserSessionStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E8BDC092D817B68CD9040C5 /* UserSessionStore.swift */; }; 7F0B6EB5CB52D7B7A2BB7D15 /* BannedRoomProxyMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FD7E851E2BA8C5A8D284B2A /* BannedRoomProxyMock.swift */; }; + 7F464E540158C3C6EC24678B /* ChatsFlowCoordinatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD6906A41D00178F2FF3C097 /* ChatsFlowCoordinatorTests.swift */; }; 7F61F9ACD5EC9E845EF3EFBF /* BugReportServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EFFD3200F9960D4996159F10 /* BugReportServiceTests.swift */; }; 7F7EA51A9A43125A8CB6AC90 /* NotificationSettingsScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46D560DDA3B20C82766ACFAD /* NotificationSettingsScreenViewModel.swift */; }; 7F825CBD857D65DC986087BA /* NoticeRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F54FA7C5CB7B342EF9B9B2F /* NoticeRoomTimelineView.swift */; }; @@ -1048,6 +1049,7 @@ C5627BCC3EBBB96A943B6D93 /* RestorationTokenTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7978C9EFBDD7DE39BD86726 /* RestorationTokenTests.swift */; }; C58E305C380D3ADDF7912180 /* StickerRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 818695BED971753243FEF897 /* StickerRoomTimelineItem.swift */; }; C5A07E2D88BE7D51DCECD166 /* LoginScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D0B159AFFBBD8ECFD0E37FA /* LoginScreenModels.swift */; }; + C5E3A4A678B4F8900830B76A /* NavigationTabCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C39B1D3FC8CF41D6C3B054F /* NavigationTabCoordinator.swift */; }; C67FCC854F3A6FC7A2EC04D0 /* MediaUploadPreviewScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70C86696AC9521F8ED88FBEB /* MediaUploadPreviewScreen.swift */; }; C6C06DDA8881260303FBA3A0 /* Date.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2141693488CE5446BB391964 /* Date.swift */; }; C76892321558E75101E68ED6 /* ReadableFrameModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 398817652FA8ABAE0A31AC6D /* ReadableFrameModifier.swift */; }; @@ -1667,6 +1669,7 @@ 2BB385E148DE55C85C0A02D6 /* SoftLogoutScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SoftLogoutScreenModels.swift; sourceTree = ""; }; 2BDB3E65A79779EDA5D33D8A /* AudioPlayerState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioPlayerState.swift; sourceTree = ""; }; 2BFDCA5A09EE70BC17F2EFA7 /* URLComponents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLComponents.swift; sourceTree = ""; }; + 2C39B1D3FC8CF41D6C3B054F /* NavigationTabCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationTabCoordinator.swift; sourceTree = ""; }; 2C39D91A31409775B0F4268F /* et */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = et; path = et.lproj/InfoPlist.strings; sourceTree = ""; }; 2CEBCB9676FCD1D0F13188DD /* StringTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringTests.swift; sourceTree = ""; }; 2CF9FE7E0CF9F40D1509E63A /* bg */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = bg; path = bg.lproj/Localizable.stringsdict; sourceTree = ""; }; @@ -1836,7 +1839,6 @@ 4E7F7A975514E850A834B29F /* PaginationIndicatorRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaginationIndicatorRoomTimelineView.swift; sourceTree = ""; }; 4F5F0662483ED69791D63B16 /* et */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = et; path = et.lproj/Localizable.stringsdict; sourceTree = ""; }; 4F75EF13F49DD2204E760910 /* FileRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileRoomTimelineView.swift; sourceTree = ""; }; - 4FA29BAE9B0F2D90E57B261C /* UserSessionFlowCoordinatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSessionFlowCoordinatorTests.swift; sourceTree = ""; }; 4FCB2126C091EEF2454B4D56 /* RoomFlowCoordinatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomFlowCoordinatorTests.swift; sourceTree = ""; }; 4FDD775CFD72DD2D3C8A8390 /* NotificationSettingsProxyProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationSettingsProxyProtocol.swift; sourceTree = ""; }; 502F986D57158674172C58E3 /* AppLockSetupSettingsScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockSetupSettingsScreenModels.swift; sourceTree = ""; }; @@ -1866,8 +1868,10 @@ 54AD70D6E03D2031AE1B5A52 /* TimelineReactionsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineReactionsView.swift; sourceTree = ""; }; 54C4E7B46099462F12000C91 /* DeveloperOptionsScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeveloperOptionsScreenViewModelProtocol.swift; sourceTree = ""; }; 55AEEF8142DF1B59DB40FB93 /* TimelineItemSender.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineItemSender.swift; sourceTree = ""; }; + 55B3BF242C93CD32C4C3E08D /* ChatsFlowCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatsFlowCoordinator.swift; sourceTree = ""; }; 5644919DB2022397D9D5825A /* MockSoftLogoutScreenState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockSoftLogoutScreenState.swift; sourceTree = ""; }; 565F1B2B300597C616B37888 /* FullscreenDialog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FullscreenDialog.swift; sourceTree = ""; }; + 566FB9DA81607C2739D8C6A0 /* ChatsFlowCoordinatorStateMachine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatsFlowCoordinatorStateMachine.swift; sourceTree = ""; }; 56852036214ABA9D7D305768 /* ResolveVerifiedUserSendFailureScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResolveVerifiedUserSendFailureScreenViewModelProtocol.swift; sourceTree = ""; }; 56D6F88FE35A0979D2821E06 /* AppLockScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockScreen.swift; sourceTree = ""; }; 57084488B03BDB33C7B7CA0E /* ResolveVerifiedUserSendFailureScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResolveVerifiedUserSendFailureScreenViewModelTests.swift; sourceTree = ""; }; @@ -2532,6 +2536,7 @@ DCDAB580109C09A6AA97AF7E /* PollFormScreenTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollFormScreenTests.swift; sourceTree = ""; }; DCF239C619971FDE48132550 /* SecureBackupLogoutConfirmationScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureBackupLogoutConfirmationScreenModels.swift; sourceTree = ""; }; DD3C65634A34467CB407A061 /* target.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = target.yml; sourceTree = ""; }; + DD6906A41D00178F2FF3C097 /* ChatsFlowCoordinatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatsFlowCoordinatorTests.swift; sourceTree = ""; }; DD955A0380C287C418F1A74D /* PhotoLibraryManagerMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhotoLibraryManagerMock.swift; sourceTree = ""; }; DD97F9661ABF08CE002054A2 /* AppLockServiceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockServiceTests.swift; sourceTree = ""; }; DE5127D6EA05B2E45D0A7D59 /* JoinRoomScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JoinRoomScreenViewModelTests.swift; sourceTree = ""; }; @@ -2588,7 +2593,6 @@ E78FC546F28E045A560F2963 /* EncryptionKeyProviderProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EncryptionKeyProviderProtocol.swift; sourceTree = ""; }; E8294DB9E95C0C0630418466 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = ""; }; E8495F37D6245AD0CFA1F60B /* AppLockTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockTests.swift; sourceTree = ""; }; - E8774CF614849664B5B3C2A1 /* UserSessionFlowCoordinatorStateMachine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSessionFlowCoordinatorStateMachine.swift; sourceTree = ""; }; E8A1F98AE670377B20679FF5 /* MediaPlayerProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaPlayerProvider.swift; sourceTree = ""; }; E8AE4B3273BA189FDCD4055C /* UserIndicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserIndicator.swift; sourceTree = ""; }; E8CA187FE656EE5A3F6C7DE5 /* UIFont+AttributedStringBuilder.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "UIFont+AttributedStringBuilder.m"; sourceTree = ""; }; @@ -4102,6 +4106,8 @@ 0DBB08A95EFA668F2CF27211 /* AppLockSetupFlowCoordinator.swift */, A9B069D7772DDF6513E0F1B8 /* AuthenticationFlowCoordinator.swift */, 7367B3B9A8CAF902220F31D1 /* BugReportFlowCoordinator.swift */, + 55B3BF242C93CD32C4C3E08D /* ChatsFlowCoordinator.swift */, + 566FB9DA81607C2739D8C6A0 /* ChatsFlowCoordinatorStateMachine.swift */, A07B011547B201A836C03052 /* EncryptionResetFlowCoordinator.swift */, ECB836DD8BE31931F51B8AC9 /* EncryptionSettingsFlowCoordinator.swift */, 2178B951602AA921A5FD9DC8 /* MediaEventsTimelineFlowCoordinator.swift */, @@ -4112,7 +4118,6 @@ 0833F51229E166BCA141D004 /* RoomRolesAndPermissionsFlowCoordinator.swift */, D28F7A6CEEA4A2815B0F0F55 /* SettingsFlowCoordinator.swift */, C99FDEEB71173C4C6FA2734C /* UserSessionFlowCoordinator.swift */, - E8774CF614849664B5B3C2A1 /* UserSessionFlowCoordinatorStateMachine.swift */, ); path = FlowCoordinators; sourceTree = ""; @@ -4405,6 +4410,7 @@ 7EECE8B331CD169790EF284F /* BugReportScreenViewModelTests.swift */, EFFD3200F9960D4996159F10 /* BugReportServiceTests.swift */, CAD9547E47C58930E2CE8306 /* CallScreenViewModelTests.swift */, + DD6906A41D00178F2FF3C097 /* ChatsFlowCoordinatorTests.swift */, D5EA0312A6262484AA393AC9 /* CompletionSuggestionServiceTests.swift */, CA29952595B804DA221A0C1D /* ComposerToolbarViewModelTests.swift */, 69D42EE0102D2857933625DD /* CreateRoomViewModelTests.swift */, @@ -4494,7 +4500,6 @@ 2429224EB0EEA34D35CE9249 /* UserIndicatorControllerTests.swift */, BA241DEEF7C8A7181C0AEDC9 /* UserPreferenceTests.swift */, 71E2E5103702D13361D09100 /* UserProfileScreenViewModelTests.swift */, - 4FA29BAE9B0F2D90E57B261C /* UserSessionFlowCoordinatorTests.swift */, 283974987DA7EC61D2AB57D9 /* VoiceMessageCacheTests.swift */, AC4F10BDD56FA77FEC742333 /* VoiceMessageMediaManagerTests.swift */, D93C94C30E3135BC9290DE13 /* VoiceMessageRecorderTests.swift */, @@ -4574,6 +4579,7 @@ B8F28602AC7AC881AED37EBA /* NavigationCoordinators.swift */, 9A22A05E472533ED3C5A31B3 /* NavigationModule.swift */, CA28F29C9F93E93CC3C2C715 /* NavigationRootCoordinator.swift */, + 2C39B1D3FC8CF41D6C3B054F /* NavigationTabCoordinator.swift */, ); path = Navigation; sourceTree = ""; @@ -7024,6 +7030,7 @@ 1B2F9F368619FFF8C63C87CC /* BugReportScreenViewModelTests.swift in Sources */, 7F61F9ACD5EC9E845EF3EFBF /* BugReportServiceTests.swift in Sources */, 366D5BFE52CB79E804C7D095 /* CallScreenViewModelTests.swift in Sources */, + 7F464E540158C3C6EC24678B /* ChatsFlowCoordinatorTests.swift in Sources */, B5321A1F5B26A0F3EC54909E /* CollapsibleFlowLayoutTests.swift in Sources */, 3A164187907DA43B7858F9EC /* CompletionSuggestionServiceTests.swift in Sources */, 0C932A5158C1D0604DFC5750 /* ComposerToolbarViewModelTests.swift in Sources */, @@ -7127,7 +7134,6 @@ A1DF0E1E526A981ED6D5DF44 /* UserIndicatorControllerTests.swift in Sources */, 04F17DE71A50206336749BAC /* UserPreferenceTests.swift in Sources */, 73F547BEB41D3DAFAAF6E0AF /* UserProfileScreenViewModelTests.swift in Sources */, - 627139A3D79F032BA81E3A53 /* UserSessionFlowCoordinatorTests.swift in Sources */, 81A7C020CB5F6232242A8414 /* UserSessionTests.swift in Sources */, 21AFEFB8CEFE56A3811A1F5B /* VoiceMessageCacheTests.swift in Sources */, 44BDD670FF9095ACE240A3A2 /* VoiceMessageMediaManagerTests.swift in Sources */, @@ -7344,6 +7350,8 @@ E14E469CD97550D0FC58F3CA /* CancellableTask.swift in Sources */, DF8F1211F2B0B56F0FCCA5C2 /* CertificateValidatorHook.swift in Sources */, D885B783B95AD7832D4EF5DD /* CharacterSet.swift in Sources */, + 3C1E27520258D4C89058839E /* ChatsFlowCoordinator.swift in Sources */, + 4E1E13E5B913D35959E6BFCC /* ChatsFlowCoordinatorStateMachine.swift in Sources */, A52090A4FE0DB826578DFC03 /* Client.swift in Sources */, C80E06ED97CE52704A46C148 /* ClientBuilder.swift in Sources */, 87CEA3E07B602705BC2D2A20 /* ClientBuilderHook.swift in Sources */, @@ -7626,6 +7634,7 @@ FA2BBAE9FC5E2E9F960C0980 /* NavigationCoordinators.swift in Sources */, 71C1347F23868324A4F43940 /* NavigationModule.swift in Sources */, B5E455C9689EA600EDB3E9E0 /* NavigationRootCoordinator.swift in Sources */, + C5E3A4A678B4F8900830B76A /* NavigationTabCoordinator.swift in Sources */, 93BAF04D9CCBC0A8841414D0 /* NetworkMonitor.swift in Sources */, 96B3606E30F824095B1DD022 /* NetworkMonitorMock.swift in Sources */, 865DD5CA474C6AE6C2BC008E /* NetworkMonitorProtocol.swift in Sources */, @@ -8097,7 +8106,6 @@ 69A9B430397C15075D86193F /* UserPropertiesExt.swift in Sources */, 8AB8ED1051216546CB35FA0E /* UserSession.swift in Sources */, 4A618590DEB72C4F186BFED4 /* UserSessionFlowCoordinator.swift in Sources */, - 3113065AABBC14CEAE6843FA /* UserSessionFlowCoordinatorStateMachine.swift in Sources */, 6586E1F1D5F0651D0638FFAF /* UserSessionMock.swift in Sources */, 978BB24F2A5D31EE59EEC249 /* UserSessionProtocol.swift in Sources */, 7E91BAC17963ED41208F489B /* UserSessionStore.swift in Sources */, @@ -9176,7 +9184,7 @@ repositoryURL = "https://github.com/element-hq/compound-ios"; requirement = { kind = revision; - revision = 089904dc1aeb3bacce3b7bb757b7637274e46008; + revision = afc59afb1b1e4f4960e2f2a15e52d4e2e33fc889; }; }; F76A08D0EA29A07A54F4EB4D /* XCRemoteSwiftPackageReference "swift-collections" */ = { diff --git a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 4baeb497e..e9e25de2d 100644 --- a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -6,8 +6,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/element-hq/compound-design-tokens", "state" : { - "revision" : "9da344135825e0a949e41b7a68987ec9205b1d27", - "version" : "5.0.2" + "revision" : "be5d26dfd4ad659b0d3b3aec1ad1cccd0dc8d063", + "version" : "6.0.0" } }, { @@ -15,7 +15,7 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/element-hq/compound-ios", "state" : { - "revision" : "089904dc1aeb3bacce3b7bb757b7637274e46008" + "revision" : "afc59afb1b1e4f4960e2f2a15e52d4e2e33fc889" } }, { diff --git a/ElementX/Sources/Application/AppCoordinator.swift b/ElementX/Sources/Application/AppCoordinator.swift index 0049163f4..76d4de04f 100644 --- a/ElementX/Sources/Application/AppCoordinator.swift +++ b/ElementX/Sources/Application/AppCoordinator.swift @@ -46,7 +46,7 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg private let appLockFlowCoordinator: AppLockFlowCoordinator // periphery:ignore - used to avoid deallocation private var appLockSetupFlowCoordinator: AppLockSetupFlowCoordinator? - private var userSessionFlowCoordinator: ChatsFlowCoordinator? + private var userSessionFlowCoordinator: UserSessionFlowCoordinator? private var softLogoutCoordinator: SoftLogoutScreenCoordinator? private var appDelegateObserver: AnyCancellable? private var userSessionObserver: AnyCancellable? @@ -640,18 +640,18 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg fatalError("User session not setup") } - let userSessionFlowCoordinator = ChatsFlowCoordinator(userSession: userSession, - navigationRootCoordinator: navigationRootCoordinator, - appLockService: appLockFlowCoordinator.appLockService, - bugReportService: ServiceLocator.shared.bugReportService, - elementCallService: elementCallService, - timelineControllerFactory: TimelineControllerFactory(), - appMediator: appMediator, - appSettings: appSettings, - appHooks: appHooks, - analytics: ServiceLocator.shared.analytics, - notificationManager: notificationManager, - isNewLogin: isNewLogin) + let userSessionFlowCoordinator = UserSessionFlowCoordinator(userSession: userSession, + navigationRootCoordinator: navigationRootCoordinator, + appLockService: appLockFlowCoordinator.appLockService, + bugReportService: ServiceLocator.shared.bugReportService, + elementCallService: elementCallService, + timelineControllerFactory: TimelineControllerFactory(), + appMediator: appMediator, + appSettings: appSettings, + appHooks: appHooks, + analytics: ServiceLocator.shared.analytics, + notificationManager: notificationManager, + isNewLogin: isNewLogin) userSessionFlowCoordinator.actionsPublisher .sink { [weak self] action in diff --git a/ElementX/Sources/FlowCoordinators/ChatsFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/ChatsFlowCoordinator.swift index 7b246e6c4..54b5f7ce0 100644 --- a/ElementX/Sources/FlowCoordinators/ChatsFlowCoordinator.swift +++ b/ElementX/Sources/FlowCoordinators/ChatsFlowCoordinator.swift @@ -20,7 +20,6 @@ enum ChatsFlowCoordinatorAction { class ChatsFlowCoordinator: FlowCoordinatorProtocol { private let userSession: UserSessionProtocol - private let navigationRootCoordinator: NavigationRootCoordinator private let navigationSplitCoordinator: NavigationSplitCoordinator private let bugReportService: BugReportServiceProtocol private let elementCallService: ElementCallServiceProtocol @@ -65,7 +64,7 @@ class ChatsFlowCoordinator: FlowCoordinatorProtocol { var statePublisher: AnyPublisher { stateMachine.statePublisher } init(userSession: UserSessionProtocol, - navigationRootCoordinator: NavigationRootCoordinator, + navigationSplitCoordinator: NavigationSplitCoordinator, appLockService: AppLockServiceProtocol, bugReportService: BugReportServiceProtocol, elementCallService: ElementCallServiceProtocol, @@ -78,7 +77,7 @@ class ChatsFlowCoordinator: FlowCoordinatorProtocol { isNewLogin: Bool) { stateMachine = ChatsFlowCoordinatorStateMachine() self.userSession = userSession - self.navigationRootCoordinator = navigationRootCoordinator + self.navigationSplitCoordinator = navigationSplitCoordinator self.bugReportService = bugReportService self.elementCallService = elementCallService self.timelineControllerFactory = timelineControllerFactory @@ -88,8 +87,6 @@ class ChatsFlowCoordinator: FlowCoordinatorProtocol { self.analytics = analytics self.notificationManager = notificationManager - navigationSplitCoordinator = NavigationSplitCoordinator(placeholderCoordinator: PlaceholderScreenCoordinator()) - sidebarNavigationStackCoordinator = NavigationStackCoordinator(navigationSplitCoordinator: navigationSplitCoordinator) detailNavigationStackCoordinator = NavigationStackCoordinator(navigationSplitCoordinator: navigationSplitCoordinator) @@ -554,8 +551,6 @@ class ChatsFlowCoordinator: FlowCoordinatorProtocol { .store(in: &cancellables) sidebarNavigationStackCoordinator.setRootCoordinator(coordinator) - - navigationRootCoordinator.setRootCoordinator(navigationSplitCoordinator) } private func presentReportRoom(for roomID: String) async { diff --git a/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift new file mode 100644 index 000000000..10302b9b7 --- /dev/null +++ b/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift @@ -0,0 +1,106 @@ +// +// Copyright 2025 New Vector Ltd. +// +// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +// Please see LICENSE files in the repository root for full details. +// + +import Combine +import Compound +import MatrixRustSDK +import SwiftUI + +enum UserSessionFlowCoordinatorAction { + case logout + case clearCache + /// Logout without a confirmation. The user forgot their PIN. + case forceLogout +} + +class UserSessionFlowCoordinator: FlowCoordinatorProtocol { + private let userSession: UserSessionProtocol + private let navigationRootCoordinator: NavigationRootCoordinator + private let navigationTabCoordinator: NavigationTabCoordinator + + private let chatsFlowCoordinator: ChatsFlowCoordinator + + private let actionsSubject: PassthroughSubject = .init() + var actionsPublisher: AnyPublisher { + actionsSubject.eraseToAnyPublisher() + } + + private var cancellables: Set = [] + + init(userSession: UserSessionProtocol, + navigationRootCoordinator: NavigationRootCoordinator, + appLockService: AppLockServiceProtocol, + bugReportService: BugReportServiceProtocol, + elementCallService: ElementCallServiceProtocol, + timelineControllerFactory: TimelineControllerFactoryProtocol, + appMediator: AppMediatorProtocol, + appSettings: AppSettings, + appHooks: AppHooks, + analytics: AnalyticsService, + notificationManager: NotificationManagerProtocol, + isNewLogin: Bool) { + self.userSession = userSession + self.navigationRootCoordinator = navigationRootCoordinator + + navigationTabCoordinator = NavigationTabCoordinator() + navigationRootCoordinator.setRootCoordinator(navigationTabCoordinator) + + let chatsSplitCoordinator = NavigationSplitCoordinator(placeholderCoordinator: PlaceholderScreenCoordinator()) + chatsFlowCoordinator = ChatsFlowCoordinator(userSession: userSession, + navigationSplitCoordinator: chatsSplitCoordinator, + appLockService: appLockService, + bugReportService: bugReportService, + elementCallService: elementCallService, + timelineControllerFactory: timelineControllerFactory, + appMediator: appMediator, + appSettings: appSettings, + appHooks: appHooks, + analytics: analytics, + notificationManager: notificationManager, + isNewLogin: isNewLogin) + + navigationTabCoordinator.setTabs([ + .init(coordinator: chatsSplitCoordinator, title: L10n.screenHomeTabChats, icon: \.chat, selectedIcon: \.chatSolid), + .init(coordinator: BlankFormCoordinator(), title: L10n.screenHomeTabSpaces, icon: \.space, selectedIcon: \.spaceSolid) + ]) + + chatsFlowCoordinator.actionsPublisher + .sink { [weak self] action in + guard let self else { return } + switch action { + case .logout: + actionsSubject.send(.logout) + case .clearCache: + actionsSubject.send(.clearCache) + case .forceLogout: + actionsSubject.send(.forceLogout) + } + } + .store(in: &cancellables) + } + + func start() { + chatsFlowCoordinator.start() + } + + func stop() { + chatsFlowCoordinator.stop() + } + + func handleAppRoute(_ appRoute: AppRoute, animated: Bool) { + chatsFlowCoordinator.handleAppRoute(appRoute, animated: animated) + } + + func clearRoute(animated: Bool) { + chatsFlowCoordinator.clearRoute(animated: animated) + } + + #warning("Should this be a publisher instead??") + func isDisplayingRoomScreen(withRoomID roomID: String) -> Bool { + chatsFlowCoordinator.isDisplayingRoomScreen(withRoomID: roomID) + } +} diff --git a/ElementX/Sources/UITests/UITestsAppCoordinator.swift b/ElementX/Sources/UITests/UITestsAppCoordinator.swift index 97296e9e5..9054655e6 100644 --- a/ElementX/Sources/UITests/UITestsAppCoordinator.swift +++ b/ElementX/Sources/UITests/UITestsAppCoordinator.swift @@ -582,26 +582,25 @@ class MockScreen: Identifiable { appSettings.hasRunIdentityConfirmationOnboarding = true appSettings.hasRunNotificationPermissionsOnboarding = true appSettings.analyticsConsentState = .optedOut - let navigationSplitCoordinator = NavigationSplitCoordinator(placeholderCoordinator: PlaceholderScreenCoordinator()) let clientProxy = ClientProxyMock(.init(userID: "@mock:client.com", deviceID: "MOCKCLIENT", roomSummaryProvider: RoomSummaryProviderMock(.init(state: .loaded(.mockRooms))))) let appMediator = AppMediatorMock.default appMediator.underlyingWindowManager = windowManager - let flowCoordinator = ChatsFlowCoordinator(userSession: UserSessionMock(.init(clientProxy: clientProxy)), - navigationRootCoordinator: navigationRootCoordinator, - appLockService: AppLockService(keychainController: KeychainControllerMock(), - appSettings: ServiceLocator.shared.settings), - bugReportService: BugReportServiceMock(.init()), - elementCallService: ElementCallServiceMock(.init()), - timelineControllerFactory: TimelineControllerFactoryMock(.init()), - appMediator: appMediator, - appSettings: appSettings, - appHooks: AppHooks(), - analytics: ServiceLocator.shared.analytics, - notificationManager: NotificationManagerMock(), - isNewLogin: false) + let flowCoordinator = UserSessionFlowCoordinator(userSession: UserSessionMock(.init(clientProxy: clientProxy)), + navigationRootCoordinator: navigationRootCoordinator, + appLockService: AppLockService(keychainController: KeychainControllerMock(), + appSettings: ServiceLocator.shared.settings), + bugReportService: BugReportServiceMock(.init()), + elementCallService: ElementCallServiceMock(.init()), + timelineControllerFactory: TimelineControllerFactoryMock(.init()), + appMediator: appMediator, + appSettings: appSettings, + appHooks: AppHooks(), + analytics: ServiceLocator.shared.analytics, + notificationManager: NotificationManagerMock(), + isNewLogin: false) flowCoordinator.start() @@ -729,6 +728,7 @@ class MockScreen: Identifiable { appSettings.hasRunNotificationPermissionsOnboarding = true appSettings.analyticsConsentState = .optedOut let navigationSplitCoordinator = NavigationSplitCoordinator(placeholderCoordinator: PlaceholderScreenCoordinator()) + navigationRootCoordinator.setRootCoordinator(navigationSplitCoordinator) let clientProxy = ClientProxyMock(.init(userID: "@mock:client.com", roomSummaryProvider: RoomSummaryProviderMock(.init(state: .loaded(.mockRooms))))) @@ -746,7 +746,7 @@ class MockScreen: Identifiable { appSettings: ServiceLocator.shared.settings) let flowCoordinator = ChatsFlowCoordinator(userSession: UserSessionMock(.init(clientProxy: clientProxy)), - navigationRootCoordinator: navigationRootCoordinator, + navigationSplitCoordinator: navigationSplitCoordinator, appLockService: AppLockService(keychainController: KeychainControllerMock(), appSettings: ServiceLocator.shared.settings), bugReportService: BugReportServiceMock(.init()), diff --git a/UnitTests/Sources/ChatsFlowCoordinatorTests.swift b/UnitTests/Sources/ChatsFlowCoordinatorTests.swift index 9debfa8ad..b24fbc461 100644 --- a/UnitTests/Sources/ChatsFlowCoordinatorTests.swift +++ b/UnitTests/Sources/ChatsFlowCoordinatorTests.swift @@ -15,12 +15,11 @@ class ChatsFlowCoordinatorTests: XCTestCase { var clientProxy: ClientProxyMock! var timelineControllerFactory: TimelineControllerFactoryMock! var chatsFlowCoordinator: ChatsFlowCoordinator! - var navigationRootCoordinator: NavigationRootCoordinator! + var splitCoordinator: NavigationSplitCoordinator! var notificationManager: NotificationManagerMock! var cancellables = Set() - var splitCoordinator: NavigationSplitCoordinator? { navigationRootCoordinator.rootCoordinator as? NavigationSplitCoordinator } var detailCoordinator: CoordinatorProtocol? { splitCoordinator?.detailCoordinator } var detailNavigationStack: NavigationStackCoordinator? { detailCoordinator as? NavigationStackCoordinator } @@ -29,12 +28,12 @@ class ChatsFlowCoordinatorTests: XCTestCase { clientProxy = ClientProxyMock(.init(userID: "hi@bob", roomSummaryProvider: RoomSummaryProviderMock(.init(state: .loaded(.mockRooms))))) timelineControllerFactory = TimelineControllerFactoryMock(.init()) - navigationRootCoordinator = NavigationRootCoordinator() + splitCoordinator = NavigationSplitCoordinator(placeholderCoordinator: PlaceholderScreenCoordinator()) notificationManager = NotificationManagerMock() chatsFlowCoordinator = ChatsFlowCoordinator(userSession: UserSessionMock(.init(clientProxy: clientProxy)), - navigationRootCoordinator: navigationRootCoordinator, + navigationSplitCoordinator: splitCoordinator, appLockService: AppLockServiceMock(), bugReportService: BugReportServiceMock(.init()), elementCallService: ElementCallServiceMock(.init()), diff --git a/project.yml b/project.yml index 921dcb4f3..471e5f58d 100644 --- a/project.yml +++ b/project.yml @@ -72,7 +72,7 @@ packages: # path: ../matrix-rust-sdk Compound: url: https://github.com/element-hq/compound-ios - revision: 089904dc1aeb3bacce3b7bb757b7637274e46008 + revision: afc59afb1b1e4f4960e2f2a15e52d4e2e33fc889 # path: ../compound-ios AnalyticsEvents: url: https://github.com/matrix-org/matrix-analytics-events