mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-02 12:07:55 +00:00
Introduce a StartChatFlowCoordinator instead of handing a navigation stack to the Screen Coordinator. (#4674)
* Introduce a basic StartChatFlowCoordinator. * Move the rest of the start chat flow from the screen coordinator into the flow coordinator. * Add a UI test for the entire start chat flow. * Refactor CreateRoom… to CreateRoomScreen…
This commit is contained in:
@@ -78,7 +78,6 @@
|
||||
0B05A35FF5D1ED9E8A0B41A7 /* AuthenticationStartScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = B65DDCF8E41759890355ACBC /* AuthenticationStartScreenViewModelProtocol.swift */; };
|
||||
0BAF83521871E69D222EE8E4 /* ClientBuilderHook.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AC0CD1CAFD3F8B057F9AEA5 /* ClientBuilderHook.swift */; };
|
||||
0BDA19079FD6E17C5AC62E22 /* RoomDetailsEditScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB06F22CFA34885B40976061 /* RoomDetailsEditScreen.swift */; };
|
||||
0BE4D5CBF86956410F071F91 /* CreateRoomViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15A657D96779D1DEB8EF1327 /* CreateRoomViewModel.swift */; };
|
||||
0BFA67AFD757EE2BA569836A /* ScrollViewAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53482ECA4B6633961EC224F5 /* ScrollViewAdapter.swift */; };
|
||||
0C1E537A49ABB386F7554D4A /* HighlightedTimelineItemModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 59B7CC77B82C6C67DE3AD869 /* HighlightedTimelineItemModifier.swift */; };
|
||||
0C346A4AD174F441EDB1414E /* IdentityConfirmationScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB76A9AFC6CCAD4998D9B045 /* IdentityConfirmationScreenViewModel.swift */; };
|
||||
@@ -194,6 +193,7 @@
|
||||
21813AF91CFC6F3E3896DB53 /* AppLockSetupBiometricsScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 10F130DF775CE6BC51A4E392 /* AppLockSetupBiometricsScreenModels.swift */; };
|
||||
21AFEFB8CEFE56A3811A1F5B /* VoiceMessageCacheTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 283974987DA7EC61D2AB57D9 /* VoiceMessageCacheTests.swift */; };
|
||||
21F29351EDD7B2A5534EE862 /* SecureBackupKeyBackupScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD558A898847C179E4B7A237 /* SecureBackupKeyBackupScreen.swift */; };
|
||||
221A7F92116722322AE150AE /* StartChatTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0367E0AF1163F42292D788BD /* StartChatTests.swift */; };
|
||||
22882C710BC99EC34A5024A0 /* UITestsScreenIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CEBE5EA91E8691EDF364EC2 /* UITestsScreenIdentifier.swift */; };
|
||||
22B380C579C148BA0BFB5952 /* Secrets.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3557ACB95D0F666EF5AF0CE /* Secrets.swift */; };
|
||||
22BA593A5E19D8D3DE2FAA6F /* TimelineEventContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F926D08EB3D622A480BCA71 /* TimelineEventContent.swift */; };
|
||||
@@ -233,9 +233,7 @@
|
||||
281BED345D59A9A6A99E9D98 /* UNNotificationContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE148A4FFEE853C5A281500C /* UNNotificationContent.swift */; };
|
||||
2835FD52F3F618D07F799B3D /* Publisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7310D8DFE01AF45F0689C3AA /* Publisher.swift */; };
|
||||
288408E6151D7BD3EBAA073A /* RoomScreenHook.swift in Sources */ = {isa = PBXBuildFile; fileRef = B343C5255FB408DDE853CFDF /* RoomScreenHook.swift */; };
|
||||
28AB1614E749D1147A2AC6C2 /* CreateRoomScreenTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2214C32EA81FA9168D923D4C /* CreateRoomScreenTests.swift */; };
|
||||
28E8C44DD6E39BEB2A1599C8 /* AccessibilityTestsAppCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CABD320DE5566D133890B24 /* AccessibilityTestsAppCoordinator.swift */; };
|
||||
292827744227DF61C930BDDB /* CreateRoomScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = FB0D6CB491777E7FC6B5BA12 /* CreateRoomScreen.swift */; };
|
||||
2932570AA418974979D16DED /* UserPreference.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E6DE144D887A254F4CAF203 /* UserPreference.swift */; };
|
||||
29491EE7AE37E239E839C5A3 /* LocationSharingScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8BEBF0E59F25E842EDB6FD11 /* LocationSharingScreenModels.swift */; };
|
||||
2955F4C160CFD7794D819C64 /* EffectsScene.swift in Sources */ = {isa = PBXBuildFile; fileRef = 024F7398C5FC12586FB10E9D /* EffectsScene.swift */; };
|
||||
@@ -399,7 +397,6 @@
|
||||
47305C0911C9E1AA774A4000 /* TemplateScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA90BD288E5AE6BC643AFDDF /* TemplateScreenCoordinator.swift */; };
|
||||
4764FC9A843D1F9865EDC29C /* EditRoomAddressScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4048547AC50ADCF201684E87 /* EditRoomAddressScreen.swift */; };
|
||||
478C363F63A5DFC3C83E334C /* MediaProviderMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F65E4BB9E82EB8373207CF8 /* MediaProviderMock.swift */; };
|
||||
4799A852132F1744E2825994 /* CreateRoomViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 340179A0FC1AD4AEDA7FC134 /* CreateRoomViewModelProtocol.swift */; };
|
||||
4807E8F51DB54F56B25E1C7E /* AppLockSetupSettingsScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D8C38663020DF2EB2D13F5E /* AppLockSetupSettingsScreenViewModel.swift */; };
|
||||
48416BBEB8DDF3E4DED0EDB6 /* ElementCallServiceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FC8B21E86B137BE4E91F82A /* ElementCallServiceProtocol.swift */; };
|
||||
484202C5D50983442D24D061 /* AttributedString.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52BD6ED18E2EB61E28C340AD /* AttributedString.swift */; };
|
||||
@@ -482,7 +479,6 @@
|
||||
55DF6DEEF2CEEF40F84B53B0 /* VoiceMessageRoomPlaybackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E3B41C36800DD4558D7BDA7 /* VoiceMessageRoomPlaybackView.swift */; };
|
||||
5618ED25F092DF5712003829 /* KeychainControllerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5E94DCFEE803E5ABAE8ACCE /* KeychainControllerProtocol.swift */; };
|
||||
562EFB9AB62B38830D9AA778 /* TimelineMediaFrame.swift in Sources */ = {isa = PBXBuildFile; fileRef = 933B074F006F8E930DB98B4E /* TimelineMediaFrame.swift */; };
|
||||
564BF06B3E93D6DD55F903B2 /* CreateRoomCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C618CA2B6C8758B06C88013C /* CreateRoomCoordinator.swift */; };
|
||||
565868808A1DA565707394ED /* CurrentValuePublisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 127C8472672A5BA09EF1ACF8 /* CurrentValuePublisher.swift */; };
|
||||
56DACDD379A86A1F5DEFE7BE /* AuthenticationServiceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E75948AA1FE1D1A7809931F /* AuthenticationServiceProtocol.swift */; };
|
||||
56F0A22972A3BB519DA2261C /* HomeScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24F5530B2212862FA4BEFF2D /* HomeScreenViewModelProtocol.swift */; };
|
||||
@@ -883,6 +879,7 @@
|
||||
9D79B94493FB32249F7E472F /* PlaceholderAvatarImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C705E605EF57C19DBE86FFA1 /* PlaceholderAvatarImage.swift */; };
|
||||
9D9690D2FD4CD26FF670620F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C75EF87651B00A176AB08E97 /* AppDelegate.swift */; };
|
||||
9DB4B303ECC05F0F33582594 /* SpacesAnnouncementSheetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7DA8FFD31B18324CC04A823 /* SpacesAnnouncementSheetView.swift */; };
|
||||
9DBF6524DFD8143A4D6A17F0 /* CreateRoomScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D432771B389EC486E7F90C6 /* CreateRoomScreenModels.swift */; };
|
||||
9DD5AA10E85137140FEA86A3 /* MediaProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = F17EFA1D3D09FC2F9C5E1CB2 /* MediaProvider.swift */; };
|
||||
9DD84E014ADFB2DD813022D5 /* RoomDetailsEditScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00E5B2CBEF8F96424F095508 /* RoomDetailsEditScreenViewModelTests.swift */; };
|
||||
9DE801D278AC34737467F937 /* VoiceMessageMediaManagerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 889DEDD63C68ABDA8AD29812 /* VoiceMessageMediaManagerProtocol.swift */; };
|
||||
@@ -896,6 +893,7 @@
|
||||
9FB41B0E8B2AA9B404E52C8B /* AppLockSetupBiometricsScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CCC6C31102E1D8B9106DEDE /* AppLockSetupBiometricsScreenViewModelProtocol.swift */; };
|
||||
9FBE1FB20171012260A32492 /* TimelineSenderAvatarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D53FCCE44F96E0BC411A6CF0 /* TimelineSenderAvatarView.swift */; };
|
||||
9FBF0078657CA4F2A9747E98 /* SpaceRoomListProxyMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DB2A5FF1E68BA580A20D405 /* SpaceRoomListProxyMock.swift */; };
|
||||
9FC79DA30AE0E1502DAEBD51 /* StartChatFlowCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 477A8C656B7B26E99C35927F /* StartChatFlowCoordinator.swift */; };
|
||||
A009BDFB0A6816D4C392ADCB /* SettingsScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AF715D4FD4710EBB637D661 /* SettingsScreenViewModelProtocol.swift */; };
|
||||
A021827B528F1EDC9101CA58 /* AppCoordinatorProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = FBC776F301D374A3298C69DA /* AppCoordinatorProtocol.swift */; };
|
||||
A0601810597769B81C2358AF /* EncryptionResetPasswordScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A2B5274C1D3D2999D643786 /* EncryptionResetPasswordScreenViewModelProtocol.swift */; };
|
||||
@@ -947,6 +945,7 @@
|
||||
A722F426FD81FC67706BB1E0 /* CustomLayoutLabelStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42236480CF0431535EBE8387 /* CustomLayoutLabelStyle.swift */; };
|
||||
A74438ED16F8683A4B793E6A /* AnalyticsSettingsScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BCE3FAF40932AC7C7639AC4 /* AnalyticsSettingsScreenViewModel.swift */; };
|
||||
A7D48E44D485B143AADDB77D /* Strings+Untranslated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A18F6CE4D694D21E4EA9B25 /* Strings+Untranslated.swift */; };
|
||||
A7DB75E090542331F6668A23 /* CreateRoomScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF19027E7FFA5E63D148873A /* CreateRoomScreenViewModel.swift */; };
|
||||
A808DC3F72D15C6C5A52317E /* TimelineItemDebugView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCDA016D05107DED3B9495CB /* TimelineItemDebugView.swift */; };
|
||||
A816F7087C495D85048AC50E /* RoomMemberDetailsScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B6E30BB748F3F480F077969 /* RoomMemberDetailsScreenModels.swift */; };
|
||||
A851635B3255C6DC07034A12 /* RoomScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8108C8F0ACF6A7EB72D0117 /* RoomScreenCoordinator.swift */; };
|
||||
@@ -1080,7 +1079,6 @@
|
||||
C280BD841DA2EB6B5BC84F84 /* MapTilerConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A1F2AAA3F0F2B72D2FFE4D0 /* MapTilerConfiguration.swift */; };
|
||||
C2879369106A419A5071F1F8 /* VoiceMessageRecorder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26B0A96B8FE4849227945067 /* VoiceMessageRecorder.swift */; };
|
||||
C292A8DA07E6DBD66E946383 /* RageshakeConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FC598338E7CF41107293AB5 /* RageshakeConfiguration.swift */; };
|
||||
C32765D740C81AD4C42E8F50 /* CreateRoomFlowParameters.swift in Sources */ = {isa = PBXBuildFile; fileRef = 935C2FB18EFB8EEE96B26330 /* CreateRoomFlowParameters.swift */; };
|
||||
C3317EF833AB4060988DF098 /* SAS.strings in Resources */ = {isa = PBXBuildFile; fileRef = 135FC689EA39AE1D34153B58 /* SAS.strings */; };
|
||||
C3522917C0C367C403429EEC /* CoordinatorProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = B251F5B4511D1CA0BA8361FE /* CoordinatorProtocol.swift */; };
|
||||
C3AFDF6349E54290AA31EC88 /* preview_video.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 45A4B934BA41D6C255900265 /* preview_video.jpg */; };
|
||||
@@ -1148,6 +1146,7 @@
|
||||
CE3B7FC34FB2C279AAA5EA01 /* AVMetadataMachineReadableCodeObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3339B1DDB1341E833D2555BC /* AVMetadataMachineReadableCodeObject.swift */; };
|
||||
CE4B342F9DD747CF4BEDB5AB /* TestablePreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = E43F773904F87FF5ADFE4DD1 /* TestablePreview.swift */; };
|
||||
CE6F237360875D3D573FD0B2 /* RoomNotificationSettingsProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD6B522BD637845AB9570B10 /* RoomNotificationSettingsProxy.swift */; };
|
||||
CE8296D4AD30DDC6D0C67A74 /* CreateRoomScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = EFD1CFB730C7417925264F17 /* CreateRoomScreen.swift */; };
|
||||
CE9530A4CA661E090635C2F2 /* NotificationItemProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25F7FE40EF7490A7E09D7BE6 /* NotificationItemProxy.swift */; };
|
||||
CEAEA57B7665C8E790599A78 /* BlockedUsersScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 240610DF32F3213BEC5611D7 /* BlockedUsersScreenViewModelTests.swift */; };
|
||||
CEB8FB1269DE20536608B957 /* LoginMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B41FABA2B0AEF4389986495 /* LoginMode.swift */; };
|
||||
@@ -1177,6 +1176,7 @@
|
||||
D31B34B3902BC597593F3ABB /* preview_image.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 200626E8353AB2729444F991 /* preview_image.jpg */; };
|
||||
D33AC79A50DFC26D2498DD28 /* FileRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5098DA7799946A61E34A2373 /* FileRoomTimelineItem.swift */; };
|
||||
D34E328E9E65904358248FDD /* GlobalSearchScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 436A0D98D372B17EAE9AA999 /* GlobalSearchScreenModels.swift */; };
|
||||
D38E59C48BE5499A48D12031 /* CreateRoomScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64AC8FCE224D4185F28636FF /* CreateRoomScreenCoordinator.swift */; };
|
||||
D3FD96913D2B1AAA3149DAC7 /* CreateRoomViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69D42EE0102D2857933625DD /* CreateRoomViewModelTests.swift */; };
|
||||
D43F0503EF2CBC55272538FE /* SDKGeneratedMocks.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2F079B5DBD0D85FEA687AAE /* SDKGeneratedMocks.swift */; };
|
||||
D46C33F8B61B55F0C8C2D15F /* LocationRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B2AC540DE619B36832A5DB5 /* LocationRoomTimelineItem.swift */; };
|
||||
@@ -1204,6 +1204,7 @@
|
||||
DA10C99BA43A0F1E732F6274 /* LogLevel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2711E5996016ABD6EAAEB58A /* LogLevel.swift */; };
|
||||
DA7E867F5EAFF8E20B2EE3B6 /* SecureBackupScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B3D16709ADD4F4BCC710B1E /* SecureBackupScreenModels.swift */; };
|
||||
DAF63A9CF9932CA8F6830F11 /* ShareExtensionModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCAC01A97A43BE07B9E94E43 /* ShareExtensionModels.swift */; };
|
||||
DB01FDD050A43B72C5425549 /* CreateRoomScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AC5E094DFE18C3AB7619D34 /* CreateRoomScreenViewModelProtocol.swift */; };
|
||||
DB079D1929B5A5F52D207C83 /* RoomDetailsScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 466C71A0FED9BFF287613C82 /* RoomDetailsScreenModels.swift */; };
|
||||
DB5200B87C4CE9DF0024AC4E /* SpaceServiceProxyProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C57DB49B8426AA721BF85D83 /* SpaceServiceProxyProtocol.swift */; };
|
||||
DB65401349C143DFF883E2B0 /* AnalyticsPromptScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C8EC6EA7EDFCE46710DA306 /* AnalyticsPromptScreenViewModel.swift */; };
|
||||
@@ -1379,7 +1380,6 @@
|
||||
F8C87130FD999F7F1076208C /* RoomChangePermissionsScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89AAEA70CFF3284920811941 /* RoomChangePermissionsScreen.swift */; };
|
||||
F8E725D42023ECA091349245 /* AudioRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57EAAF82432B0B53881CF826 /* AudioRoomTimelineItem.swift */; };
|
||||
F8F47CE757EE656905F01F2C /* RoomRolesAndPermissionsScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90DFF217B3D9D0941283278C /* RoomRolesAndPermissionsScreenViewModelProtocol.swift */; };
|
||||
F9788AE0B9EA19F1190ED14F /* StartChatScreenTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B927A399A5418DA40A5CA15 /* StartChatScreenTests.swift */; };
|
||||
F9842667B68DC6FA1F9ECCBB /* NSItemProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = F72EFC8C634469F9262659C7 /* NSItemProvider.swift */; };
|
||||
F996259EAE68664BC345C197 /* Application.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D9DBE1C69F3A60D93B2203F /* Application.swift */; };
|
||||
F99FB21EFC6D99D247FE7CBE /* SwiftState in Frameworks */ = {isa = PBXBuildFile; productRef = 19CD5B074D7DD44AF4C58BB6 /* SwiftState */; };
|
||||
@@ -1390,7 +1390,6 @@
|
||||
FA71CD334F2D2289BEF0D749 /* SecureBackupRecoveryKeyScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A2FCA3D0F239B9E911B966B /* SecureBackupRecoveryKeyScreen.swift */; };
|
||||
FA9C427FFB11B1AA2DCC5602 /* RoomProxyProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47111410B6E659A697D472B5 /* RoomProxyProtocol.swift */; };
|
||||
FB0A9D06FC9122E37992D962 /* LayoutDirection.swift in Sources */ = {isa = PBXBuildFile; fileRef = C14D83B2B7CD5501A0089EFC /* LayoutDirection.swift */; };
|
||||
FB53CD9B74A15B3B94F9F788 /* CreateRoomModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B849D2FF2CC12BA411A1651 /* CreateRoomModels.swift */; };
|
||||
FB595EC9C00AB32F39034055 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A37E2FACFD041CE466223CD /* SceneDelegate.swift */; };
|
||||
FBD402E3170EB1ED0D1AA672 /* EncryptionKeyProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2355398E4A55DA5A89128AD1 /* EncryptionKeyProvider.swift */; };
|
||||
FBF09B6C900415800DDF2A21 /* EmojiProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C113E0CB7E15E9765B1817A /* EmojiProvider.swift */; };
|
||||
@@ -1517,6 +1516,7 @@
|
||||
03277E40D0E0DE0712021A71 /* ServerConfirmationScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerConfirmationScreenCoordinator.swift; sourceTree = "<group>"; };
|
||||
033DB41C51865A2E83174E87 /* target.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = target.yml; sourceTree = "<group>"; };
|
||||
035177BCD8E8308B098AC3C2 /* WindowManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowManager.swift; sourceTree = "<group>"; };
|
||||
0367E0AF1163F42292D788BD /* StartChatTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartChatTests.swift; sourceTree = "<group>"; };
|
||||
0376C429FAB1687C3D905F3E /* MockCoder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockCoder.swift; sourceTree = "<group>"; };
|
||||
03DD998E523D4EC93C7ED703 /* RoomNotificationSettingsScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomNotificationSettingsScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
||||
03FABD73FD8086EFAB699F42 /* MediaUploadPreviewScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaUploadPreviewScreenViewModelTests.swift; sourceTree = "<group>"; };
|
||||
@@ -1556,6 +1556,7 @@
|
||||
0A459AE4B6566B2FA99E86B2 /* TimelineItemBubbledStylerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineItemBubbledStylerView.swift; sourceTree = "<group>"; };
|
||||
0A81FD0C60175FA081EB19AD /* EventTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventTimelineItem.swift; sourceTree = "<group>"; };
|
||||
0AB7A0C06CB527A1095DEB33 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = da; path = da.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||
0AC5E094DFE18C3AB7619D34 /* CreateRoomScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateRoomScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
||||
0B0E0B55E2EE75AF67029924 /* SwipeToReplyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwipeToReplyView.swift; sourceTree = "<group>"; };
|
||||
0B32BBA8887BD7A5C4ECF16F /* RoomModerationRole.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomModerationRole.swift; sourceTree = "<group>"; };
|
||||
0B987FC3FDBAA0E1C5AA235C /* PaginationIndicatorRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaginationIndicatorRoomTimelineItem.swift; sourceTree = "<group>"; };
|
||||
@@ -1569,6 +1570,7 @@
|
||||
0CB569EAA5017B5B23970655 /* pt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pt; path = pt.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
0CCC6C31102E1D8B9106DEDE /* AppLockSetupBiometricsScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockSetupBiometricsScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
||||
0D0B159AFFBBD8ECFD0E37FA /* LoginScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginScreenModels.swift; sourceTree = "<group>"; };
|
||||
0D432771B389EC486E7F90C6 /* CreateRoomScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateRoomScreenModels.swift; sourceTree = "<group>"; };
|
||||
0D879FC4E881E748BB9B34DC /* RoomChangePermissionsScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomChangePermissionsScreenCoordinator.swift; sourceTree = "<group>"; };
|
||||
0D8F620C8B314840D8602E3F /* NSE.appex */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = "wrapper.app-extension"; path = NSE.appex; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
0DBB08A95EFA668F2CF27211 /* AppLockSetupFlowCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockSetupFlowCoordinator.swift; sourceTree = "<group>"; };
|
||||
@@ -1615,7 +1617,6 @@
|
||||
1562EAF6231151A675BED7A9 /* RoomDirectorySearchScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDirectorySearchScreenCoordinator.swift; sourceTree = "<group>"; };
|
||||
15748C254911E3654C93B0ED /* MentionBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MentionBuilder.swift; sourceTree = "<group>"; };
|
||||
1575947B7A6FE08C57FE5EE4 /* NetworkMonitorProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkMonitorProtocol.swift; sourceTree = "<group>"; };
|
||||
15A657D96779D1DEB8EF1327 /* CreateRoomViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateRoomViewModel.swift; sourceTree = "<group>"; };
|
||||
161CD412E75F4086F422AE39 /* SessionVerificationScreenStateMachine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionVerificationScreenStateMachine.swift; sourceTree = "<group>"; };
|
||||
1627F2D56477BD331F6D732C /* RoomHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomHeaderView.swift; sourceTree = "<group>"; };
|
||||
166D45E1861A73B232109843 /* RoomDetailsScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDetailsScreenViewModelTests.swift; sourceTree = "<group>"; };
|
||||
@@ -1686,7 +1687,6 @@
|
||||
218AB05B4E3889731959C5F1 /* EventBasedTimelineItemProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventBasedTimelineItemProtocol.swift; sourceTree = "<group>"; };
|
||||
21BA866267F84BF4350B0CB7 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "pt-BR"; path = "pt-BR.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
|
||||
21DD8599815136EFF5B73F38 /* UserFlowTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserFlowTests.swift; sourceTree = "<group>"; };
|
||||
2214C32EA81FA9168D923D4C /* CreateRoomScreenTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateRoomScreenTests.swift; sourceTree = "<group>"; };
|
||||
226A56746AD99ABDA97189E1 /* SpaceServiceProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpaceServiceProxy.swift; sourceTree = "<group>"; };
|
||||
22730A30C50AC2E3D5BA8642 /* InviteUsersScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InviteUsersScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
||||
227AC5D71A4CE43512062243 /* URL.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URL.swift; sourceTree = "<group>"; };
|
||||
@@ -1786,7 +1786,6 @@
|
||||
3339B1DDB1341E833D2555BC /* AVMetadataMachineReadableCodeObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AVMetadataMachineReadableCodeObject.swift; sourceTree = "<group>"; };
|
||||
33AE897D86784CCA5E4E9227 /* ElementCallService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ElementCallService.swift; 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>"; };
|
||||
342BEBC3C5FC3F9943C41C4C /* TemplateScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemplateScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
||||
345172AD4377E83A44BD864F /* MessageComposerTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageComposerTextField.swift; sourceTree = "<group>"; };
|
||||
347D708104CCEF771428C9A3 /* PollFormScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollFormScreenViewModelTests.swift; sourceTree = "<group>"; };
|
||||
@@ -1817,7 +1816,6 @@
|
||||
3AB34956C87731AB094DB33A /* URLTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLTests.swift; sourceTree = "<group>"; };
|
||||
3AD253E7EFF88F308D644272 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/SAS.strings"; sourceTree = "<group>"; };
|
||||
3B5E97E9615A158C76B2AB77 /* DateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateTests.swift; sourceTree = "<group>"; };
|
||||
3B927A399A5418DA40A5CA15 /* StartChatScreenTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartChatScreenTests.swift; sourceTree = "<group>"; };
|
||||
3BAC027034248429A438886B /* AppMediatorMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppMediatorMock.swift; sourceTree = "<group>"; };
|
||||
3BC1B7CB061C9865B2B91B56 /* QRCodeLoginScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCodeLoginScreenViewModel.swift; sourceTree = "<group>"; };
|
||||
3BDCCD2F6B405C14B9BCE94E /* JoinRoomScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JoinRoomScreenCoordinator.swift; sourceTree = "<group>"; };
|
||||
@@ -1894,6 +1892,7 @@
|
||||
471BB7276C97AF60B3A5463B /* RoomDirectorySearchProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDirectorySearchProxy.swift; sourceTree = "<group>"; };
|
||||
475D47D0BFE961B02BAC5D49 /* id */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = id; path = id.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||
475EB595D7527E9A8A14043E /* uz */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uz; path = uz.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
477A8C656B7B26E99C35927F /* StartChatFlowCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartChatFlowCoordinator.swift; sourceTree = "<group>"; };
|
||||
47873756E45B46683D97DC32 /* LegalInformationScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegalInformationScreenModels.swift; sourceTree = "<group>"; };
|
||||
47EBB5D698CE9A25BB553A2D /* Strings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Strings.swift; sourceTree = "<group>"; };
|
||||
47F29139BC2A804CE5E0757E /* MediaUploadPreviewScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaUploadPreviewScreenViewModel.swift; sourceTree = "<group>"; };
|
||||
@@ -2028,6 +2027,7 @@
|
||||
646B50583A2CE6DA67F7739A /* SpaceScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpaceScreen.swift; sourceTree = "<group>"; };
|
||||
648DD1C10E4957CB791FE0B8 /* OverridableAvatarImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OverridableAvatarImage.swift; sourceTree = "<group>"; };
|
||||
6493AC9979CEB1410302BFE3 /* RoomDetailsScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDetailsScreenCoordinator.swift; sourceTree = "<group>"; };
|
||||
64AC8FCE224D4185F28636FF /* CreateRoomScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateRoomScreenCoordinator.swift; sourceTree = "<group>"; };
|
||||
64F49FB9EE2913234F06CE68 /* MediaPickerScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaPickerScreenCoordinator.swift; sourceTree = "<group>"; };
|
||||
6509708F54FC883604DFDC95 /* TimelineViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineViewModelTests.swift; sourceTree = "<group>"; };
|
||||
653610CB5F9776EAAAB98155 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = fr; path = fr.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||
@@ -2137,7 +2137,6 @@
|
||||
7B19B2BCC779ED934E0BBC2A /* AudioPlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioPlayer.swift; sourceTree = "<group>"; };
|
||||
7B25F959A434BB9923A3223F /* ExpiringTaskRunner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExpiringTaskRunner.swift; sourceTree = "<group>"; };
|
||||
7B3D16709ADD4F4BCC710B1E /* SecureBackupScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureBackupScreenModels.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>"; };
|
||||
7BBADF8010C813D905C172CE /* SecurityAndPrivacyScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecurityAndPrivacyScreenModels.swift; sourceTree = "<group>"; };
|
||||
7BD5523BDEDB247E29228476 /* AppSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSettings.swift; sourceTree = "<group>"; };
|
||||
@@ -2272,7 +2271,6 @@
|
||||
933B074F006F8E930DB98B4E /* TimelineMediaFrame.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineMediaFrame.swift; sourceTree = "<group>"; };
|
||||
9342F5D6729627B6393AF853 /* ServerConfirmationScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerConfirmationScreenModels.swift; sourceTree = "<group>"; };
|
||||
9349F590E35CE514A71E6764 /* LoginHomeserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginHomeserver.swift; sourceTree = "<group>"; };
|
||||
935C2FB18EFB8EEE96B26330 /* CreateRoomFlowParameters.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateRoomFlowParameters.swift; sourceTree = "<group>"; };
|
||||
93C713D124FE915ABF47A6B7 /* TimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineView.swift; sourceTree = "<group>"; };
|
||||
93CF7B19FFCF8EFBE0A8696A /* RoomScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomScreenViewModelTests.swift; sourceTree = "<group>"; };
|
||||
93E1FF0DFBB3768F79FDBF6D /* AVMetadataMachineReadableCodeObjectExtensionsTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AVMetadataMachineReadableCodeObjectExtensionsTest.swift; sourceTree = "<group>"; };
|
||||
@@ -2547,7 +2545,6 @@
|
||||
C5AEB5907E24092D741718AF /* ResolveVerifiedUserSendFailureScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResolveVerifiedUserSendFailureScreenCoordinator.swift; sourceTree = "<group>"; };
|
||||
C5F06F2F09B2EDD067DC2174 /* NotificationSettingsScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationSettingsScreen.swift; sourceTree = "<group>"; };
|
||||
C616D90B1E2F033CAA325439 /* StaticLocationScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StaticLocationScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
||||
C618CA2B6C8758B06C88013C /* CreateRoomCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateRoomCoordinator.swift; sourceTree = "<group>"; };
|
||||
C687844F60BFF532D49A994C /* AnalyticsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsTests.swift; sourceTree = "<group>"; };
|
||||
C6A9F49B3EE59147AF2F70BB /* SeparatorRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeparatorRoomTimelineItem.swift; sourceTree = "<group>"; };
|
||||
C705E605EF57C19DBE86FFA1 /* PlaceholderAvatarImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaceholderAvatarImage.swift; sourceTree = "<group>"; };
|
||||
@@ -2592,6 +2589,7 @@
|
||||
CDE3F3911FF7CC639BDE5844 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
CEE20623EB4A9B88FB29F2BA /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/SAS.strings; sourceTree = "<group>"; };
|
||||
CEE41494C837AA403A06A5D9 /* UnitTests.xctestplan */ = {isa = PBXFileReference; path = UnitTests.xctestplan; sourceTree = "<group>"; };
|
||||
CF19027E7FFA5E63D148873A /* CreateRoomScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateRoomScreenViewModel.swift; sourceTree = "<group>"; };
|
||||
CF847A34FC4C8C937CD39E08 /* LabsScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabsScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
||||
CFFA5E881D281810AB428EA3 /* RoomPowerLevelsProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomPowerLevelsProxy.swift; sourceTree = "<group>"; };
|
||||
D01FD1171FF40E34D707FD00 /* BigIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BigIcon.swift; sourceTree = "<group>"; };
|
||||
@@ -2766,6 +2764,7 @@
|
||||
EF1593DD87F974F8509BB619 /* ElementAnimations.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ElementAnimations.swift; sourceTree = "<group>"; };
|
||||
EF36FC3DE25B20B7FA91F1FD /* SpaceServiceProxyMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpaceServiceProxyMock.swift; sourceTree = "<group>"; };
|
||||
EF98A02DED04075F7CF0C721 /* id */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = id; path = id.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
EFD1CFB730C7417925264F17 /* CreateRoomScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateRoomScreen.swift; sourceTree = "<group>"; };
|
||||
EFF7BF82A950B91BC5469E91 /* ViewFrameReader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewFrameReader.swift; sourceTree = "<group>"; };
|
||||
EFFD3200F9960D4996159F10 /* BugReportServiceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BugReportServiceTests.swift; sourceTree = "<group>"; };
|
||||
F0096BC5DA86AF6B6E5742AC /* RoomPermissionsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomPermissionsTests.swift; sourceTree = "<group>"; };
|
||||
@@ -2819,7 +2818,6 @@
|
||||
FA723686F23EF45E2B398FBC /* TestablePreviewsDictionary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestablePreviewsDictionary.swift; sourceTree = "<group>"; };
|
||||
FA7BB497B2F539C17E88F6B7 /* NotificationSettingsEditScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationSettingsEditScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
||||
FABAC5C4373B0EC24D399663 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/SAS.strings"; sourceTree = "<group>"; };
|
||||
FB0D6CB491777E7FC6B5BA12 /* CreateRoomScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateRoomScreen.swift; sourceTree = "<group>"; };
|
||||
FB7BAD55A4E2B8E5828CD64C /* SoftLogoutScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SoftLogoutScreenViewModel.swift; sourceTree = "<group>"; };
|
||||
FB9EABCA9348DFA27439A809 /* WaveformCursorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WaveformCursorView.swift; sourceTree = "<group>"; };
|
||||
FBB0328F2887BF0A65BC5D49 /* NotificationSettingsEditScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationSettingsEditScreen.swift; sourceTree = "<group>"; };
|
||||
@@ -3055,7 +3053,6 @@
|
||||
0ED3F5C21537519389C07644 /* BugReport */,
|
||||
8039515BAA53B7C3275AC64A /* Client */,
|
||||
8B5E91450E85A9689931B221 /* ComposerDraft */,
|
||||
8C3BAE06B336D97DABBE2509 /* CreateRoom */,
|
||||
92E99C57D7F92ED16F73282C /* ElementCall */,
|
||||
39557ADF21345E18F3865B9E /* Emojis */,
|
||||
CA555F7C7CA382ACACF0D82B /* Keychain */,
|
||||
@@ -4305,6 +4302,7 @@
|
||||
5A4EF5724C0F894911AF7811 /* SpaceExplorerFlowCoordinator.swift */,
|
||||
EDDE826EAB1BAB80C1104980 /* SpaceFlowCoordinator.swift */,
|
||||
BDE3EDEA7E64D68FEB828F83 /* SpaceSettingsFlowCoordinator.swift */,
|
||||
477A8C656B7B26E99C35927F /* StartChatFlowCoordinator.swift */,
|
||||
C99FDEEB71173C4C6FA2734C /* UserSessionFlowCoordinator.swift */,
|
||||
);
|
||||
path = FlowCoordinators;
|
||||
@@ -4962,6 +4960,18 @@
|
||||
path = View;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
821EB0D1C0019E3C7BBAEDBB /* CreateRoomScreen */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
64AC8FCE224D4185F28636FF /* CreateRoomScreenCoordinator.swift */,
|
||||
0D432771B389EC486E7F90C6 /* CreateRoomScreenModels.swift */,
|
||||
CF19027E7FFA5E63D148873A /* CreateRoomScreenViewModel.swift */,
|
||||
0AC5E094DFE18C3AB7619D34 /* CreateRoomScreenViewModelProtocol.swift */,
|
||||
A7694BCE812C4D7B2B1B42DF /* View */,
|
||||
);
|
||||
path = CreateRoomScreen;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
823ED0EC3F1B6CF47D284011 /* Tools */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -5117,14 +5127,6 @@
|
||||
path = ComposerDraft;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
8C3BAE06B336D97DABBE2509 /* CreateRoom */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
935C2FB18EFB8EEE96B26330 /* CreateRoomFlowParameters.swift */,
|
||||
);
|
||||
path = CreateRoom;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
8F074E22FD93E64211971845 /* Onboarding */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -5252,7 +5254,6 @@
|
||||
E8495F37D6245AD0CFA1F60B /* AppLockTests.swift */,
|
||||
37F46CC4FD89ECF4CF26391A /* AuthenticationFlowCoordinatorTests.swift */,
|
||||
27DF257F5D968E5DD719583C /* BugReportTests.swift */,
|
||||
2214C32EA81FA9168D923D4C /* CreateRoomScreenTests.swift */,
|
||||
89BB11A792EF6F70B95B467E /* EncryptionResetTests.swift */,
|
||||
57AD14D3ADADE8F6A10F9E88 /* EncryptionSettingsTests.swift */,
|
||||
DCDAB580109C09A6AA97AF7E /* PollFormScreenTests.swift */,
|
||||
@@ -5261,7 +5262,7 @@
|
||||
D97A4E73EA97CA08D2BB9806 /* RoomScreenTests.swift */,
|
||||
6640DB5B9171D163E6742639 /* ServerSelectionTests.swift */,
|
||||
D8AA084E10B80D64449C02A9 /* SessionVerificationTests.swift */,
|
||||
3B927A399A5418DA40A5CA15 /* StartChatScreenTests.swift */,
|
||||
0367E0AF1163F42292D788BD /* StartChatTests.swift */,
|
||||
F899D02CF26EA7675EEBE74C /* UserSessionScreenTests.swift */,
|
||||
);
|
||||
path = Sources;
|
||||
@@ -5522,6 +5523,14 @@
|
||||
path = View;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
A7694BCE812C4D7B2B1B42DF /* View */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
EFD1CFB730C7417925264F17 /* CreateRoomScreen.swift */,
|
||||
);
|
||||
path = View;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
A78C2592419CA4C76FBA8FD2 /* Application */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -5839,18 +5848,6 @@
|
||||
path = MapLibre;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
C18958141C8ED6D778F779A4 /* CreateRoom */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
C618CA2B6C8758B06C88013C /* CreateRoomCoordinator.swift */,
|
||||
7B849D2FF2CC12BA411A1651 /* CreateRoomModels.swift */,
|
||||
15A657D96779D1DEB8EF1327 /* CreateRoomViewModel.swift */,
|
||||
340179A0FC1AD4AEDA7FC134 /* CreateRoomViewModelProtocol.swift */,
|
||||
F84100ED0C09031BAB7BB77E /* View */,
|
||||
);
|
||||
path = CreateRoom;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
C1CD278862878F9545608040 /* SessionVerificationScreen */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -6233,7 +6230,7 @@
|
||||
53FB148CD26AFB6A5B9E20B3 /* BugReportScreen */,
|
||||
1185EECDD07495D65AC84AFC /* CallScreen */,
|
||||
90DC2E28718955ED87AD1456 /* CreatePollScreen */,
|
||||
C18958141C8ED6D778F779A4 /* CreateRoom */,
|
||||
821EB0D1C0019E3C7BBAEDBB /* CreateRoomScreen */,
|
||||
3E1CCC4B607946CE90B4A827 /* DeclineAndBlockScreen */,
|
||||
45F2BCFD6E9A6F040CC20582 /* EditRoomAddressScreen */,
|
||||
F5A65D1D3B83593598DC278D /* EmojiPickerScreen */,
|
||||
@@ -6486,14 +6483,6 @@
|
||||
path = IdentityConfirmedScreen;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
F84100ED0C09031BAB7BB77E /* View */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
FB0D6CB491777E7FC6B5BA12 /* CreateRoomScreen.swift */,
|
||||
);
|
||||
path = View;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
FA1D480A302295CFC3582543 /* View */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -7718,12 +7707,11 @@
|
||||
EA6613B29BA671F39CE1B1D2 /* ConfirmationDialog.swift in Sources */,
|
||||
AC7AA215D60FBC307F984028 /* Consumable.swift in Sources */,
|
||||
C3522917C0C367C403429EEC /* CoordinatorProtocol.swift in Sources */,
|
||||
564BF06B3E93D6DD55F903B2 /* CreateRoomCoordinator.swift in Sources */,
|
||||
C32765D740C81AD4C42E8F50 /* CreateRoomFlowParameters.swift in Sources */,
|
||||
FB53CD9B74A15B3B94F9F788 /* CreateRoomModels.swift in Sources */,
|
||||
292827744227DF61C930BDDB /* CreateRoomScreen.swift in Sources */,
|
||||
0BE4D5CBF86956410F071F91 /* CreateRoomViewModel.swift in Sources */,
|
||||
4799A852132F1744E2825994 /* CreateRoomViewModelProtocol.swift in Sources */,
|
||||
CE8296D4AD30DDC6D0C67A74 /* CreateRoomScreen.swift in Sources */,
|
||||
D38E59C48BE5499A48D12031 /* CreateRoomScreenCoordinator.swift in Sources */,
|
||||
9DBF6524DFD8143A4D6A17F0 /* CreateRoomScreenModels.swift in Sources */,
|
||||
A7DB75E090542331F6668A23 /* CreateRoomScreenViewModel.swift in Sources */,
|
||||
DB01FDD050A43B72C5425549 /* CreateRoomScreenViewModelProtocol.swift in Sources */,
|
||||
565868808A1DA565707394ED /* CurrentValuePublisher.swift in Sources */,
|
||||
A722F426FD81FC67706BB1E0 /* CustomLayoutLabelStyle.swift in Sources */,
|
||||
12C867E85E6D12EEDFD0B127 /* CustomStringConvertible.swift in Sources */,
|
||||
@@ -8346,6 +8334,7 @@
|
||||
9DB4B303ECC05F0F33582594 /* SpacesAnnouncementSheetView.swift in Sources */,
|
||||
DF004A5B2EABBD0574D06A04 /* SplashScreenCoordinator.swift in Sources */,
|
||||
E1C67E5D9E22135A8FEBBD60 /* StackedAvatarsView.swift in Sources */,
|
||||
9FC79DA30AE0E1502DAEBD51 /* StartChatFlowCoordinator.swift in Sources */,
|
||||
3DAF325D8AE461F7CDB282BD /* StartChatScreen.swift in Sources */,
|
||||
6CD61FAF03E8986523C2ABB8 /* StartChatScreenCoordinator.swift in Sources */,
|
||||
C051475DFF4C8EBDDF4DC8E4 /* StartChatScreenModels.swift in Sources */,
|
||||
@@ -8541,7 +8530,6 @@
|
||||
CB07184D37D5D65327A5A693 /* AuthenticationFlowCoordinatorTests.swift in Sources */,
|
||||
9EF72884B74BC39B01640098 /* BugReportTests.swift in Sources */,
|
||||
94D0F36A87E596A93C0C178A /* Bundle.swift in Sources */,
|
||||
28AB1614E749D1147A2AC6C2 /* CreateRoomScreenTests.swift in Sources */,
|
||||
8D24671992A1C1753B211221 /* EncryptionResetTests.swift in Sources */,
|
||||
E5E43A0CA99AF5BA11B194A2 /* EncryptionSettingsTests.swift in Sources */,
|
||||
A950C95855C474F75B30CA7B /* PollFormScreenTests.swift in Sources */,
|
||||
@@ -8550,7 +8538,7 @@
|
||||
94EB81606839C175CA6C2ADB /* RoomScreenTests.swift in Sources */,
|
||||
E9985DCD1B0D026D7E8BF809 /* ServerSelectionTests.swift in Sources */,
|
||||
D5C2DA52162A978743A183F2 /* SessionVerificationTests.swift in Sources */,
|
||||
F9788AE0B9EA19F1190ED14F /* StartChatScreenTests.swift in Sources */,
|
||||
221A7F92116722322AE150AE /* StartChatTests.swift in Sources */,
|
||||
54AE8860D668AFD96E7E177B /* UITestsScreenIdentifier.swift in Sources */,
|
||||
84EFCB95F9DA2979C8042B26 /* UITestsSignalling.swift in Sources */,
|
||||
B22D857D1E8FCA6DD74A58E3 /* UserSessionScreenTests.swift in Sources */,
|
||||
|
||||
@@ -36,9 +36,10 @@ class ChatsFlowCoordinator: FlowCoordinatorProtocol {
|
||||
|
||||
// periphery:ignore - retaining purpose
|
||||
private var bugReportFlowCoordinator: BugReportFlowCoordinator?
|
||||
|
||||
// periphery:ignore - retaining purpose
|
||||
private var encryptionResetFlowCoordinator: EncryptionResetFlowCoordinator?
|
||||
// periphery:ignore - retaining purpose
|
||||
private var startChatFlowCoordinator: StartChatFlowCoordinator?
|
||||
|
||||
// periphery:ignore - retaining purpose
|
||||
private var globalSearchScreenCoordinator: GlobalSearchScreenCoordinator?
|
||||
@@ -223,12 +224,12 @@ class ChatsFlowCoordinator: FlowCoordinatorProtocol {
|
||||
case (.roomList, .startEncryptionResetFlow, .encryptionResetFlow):
|
||||
startEncryptionResetFlow(animated: animated)
|
||||
case (.encryptionResetFlow, .finishedEncryptionResetFlow, .roomList):
|
||||
break
|
||||
encryptionResetFlowCoordinator = nil
|
||||
|
||||
case (.roomList, .showStartChatScreen, .startChatScreen):
|
||||
presentStartChat(animated: animated)
|
||||
case (.startChatScreen, .dismissedStartChatScreen, .roomList):
|
||||
break
|
||||
case (.roomList, .startStartChatFlow, .startChatFlow):
|
||||
startStartChatFlow(animated: animated)
|
||||
case (.startChatFlow, .finishedStartChatFlow, .roomList):
|
||||
startChatFlowCoordinator = nil
|
||||
|
||||
case (.roomList, .showRoomDirectorySearchScreen, .roomDirectorySearchScreen):
|
||||
presentRoomDirectorySearch()
|
||||
@@ -398,7 +399,7 @@ class ChatsFlowCoordinator: FlowCoordinatorProtocol {
|
||||
case .presentEncryptionResetScreen:
|
||||
stateMachine.processEvent(.startEncryptionResetFlow)
|
||||
case .presentStartChatScreen:
|
||||
stateMachine.processEvent(.showStartChatScreen)
|
||||
stateMachine.processEvent(.startStartChatFlow)
|
||||
case .presentGlobalSearch:
|
||||
presentGlobalSearch()
|
||||
case .logout:
|
||||
@@ -565,39 +566,34 @@ class ChatsFlowCoordinator: FlowCoordinatorProtocol {
|
||||
|
||||
// MARK: Start Chat
|
||||
|
||||
private func presentStartChat(animated: Bool) {
|
||||
let startChatNavigationStackCoordinator = NavigationStackCoordinator()
|
||||
|
||||
let userDiscoveryService = UserDiscoveryService(clientProxy: userSession.clientProxy)
|
||||
let parameters = StartChatScreenCoordinatorParameters(orientationManager: flowParameters.windowManager,
|
||||
userSession: userSession,
|
||||
userIndicatorController: flowParameters.userIndicatorController,
|
||||
navigationStackCoordinator: startChatNavigationStackCoordinator,
|
||||
userDiscoveryService: userDiscoveryService,
|
||||
mediaUploadingPreprocessor: MediaUploadingPreprocessor(appSettings: flowParameters.appSettings),
|
||||
appSettings: flowParameters.appSettings,
|
||||
analytics: flowParameters.analytics)
|
||||
private func startStartChatFlow(animated: Bool) {
|
||||
let navigationStackCoordinator = NavigationStackCoordinator()
|
||||
let coordinator = StartChatFlowCoordinator(userDiscoveryService: UserDiscoveryService(clientProxy: userSession.clientProxy),
|
||||
navigationStackCoordinator: navigationStackCoordinator,
|
||||
flowParameters: flowParameters)
|
||||
|
||||
let coordinator = StartChatScreenCoordinator(parameters: parameters)
|
||||
coordinator.actions.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
switch action {
|
||||
case .close:
|
||||
navigationSplitCoordinator.setSheetCoordinator(nil)
|
||||
case .openRoom(let roomID):
|
||||
navigationSplitCoordinator.setSheetCoordinator(nil)
|
||||
stateMachine.processEvent(.selectRoom(roomID: roomID, via: [], entryPoint: .room))
|
||||
case .openRoomDirectorySearch:
|
||||
navigationSplitCoordinator.setSheetCoordinator(nil)
|
||||
stateMachine.processEvent(.showRoomDirectorySearchScreen)
|
||||
coordinator.actionsPublisher
|
||||
.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
switch action {
|
||||
case .finished(let roomID):
|
||||
navigationSplitCoordinator.setSheetCoordinator(nil)
|
||||
|
||||
if let roomID {
|
||||
stateMachine.processEvent(.selectRoom(roomID: roomID, via: [], entryPoint: .room))
|
||||
}
|
||||
case .showRoomDirectory:
|
||||
navigationSplitCoordinator.setSheetCoordinator(nil)
|
||||
stateMachine.processEvent(.showRoomDirectorySearchScreen)
|
||||
}
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
startChatNavigationStackCoordinator.setRootCoordinator(coordinator)
|
||||
|
||||
navigationSplitCoordinator.setSheetCoordinator(startChatNavigationStackCoordinator, animated: animated) { [weak self] in
|
||||
self?.stateMachine.processEvent(.dismissedStartChatScreen)
|
||||
.store(in: &cancellables)
|
||||
|
||||
startChatFlowCoordinator = coordinator
|
||||
coordinator.start()
|
||||
|
||||
navigationSplitCoordinator.setSheetCoordinator(navigationStackCoordinator, animated: animated) { [weak self] in
|
||||
self?.stateMachine.processEvent(.finishedStartChatFlow)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -639,10 +635,8 @@ class ChatsFlowCoordinator: FlowCoordinatorProtocol {
|
||||
guard let self else { return }
|
||||
switch action {
|
||||
case .resetComplete:
|
||||
encryptionResetFlowCoordinator = nil
|
||||
navigationSplitCoordinator.setSheetCoordinator(nil)
|
||||
case .cancel:
|
||||
encryptionResetFlowCoordinator = nil
|
||||
navigationSplitCoordinator.setSheetCoordinator(nil)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,8 +33,8 @@ class ChatsFlowCoordinatorStateMachine {
|
||||
/// Showing the encryption reset flow.
|
||||
case encryptionResetFlow(detailState: DetailState?)
|
||||
|
||||
/// Showing the start chat screen
|
||||
case startChatScreen(detailState: DetailState?)
|
||||
/// Showing the start chat flow
|
||||
case startChatFlow(detailState: DetailState?)
|
||||
|
||||
/// Showing the logout flows
|
||||
case logoutConfirmationScreen(detailState: DetailState?)
|
||||
@@ -61,7 +61,7 @@ class ChatsFlowCoordinatorStateMachine {
|
||||
.feedbackScreen(let detailState),
|
||||
.recoveryKeyScreen(let detailState),
|
||||
.encryptionResetFlow(let detailState),
|
||||
.startChatScreen(let detailState),
|
||||
.startChatFlow(let detailState),
|
||||
.logoutConfirmationScreen(let detailState),
|
||||
.roomDirectorySearchScreen(let detailState),
|
||||
.reportRoomScreen(let detailState),
|
||||
@@ -111,10 +111,10 @@ class ChatsFlowCoordinatorStateMachine {
|
||||
/// The encryption reset flow is complete and has been dismissed.
|
||||
case finishedEncryptionResetFlow
|
||||
|
||||
/// Request the start of the start chat flow
|
||||
case showStartChatScreen
|
||||
/// Start chat has been dismissed
|
||||
case dismissedStartChatScreen
|
||||
/// Request the start of the start chat flow.
|
||||
case startStartChatFlow
|
||||
/// The Start Chat flow is complete and has been dismissed.
|
||||
case finishedStartChatFlow
|
||||
|
||||
/// Request presentation of the room directory search screen.
|
||||
case showRoomDirectorySearchScreen
|
||||
@@ -177,9 +177,9 @@ class ChatsFlowCoordinatorStateMachine {
|
||||
case (.encryptionResetFlow(let detailState), .finishedEncryptionResetFlow):
|
||||
return .roomList(detailState: detailState)
|
||||
|
||||
case (.roomList(let detailState), .showStartChatScreen):
|
||||
return .startChatScreen(detailState: detailState)
|
||||
case (.startChatScreen(let detailState), .dismissedStartChatScreen):
|
||||
case (.roomList(let detailState), .startStartChatFlow):
|
||||
return .startChatFlow(detailState: detailState)
|
||||
case (.startChatFlow(let detailState), .finishedStartChatFlow):
|
||||
return .roomList(detailState: detailState)
|
||||
|
||||
case (.roomList(let detailState), .showRoomDirectorySearchScreen):
|
||||
|
||||
@@ -1267,12 +1267,10 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
||||
}
|
||||
|
||||
private func presentInviteUsersScreen() {
|
||||
let selectedUsersSubject: CurrentValueSubject<[UserProfileProxy], Never> = .init([])
|
||||
|
||||
let stackCoordinator = NavigationStackCoordinator()
|
||||
let inviteParameters = InviteUsersScreenCoordinatorParameters(userSession: userSession,
|
||||
selectedUsers: .init(selectedUsersSubject),
|
||||
roomType: .room(roomProxy: roomProxy),
|
||||
roomProxy: roomProxy,
|
||||
isSkippable: false,
|
||||
userDiscoveryService: UserDiscoveryService(clientProxy: userSession.clientProxy),
|
||||
userIndicatorController: flowParameters.userIndicatorController,
|
||||
appSettings: flowParameters.appSettings)
|
||||
@@ -1286,8 +1284,6 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
||||
switch action {
|
||||
case .dismiss:
|
||||
navigationStackCoordinator.setSheetCoordinator(nil)
|
||||
case .proceed:
|
||||
fatalError("Not handled in this flow.")
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
@@ -255,8 +255,8 @@ final class RoomMembersFlowCoordinator: FlowCoordinatorProtocol {
|
||||
private func presentInviteUsersScreen() {
|
||||
let stackCoordinator = NavigationStackCoordinator()
|
||||
let inviteParameters = InviteUsersScreenCoordinatorParameters(userSession: flowParameters.userSession,
|
||||
selectedUsers: nil,
|
||||
roomType: .room(roomProxy: roomProxy),
|
||||
roomProxy: roomProxy,
|
||||
isSkippable: false,
|
||||
userDiscoveryService: UserDiscoveryService(clientProxy: flowParameters.userSession.clientProxy),
|
||||
userIndicatorController: flowParameters.userIndicatorController,
|
||||
appSettings: flowParameters.appSettings)
|
||||
@@ -270,8 +270,6 @@ final class RoomMembersFlowCoordinator: FlowCoordinatorProtocol {
|
||||
switch action {
|
||||
case .dismiss:
|
||||
navigationStackCoordinator.setSheetCoordinator(nil)
|
||||
case .proceed:
|
||||
fatalError("Not handled in this flow.")
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
@@ -0,0 +1,237 @@
|
||||
//
|
||||
// Copyright 2025 Element Creations Ltd.
|
||||
// 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 Foundation
|
||||
import SwiftState
|
||||
|
||||
enum StartChatFlowCoordinatorAction {
|
||||
case finished(roomID: String?)
|
||||
case showRoomDirectory
|
||||
}
|
||||
|
||||
class StartChatFlowCoordinator: FlowCoordinatorProtocol {
|
||||
private let userDiscoveryService: UserDiscoveryServiceProtocol
|
||||
private let navigationStackCoordinator: NavigationStackCoordinator
|
||||
|
||||
private let flowParameters: CommonFlowParameters
|
||||
|
||||
private var createRoomScreenCoordinator: CreateRoomScreenCoordinator?
|
||||
|
||||
indirect enum State: StateType {
|
||||
/// The state machine hasn't started.
|
||||
case initial
|
||||
|
||||
/// Shown when the flow is started with options to create a room/DM, join by alias, use the room directory etc.
|
||||
case startChat
|
||||
/// The user is creating a new room.
|
||||
case createRoom
|
||||
/// The user is selecting an avatar for the new room.
|
||||
case roomAvatarPicker
|
||||
/// The user is inviting users to a newly created room.
|
||||
case inviteUsers
|
||||
}
|
||||
|
||||
enum Event: EventType {
|
||||
/// The flow is being started.
|
||||
case start
|
||||
|
||||
/// The user would like to create a room.
|
||||
case createRoom
|
||||
/// The user dismissed the create room screen.
|
||||
case dismissedCreateRoom
|
||||
|
||||
/// The user would like to pick an avatar for the room.
|
||||
case presentRoomAvatarPicker
|
||||
/// The user finished picking the avatar.
|
||||
case dismissedRoomAvatarPicker
|
||||
|
||||
/// The user's room was created successfully.
|
||||
case createdRoom
|
||||
}
|
||||
|
||||
private let stateMachine: StateMachine<State, Event>
|
||||
private var cancellables: Set<AnyCancellable> = []
|
||||
|
||||
private let actionsSubject: PassthroughSubject<StartChatFlowCoordinatorAction, Never> = .init()
|
||||
var actionsPublisher: AnyPublisher<StartChatFlowCoordinatorAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
init(userDiscoveryService: UserDiscoveryServiceProtocol,
|
||||
navigationStackCoordinator: NavigationStackCoordinator,
|
||||
flowParameters: CommonFlowParameters) {
|
||||
self.userDiscoveryService = userDiscoveryService
|
||||
self.navigationStackCoordinator = navigationStackCoordinator
|
||||
|
||||
self.flowParameters = flowParameters
|
||||
|
||||
stateMachine = .init(state: .initial)
|
||||
configureStateMachine()
|
||||
}
|
||||
|
||||
func start(animated: Bool) {
|
||||
stateMachine.tryEvent(.start)
|
||||
}
|
||||
|
||||
func handleAppRoute(_ appRoute: AppRoute, animated: Bool) {
|
||||
// There aren't any routes to this screen yet, so clear the stacks.
|
||||
clearRoute(animated: animated)
|
||||
}
|
||||
|
||||
func clearRoute(animated: Bool) {
|
||||
switch stateMachine.state {
|
||||
case .initial:
|
||||
break
|
||||
case .startChat:
|
||||
navigationStackCoordinator.setRootCoordinator(nil, animated: animated) // StartChatScreen
|
||||
case .createRoom:
|
||||
navigationStackCoordinator.pop(animated: animated) // CreateRoomScreen
|
||||
navigationStackCoordinator.setRootCoordinator(nil, animated: animated) // StartChatScreen
|
||||
case .roomAvatarPicker:
|
||||
navigationStackCoordinator.setSheetCoordinator(nil, animated: animated) // Media Picker
|
||||
clearRoute(animated: animated) // Re-run with the state machine back in the .createRoom state.
|
||||
case .inviteUsers:
|
||||
navigationStackCoordinator.pop(animated: animated) // InviteUsersScreen
|
||||
navigationStackCoordinator.pop(animated: animated) // CreateRoomScreen
|
||||
navigationStackCoordinator.setRootCoordinator(nil, animated: animated) // StartChatScreen
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func configureStateMachine() {
|
||||
stateMachine.addRoutes(event: .start, transitions: [.initial => .startChat]) { [weak self] _ in
|
||||
self?.presentStartChatScreen()
|
||||
}
|
||||
|
||||
stateMachine.addRoutes(event: .createRoom, transitions: [.startChat => .createRoom]) { [weak self] _ in
|
||||
self?.presentCreateRoomScreen()
|
||||
}
|
||||
stateMachine.addRoutes(event: .dismissedCreateRoom, transitions: [.createRoom => .startChat]) { [weak self] _ in
|
||||
self?.createRoomScreenCoordinator = nil
|
||||
}
|
||||
|
||||
stateMachine.addRoutes(event: .presentRoomAvatarPicker, transitions: [.createRoom => .roomAvatarPicker]) { [weak self] context in
|
||||
guard let mode = context.userInfo as? MediaPickerScreenMode else {
|
||||
fatalError("A picker mode is required for the room avatar.")
|
||||
}
|
||||
self?.presentRoomAvatarPicker(mode)
|
||||
}
|
||||
stateMachine.addRoutes(event: .dismissedRoomAvatarPicker, transitions: [.roomAvatarPicker => .createRoom])
|
||||
|
||||
stateMachine.addRoutes(event: .createdRoom, transitions: [.createRoom => .inviteUsers]) { [weak self] context in
|
||||
guard let roomProxy = context.userInfo as? JoinedRoomProxyProtocol else {
|
||||
fatalError("A room proxy is required to invite users.")
|
||||
}
|
||||
self?.presentInviteUsersScreen(roomProxy: roomProxy)
|
||||
}
|
||||
|
||||
stateMachine.addErrorHandler { context in
|
||||
if context.fromState == context.toState {
|
||||
MXLog.error("Transition between equal states: \(context.fromState)")
|
||||
} else {
|
||||
fatalError("Unexpected transition: \(context)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func presentStartChatScreen() {
|
||||
let parameters = StartChatScreenCoordinatorParameters(userSession: flowParameters.userSession,
|
||||
userDiscoveryService: userDiscoveryService,
|
||||
userIndicatorController: flowParameters.userIndicatorController,
|
||||
appSettings: flowParameters.appSettings,
|
||||
analytics: flowParameters.analytics)
|
||||
|
||||
let coordinator = StartChatScreenCoordinator(parameters: parameters)
|
||||
coordinator.actions.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
switch action {
|
||||
case .close:
|
||||
actionsSubject.send(.finished(roomID: nil))
|
||||
case .createRoom:
|
||||
stateMachine.tryEvent(.createRoom)
|
||||
case .openRoom(let roomID):
|
||||
actionsSubject.send(.finished(roomID: roomID))
|
||||
case .openRoomDirectorySearch:
|
||||
actionsSubject.send(.showRoomDirectory)
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
navigationStackCoordinator.setRootCoordinator(coordinator)
|
||||
}
|
||||
|
||||
private func presentCreateRoomScreen() {
|
||||
let createParameters = CreateRoomScreenCoordinatorParameters(userSession: flowParameters.userSession,
|
||||
userIndicatorController: flowParameters.userIndicatorController,
|
||||
appSettings: flowParameters.appSettings,
|
||||
analytics: flowParameters.analytics)
|
||||
let coordinator = CreateRoomScreenCoordinator(parameters: createParameters)
|
||||
coordinator.actions.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
switch action {
|
||||
case .createdRoom(let roomProxy):
|
||||
stateMachine.tryEvent(.createdRoom, userInfo: roomProxy)
|
||||
case .displayMediaPickerWithMode(let mode):
|
||||
stateMachine.tryEvent(.presentRoomAvatarPicker, userInfo: mode)
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
createRoomScreenCoordinator = coordinator
|
||||
navigationStackCoordinator.push(coordinator) { [weak self] in
|
||||
self?.stateMachine.tryEvent(.dismissedCreateRoom)
|
||||
}
|
||||
}
|
||||
|
||||
private func presentRoomAvatarPicker(_ mode: MediaPickerScreenMode) {
|
||||
let stackCoordinator = NavigationStackCoordinator()
|
||||
|
||||
let mediaPickerCoordinator = MediaPickerScreenCoordinator(mode: mode,
|
||||
userIndicatorController: flowParameters.userIndicatorController,
|
||||
orientationManager: flowParameters.windowManager) { [weak self] action in
|
||||
guard let self else { return }
|
||||
switch action {
|
||||
case .cancel:
|
||||
navigationStackCoordinator.setSheetCoordinator(nil)
|
||||
case .selectedMediaAtURLs(let urls):
|
||||
guard urls.count == 1 else { fatalError("Received an invalid number of URLs") }
|
||||
|
||||
navigationStackCoordinator.setSheetCoordinator(nil)
|
||||
createRoomScreenCoordinator?.updateAvatar(fileURL: urls[0])
|
||||
}
|
||||
}
|
||||
|
||||
stackCoordinator.setRootCoordinator(mediaPickerCoordinator)
|
||||
|
||||
navigationStackCoordinator.setSheetCoordinator(stackCoordinator) { [weak self] in
|
||||
self?.stateMachine.tryEvent(.dismissedRoomAvatarPicker)
|
||||
}
|
||||
}
|
||||
|
||||
private func presentInviteUsersScreen(roomProxy: JoinedRoomProxyProtocol) {
|
||||
let inviteParameters = InviteUsersScreenCoordinatorParameters(userSession: flowParameters.userSession,
|
||||
roomProxy: roomProxy,
|
||||
isSkippable: true,
|
||||
userDiscoveryService: userDiscoveryService,
|
||||
userIndicatorController: flowParameters.userIndicatorController,
|
||||
appSettings: flowParameters.appSettings)
|
||||
let coordinator = InviteUsersScreenCoordinator(parameters: inviteParameters)
|
||||
coordinator.actions.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
switch action {
|
||||
case .dismiss:
|
||||
actionsSubject.send(.finished(roomID: roomProxy.id))
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
navigationStackCoordinator.push(coordinator)
|
||||
}
|
||||
}
|
||||
@@ -123,6 +123,7 @@ extension JoinedRoomProxyMock {
|
||||
|
||||
powerLevelsReturnValue = .success(powerLevelsProxyMock)
|
||||
|
||||
inviteUserIDReturnValue = .success(())
|
||||
kickUserReasonReturnValue = .success(())
|
||||
banUserReasonReturnValue = .success(())
|
||||
unbanUserReturnValue = .success(())
|
||||
|
||||
@@ -270,8 +270,11 @@ enum A11yIdentifiers {
|
||||
}
|
||||
|
||||
struct CreateRoomScreen {
|
||||
let create = "create_room-create"
|
||||
let roomAvatar = "create_room-room_avatar"
|
||||
let roomName = "create_room-room_name"
|
||||
let roomTopic = "create_room-room_topic"
|
||||
let mediaPicker = "create_room-media_picker"
|
||||
}
|
||||
|
||||
struct PollFormScreen {
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
//
|
||||
// Copyright 2025 Element Creations Ltd.
|
||||
// Copyright 2022-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 SwiftUI
|
||||
|
||||
struct CreateRoomCoordinatorParameters {
|
||||
let userSession: UserSessionProtocol
|
||||
let userIndicatorController: UserIndicatorControllerProtocol
|
||||
let createRoomParameters: CurrentValuePublisher<CreateRoomFlowParameters, Never>
|
||||
let selectedUsers: [UserProfileProxy]
|
||||
let appSettings: AppSettings
|
||||
let analytics: AnalyticsService
|
||||
}
|
||||
|
||||
enum CreateRoomCoordinatorAction {
|
||||
case openRoom(withIdentifier: String)
|
||||
case updateSelectedUsers([UserProfileProxy])
|
||||
case updateDetails(CreateRoomFlowParameters)
|
||||
case displayMediaPickerWithMode(MediaPickerScreenMode)
|
||||
case removeImage
|
||||
}
|
||||
|
||||
final class CreateRoomCoordinator: CoordinatorProtocol {
|
||||
private var viewModel: CreateRoomViewModelProtocol
|
||||
private let actionsSubject: PassthroughSubject<CreateRoomCoordinatorAction, Never> = .init()
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
var actions: AnyPublisher<CreateRoomCoordinatorAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
init(parameters: CreateRoomCoordinatorParameters) {
|
||||
viewModel = CreateRoomViewModel(userSession: parameters.userSession,
|
||||
createRoomParameters: parameters.createRoomParameters,
|
||||
selectedUsers: parameters.selectedUsers,
|
||||
analytics: parameters.analytics,
|
||||
userIndicatorController: parameters.userIndicatorController,
|
||||
appSettings: parameters.appSettings)
|
||||
}
|
||||
|
||||
func start() {
|
||||
viewModel.actions.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
switch action {
|
||||
case .updateSelectedUsers(let users):
|
||||
actionsSubject.send(.updateSelectedUsers(users))
|
||||
case .openRoom(let identifier):
|
||||
actionsSubject.send(.openRoom(withIdentifier: identifier))
|
||||
case .updateDetails(let details):
|
||||
actionsSubject.send(.updateDetails(details))
|
||||
case .displayCameraPicker:
|
||||
actionsSubject.send(.displayMediaPickerWithMode(.init(source: .camera, selectionType: .single)))
|
||||
case .displayMediaPicker:
|
||||
actionsSubject.send(.displayMediaPickerWithMode(.init(source: .photoLibrary, selectionType: .single)))
|
||||
case .removeImage:
|
||||
actionsSubject.send(.removeImage)
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
func toPresentable() -> AnyView {
|
||||
AnyView(CreateRoomScreen(context: viewModel.context))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
//
|
||||
// Copyright 2025 Element Creations Ltd.
|
||||
// Copyright 2022-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 SwiftUI
|
||||
|
||||
struct CreateRoomScreenCoordinatorParameters {
|
||||
let userSession: UserSessionProtocol
|
||||
let userIndicatorController: UserIndicatorControllerProtocol
|
||||
let appSettings: AppSettings
|
||||
let analytics: AnalyticsService
|
||||
}
|
||||
|
||||
enum CreateRoomScreenCoordinatorAction {
|
||||
case createdRoom(JoinedRoomProxyProtocol)
|
||||
case displayMediaPickerWithMode(MediaPickerScreenMode)
|
||||
}
|
||||
|
||||
final class CreateRoomScreenCoordinator: CoordinatorProtocol {
|
||||
private var viewModel: CreateRoomScreenViewModelProtocol
|
||||
private let actionsSubject: PassthroughSubject<CreateRoomScreenCoordinatorAction, Never> = .init()
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
var actions: AnyPublisher<CreateRoomScreenCoordinatorAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
init(parameters: CreateRoomScreenCoordinatorParameters) {
|
||||
viewModel = CreateRoomScreenViewModel(userSession: parameters.userSession,
|
||||
analytics: parameters.analytics,
|
||||
userIndicatorController: parameters.userIndicatorController,
|
||||
appSettings: parameters.appSettings)
|
||||
}
|
||||
|
||||
func start() {
|
||||
viewModel.actions.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
switch action {
|
||||
case .createdRoom(let roomProxy):
|
||||
actionsSubject.send(.createdRoom(roomProxy))
|
||||
case .displayCameraPicker:
|
||||
actionsSubject.send(.displayMediaPickerWithMode(.init(source: .camera, selectionType: .single)))
|
||||
case .displayMediaPicker:
|
||||
actionsSubject.send(.displayMediaPickerWithMode(.init(source: .photoLibrary, selectionType: .single)))
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
func toPresentable() -> AnyView {
|
||||
AnyView(CreateRoomScreen(context: viewModel.context))
|
||||
}
|
||||
|
||||
func updateAvatar(fileURL: URL) {
|
||||
viewModel.updateAvatar(fileURL: fileURL)
|
||||
}
|
||||
}
|
||||
+9
-14
@@ -16,28 +16,24 @@ enum CreateRoomScreenErrorType: Error {
|
||||
case unknown
|
||||
}
|
||||
|
||||
enum CreateRoomViewModelAction {
|
||||
case openRoom(withIdentifier: String)
|
||||
case updateSelectedUsers([UserProfileProxy])
|
||||
case updateDetails(CreateRoomFlowParameters)
|
||||
enum CreateRoomScreenViewModelAction {
|
||||
case createdRoom(JoinedRoomProxyProtocol)
|
||||
case displayMediaPicker
|
||||
case displayCameraPicker
|
||||
case removeImage
|
||||
}
|
||||
|
||||
struct CreateRoomViewState: BindableState {
|
||||
struct CreateRoomScreenViewState: BindableState {
|
||||
var roomName: String
|
||||
let serverName: String
|
||||
let isKnockingFeatureEnabled: Bool
|
||||
var selectedUsers: [UserProfileProxy]
|
||||
var aliasLocalPart: String
|
||||
var bindings: CreateRoomViewStateBindings
|
||||
var bindings: CreateRoomScreenViewStateBindings
|
||||
var avatarURL: URL?
|
||||
var canCreateRoom: Bool {
|
||||
!roomName.isEmpty && aliasErrors.isEmpty
|
||||
}
|
||||
|
||||
var aliasErrors: Set<CreateRoomAliasErrorState> = []
|
||||
var aliasErrors: Set<CreateRoomScreenAliasErrorState> = []
|
||||
var aliasErrorDescription: String? {
|
||||
if aliasErrors.contains(.alreadyExists) {
|
||||
L10n.errorRoomAddressAlreadyExists
|
||||
@@ -49,7 +45,7 @@ struct CreateRoomViewState: BindableState {
|
||||
}
|
||||
}
|
||||
|
||||
struct CreateRoomViewStateBindings {
|
||||
struct CreateRoomScreenViewStateBindings {
|
||||
var roomTopic: String
|
||||
var isRoomPrivate: Bool
|
||||
var isKnockingOnly: Bool
|
||||
@@ -59,9 +55,8 @@ struct CreateRoomViewStateBindings {
|
||||
var alertInfo: AlertInfo<CreateRoomScreenErrorType>?
|
||||
}
|
||||
|
||||
enum CreateRoomViewAction {
|
||||
enum CreateRoomScreenViewAction {
|
||||
case createRoom
|
||||
case deselectUser(UserProfileProxy)
|
||||
case displayCameraPicker
|
||||
case displayMediaPicker
|
||||
case removeImage
|
||||
@@ -69,12 +64,12 @@ enum CreateRoomViewAction {
|
||||
case updateAliasLocalPart(String)
|
||||
}
|
||||
|
||||
enum CreateRoomAliasErrorState {
|
||||
enum CreateRoomScreenAliasErrorState {
|
||||
case alreadyExists
|
||||
case invalidSymbols
|
||||
}
|
||||
|
||||
extension Set<CreateRoomAliasErrorState> {
|
||||
extension Set<CreateRoomScreenAliasErrorState> {
|
||||
var errorDescription: String? {
|
||||
if contains(.alreadyExists) {
|
||||
return L10n.errorRoomAddressAlreadyExists
|
||||
+81
-63
@@ -10,83 +10,70 @@ import Combine
|
||||
import MatrixRustSDK
|
||||
import SwiftUI
|
||||
|
||||
typealias CreateRoomViewModelType = StateStoreViewModel<CreateRoomViewState, CreateRoomViewAction>
|
||||
typealias CreateRoomScreenViewModelType = StateStoreViewModel<CreateRoomScreenViewState, CreateRoomScreenViewAction>
|
||||
|
||||
class CreateRoomViewModel: CreateRoomViewModelType, CreateRoomViewModelProtocol {
|
||||
class CreateRoomScreenViewModel: CreateRoomScreenViewModelType, CreateRoomScreenViewModelProtocol {
|
||||
struct Parameters {
|
||||
var name = ""
|
||||
var topic = ""
|
||||
var isRoomPrivate = true
|
||||
var isKnockingOnly = false
|
||||
var avatarImageMedia: MediaInfo?
|
||||
var aliasLocalPart: String?
|
||||
}
|
||||
|
||||
private let userSession: UserSessionProtocol
|
||||
private var createRoomParameters: CreateRoomFlowParameters
|
||||
private var parameters: Parameters
|
||||
private let mediaUploadingPreprocessor: MediaUploadingPreprocessor
|
||||
private let analytics: AnalyticsService
|
||||
private let userIndicatorController: UserIndicatorControllerProtocol
|
||||
private var syncNameAndAlias = true
|
||||
@CancellableTask private var checkAliasAvailabilityTask: Task<Void, Never>?
|
||||
|
||||
private var actionsSubject: PassthroughSubject<CreateRoomViewModelAction, Never> = .init()
|
||||
private var actionsSubject: PassthroughSubject<CreateRoomScreenViewModelAction, Never> = .init()
|
||||
|
||||
var actions: AnyPublisher<CreateRoomViewModelAction, Never> {
|
||||
var actions: AnyPublisher<CreateRoomScreenViewModelAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
init(userSession: UserSessionProtocol,
|
||||
createRoomParameters: CurrentValuePublisher<CreateRoomFlowParameters, Never>,
|
||||
selectedUsers: [UserProfileProxy],
|
||||
initialParameters: Parameters = .init(),
|
||||
analytics: AnalyticsService,
|
||||
userIndicatorController: UserIndicatorControllerProtocol,
|
||||
appSettings: AppSettings) {
|
||||
let parameters = createRoomParameters.value
|
||||
|
||||
self.userSession = userSession
|
||||
self.createRoomParameters = parameters
|
||||
parameters = initialParameters
|
||||
mediaUploadingPreprocessor = MediaUploadingPreprocessor(appSettings: appSettings)
|
||||
self.analytics = analytics
|
||||
self.userIndicatorController = userIndicatorController
|
||||
|
||||
let bindings = CreateRoomViewStateBindings(roomTopic: parameters.topic,
|
||||
isRoomPrivate: parameters.isRoomPrivate,
|
||||
isKnockingOnly: appSettings.knockingEnabled ? parameters.isKnockingOnly : false)
|
||||
let bindings = CreateRoomScreenViewStateBindings(roomTopic: parameters.topic,
|
||||
isRoomPrivate: parameters.isRoomPrivate,
|
||||
isKnockingOnly: appSettings.knockingEnabled ? parameters.isKnockingOnly : false)
|
||||
|
||||
super.init(initialViewState: CreateRoomViewState(roomName: parameters.name,
|
||||
serverName: userSession.clientProxy.userIDServerName ?? "",
|
||||
isKnockingFeatureEnabled: appSettings.knockingEnabled,
|
||||
selectedUsers: selectedUsers,
|
||||
aliasLocalPart: parameters.aliasLocalPart ?? roomAliasNameFromRoomDisplayName(roomName: parameters.name),
|
||||
bindings: bindings),
|
||||
super.init(initialViewState: CreateRoomScreenViewState(roomName: parameters.name,
|
||||
serverName: userSession.clientProxy.userIDServerName ?? "",
|
||||
isKnockingFeatureEnabled: appSettings.knockingEnabled,
|
||||
aliasLocalPart: parameters.aliasLocalPart ?? roomAliasNameFromRoomDisplayName(roomName: parameters.name),
|
||||
bindings: bindings),
|
||||
mediaProvider: userSession.mediaProvider)
|
||||
|
||||
createRoomParameters
|
||||
.map(\.avatarImageMedia)
|
||||
.removeDuplicates { $0?.url == $1?.url }
|
||||
.sink { [weak self] mediaInfo in
|
||||
self?.createRoomParameters.avatarImageMedia = mediaInfo
|
||||
switch mediaInfo {
|
||||
case .image(_, let thumbnailURL, _):
|
||||
self?.state.avatarURL = thumbnailURL
|
||||
case nil:
|
||||
self?.state.avatarURL = nil
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
setupBindings()
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
override func process(viewAction: CreateRoomViewAction) {
|
||||
override func process(viewAction: CreateRoomScreenViewAction) {
|
||||
switch viewAction {
|
||||
case .createRoom:
|
||||
Task {
|
||||
await createRoom()
|
||||
}
|
||||
case .deselectUser(let user):
|
||||
state.selectedUsers.removeAll { $0.userID == user.userID }
|
||||
actionsSubject.send(.updateSelectedUsers(state.selectedUsers))
|
||||
Task { await createRoom() }
|
||||
case .displayCameraPicker:
|
||||
actionsSubject.send(.displayCameraPicker)
|
||||
case .displayMediaPicker:
|
||||
actionsSubject.send(.displayMediaPicker)
|
||||
case .removeImage:
|
||||
actionsSubject.send(.removeImage)
|
||||
parameters.avatarImageMedia = nil
|
||||
state.avatarURL = nil
|
||||
case .updateAliasLocalPart(let aliasLocalPart):
|
||||
state.aliasLocalPart = aliasLocalPart.lowercased()
|
||||
// If this has been called this means that the user wants a custom address not necessarily reflecting the name
|
||||
@@ -104,6 +91,32 @@ class CreateRoomViewModel: CreateRoomViewModelType, CreateRoomViewModelProtocol
|
||||
}
|
||||
}
|
||||
|
||||
func updateAvatar(fileURL: URL) {
|
||||
showLoadingIndicator()
|
||||
Task { [weak self] in
|
||||
guard let self else { return }
|
||||
do {
|
||||
guard case let .success(maxUploadSize) = await userSession.clientProxy.maxMediaUploadSize else {
|
||||
MXLog.error("Failed to get max upload size")
|
||||
userIndicatorController.alertInfo = AlertInfo(id: .init())
|
||||
return
|
||||
}
|
||||
let mediaInfo = try await mediaUploadingPreprocessor.processMedia(at: fileURL, maxUploadSize: maxUploadSize).get()
|
||||
|
||||
switch mediaInfo {
|
||||
case .image(_, let thumbnailURL, _):
|
||||
parameters.avatarImageMedia = mediaInfo
|
||||
state.avatarURL = thumbnailURL
|
||||
default:
|
||||
break
|
||||
}
|
||||
} catch {
|
||||
userIndicatorController.alertInfo = AlertInfo(id: .init())
|
||||
}
|
||||
hideLoadingIndicator()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func setupBindings() {
|
||||
@@ -135,7 +148,6 @@ class CreateRoomViewModel: CreateRoomViewModelType, CreateRoomViewModelProtocol
|
||||
.sink { [weak self] state in
|
||||
guard let self else { return }
|
||||
updateParameters(state: state)
|
||||
actionsSubject.send(.updateDetails(createRoomParameters))
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
@@ -183,15 +195,15 @@ class CreateRoomViewModel: CreateRoomViewModelType, CreateRoomViewModelProtocol
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
private func updateParameters(state: CreateRoomViewState) {
|
||||
createRoomParameters.name = state.roomName
|
||||
createRoomParameters.topic = state.bindings.roomTopic
|
||||
createRoomParameters.isRoomPrivate = state.bindings.isRoomPrivate
|
||||
createRoomParameters.isKnockingOnly = state.bindings.isKnockingOnly
|
||||
private func updateParameters(state: CreateRoomScreenViewState) {
|
||||
parameters.name = state.roomName
|
||||
parameters.topic = state.bindings.roomTopic
|
||||
parameters.isRoomPrivate = state.bindings.isRoomPrivate
|
||||
parameters.isKnockingOnly = state.bindings.isKnockingOnly
|
||||
if state.isKnockingFeatureEnabled, !state.aliasLocalPart.isEmpty {
|
||||
createRoomParameters.aliasLocalPart = state.aliasLocalPart
|
||||
parameters.aliasLocalPart = state.aliasLocalPart
|
||||
} else {
|
||||
createRoomParameters.aliasLocalPart = nil
|
||||
parameters.aliasLocalPart = nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,8 +217,8 @@ class CreateRoomViewModel: CreateRoomViewModelType, CreateRoomViewModelProtocol
|
||||
updateParameters(state: state)
|
||||
|
||||
// Better to double check the errors also when trying to create the room
|
||||
if state.isKnockingFeatureEnabled, !createRoomParameters.isRoomPrivate {
|
||||
guard let canonicalAlias = String.makeCanonicalAlias(aliasLocalPart: createRoomParameters.aliasLocalPart,
|
||||
if state.isKnockingFeatureEnabled, !parameters.isRoomPrivate {
|
||||
guard let canonicalAlias = String.makeCanonicalAlias(aliasLocalPart: parameters.aliasLocalPart,
|
||||
serverName: state.serverName),
|
||||
isRoomAliasFormatValid(alias: canonicalAlias) else {
|
||||
state.aliasErrors = [.invalidSymbols]
|
||||
@@ -226,7 +238,7 @@ class CreateRoomViewModel: CreateRoomViewModelType, CreateRoomViewModelProtocol
|
||||
}
|
||||
|
||||
let avatarURL: URL?
|
||||
if let media = createRoomParameters.avatarImageMedia {
|
||||
if let media = parameters.avatarImageMedia {
|
||||
switch await userSession.clientProxy.uploadMedia(media) {
|
||||
case .success(let url):
|
||||
avatarURL = URL(string: url)
|
||||
@@ -251,17 +263,23 @@ class CreateRoomViewModel: CreateRoomViewModelType, CreateRoomViewModelProtocol
|
||||
avatarURL = nil
|
||||
}
|
||||
|
||||
switch await userSession.clientProxy.createRoom(name: createRoomParameters.name,
|
||||
topic: createRoomParameters.topic.isBlank ? nil : createRoomParameters.topic,
|
||||
isRoomPrivate: createRoomParameters.isRoomPrivate,
|
||||
switch await userSession.clientProxy.createRoom(name: parameters.name,
|
||||
topic: parameters.topic.isBlank ? nil : parameters.topic,
|
||||
isRoomPrivate: parameters.isRoomPrivate,
|
||||
// As of right now we don't want to make private rooms with the knock rule
|
||||
isKnockingOnly: createRoomParameters.isRoomPrivate ? false : createRoomParameters.isKnockingOnly,
|
||||
userIDs: state.selectedUsers.map(\.userID),
|
||||
isKnockingOnly: parameters.isRoomPrivate ? false : parameters.isKnockingOnly,
|
||||
userIDs: [], // The invite users screen is shown next so we don't need to invite anyone right now.
|
||||
avatarURL: avatarURL,
|
||||
aliasLocalPart: createRoomParameters.isRoomPrivate ? nil : createRoomParameters.aliasLocalPart) {
|
||||
case .success(let roomId):
|
||||
aliasLocalPart: parameters.isRoomPrivate ? nil : parameters.aliasLocalPart) {
|
||||
case .success(let roomID):
|
||||
guard case let .joined(roomProxy) = await userSession.clientProxy.roomForIdentifier(roomID) else {
|
||||
state.bindings.alertInfo = AlertInfo(id: .failedCreatingRoom,
|
||||
title: L10n.commonError,
|
||||
message: L10n.screenStartChatErrorStartingChat)
|
||||
return
|
||||
}
|
||||
analytics.trackCreatedRoom(isDM: false)
|
||||
actionsSubject.send(.openRoom(withIdentifier: roomId))
|
||||
actionsSubject.send(.createdRoom(roomProxy))
|
||||
case .failure:
|
||||
state.bindings.alertInfo = AlertInfo(id: .failedCreatingRoom,
|
||||
title: L10n.commonError,
|
||||
@@ -271,7 +289,7 @@ class CreateRoomViewModel: CreateRoomViewModelType, CreateRoomViewModelProtocol
|
||||
|
||||
// MARK: Loading indicator
|
||||
|
||||
private static let loadingIndicatorIdentifier = "\(CreateRoomViewModel.self)-Loading"
|
||||
private static let loadingIndicatorIdentifier = "\(CreateRoomScreenViewModel.self)-Loading"
|
||||
|
||||
private func showLoadingIndicator() {
|
||||
userIndicatorController.submitIndicator(UserIndicator(id: Self.loadingIndicatorIdentifier,
|
||||
+6
-3
@@ -7,9 +7,12 @@
|
||||
//
|
||||
|
||||
import Combine
|
||||
import Foundation
|
||||
|
||||
@MainActor
|
||||
protocol CreateRoomViewModelProtocol {
|
||||
var actions: AnyPublisher<CreateRoomViewModelAction, Never> { get }
|
||||
var context: CreateRoomViewModelType.Context { get }
|
||||
protocol CreateRoomScreenViewModelProtocol {
|
||||
var actions: AnyPublisher<CreateRoomScreenViewModelAction, Never> { get }
|
||||
var context: CreateRoomScreenViewModelType.Context { get }
|
||||
|
||||
func updateAvatar(fileURL: URL)
|
||||
}
|
||||
+30
-77
@@ -10,7 +10,7 @@ import Compound
|
||||
import SwiftUI
|
||||
|
||||
struct CreateRoomScreen: View {
|
||||
@ObservedObject var context: CreateRoomViewModel.Context
|
||||
@ObservedObject var context: CreateRoomScreenViewModel.Context
|
||||
@FocusState private var focus: Focus?
|
||||
|
||||
private enum Focus {
|
||||
@@ -52,7 +52,6 @@ struct CreateRoomScreen: View {
|
||||
.navigationTitle(L10n.screenCreateRoomTitle)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar { toolbar }
|
||||
.readFrame($frame)
|
||||
.alert(item: $context.alertInfo)
|
||||
.shouldScrollOnKeyboardDidShow(focus == .alias, to: Focus.alias)
|
||||
}
|
||||
@@ -109,6 +108,7 @@ struct CreateRoomScreen: View {
|
||||
}
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.accessibilityIdentifier(A11yIdentifiers.createRoomScreen.roomAvatar)
|
||||
.confirmationDialog("", isPresented: $context.showAttachmentConfirmationDialog) {
|
||||
Button(L10n.actionTakePhoto) {
|
||||
context.send(viewAction: .displayCameraPicker)
|
||||
@@ -116,6 +116,8 @@ struct CreateRoomScreen: View {
|
||||
Button(L10n.actionChoosePhoto) {
|
||||
context.send(viewAction: .displayMediaPicker)
|
||||
}
|
||||
.accessibilityIdentifier(A11yIdentifiers.createRoomScreen.mediaPicker)
|
||||
|
||||
if context.viewState.avatarURL != nil {
|
||||
Button(L10n.actionRemove, role: .destructive) {
|
||||
context.send(viewAction: .removeImage)
|
||||
@@ -134,32 +136,9 @@ struct CreateRoomScreen: View {
|
||||
} header: {
|
||||
Text(L10n.screenCreateRoomTopicLabel)
|
||||
.compoundListSectionHeader()
|
||||
} footer: {
|
||||
if !context.viewState.selectedUsers.isEmpty {
|
||||
selectedUsersSection
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@State private var frame: CGRect = .zero
|
||||
@ScaledMetric private var invitedUserCellWidth: CGFloat = 72
|
||||
|
||||
private var selectedUsersSection: some View {
|
||||
ScrollView(.horizontal, showsIndicators: false) {
|
||||
LazyHStack(spacing: 16) {
|
||||
ForEach(context.viewState.selectedUsers, id: \.userID) { user in
|
||||
InviteUsersScreenSelectedItem(user: user, mediaProvider: context.mediaProvider) {
|
||||
context.send(viewAction: .deselectUser(user))
|
||||
}
|
||||
.frame(width: invitedUserCellWidth)
|
||||
}
|
||||
}
|
||||
.padding(.horizontal, ListRowPadding.horizontal)
|
||||
.padding(.vertical, 22)
|
||||
}
|
||||
.frame(width: frame.width)
|
||||
}
|
||||
|
||||
private var securitySection: some View {
|
||||
Section {
|
||||
ListRow(label: .default(title: L10n.screenCreateRoomPrivateOptionTitle,
|
||||
@@ -223,6 +202,7 @@ struct CreateRoomScreen: View {
|
||||
context.send(viewAction: .createRoom)
|
||||
}
|
||||
.disabled(!context.viewState.canCreateRoom)
|
||||
.accessibilityIdentifier(A11yIdentifiers.createRoomScreen.create)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -232,65 +212,43 @@ struct CreateRoomScreen: View {
|
||||
struct CreateRoom_Previews: PreviewProvider, TestablePreview {
|
||||
static let viewModel = {
|
||||
let userSession = UserSessionMock(.init(clientProxy: ClientProxyMock(.init(userID: "@userid:example.com"))))
|
||||
let parameters = CreateRoomFlowParameters()
|
||||
let selectedUsers: [UserProfileProxy] = [.mockAlice, .mockBob, .mockCharlie]
|
||||
|
||||
return CreateRoomViewModel(userSession: userSession,
|
||||
createRoomParameters: .init(parameters),
|
||||
selectedUsers: .init(selectedUsers),
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: UserIndicatorControllerMock(),
|
||||
appSettings: ServiceLocator.shared.settings)
|
||||
}()
|
||||
|
||||
static let emtpyViewModel = {
|
||||
let userSession = UserSessionMock(.init(clientProxy: ClientProxyMock(.init(userID: "@userid:example.com"))))
|
||||
let parameters = CreateRoomFlowParameters()
|
||||
return CreateRoomViewModel(userSession: userSession,
|
||||
createRoomParameters: .init(parameters),
|
||||
selectedUsers: .init([]),
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: UserIndicatorControllerMock(),
|
||||
appSettings: ServiceLocator.shared.settings)
|
||||
return CreateRoomScreenViewModel(userSession: userSession,
|
||||
initialParameters: .init(),
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: UserIndicatorControllerMock(),
|
||||
appSettings: ServiceLocator.shared.settings)
|
||||
}()
|
||||
|
||||
static let publicRoomViewModel = {
|
||||
let userSession = UserSessionMock(.init(clientProxy: ClientProxyMock(.init(userIDServerName: "example.org", userID: "@userid:example.com"))))
|
||||
let parameters = CreateRoomFlowParameters(isRoomPrivate: false)
|
||||
let selectedUsers: [UserProfileProxy] = [.mockAlice, .mockBob, .mockCharlie]
|
||||
ServiceLocator.shared.settings.knockingEnabled = true
|
||||
return CreateRoomViewModel(userSession: userSession,
|
||||
createRoomParameters: .init(parameters),
|
||||
selectedUsers: .init([]),
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: UserIndicatorControllerMock(),
|
||||
appSettings: ServiceLocator.shared.settings)
|
||||
return CreateRoomScreenViewModel(userSession: userSession,
|
||||
initialParameters: .init(isRoomPrivate: false),
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: UserIndicatorControllerMock(),
|
||||
appSettings: ServiceLocator.shared.settings)
|
||||
}()
|
||||
|
||||
static let publicRoomInvalidAliasViewModel = {
|
||||
let userSession = UserSessionMock(.init(clientProxy: ClientProxyMock(.init(userIDServerName: "example.org", userID: "@userid:example.com"))))
|
||||
let parameters = CreateRoomFlowParameters(isRoomPrivate: false, aliasLocalPart: "#:")
|
||||
ServiceLocator.shared.settings.knockingEnabled = true
|
||||
return CreateRoomViewModel(userSession: userSession,
|
||||
createRoomParameters: .init(parameters),
|
||||
selectedUsers: .init([]),
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: UserIndicatorControllerMock(),
|
||||
appSettings: ServiceLocator.shared.settings)
|
||||
return CreateRoomScreenViewModel(userSession: userSession,
|
||||
initialParameters: .init(isRoomPrivate: false, aliasLocalPart: "#:"),
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: UserIndicatorControllerMock(),
|
||||
appSettings: ServiceLocator.shared.settings)
|
||||
}()
|
||||
|
||||
static let publicRoomExistingAliasViewModel = {
|
||||
let clientProxy = ClientProxyMock(.init(userIDServerName: "example.org", userID: "@userid:example.com"))
|
||||
clientProxy.isAliasAvailableReturnValue = .success(false)
|
||||
let userSession = UserSessionMock(.init(clientProxy: clientProxy))
|
||||
let parameters = CreateRoomFlowParameters(isRoomPrivate: false, aliasLocalPart: "existing")
|
||||
ServiceLocator.shared.settings.knockingEnabled = true
|
||||
return CreateRoomViewModel(userSession: userSession,
|
||||
createRoomParameters: .init(parameters),
|
||||
selectedUsers: .init([]),
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: UserIndicatorControllerMock(),
|
||||
appSettings: ServiceLocator.shared.settings)
|
||||
return CreateRoomScreenViewModel(userSession: userSession,
|
||||
initialParameters: .init(isRoomPrivate: false, aliasLocalPart: "existing"),
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: UserIndicatorControllerMock(),
|
||||
appSettings: ServiceLocator.shared.settings)
|
||||
}()
|
||||
|
||||
static var previews: some View {
|
||||
@@ -298,27 +256,22 @@ struct CreateRoom_Previews: PreviewProvider, TestablePreview {
|
||||
CreateRoomScreen(context: viewModel.context)
|
||||
}
|
||||
.previewDisplayName("Create Room")
|
||||
NavigationStack {
|
||||
CreateRoomScreen(context: emtpyViewModel.context)
|
||||
}
|
||||
.previewDisplayName("Create Room without users")
|
||||
|
||||
NavigationStack {
|
||||
CreateRoomScreen(context: publicRoomViewModel.context)
|
||||
}
|
||||
.previewDisplayName("Create Public Room")
|
||||
|
||||
NavigationStack {
|
||||
CreateRoomScreen(context: publicRoomInvalidAliasViewModel.context)
|
||||
}
|
||||
.snapshotPreferences(expect: publicRoomExistingAliasViewModel.context.$viewState.map { state in
|
||||
!state.aliasErrors.isEmpty
|
||||
})
|
||||
.snapshotPreferences(expect: publicRoomInvalidAliasViewModel.context.$viewState.map { !$0.aliasErrors.isEmpty })
|
||||
.previewDisplayName("Create Public Room, invalid alias")
|
||||
|
||||
NavigationStack {
|
||||
CreateRoomScreen(context: publicRoomExistingAliasViewModel.context)
|
||||
}
|
||||
.snapshotPreferences(expect: publicRoomExistingAliasViewModel.context.$viewState.map { state in
|
||||
!state.aliasErrors.isEmpty
|
||||
})
|
||||
.snapshotPreferences(expect: publicRoomExistingAliasViewModel.context.$viewState.map { !$0.aliasErrors.isEmpty })
|
||||
.previewDisplayName("Create Public Room, existing alias")
|
||||
}
|
||||
}
|
||||
@@ -11,8 +11,8 @@ import SwiftUI
|
||||
|
||||
struct InviteUsersScreenCoordinatorParameters {
|
||||
let userSession: UserSessionProtocol
|
||||
let selectedUsers: CurrentValuePublisher<[UserProfileProxy], Never>?
|
||||
let roomType: InviteUsersScreenRoomType
|
||||
let roomProxy: JoinedRoomProxyProtocol
|
||||
let isSkippable: Bool
|
||||
let userDiscoveryService: UserDiscoveryServiceProtocol
|
||||
let userIndicatorController: UserIndicatorControllerProtocol
|
||||
let appSettings: AppSettings
|
||||
@@ -20,7 +20,6 @@ struct InviteUsersScreenCoordinatorParameters {
|
||||
|
||||
enum InviteUsersScreenCoordinatorAction {
|
||||
case dismiss
|
||||
case proceed(selectedUsers: [UserProfileProxy])
|
||||
}
|
||||
|
||||
final class InviteUsersScreenCoordinator: CoordinatorProtocol {
|
||||
@@ -34,8 +33,8 @@ final class InviteUsersScreenCoordinator: CoordinatorProtocol {
|
||||
|
||||
init(parameters: InviteUsersScreenCoordinatorParameters) {
|
||||
viewModel = InviteUsersScreenViewModel(userSession: parameters.userSession,
|
||||
selectedUsers: parameters.selectedUsers,
|
||||
roomType: parameters.roomType,
|
||||
roomProxy: parameters.roomProxy,
|
||||
isSkippable: parameters.isSkippable,
|
||||
userDiscoveryService: parameters.userDiscoveryService,
|
||||
userIndicatorController: parameters.userIndicatorController,
|
||||
appSettings: parameters.appSettings)
|
||||
@@ -47,8 +46,6 @@ final class InviteUsersScreenCoordinator: CoordinatorProtocol {
|
||||
switch action {
|
||||
case .dismiss:
|
||||
actionsSubject.send(.dismiss)
|
||||
case .proceed(let selectedUsers):
|
||||
actionsSubject.send(.proceed(selectedUsers: selectedUsers))
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
@@ -16,7 +16,6 @@ enum InviteUsersScreenErrorType: Error {
|
||||
|
||||
enum InviteUsersScreenViewModelAction {
|
||||
case dismiss
|
||||
case proceed(selectedUsers: [UserProfileProxy])
|
||||
}
|
||||
|
||||
enum InviteUsersScreenRoomType {
|
||||
@@ -53,18 +52,18 @@ struct InviteUsersScreenViewState: BindableState {
|
||||
membershipState[user.userID]
|
||||
}
|
||||
|
||||
let isCreatingRoom: Bool
|
||||
let isSkippable: Bool
|
||||
|
||||
var actionText: String {
|
||||
if isCreatingRoom {
|
||||
return selectedUsers.isEmpty ? L10n.actionSkip : L10n.actionNext
|
||||
if isSkippable, selectedUsers.isEmpty {
|
||||
L10n.actionSkip
|
||||
} else {
|
||||
return L10n.actionInvite
|
||||
L10n.actionInvite
|
||||
}
|
||||
}
|
||||
|
||||
var isActionDisabled: Bool {
|
||||
isCreatingRoom ? false : selectedUsers.isEmpty
|
||||
isSkippable ? false : selectedUsers.isEmpty
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ import SwiftUI
|
||||
typealias InviteUsersScreenViewModelType = StateStoreViewModel<InviteUsersScreenViewState, InviteUsersScreenViewAction>
|
||||
|
||||
class InviteUsersScreenViewModel: InviteUsersScreenViewModelType, InviteUsersScreenViewModelProtocol {
|
||||
private let roomType: InviteUsersScreenRoomType
|
||||
private let roomProxy: JoinedRoomProxyProtocol
|
||||
private let userDiscoveryService: UserDiscoveryServiceProtocol
|
||||
private let userIndicatorController: UserIndicatorControllerProtocol
|
||||
private let appSettings: AppSettings
|
||||
@@ -25,22 +25,19 @@ class InviteUsersScreenViewModel: InviteUsersScreenViewModelType, InviteUsersScr
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
private let selectedUsers: CurrentValuePublisher<[UserProfileProxy], Never>?
|
||||
|
||||
init(userSession: UserSessionProtocol,
|
||||
selectedUsers: CurrentValuePublisher<[UserProfileProxy], Never>?,
|
||||
roomType: InviteUsersScreenRoomType,
|
||||
roomProxy: JoinedRoomProxyProtocol,
|
||||
isSkippable: Bool,
|
||||
userDiscoveryService: UserDiscoveryServiceProtocol,
|
||||
userIndicatorController: UserIndicatorControllerProtocol,
|
||||
appSettings: AppSettings) {
|
||||
self.roomType = roomType
|
||||
self.roomProxy = roomProxy
|
||||
self.userDiscoveryService = userDiscoveryService
|
||||
self.userIndicatorController = userIndicatorController
|
||||
self.appSettings = appSettings
|
||||
self.selectedUsers = selectedUsers
|
||||
|
||||
super.init(initialViewState: InviteUsersScreenViewState(selectedUsers: selectedUsers?.value ?? [],
|
||||
isCreatingRoom: roomType.isCreatingRoom),
|
||||
super.init(initialViewState: InviteUsersScreenViewState(selectedUsers: [],
|
||||
isSkippable: isSkippable),
|
||||
mediaProvider: userSession.mediaProvider)
|
||||
|
||||
setupSubscriptions()
|
||||
@@ -62,12 +59,7 @@ class InviteUsersScreenViewModel: InviteUsersScreenViewModelType, InviteUsersScr
|
||||
case .cancel:
|
||||
actionsSubject.send(.dismiss)
|
||||
case .proceed:
|
||||
switch roomType {
|
||||
case .draft:
|
||||
actionsSubject.send(.proceed(selectedUsers: state.selectedUsers))
|
||||
case .room(let roomProxy):
|
||||
inviteUsers(state.selectedUsers.map(\.userID), roomProxy: roomProxy)
|
||||
}
|
||||
inviteUsers(state.selectedUsers.map(\.userID), roomProxy: roomProxy)
|
||||
case .toggleUser(let user):
|
||||
toggleUser(user)
|
||||
}
|
||||
@@ -78,7 +70,7 @@ class InviteUsersScreenViewModel: InviteUsersScreenViewModelType, InviteUsersScr
|
||||
private func toggleUser(_ user: UserProfileProxy) {
|
||||
if state.selectedUsers.contains(user) {
|
||||
state.scrollToLastID = nil
|
||||
state.selectedUsers.removeAll(where: { $0.userID == user.userID })
|
||||
state.selectedUsers.removeAll { $0.userID == user.userID }
|
||||
} else {
|
||||
state.scrollToLastID = user.userID
|
||||
state.selectedUsers.append(user)
|
||||
@@ -87,15 +79,14 @@ class InviteUsersScreenViewModel: InviteUsersScreenViewModelType, InviteUsersScr
|
||||
|
||||
private func inviteUsers(_ users: [String], roomProxy: JoinedRoomProxyProtocol) {
|
||||
if appSettings.enableKeyShareOnInvite {
|
||||
showLoader(title: L10n.screenRoomDetailsInvitePeoplePreparing,
|
||||
message: L10n.screenRoomDetailsInvitePeopleDontClose)
|
||||
showLoadingIndicator(title: L10n.screenRoomDetailsInvitePeoplePreparing, message: L10n.screenRoomDetailsInvitePeopleDontClose)
|
||||
} else {
|
||||
showLoader()
|
||||
showLoadingIndicator()
|
||||
}
|
||||
|
||||
Task {
|
||||
defer {
|
||||
hideLoader()
|
||||
hideLoadingIndicator()
|
||||
actionsSubject.send(.dismiss)
|
||||
}
|
||||
|
||||
@@ -122,7 +113,7 @@ class InviteUsersScreenViewModel: InviteUsersScreenViewModelType, InviteUsersScr
|
||||
}
|
||||
|
||||
private func buildMembershipStateIfNeeded(members: [RoomMemberProxyProtocol]) {
|
||||
showLoader()
|
||||
showLoadingIndicator()
|
||||
|
||||
Task.detached { [members] in
|
||||
// accessing RoomMember's properties is very slow. We need to do it in a background thread.
|
||||
@@ -133,7 +124,7 @@ class InviteUsersScreenViewModel: InviteUsersScreenViewModelType, InviteUsersScr
|
||||
|
||||
Task { @MainActor in
|
||||
self.state.membershipState = membershipState
|
||||
self.hideLoader()
|
||||
self.hideLoadingIndicator()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -150,25 +141,13 @@ class InviteUsersScreenViewModel: InviteUsersScreenViewModelType, InviteUsersScr
|
||||
self?.fetchUsers()
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
if let selectedUsers {
|
||||
selectedUsers
|
||||
.sink { [weak self] users in
|
||||
self?.state.selectedUsers = users
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
}
|
||||
|
||||
private func fetchMembersIfNeeded() {
|
||||
guard case let .room(roomProxy) = roomType else {
|
||||
return
|
||||
}
|
||||
|
||||
Task {
|
||||
showLoader()
|
||||
showLoadingIndicator()
|
||||
await roomProxy.updateMembers()
|
||||
hideLoader()
|
||||
hideLoadingIndicator()
|
||||
}
|
||||
|
||||
roomProxy.membersPublisher
|
||||
@@ -211,8 +190,8 @@ class InviteUsersScreenViewModel: InviteUsersScreenViewModelType, InviteUsersScr
|
||||
|
||||
private let userIndicatorID = UUID().uuidString
|
||||
|
||||
private func showLoader(title: String = L10n.commonLoading,
|
||||
message: String? = nil) {
|
||||
private func showLoadingIndicator(title: String = L10n.commonLoading,
|
||||
message: String? = nil) {
|
||||
userIndicatorController.submitIndicator(UserIndicator(id: userIndicatorID,
|
||||
type: .modal,
|
||||
title: title,
|
||||
@@ -221,18 +200,7 @@ class InviteUsersScreenViewModel: InviteUsersScreenViewModelType, InviteUsersScr
|
||||
delay: .milliseconds(200))
|
||||
}
|
||||
|
||||
private func hideLoader() {
|
||||
private func hideLoadingIndicator() {
|
||||
userIndicatorController.retractIndicatorWithId(userIndicatorID)
|
||||
}
|
||||
}
|
||||
|
||||
private extension InviteUsersScreenRoomType {
|
||||
var isCreatingRoom: Bool {
|
||||
switch self {
|
||||
case .draft:
|
||||
return true
|
||||
case .room:
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ struct InviteUsersScreen: View {
|
||||
accessibilityFocusOnStart: true)
|
||||
.compoundSearchField()
|
||||
.alert(item: $context.alertInfo)
|
||||
.navigationBarBackButtonHidden(context.viewState.isSkippable)
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
@@ -126,7 +127,7 @@ struct InviteUsersScreen: View {
|
||||
|
||||
@ToolbarContentBuilder
|
||||
private var toolbar: some ToolbarContent {
|
||||
if !context.viewState.isCreatingRoom {
|
||||
if !context.viewState.isSkippable {
|
||||
ToolbarItem(placement: .cancellationAction) {
|
||||
Button(L10n.actionCancel) {
|
||||
context.send(viewAction: .cancel)
|
||||
@@ -155,8 +156,8 @@ struct InviteUsersScreen_Previews: PreviewProvider, TestablePreview {
|
||||
let userDiscoveryService = UserDiscoveryServiceMock()
|
||||
userDiscoveryService.searchProfilesWithReturnValue = .success([.mockAlice])
|
||||
return InviteUsersScreenViewModel(userSession: UserSessionMock(.init()),
|
||||
selectedUsers: .init([]),
|
||||
roomType: .draft,
|
||||
roomProxy: JoinedRoomProxyMock(.init()),
|
||||
isSkippable: true,
|
||||
userDiscoveryService: userDiscoveryService,
|
||||
userIndicatorController: UserIndicatorControllerMock(),
|
||||
appSettings: ServiceLocator.shared.settings)
|
||||
|
||||
@@ -10,38 +10,26 @@ import Combine
|
||||
import SwiftUI
|
||||
|
||||
struct StartChatScreenCoordinatorParameters {
|
||||
let orientationManager: OrientationManagerProtocol
|
||||
let userSession: UserSessionProtocol
|
||||
let userIndicatorController: UserIndicatorControllerProtocol
|
||||
weak var navigationStackCoordinator: NavigationStackCoordinator?
|
||||
let userDiscoveryService: UserDiscoveryServiceProtocol
|
||||
let mediaUploadingPreprocessor: MediaUploadingPreprocessor
|
||||
let userIndicatorController: UserIndicatorControllerProtocol
|
||||
let appSettings: AppSettings
|
||||
let analytics: AnalyticsService
|
||||
}
|
||||
|
||||
enum StartChatScreenCoordinatorAction {
|
||||
case close
|
||||
case openRoom(withIdentifier: String)
|
||||
case createRoom
|
||||
case openRoom(roomID: String)
|
||||
case openRoomDirectorySearch
|
||||
}
|
||||
|
||||
final class StartChatScreenCoordinator: CoordinatorProtocol {
|
||||
private let parameters: StartChatScreenCoordinatorParameters
|
||||
private var viewModel: StartChatScreenViewModelProtocol
|
||||
private let actionsSubject: PassthroughSubject<StartChatScreenCoordinatorAction, Never> = .init()
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
private var createRoomParameters = CurrentValueSubject<CreateRoomFlowParameters, Never>(.init())
|
||||
private var createRoomParametersPublisher: CurrentValuePublisher<CreateRoomFlowParameters, Never> {
|
||||
createRoomParameters.asCurrentValuePublisher()
|
||||
}
|
||||
|
||||
private let selectedUsers = CurrentValueSubject<[UserProfileProxy], Never>([])
|
||||
private var selectedUsersPublisher: CurrentValuePublisher<[UserProfileProxy], Never> {
|
||||
selectedUsers.asCurrentValuePublisher()
|
||||
}
|
||||
|
||||
private let actionsSubject: PassthroughSubject<StartChatScreenCoordinatorAction, Never> = .init()
|
||||
var actions: AnyPublisher<StartChatScreenCoordinatorAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
@@ -63,10 +51,9 @@ final class StartChatScreenCoordinator: CoordinatorProtocol {
|
||||
case .close:
|
||||
actionsSubject.send(.close)
|
||||
case .createRoom:
|
||||
// before creating a room we select the users we would like to invite in that room
|
||||
presentInviteUsersScreen()
|
||||
case .showRoom(let identifier):
|
||||
actionsSubject.send(.openRoom(withIdentifier: identifier))
|
||||
actionsSubject.send(.createRoom)
|
||||
case .showRoom(let roomID):
|
||||
actionsSubject.send(.openRoom(roomID: roomID))
|
||||
case .openRoomDirectorySearch:
|
||||
actionsSubject.send(.openRoomDirectorySearch)
|
||||
}
|
||||
@@ -79,126 +66,4 @@ final class StartChatScreenCoordinator: CoordinatorProtocol {
|
||||
func toPresentable() -> AnyView {
|
||||
AnyView(StartChatScreen(context: viewModel.context))
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func presentInviteUsersScreen() {
|
||||
let inviteParameters = InviteUsersScreenCoordinatorParameters(userSession: parameters.userSession,
|
||||
selectedUsers: selectedUsersPublisher,
|
||||
roomType: .draft,
|
||||
userDiscoveryService: parameters.userDiscoveryService,
|
||||
userIndicatorController: parameters.userIndicatorController,
|
||||
appSettings: parameters.appSettings)
|
||||
let coordinator = InviteUsersScreenCoordinator(parameters: inviteParameters)
|
||||
coordinator.actions.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
switch action {
|
||||
case .dismiss:
|
||||
fatalError("Not shown in this flow.")
|
||||
case .proceed(let selectedUsers):
|
||||
self.selectedUsers.send(selectedUsers)
|
||||
openCreateRoomScreen()
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
parameters.navigationStackCoordinator?.push(coordinator) { [weak self] in
|
||||
self?.createRoomParameters.send(.init())
|
||||
self?.selectedUsers.send([])
|
||||
}
|
||||
}
|
||||
|
||||
private func openCreateRoomScreen() {
|
||||
let createParameters = CreateRoomCoordinatorParameters(userSession: parameters.userSession,
|
||||
userIndicatorController: parameters.userIndicatorController,
|
||||
createRoomParameters: createRoomParametersPublisher,
|
||||
selectedUsers: selectedUsers.value,
|
||||
appSettings: parameters.appSettings,
|
||||
analytics: parameters.analytics)
|
||||
let coordinator = CreateRoomCoordinator(parameters: createParameters)
|
||||
coordinator.actions.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
switch action {
|
||||
case .updateSelectedUsers(let users):
|
||||
self.selectedUsers.send(users)
|
||||
case .updateDetails(let details):
|
||||
self.createRoomParameters.send(details)
|
||||
case .openRoom(let identifier):
|
||||
self.actionsSubject.send(.openRoom(withIdentifier: identifier))
|
||||
case .displayMediaPickerWithMode(let mode):
|
||||
self.displayMediaPickerWithMode(mode)
|
||||
case .removeImage:
|
||||
var parameters = self.createRoomParameters.value
|
||||
parameters.avatarImageMedia = nil
|
||||
self.createRoomParameters.send(parameters)
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
parameters.navigationStackCoordinator?.push(coordinator)
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func displayMediaPickerWithMode(_ mode: MediaPickerScreenMode) {
|
||||
let stackCoordinator = NavigationStackCoordinator()
|
||||
|
||||
let mediaPickerCoordinator = MediaPickerScreenCoordinator(mode: mode,
|
||||
userIndicatorController: parameters.userIndicatorController,
|
||||
orientationManager: parameters.orientationManager) { [weak self] action in
|
||||
guard let self else { return }
|
||||
switch action {
|
||||
case .cancel:
|
||||
parameters.navigationStackCoordinator?.setSheetCoordinator(nil)
|
||||
case .selectedMediaAtURLs(let urls):
|
||||
guard urls.count == 1,
|
||||
let url = urls.first else {
|
||||
fatalError("Received an invalid number of URLs")
|
||||
}
|
||||
|
||||
processAvatar(from: url)
|
||||
}
|
||||
}
|
||||
|
||||
stackCoordinator.setRootCoordinator(mediaPickerCoordinator)
|
||||
|
||||
parameters.navigationStackCoordinator?.setSheetCoordinator(stackCoordinator)
|
||||
}
|
||||
|
||||
private func processAvatar(from url: URL) {
|
||||
parameters.navigationStackCoordinator?.setSheetCoordinator(nil)
|
||||
showLoadingIndicator()
|
||||
Task { [weak self] in
|
||||
guard let self else { return }
|
||||
do {
|
||||
guard case let .success(maxUploadSize) = await parameters.userSession.clientProxy.maxMediaUploadSize else {
|
||||
MXLog.error("Failed to get max upload size")
|
||||
parameters.userIndicatorController.alertInfo = AlertInfo(id: .init())
|
||||
return
|
||||
}
|
||||
let media = try await parameters.mediaUploadingPreprocessor.processMedia(at: url, maxUploadSize: maxUploadSize).get()
|
||||
var parameters = createRoomParameters.value
|
||||
parameters.avatarImageMedia = media
|
||||
createRoomParameters.send(parameters)
|
||||
} catch {
|
||||
parameters.userIndicatorController.alertInfo = AlertInfo(id: .init())
|
||||
}
|
||||
hideLoadingIndicator()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Loading indicator
|
||||
|
||||
private static let loadingIndicatorIdentifier = "\(StartChatScreenCoordinator.self)-Loading"
|
||||
|
||||
private func showLoadingIndicator() {
|
||||
parameters.userIndicatorController.submitIndicator(UserIndicator(id: Self.loadingIndicatorIdentifier,
|
||||
type: .modal,
|
||||
title: L10n.commonLoading,
|
||||
persistent: true))
|
||||
}
|
||||
|
||||
private func hideLoadingIndicator() {
|
||||
parameters.userIndicatorController.retractIndicatorWithId(Self.loadingIndicatorIdentifier)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ enum StartChatScreenErrorType: Error {
|
||||
enum StartChatScreenViewModelAction: Equatable {
|
||||
case close
|
||||
case createRoom
|
||||
case showRoom(withIdentifier: String)
|
||||
case showRoom(roomID: String)
|
||||
case openRoomDirectorySearch
|
||||
}
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ class StartChatScreenViewModel: StartChatScreenViewModelType, StartChatScreenVie
|
||||
switch currentDirectRoom {
|
||||
case .success(.some(let roomId)):
|
||||
hideLoadingIndicator()
|
||||
actionsSubject.send(.showRoom(withIdentifier: roomId))
|
||||
actionsSubject.send(.showRoom(roomID: roomId))
|
||||
case .success:
|
||||
hideLoadingIndicator()
|
||||
state.bindings.selectedUserToInvite = user
|
||||
@@ -190,7 +190,7 @@ class StartChatScreenViewModel: StartChatScreenViewModelType, StartChatScreenVie
|
||||
switch await userSession.clientProxy.createDirectRoom(with: user.userID, expectedRoomName: user.displayName) {
|
||||
case .success(let roomId):
|
||||
analytics.trackCreatedRoom(isDM: true)
|
||||
actionsSubject.send(.showRoom(withIdentifier: roomId))
|
||||
actionsSubject.send(.showRoom(roomID: roomId))
|
||||
case .failure:
|
||||
displayError()
|
||||
}
|
||||
@@ -205,7 +205,7 @@ class StartChatScreenViewModel: StartChatScreenViewModelType, StartChatScreenVie
|
||||
private func joinRoomByAddress() {
|
||||
if case let .addressFound(lastTestedAddress, roomID) = internalRoomAddressState,
|
||||
lastTestedAddress == state.bindings.roomAddress {
|
||||
actionsSubject.send(.showRoom(withIdentifier: roomID))
|
||||
actionsSubject.send(.showRoom(roomID: roomID))
|
||||
} else if let resolveAliasTask {
|
||||
// If the task is still running we wait for it to complete and we check the state again
|
||||
showLoadingIndicator(delay: .milliseconds(250))
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
//
|
||||
// Copyright 2025 Element Creations Ltd.
|
||||
// Copyright 2023-2025 New Vector Ltd.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// This parameters are only used in the create room flow for having persisted informations between screens
|
||||
struct CreateRoomFlowParameters {
|
||||
var name = ""
|
||||
var topic = ""
|
||||
var isRoomPrivate = true
|
||||
var isKnockingOnly = false
|
||||
var avatarImageMedia: MediaInfo?
|
||||
var aliasLocalPart: String?
|
||||
}
|
||||
@@ -648,67 +648,48 @@ class MockScreen: Identifiable {
|
||||
retainedState.append(coordinator)
|
||||
coordinator.start()
|
||||
return navigationStackCoordinator
|
||||
case .startChat:
|
||||
let navigationStackCoordinator = NavigationStackCoordinator()
|
||||
let userDiscoveryMock = UserDiscoveryServiceMock()
|
||||
userDiscoveryMock.searchProfilesWithReturnValue = .success([])
|
||||
let userSession = UserSessionMock(.init(clientProxy: ClientProxyMock(.init(userID: "@mock:client.com"))))
|
||||
let parameters: StartChatScreenCoordinatorParameters = .init(orientationManager: OrientationManagerMock(),
|
||||
userSession: userSession,
|
||||
userIndicatorController: UserIndicatorControllerMock(),
|
||||
navigationStackCoordinator: navigationStackCoordinator,
|
||||
userDiscoveryService: userDiscoveryMock,
|
||||
mediaUploadingPreprocessor: MediaUploadingPreprocessor(appSettings: ServiceLocator.shared.settings),
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analytics: ServiceLocator.shared.analytics)
|
||||
let coordinator = StartChatScreenCoordinator(parameters: parameters)
|
||||
navigationStackCoordinator.setRootCoordinator(coordinator)
|
||||
return navigationStackCoordinator
|
||||
case .startChatWithSearchResults:
|
||||
let navigationStackCoordinator = NavigationStackCoordinator()
|
||||
case .startChatFlow:
|
||||
let clientProxy = ClientProxyMock(.init(userID: "@mock:client.com"))
|
||||
let userDiscoveryMock = UserDiscoveryServiceMock()
|
||||
userDiscoveryMock.searchProfilesWithReturnValue = .success([.mockBob, .mockBobby])
|
||||
let userSession = UserSessionMock(.init(clientProxy: clientProxy))
|
||||
let coordinator = StartChatScreenCoordinator(parameters: .init(orientationManager: OrientationManagerMock(),
|
||||
userSession: userSession,
|
||||
userIndicatorController: UserIndicatorControllerMock(),
|
||||
navigationStackCoordinator: navigationStackCoordinator,
|
||||
userDiscoveryService: userDiscoveryMock,
|
||||
mediaUploadingPreprocessor: MediaUploadingPreprocessor(appSettings: ServiceLocator.shared.settings),
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analytics: ServiceLocator.shared.analytics))
|
||||
navigationStackCoordinator.setRootCoordinator(coordinator)
|
||||
return navigationStackCoordinator
|
||||
case .createRoom:
|
||||
clientProxy.createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLAliasLocalPartReturnValue = .success("!new-room:client.com")
|
||||
clientProxy.roomForIdentifierClosure = { roomID in .joined(JoinedRoomProxyMock(.init(id: roomID, members: []))) }
|
||||
|
||||
let userDiscoveryService = UserDiscoveryServiceMock()
|
||||
userDiscoveryService.searchProfilesWithReturnValue = .success([.mockBob, .mockBobby])
|
||||
|
||||
let navigationStackCoordinator = NavigationStackCoordinator()
|
||||
let clientProxy = ClientProxyMock(.init(userID: "@mock:client.com"))
|
||||
let mockUserSession = UserSessionMock(.init(clientProxy: clientProxy))
|
||||
let createRoomParameters = CreateRoomFlowParameters()
|
||||
let selectedUsers: [UserProfileProxy] = [.mockAlice, .mockBob, .mockCharlie]
|
||||
let parameters = CreateRoomCoordinatorParameters(userSession: mockUserSession,
|
||||
userIndicatorController: UserIndicatorControllerMock(),
|
||||
createRoomParameters: .init(createRoomParameters),
|
||||
selectedUsers: .init(selectedUsers),
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analytics: ServiceLocator.shared.analytics)
|
||||
let coordinator = CreateRoomCoordinator(parameters: parameters)
|
||||
navigationStackCoordinator.setRootCoordinator(coordinator)
|
||||
return navigationStackCoordinator
|
||||
case .createRoomNoUsers:
|
||||
let navigationStackCoordinator = NavigationStackCoordinator()
|
||||
let clientProxy = ClientProxyMock(.init(userID: "@mock:client.com"))
|
||||
let mockUserSession = UserSessionMock(.init(clientProxy: clientProxy))
|
||||
let createRoomParameters = CreateRoomFlowParameters()
|
||||
let parameters = CreateRoomCoordinatorParameters(userSession: mockUserSession,
|
||||
userIndicatorController: UserIndicatorControllerMock(),
|
||||
createRoomParameters: .init(createRoomParameters),
|
||||
selectedUsers: .init([]),
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analytics: ServiceLocator.shared.analytics)
|
||||
let coordinator = CreateRoomCoordinator(parameters: parameters)
|
||||
navigationStackCoordinator.setRootCoordinator(coordinator)
|
||||
return navigationStackCoordinator
|
||||
let flowCoordinator = StartChatFlowCoordinator(userDiscoveryService: userDiscoveryService,
|
||||
navigationStackCoordinator: navigationStackCoordinator,
|
||||
flowParameters: CommonFlowParameters(userSession: UserSessionMock(.init(clientProxy: clientProxy)),
|
||||
bugReportService: BugReportServiceMock(.init()),
|
||||
elementCallService: ElementCallServiceMock(.init()),
|
||||
timelineControllerFactory: TimelineControllerFactoryMock(.init()),
|
||||
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings),
|
||||
linkMetadataProvider: LinkMetadataProvider(),
|
||||
appMediator: AppMediatorMock.default,
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
appHooks: AppHooks(),
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: UserIndicatorControllerMock(),
|
||||
notificationManager: NotificationManagerMock(),
|
||||
stateMachineFactory: StateMachineFactory()))
|
||||
flowCoordinator.actionsPublisher
|
||||
.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
switch action {
|
||||
case .finished(let roomID):
|
||||
navigationRootCoordinator.setSheetCoordinator(nil)
|
||||
case .showRoomDirectory:
|
||||
break // The test doesn't cover this.
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
retainedState.append(flowCoordinator)
|
||||
flowCoordinator.start()
|
||||
|
||||
// Use a sheet on top the the placeholder so we can test the dismissal.
|
||||
navigationRootCoordinator.setSheetCoordinator(navigationStackCoordinator)
|
||||
return PlaceholderScreenCoordinator(hideBrandChrome: false)
|
||||
case .createPoll:
|
||||
let navigationStackCoordinator = NavigationStackCoordinator()
|
||||
let coordinator = PollFormScreenCoordinator(parameters: .init(mode: .new,
|
||||
|
||||
@@ -22,8 +22,6 @@ enum UITestsScreenIdentifier: String {
|
||||
case multipleProvidersAuthenticationFlow
|
||||
case bugReport
|
||||
case createPoll
|
||||
case createRoom
|
||||
case createRoomNoUsers
|
||||
case encryptionSettings
|
||||
case encryptionSettingsOutOfSync
|
||||
case encryptionReset
|
||||
@@ -44,8 +42,7 @@ enum UITestsScreenIdentifier: String {
|
||||
case roomWithUndisclosedPolls
|
||||
case serverSelection
|
||||
case sessionVerification
|
||||
case startChat
|
||||
case startChatWithSearchResults
|
||||
case startChatFlow
|
||||
case userSessionScreen
|
||||
case userSessionScreenReply
|
||||
case userSessionSpacesFlow
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:e1624d4be85be4801b7a407ed8d09c815d98b1eb4422d4fc8095795e3a73cd17
|
||||
size 144918
|
||||
oid sha256:27b070d92b8846ab416aeb3964d3b698b15aae6a6a5e03f103546bc00b57a291
|
||||
size 144359
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:5d668d5fd1d22b0fc782dcf22c1d674fae87d61b289451887e48011ea2c288a9
|
||||
size 173173
|
||||
oid sha256:96418567c90c7e7a42b4a7129cc803ce0f50de55cb14021fe8c9b5042810c4e4
|
||||
size 171338
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:8e3e2883875feedec7a243f676918e3d6f4d05826702a8f2c2807fc5a0bc7bdf
|
||||
size 96282
|
||||
oid sha256:209bc72d50cf549621b1bb690349290aa9f12fd79501cc3089b1dc0a525495c2
|
||||
size 95602
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:fd0da3fb9239fe4f131b4a19057bd49bda67b72ba7caf17d8f86cb9f6a239fc0
|
||||
size 125320
|
||||
oid sha256:a9f24fc08ec1f408042f6311ddada15f01413f93806bba7742b417743c8cf011
|
||||
size 125251
|
||||
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:27b070d92b8846ab416aeb3964d3b698b15aae6a6a5e03f103546bc00b57a291
|
||||
size 144359
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:96418567c90c7e7a42b4a7129cc803ce0f50de55cb14021fe8c9b5042810c4e4
|
||||
size 171338
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:209bc72d50cf549621b1bb690349290aa9f12fd79501cc3089b1dc0a525495c2
|
||||
size 95602
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:a9f24fc08ec1f408042f6311ddada15f01413f93806bba7742b417743c8cf011
|
||||
size 125251
|
||||
@@ -1,33 +0,0 @@
|
||||
//
|
||||
// Copyright 2025 Element Creations Ltd.
|
||||
// Copyright 2022-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 XCTest
|
||||
|
||||
@MainActor
|
||||
class CreateRoomScreenUITests: XCTestCase {
|
||||
func testLanding() async throws {
|
||||
let app = Application.launch(.createRoom)
|
||||
try await app.assertScreenshot()
|
||||
}
|
||||
|
||||
func testLandingWithoutUsers() async throws {
|
||||
let app = Application.launch(.createRoomNoUsers)
|
||||
try await app.assertScreenshot()
|
||||
}
|
||||
|
||||
func testLongInputNameText() async throws {
|
||||
let app = Application.launch(.createRoom)
|
||||
|
||||
// typeText sometimes misses letters but it's faster than typing one letter at a time
|
||||
// repeat the same letter enough times to avoid that but also to work on iPads
|
||||
app.textFields[A11yIdentifiers.createRoomScreen.roomName].tap()
|
||||
app.textFields[A11yIdentifiers.createRoomScreen.roomName].typeText(.init(repeating: "x", count: 200))
|
||||
app.textFields[A11yIdentifiers.createRoomScreen.roomName].typeText("\n")
|
||||
try await app.assertScreenshot()
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
//
|
||||
// Copyright 2025 Element Creations Ltd.
|
||||
// Copyright 2022-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 XCTest
|
||||
|
||||
@MainActor
|
||||
class StartChatScreenUITests: XCTestCase {
|
||||
func testLanding() async throws {
|
||||
let app = Application.launch(.startChat)
|
||||
try await app.assertScreenshot()
|
||||
}
|
||||
|
||||
func testSearchWithNoResults() async throws {
|
||||
let app = Application.launch(.startChat)
|
||||
let searchField = app.searchFields.firstMatch
|
||||
searchField.clearAndTypeText("None\n", app: app)
|
||||
XCTAssert(app.staticTexts[A11yIdentifiers.startChatScreen.searchNoResults].waitForExistence(timeout: 1.0))
|
||||
try await app.assertScreenshot()
|
||||
}
|
||||
|
||||
func testSearchWithResults() async throws {
|
||||
let app = Application.launch(.startChatWithSearchResults)
|
||||
let searchField = app.searchFields.firstMatch
|
||||
searchField.clearAndTypeText("Bob\n", app: app)
|
||||
XCTAssertFalse(app.staticTexts[A11yIdentifiers.startChatScreen.searchNoResults].waitForExistence(timeout: 1.0))
|
||||
XCTAssertEqual(app.collectionViews.firstMatch.cells.count, 2)
|
||||
try await app.assertScreenshot()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
//
|
||||
// Copyright 2025 Element Creations Ltd.
|
||||
// Copyright 2022-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 XCTest
|
||||
|
||||
@MainActor
|
||||
class StartChatTests: XCTestCase {
|
||||
enum Step {
|
||||
static let startChat = 1
|
||||
static let startChatWithResults = 2
|
||||
static let createRoom = 3
|
||||
static let createRoomAvatarPicker = 4
|
||||
static let createRoomFilled = 5
|
||||
static let inviteUsers = 6
|
||||
static let inviteUsersWithResults = 7
|
||||
static let inviteUsersSelectedResults = 8
|
||||
static let dismissed = 99
|
||||
}
|
||||
|
||||
func testFlow() async throws {
|
||||
let app = Application.launch(.startChatFlow)
|
||||
try await app.assertScreenshot(step: Step.startChat)
|
||||
|
||||
let startChatSearchField = app.searchFields.firstMatch
|
||||
startChatSearchField.clearAndTypeText("Bob\n", app: app)
|
||||
XCTAssertFalse(app.staticTexts[A11yIdentifiers.startChatScreen.searchNoResults].waitForExistence(timeout: 1.0))
|
||||
XCTAssertEqual(app.collectionViews.firstMatch.cells.count, 2)
|
||||
try await app.assertScreenshot(step: Step.startChatWithResults)
|
||||
|
||||
startChatSearchField.clearAndTypeText("\n", app: app)
|
||||
app.buttons[A11yIdentifiers.startChatScreen.createRoom].firstMatch.tap()
|
||||
XCTAssertTrue(app.textFields[A11yIdentifiers.createRoomScreen.roomName].waitForExistence(timeout: 1.0))
|
||||
try await app.assertScreenshot(step: Step.createRoom)
|
||||
|
||||
app.buttons[A11yIdentifiers.createRoomScreen.roomAvatar].tap()
|
||||
app.popovers.buttons.element(boundBy: 2).tap() // There are 2 buttons with the accessibility identifier, so use the index to get the right one.
|
||||
let cancelButton = app.buttons["Cancel"]
|
||||
XCTAssertTrue(cancelButton.waitForExistence(timeout: 1.0))
|
||||
try await app.assertScreenshot(step: Step.createRoomAvatarPicker)
|
||||
cancelButton.tap()
|
||||
|
||||
// typeText sometimes misses letters but it's faster than typing one letter at a time
|
||||
// repeat the same letter enough times to avoid that but also to work on iPads
|
||||
app.textFields[A11yIdentifiers.createRoomScreen.roomName].tap()
|
||||
app.textFields[A11yIdentifiers.createRoomScreen.roomName].typeText(.init(repeating: "x", count: 200))
|
||||
app.textFields[A11yIdentifiers.createRoomScreen.roomName].typeText("\n")
|
||||
try await app.assertScreenshot(step: Step.createRoomFilled)
|
||||
|
||||
app.buttons[A11yIdentifiers.createRoomScreen.create].tap()
|
||||
XCTAssertTrue(app.buttons[A11yIdentifiers.inviteUsersScreen.proceed].waitForExistence(timeout: 1.0))
|
||||
try await app.assertScreenshot(step: Step.inviteUsers)
|
||||
|
||||
let inviteUsersSearchField = app.searchFields.firstMatch
|
||||
inviteUsersSearchField.clearAndTypeText("Bob\n", app: app)
|
||||
let cells = app.collectionViews.firstMatch.cells
|
||||
XCTAssertEqual(cells.count, 2)
|
||||
try await app.assertScreenshot(step: Step.inviteUsersWithResults)
|
||||
|
||||
cells.element(boundBy: 0).tap()
|
||||
// Selecting the first member has inserted the horizontal collection view above the list.
|
||||
// So to select the second member we now need to increment the index by 2 instead of 1.
|
||||
cells.element(boundBy: 2).tap()
|
||||
try await app.assertScreenshot(step: Step.inviteUsersSelectedResults)
|
||||
|
||||
app.buttons[A11yIdentifiers.inviteUsersScreen.proceed].tap()
|
||||
try await app.assertScreenshot(step: Step.dismissed)
|
||||
}
|
||||
}
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:71edb284338fe1a3d118141aa237617536b72a31d13333e7148dd2d33ccff1b1
|
||||
size 152332
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:9c6fcc3f0eea9bb87f6b5fa2c5b652496ca44eee846d3231e4c1446a1a3da2e3
|
||||
size 188761
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:c7a86c8bd9795786c49bace089439a3e8e0677bae255eba332b2175e89ab3f2b
|
||||
size 131545
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:660beea7152721b0ba27876b1f7d9e34035c21abc188c3ab3953552655041dca
|
||||
size 160050
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:2e8ebe176c64660475fdb46016a66ddaa502d30d1c60f6ccc4a52c05ca5412ed
|
||||
size 149193
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:4afad7f48503b599fb30a0e4c593079249d6c70f64770d53ce63398a89d245af
|
||||
size 183810
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:a1f07788317f9ed52b340b6e3233d50be4f82a047e6233d6abc656c6e1ba0c65
|
||||
size 167782
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:9543933266405ba35494e5bac5e1b403b0f388657b36b1689a3f27834dd27133
|
||||
size 164831
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:5201d5b8492dadaa23efd16a5e74ca96e23ce972abe4cd76a20533246a9381af
|
||||
size 198236
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:d0f8aff85f5930db29bc103b67ae2ea3bd793e6da755b74b3ec4bc3294a24664
|
||||
size 2332509
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:be0bc089efe8628afef744ec9e171a1d3f54b5ac81f3515369a58237c553da0a
|
||||
size 196815
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:02b9e0a8a3ae860c6afe7541021ec11529fb810f13ed7468ed4fbb87d7277e3e
|
||||
size 147056
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:6e2ec6a46ee642ad8235dc6cb777330bdeb8a240138409fa88fa9d1f3cc7516b
|
||||
size 166249
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:abe91d1332e9da181baa07576006a411ad36aa23dcd664341f9c1cf1e28c4a57
|
||||
size 175690
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:048a07df37bc493d91f67b5e1379ef571db047ab35780b28c23ee1990429846f
|
||||
size 222243
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:10eb8f7c10d0eb6a493c33fc223a96276897f5a8db969d7e07694c9872b47485
|
||||
size 122115
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:5bebd60c5ddaaf7d4af54563587c51f44816ac8fe5a621632a14374ca639b66b
|
||||
size 114369
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:f6a481a1eebb2bdc2a51b69da7867d330fd15b465ec66dda9a7775ea81bebc27
|
||||
size 170910
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:c0f51cb1e35bb75ae45f2f909e45f809febe00c86f7b7d88e63e9cdfb322043b
|
||||
size 2458946
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:ab217745f60435fc6251221d5650a25e4ce00153cf78ed72021e05c666d19703
|
||||
size 166171
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:7899f024d4aa043b2e318ef188bb992301aa02bc4a92fd5b59215e907272bde2
|
||||
size 88945
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:b788bbcc940303b16942de95cfcbb142e04ecc60634e409f091a20cf3fe8cef1
|
||||
size 116322
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:125138db4c413fa5ffc104694cb5400373d86faee69d0c755e2ffe992211f0ab
|
||||
size 128890
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:47d79566040b9e990cd222dc056d22de86723171364d60a0fdc3592dff9bb48e
|
||||
size 374177
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:d3e4caed4cef8b0ad70f747bb98a7e205c0b4f1c3df6083f341a9ac5bebe6e31
|
||||
size 108047
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:63a31b32e399472a8206f234665a11bb929403b5902856b85cd4f19373d0fd0a
|
||||
size 120156
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:3156d4c72c601cfbe7584cd73f516a4a32a95e6149dbd7ef31fdd4bba7d14fbf
|
||||
size 88278
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:8744cb36bbdd2afb7809f2682e00d6a3f2d1f3e2e6dfa329a50e97c76d8e9e63
|
||||
size 90212
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:b97677b7315b1d9298468da64358299ef0674644d3042fe23d9e10396131c92a
|
||||
size 101718
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:dd670a1f21b71bf5ddc7d317054145c2a049eb16fa09d6cd6f8be589d5ec45e9
|
||||
size 112321
|
||||
@@ -13,38 +13,28 @@ import XCTest
|
||||
|
||||
@MainActor
|
||||
class CreateRoomScreenViewModelTests: XCTestCase {
|
||||
var viewModel: CreateRoomViewModelProtocol!
|
||||
var viewModel: CreateRoomScreenViewModelProtocol!
|
||||
var clientProxy: ClientProxyMock!
|
||||
var userSession: UserSessionMock!
|
||||
|
||||
private let usersSubject = CurrentValueSubject<[UserProfileProxy], Never>([])
|
||||
|
||||
var context: CreateRoomViewModel.Context {
|
||||
var context: CreateRoomScreenViewModel.Context {
|
||||
viewModel.context
|
||||
}
|
||||
|
||||
override func setUpWithError() throws {
|
||||
clientProxy = ClientProxyMock(.init(userIDServerName: "matrix.org", userID: "@a:b.com"))
|
||||
clientProxy.roomForIdentifierClosure = { roomID in .joined(JoinedRoomProxyMock(.init(id: roomID))) }
|
||||
userSession = UserSessionMock(.init(clientProxy: clientProxy))
|
||||
let parameters = CreateRoomFlowParameters()
|
||||
ServiceLocator.shared.settings.knockingEnabled = true
|
||||
let viewModel = CreateRoomViewModel(userSession: userSession,
|
||||
createRoomParameters: .init(parameters),
|
||||
selectedUsers: [.mockAlice, .mockBob, .mockCharlie],
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: UserIndicatorControllerMock(),
|
||||
appSettings: ServiceLocator.shared.settings)
|
||||
let viewModel = CreateRoomScreenViewModel(userSession: userSession,
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: UserIndicatorControllerMock(),
|
||||
appSettings: ServiceLocator.shared.settings)
|
||||
self.viewModel = viewModel
|
||||
}
|
||||
|
||||
func testDeselectUser() {
|
||||
XCTAssertFalse(context.viewState.selectedUsers.isEmpty)
|
||||
XCTAssertEqual(context.viewState.selectedUsers.count, 3)
|
||||
XCTAssertEqual(context.viewState.selectedUsers.first?.userID, UserProfileProxy.mockAlice.userID)
|
||||
context.send(viewAction: .deselectUser(.mockAlice))
|
||||
XCTAssertNotEqual(context.viewState.selectedUsers.first?.userID, UserProfileProxy.mockAlice.userID)
|
||||
}
|
||||
|
||||
func testDefaultSecurity() {
|
||||
XCTAssertTrue(context.viewState.bindings.isRoomPrivate)
|
||||
}
|
||||
@@ -65,7 +55,7 @@ class CreateRoomScreenViewModelTests: XCTestCase {
|
||||
// When creating the room.
|
||||
clientProxy.createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLAliasLocalPartReturnValue = .success("1")
|
||||
let deferred = deferFulfillment(viewModel.actions) { action in
|
||||
guard case .openRoom("1") = action else { return false }
|
||||
guard case .createdRoom(let roomProxy) = action, roomProxy.id == "1" else { return false }
|
||||
return true
|
||||
}
|
||||
context.send(viewAction: .createRoom)
|
||||
|
||||
@@ -21,7 +21,10 @@ class InviteUsersScreenViewModelTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testSelectUser() {
|
||||
setupWithRoomType(roomType: .draft)
|
||||
let roomProxy = JoinedRoomProxyMock(.init(name: "newroom", members: []))
|
||||
roomProxy.inviteUserIDReturnValue = .success(())
|
||||
setupViewModel(roomProxy: roomProxy, isSkippable: true)
|
||||
|
||||
XCTAssertTrue(context.viewState.selectedUsers.isEmpty)
|
||||
context.send(viewAction: .toggleUser(.mockAlice))
|
||||
XCTAssertTrue(context.viewState.selectedUsers.count == 1)
|
||||
@@ -29,7 +32,10 @@ class InviteUsersScreenViewModelTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testReselectUser() {
|
||||
setupWithRoomType(roomType: .draft)
|
||||
let roomProxy = JoinedRoomProxyMock(.init(name: "newroom", members: []))
|
||||
roomProxy.inviteUserIDReturnValue = .success(())
|
||||
setupViewModel(roomProxy: roomProxy, isSkippable: true)
|
||||
|
||||
XCTAssertTrue(context.viewState.selectedUsers.isEmpty)
|
||||
context.send(viewAction: .toggleUser(.mockAlice))
|
||||
XCTAssertEqual(context.viewState.selectedUsers.count, 1)
|
||||
@@ -39,7 +45,10 @@ class InviteUsersScreenViewModelTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testDeselectUser() {
|
||||
setupWithRoomType(roomType: .draft)
|
||||
let roomProxy = JoinedRoomProxyMock(.init(name: "newroom", members: []))
|
||||
roomProxy.inviteUserIDReturnValue = .success(())
|
||||
setupViewModel(roomProxy: roomProxy, isSkippable: true)
|
||||
|
||||
XCTAssertTrue(context.viewState.selectedUsers.isEmpty)
|
||||
context.send(viewAction: .toggleUser(.mockAlice))
|
||||
XCTAssertEqual(context.viewState.selectedUsers.count, 1)
|
||||
@@ -52,7 +61,7 @@ class InviteUsersScreenViewModelTests: XCTestCase {
|
||||
let mockedMembers: [RoomMemberProxyMock] = [.mockAlice, .mockBob]
|
||||
let roomProxy = JoinedRoomProxyMock(.init(name: "test", members: mockedMembers))
|
||||
roomProxy.inviteUserIDReturnValue = .success(())
|
||||
setupWithRoomType(roomType: .room(roomProxy: roomProxy))
|
||||
setupViewModel(roomProxy: roomProxy, isSkippable: false)
|
||||
|
||||
let deferredState = deferFulfillment(viewModel.context.$viewState) { state in
|
||||
state.isUserSelected(.mockAlice)
|
||||
@@ -66,8 +75,6 @@ class InviteUsersScreenViewModelTests: XCTestCase {
|
||||
switch action {
|
||||
case .dismiss:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,12 +84,12 @@ class InviteUsersScreenViewModelTests: XCTestCase {
|
||||
XCTAssertEqual(roomProxy.inviteUserIDReceivedInvocations, [RoomMemberProxyMock.mockAlice.userID])
|
||||
}
|
||||
|
||||
private func setupWithRoomType(roomType: InviteUsersScreenRoomType) {
|
||||
private func setupViewModel(roomProxy: JoinedRoomProxyProtocol, isSkippable: Bool) {
|
||||
userDiscoveryService = UserDiscoveryServiceMock()
|
||||
userDiscoveryService.searchProfilesWithReturnValue = .success([])
|
||||
let viewModel = InviteUsersScreenViewModel(userSession: UserSessionMock(.init()),
|
||||
selectedUsers: nil,
|
||||
roomType: roomType,
|
||||
roomProxy: roomProxy,
|
||||
isSkippable: isSkippable,
|
||||
userDiscoveryService: userDiscoveryService,
|
||||
userIndicatorController: UserIndicatorControllerMock(),
|
||||
appSettings: ServiceLocator.shared.settings)
|
||||
|
||||
@@ -56,7 +56,7 @@ class StartChatScreenViewModelTests: XCTestCase {
|
||||
try await deferredViewState.fulfill()
|
||||
|
||||
let deferredAction = deferFulfillment(viewModel.actions) { action in
|
||||
action == .showRoom(withIdentifier: "id")
|
||||
action == .showRoom(roomID: "id")
|
||||
}
|
||||
context.send(viewAction: .joinRoomByAddress)
|
||||
try await deferredAction.fulfill()
|
||||
|
||||
Reference in New Issue
Block a user